涉及到的代码见: https:// github.com/loopyme/ulan -uncompile
大家都在说木兰编译器是在水项目,但我感觉很多人啥也不知道跟着黑,你随机抽样几个网友出来很有可能都解释不清楚Parser和lexer.所以我在家躺着也是躺着,还不如拆开木兰编译器看看源码,是好是坏拆开看.
(写在前面)读完源码我觉得:
木兰编译器按工作量可以算是一个'大型小项目',也有一定技术含量,很可能是几个同学(不超过三个)一起写的,并且具有浓厚的大力出奇迹的风格,这种风格在高校实验室代码里比较常见.代码质量和我小项目第一遍写出来差不多,应该是还没有整理重构过,全是查文档查资料跑通就行的那种.作者(团队)应该是熟读了rply和codegen的文档和教程,并修一修,补一补,写一写成了这个项目.
形象的说木兰编译器就是:有一群人找到个轮子,仔细读了读外胎的说明书,造了个外胎给轮子换上.内行觉得这外胎换了没啥意义,还没原来轮子好用,很多外行跟着起哄,以为木兰只是在原来的轮胎上贴了一层膜.
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)
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 > ./ula