「木蘭コンパイラ」を逆コンパイルし、ソースコードを分析
41684 ワード
関連するコードは以下の通りです.https://github.com/loopyme/ulan-uncompile
みんなはすべて木蘭のコンパイラが水のプロジェクトにあると言っていますが、私は多くの人が何も知らないと感じています.あなたはランダムに何人かのネットユーザーをサンプリングして、Parserとlexerを説明できない可能性が高いです.だから私は時間を見つけて、木蘭のコンパイラを分解してソースコードを見て、良いか悪いかを分解して見ます.
1. 逆コンパイル 1.1 exeコンテンツの抽出 1.2 pycファイルのパッチ 1.3逆コンパイルpycファイル 2. ソース分析 2.1プロジェクト構造 2.2 ulang.parser 2.2.1 ulang.parser.lexer 2.2.2 ulang.parser.core 2.2.3 ulang.parser.lrparserとulang.parser.parsergenerator 2.2.4 ulang.parser.error 2.3 ulang.CodeGen 2.3.1 ulang.CodeGen.ulgen 2.3.2 ulang.CodeGen.blockly 2.4 ulang.runtime 3.まとめ 4.免責声明 木蘭コンパイラはpythonの先端を変えたと思いますが、少なくとも私が思っていたシール(
木蘭のコンパイラは、車輪を見つけて、タイヤの説明書をよく読んで、タイヤを作って車輪に交換した.玄人はこのタイヤを交換しても意味がないと思っています.まだ車輪が使えないかもしれません.多くの素人が騒いでいます.木蘭は元のタイヤに膜を貼っただけだと思っています.
木蘭コンパイラはPyInstallerでパッケージ化されたpythonプロジェクトであることがわかり、このexeを逆コンパイルする考え方がはっきりしています
pyinstxtractorでPyInstallerで生成されたWindows実行可能ファイルの内容を簡単に抽出できます
PyInstallerはpycファイルのmagicとタイムスタンプを食べてしまうのでstructファイルから上位8バイトを取ってpycファイルの前に戻す必要があります.
そして手動で調整すると大成功!
このように逆コンパイルされたコードは実際には走れないが、debugはいくつかの場所で小さな問題が発生していることを発見し、これは私がソースコードを読む大まかな考え方に影響を与えない.
この項目の主な外部依存は
資料を調べてみると、著者は
Lexerは
rplyドキュメントをよく読むと、このファイルのコードは基本的に理解でき、ファイル全体の考え方ははっきりしているが、作業量は比較的大きい(
総じて言えば、作者はこの辺りで大きな精力を払うべきだと思います.
この二人は微妙だ.私が読んだときはちょっと変わった感じで、わざわざ調べてみると
私はコンパクト数と推測するしかありません(作者の環境が合わないかもしれませんがimportはできません)
総じて言えば、
このファイルはpython astをblockly xmlに変換します.たぶんvisitツリーノードで、ノードのタイプに応じて変換します.面白くて、仕事量も大きいです.
このモジュールの中でコードは特に悪くて、プロジェクト全体の力を十分に発揚して奇跡の風格を出して、あまり表現しなくて、直接コードを節選しました:
私はこのように書く(私にとって)少し楽しくて、バグを修理したような気がします.
次のように思います.
木蘭コンパイラは仕事量によって1つの“大型の小さいプロジェクト”と言えることができて、一定の技術の含有量もあって、何人かの同級生(3つを超えない)が一緒に書いたのかもしれなくて、しかも濃厚な奇跡の風格を持って、このような風格は高校の実験室のコードの中で比較的によく見られます.コードの品質は私の小さなプロジェクトの第1回の書き方とあまり悪くなくて、まだ整理して再構築したことがないはずで、すべてドキュメントを調べて資料を調べて走って通せばいいのです.著者(チーム)は
大きな問題は、他の公開庫の書類を3つ梱包して入り、名前を強引に変えて内容を変えなかったことだ.
総じて言えば、pythonに先端を変えたのですが、少なくとも私が思っていたシール(
私はいかなる会社、集団にも属していません.リバースエンジニアリングの時に使用するすべての参考情報はネット公開資料から合法的に獲得した.逆工程後の一部のコードと論理は完全に推測によって完成し、具体的なコードは原木蘭言語の実現と何の関係もなく、原木蘭プロジェクトの機能とコード品質を代表しない.本文のすべての観点と分析は個人の推測であり、実際の状況と大きな違いがある可能性がある.
みんなはすべて木蘭のコンパイラが水のプロジェクトにあると言っていますが、私は多くの人が何も知らないと感じています.あなたはランダムに何人かのネットユーザーをサンプリングして、Parserとlexerを説明できない可能性が高いです.だから私は時間を見つけて、木蘭のコンパイラを分解してソースコードを見て、良いか悪いかを分解して見ます.
文書ディレクトリ
eval
で実現したもの)を貼ったわけではないので、面白い小さなプロジェクトだと思います.ただ、「国産コンパイラ」の名前が大きすぎて、世論が加わったので、車をひっくり返しました.しかし、私がこのようなプロジェクトを書いたら(しかもプロジェクトに文字数の書類を追加しなかったら)、私は誇りに思っています.少なくともズボンを脱いでおならをしたpymips
よりどこに行ったのか分かりません.木蘭のコンパイラは、車輪を見つけて、タイヤの説明書をよく読んで、タイヤを作って車輪に交換した.玄人はこのタイヤを交換しても意味がないと思っています.まだ車輪が使えないかもしれません.多くの素人が騒いでいます.木蘭は元のタイヤに膜を貼っただけだと思っています.
1.逆コンパイル
木蘭コンパイラはPyInstallerでパッケージ化されたpythonプロジェクトであることがわかり、このexeを逆コンパイルする考え方がはっきりしています
1.1 exeコンテンツの抽出
pyinstxtractorでPyInstallerで生成されたWindows実行可能ファイルの内容を簡単に抽出できます
python ./tools/pyinstxtractor.py ./ulang-0.2.2.exe
1.2 pycファイルの修正
PyInstallerはpycファイルのmagicとタイムスタンプを食べてしまうのでstructファイルから上位8バイトを取ってpycファイルの前に戻す必要があります.
# ./tools/add_header.py
import os
with open("./ulang-0.2.2.exe_extracted/struct", "rb") as f:
header = f.read()[:4]
for filename in os.listdir("./ulang-0.2.2.exe_extracted/PYZ-00.pyz_extracted"):
if 'ulang' not in filename:
continue
with open("./ulang-0.2.2.exe_extracted/PYZ-00.pyz_extracted/" + filename, "rb") as f:
data = f.read()
with open("./pyc/" + filename, "wb") as f:
f.write(header + data)
mkdir ./pyc/
python ./tools/add_header.py
1.3 pycファイルの逆コンパイル
uncompyle6
で直接pycファイルを反コンパイルすることができて、下のshについては、私はまずls
に行って、それから熟練した列の操作でvs code
の中で整然とした指令を貼り付けます.mkdir ./ulang/
mkdir ./ulang/codegen/
mkdir ./ulang/parser/
mkdir ./ulang/runtime/
pip install uncompyle6
uncompyle6 ./pyc/ulang.codegen.blockly.pyc > ./ulang/codegen/blockly.py
uncompyle6 ./pyc/ulang.codegen.pyc > ./ulang/codegen/__init__.py
uncompyle6 ./pyc/ulang.codegen.python.pyc > ./ulang/codegen/python.py
uncompyle6 ./pyc/ulang.codegen.ulgen.pyc > ./ulang/codegen/ulgen.py
uncompyle6 ./pyc/ulang.parser.core.pyc > ./ulang/parser/core.py
uncompyle6 ./pyc/ulang.parser.error.pyc > ./ulang/parser/error.py
uncompyle6 ./pyc/ulang.parser.lexer.pyc > ./ulang/parser/lexer.py
uncompyle6 ./pyc/ulang.parser.lrparser.pyc > ./ulang/parser/lrparser.py
uncompyle6 ./pyc/ulang.parser.parsergenerator.pyc > ./ulang/parser/parsergenerator.py
uncompyle6 ./pyc/ulang.parser.pyc > ./ulang/parser/__init__.py
uncompyle6 ./pyc/ulang.pyc > ./ulang/__init__.py
uncompyle6 ./pyc/ulang.runtime.env.pyc > ./ulang/runtime/env.py
uncompyle6 ./pyc/ulang.runtime.main.pyc > ./ulang/runtime/main.py
uncompyle6 ./pyc/ulang.runtime.pyc > ./ulang/runtime/__init__.py
uncompyle6 ./pyc/ulang.runtime.repl.pyc > ./ulang/runtime/repl.py
そして手動で調整すると大成功!
このように逆コンパイルされたコードは実際には走れないが、debugはいくつかの場所で小さな問題が発生していることを発見し、これは私がソースコードを読む大まかな考え方に影響を与えない.
2.ソース分析
2.1プロジェクト構造
.
├── __init__.py
├── main.py
├── CodeGen
│ ├── __init__.py
│ ├── blockly.py
│ ├── python.py*
│ └── ulgen.py
├── parser
│ ├── __init__.py
│ ├── core.py
│ ├── error.py
│ ├── lexer.py
│ ├── lrparser.py*
│ ├── parsergenerator.py*
└── runtime
├── __init__.py
├── env.py
├── main.py
└── repl.py
*:
この項目の主な外部依存は
ast
,rply
,codegen
である.2.2 ulang.parser
ulang.parser.core.Parser
注記:A simple LR(1)parser to parse the source code of mu and yield the python ast for later using…(muのソースコードを解析しpython astを生成して後で使用するための簡単なLR(1)解析器.)資料を調べてみると、著者は
rply
の文書を熟読したはずだと推測し、文書指導に基づいてParserとLexerを実現した.以下は具体的な分析である.2.2.1 ulang.parser.lexer
ulang.parser.lexer
セグメント:lg.add('IDENTIFIER', '\\$?[_a-zA-Z][_a-zA-Z0-9]*')
lg.add('DOTDOTDOT', '\\.\\.\\.')
lg.add('DOTDOTLT', '\\.\\.)
lg.add('DOTDOT', '\\.\\.')
lg.add('DOT', '\\.')
lg.add('DOLLAR', '\\$')
lg.add('[', '\\[')
lg.add(']', '\\]')
lg.add('(', '\\(')
Lexerは
rply.LexerGenerator
教程のサンプルコードを複数回コピーして貼り付ける方式を用いて、すべての文法規則を加えて、文法分析を実現したと推測する.2.2.2 ulang.parser.core
ulang.parser.core
は比較的硬い核(結局core
)のように見え、多くの関数、装飾器などがあふれており、rply.parsergenerator
に基づいてParserを生成している.rplyドキュメントをよく読むと、このファイルのコードは基本的に理解でき、ファイル全体の考え方ははっきりしているが、作業量は比較的大きい(
ulang.parser.lexer
の場合と少し似ている).例えば複雑で冗長な装飾器のように見えますが、実はすべてrply.parsergenerator.production
でterminals(tokens)&non-terminalsシーケンスを指定しています.総じて言えば、作者はこの辺りで大きな精力を払うべきだと思います.
2.2.3 ulang.parser.lrparserとulang.parser.parsergenerator
この二人は微妙だ.私が読んだときはちょっと変わった感じで、わざわざ調べてみると
rply.parser
とrply.parsergenerator
のコピーだった私はコンパクト数と推測するしかありません(作者の環境が合わないかもしれませんがimportはできません)
2.2.4 ulang.parser.error
SyntaxError
を実現しました.中規中拠だと思います.2.3 ulang.CodeGen
ulang.CodeGen.blockly.CodeGen
注記:A simple python ast to blockly xml converter.(簡単なpython astからblockly xmlへの変換器)ulang.parser
コードのスタイルとは感じが違います.ulang.CodeGen
のファイルはそれぞれ実現されました.ulang.CodeGen.ulgen
: python ast -> ulang ulang.CodeGen.blockly
: python ast -> blockly xml ulang.CodeGen.python
はcodegen.codegen
のコピーです総じて言えば、
ulang.CodeGen
はulang.parser
より仕事量が悪いとは思いませんが、もっと面白いです.2.3.1 ulang.CodeGen.ulgen
codegen.codegen
に従って瓢箪を描き、さらにulangの構想に基づいて調整するとulang.CodeGen.ulgen
が得られ、その中でも作業量は大きい.2.3.2 ulang.CodeGen.blockly
このファイルはpython astをblockly xmlに変換します.たぶんvisitツリーノードで、ノードのタイプに応じて変換します.面白くて、仕事量も大きいです.
2.4 ulang.runtime
このモジュールの中でコードは特に悪くて、プロジェクト全体の力を十分に発揚して奇跡の風格を出して、あまり表現しなくて、直接コードを節選しました:
ulang.runtime.repl
節:# ( ,[(]) )
unclosed = []
unmatched = [0, 0, 0]
last = 2 * ['']
for tok in tokens:
c = tok.gettokentype()
last[0], last[1] = last[1], c
if c in keywords:
unclosed.append(c)
if c == 'LBRACE':
unmatched[0] += 1
elif c == 'RBRACE':
unmatched[0] -= 1
if len(unclosed):
unclosed.pop(-1)
elif c == '(':
unmatched[1] += 1
elif c == ')':
unmatched[1] -= 1
elif c == '[':
unmatched[2] += 1
elif c == ']':
unmatched[2] -= 1
unmatched_sum = sum(unmatched)
unclosed_sum = len(unclosed)
if unclosed_sum > 0:
if unmatched_sum == 0:
if last[1] == 'NEWLINE':
if (last[0] == 'NEWLINE' or last[0]) == ';':
pass
return True
return unclosed_sum == 0 and unmatched_sum == 0
私はこのように書く(私にとって)少し楽しくて、バグを修理したような気がします.
SYMBOLS = {"}": "{", "]": "[", ")": "(", "RBRACE": "LBRACE"}
last = [""] * 2
unmatched = []
unclosed = []
for tok in tokens:
c = tok.gettokentype()
last[0], last[1] = last[1], c
if c in keywords:
unclosed.append(c)
elif c == "RBRACE" and unclosed:
unclosed.pop()
if c in SYMBOLS.values(): # left/right symbol
unmatched.append(c)
elif c in SYMBOLS.keys() and unmatched.pop() != SYMBOLS[c]:
return False
return (
# NEWLINE
unclosed
and not unmatched
and last[1] == "NEWLINE"
and last[0] not in ["NEWLINE", ";"]
) or (
# closed and matched
not unclosed
and not unmatched
)
ulang.runtime.env
節:# ( )
return {
"print" : local_print,
"println" : lambda *objs: local_print(*objs, **{"end":"
"}),
"assert" : local_assert,
"len" : len,
"enumerate" : enumerate,
"all" : all,
"any" : any,
"range" : range,
"round" : round,
"input" : input,
"reverse" : reversed,
"super" : super,
"locals" : lambda: locals(),
"bool" : bool,
"float" : float,
"int" : int,
"str" : str,
"list" : list,
"dict" : dict,
"set" : set,
"tuple" : lambda *args: args,
"char" : chr,
"ord" : ord,
"bytes" : lambda s, encoding="ascii":bytes(s, encoding),
"typeof" : lambda x: x.__class__.__name__,
"isa" : lambda x, t: isinstance(x, t),
"max" : max,
"min" : min,
"map" : map,
"filter" : filter,
"zip" : zip,
"staticmethod" : staticmethod,
"property" : property,
"ceil" : math.ceil,
"floor" : math.floor,
"fabs" : math.fabs,
"sqrt" : math.sqrt,
"log" : math.log,
"log10" : math.log10,
"exp" : math.exp,
"pow" : math.pow,
"sin" : math.sin,
"cos" : math.cos,
"tan" : math.tan,
"asin" : math.asin,
"acos" : math.acos,
"atan" : math.atan,
"spawn" : builtin_spawn,
"kill" : builtin_kill,
"self" : builtin_self,
"quit" : sys.exit,
"open" : open,
"install" : pip_install,
"time" : time.time,
"year" : lambda: datetime.now().year,
"month" : lambda: datetime.now().month,
"day" : lambda: datetime.now().day,
"hour" : lambda: datetime.now().hour,
"minute" : lambda: datetime.now().minute,
"second" : lambda: datetime.now().second,
"microsecond" : lambda: datetime.now().microsecond,
"sleep" : time.sleep,
"delay" : lambda ms: time.sleep(ms / 1000),
"delayMicroseconds" : lambda us: time.sleep(us / 1000000),
"PI" : math.pi,
"ARGV" : argv,
"__builtins__" : fix_builtins(
{
"__import__" : local_import,
"__build_class__" : __build_class__,
"__name__" : "__main__",
"__file__" : fname,
"__print__" : eval_print,
"___" : None,
"__div__" : __builtin_div,
"__rem__" : __builtin_rem,
}
),
}
3.まとめ
次のように思います.
木蘭コンパイラは仕事量によって1つの“大型の小さいプロジェクト”と言えることができて、一定の技術の含有量もあって、何人かの同級生(3つを超えない)が一緒に書いたのかもしれなくて、しかも濃厚な奇跡の風格を持って、このような風格は高校の実験室のコードの中で比較的によく見られます.コードの品質は私の小さなプロジェクトの第1回の書き方とあまり悪くなくて、まだ整理して再構築したことがないはずで、すべてドキュメントを調べて資料を調べて走って通せばいいのです.著者(チーム)は
rply
とcodegen
のドキュメントとチュートリアルを熟読し、修理し、補完し、このプロジェクトを書いたはずだ.大きな問題は、他の公開庫の書類を3つ梱包して入り、名前を強引に変えて内容を変えなかったことだ.
総じて言えば、pythonに先端を変えたのですが、少なくとも私が思っていたシール(
eval
で実現したもの)を付けたわけではないので、面白い小さなプロジェクトですが、「国産コンパイラ」の名前が大きすぎて世論が加わったので、車をひっくり返しました.しかし、私がこのようなプロジェクトを書いたら(しかもプロジェクトに文字数の書類を追加しなかったら)、私は誇りに思っています.少なくともズボンを脱いでおならをしたpymips
より高いです.どこへ行ったのか分かりません.4.免責声明
私はいかなる会社、集団にも属していません.リバースエンジニアリングの時に使用するすべての参考情報はネット公開資料から合法的に獲得した.逆工程後の一部のコードと論理は完全に推測によって完成し、具体的なコードは原木蘭言語の実現と何の関係もなく、原木蘭プロジェクトの機能とコード品質を代表しない.本文のすべての観点と分析は個人の推測であり、実際の状況と大きな違いがある可能性がある.