计算机发展至今已经有了机器语言、汇编语言和高级语言三种。高级语言被翻译成机器语言的过程分为两类,第一种是编译,第二种是解释。
编译执行的程序在执行前,会先通过编译器进行编译,把高级语言转变为机器语言,然后再执行;解释执行的程序在执行时,由解释器对程序逐行解释成机器语言,一边解释一边执行。
python是一种脚本语言,但python“翻译”的过程中,不仅需要解释还需要编译,这是因为python是一门基于虚拟机的语言。
一般我们都是说python通过python解释器解释执行,但实际上python是利用字节码虚拟机解释执行的。python先把代码(.py文件)编译成字节码,字节码存放在内存中的PyCodeObject对象中,然后将交给字节码虚拟机,然后虚拟机会从编译得到的PyCodeObject对象中一条一条执行字节码指令。当Python程序运行结束时,Python解释器则将PyCodeObject写入到.pyc文件中,.pyc文件在磁盘中保存。当python程序第二次运行时,首先程序会在硬盘中寻找.pyc文件,如果找到,则直接载入,不需要编译就能载入python虚拟机执行,否则就重复上面的过程。
所以,python是先编译然后再解释执行的。
py to exe用的是pyinstaller
重点!!!
py to exe的二进制特征:
py生成的可执行程序核心是一个bootloader,这个bootloader具有如下核心代码
字符串”_MEIPASS2”是pyinstaller使用的库,而pkg类型是安装文件类型。
我们也可以直接从strings中判断,可以发现py生成的可执行程序中含有大量”Py”开头的字符串:
exe to python
反编译pyinstaller 产生的可执行文件有很多种方式,大体上可以分为两个步骤,一是由可执行文件获取pyc(pyo)文件,二是由pyc(pyo)文件得到py文件。
反编译python生成的exe需要用到pyinstaller库里的archive_viewer.py脚本。archive_viewer.py一共有四个可用命令:
U: go Up one level
O : open embedded archive name
X : extract name
Q: quit
archive_viewer.py对可执行程序进行解包分析,这里涉及到了好几个类型的文件,简单说明一下:
py是源文件,
pyc是源文件编译后的文件
pyo是源文件优化编译后的文件
pyz是一个压缩包,包含程序运行需要的所有依赖
pyd是其他语言写的python库
我们需要关注的是”helloworld”和”PYZ-00.pyz”这两个文件。一般来说*.pyz,其中包含主程序引用到的所有库,可以提取.pyz文件然后继续使用archive_viewer.py解包分析;另一个是主程序对应的文件名的文件,其中会包含主程序。
使用x命令提取主程序,另存为helloworld.pyc:
在helloworld.pyc文件头,补充8个字节:
需要补充8个字节是因为pyinstaller编译成pyc时,会把pyc的magic value去掉。magic value一共8个字节,前四个对应于编译时所用python的版本,后四个对应于编译时间,比如python2.7 的03 f3 0d 0a 01 23 45 67,python3.4 的ee 0c 0d 0a 01 23 45 67。
最后使用easy python decompiler从pyc(pyo)文件得到py文件,直接将pyc文件拖拽进程序。
实践:
首先安装pyinstaller
pip install pyinstaller
我最后的安装路径C:\Users\用户名\AppData\Local\Programs\Python\Python39\Scripts
随便写一个py文件
#just a test
print("hello world!")
打包成exe命令
pyinstaller -F -w C:\Users\solitaryang\Desktop\testpy.py
可以看到exe出来了,用die看一下:
入口函数:
特征字符串:
我的archive_viewer.py位置:
C:\Users\solitaryang\AppData\Local\Programs\Python\Python39\Lib\site-packages\PyInstaller\utils\cliutils
提取出来的文件放在了C:\Users\用户名下
前后pyc文件对比:
前:
后:
对文件头进行修复,在前添加610D0D0A00000000C2F6EC6024000000
最后一步从pyc得到py文件,方法很多
1、https://tool.lu/pyc/用这个网站在线还原
2、使用uncompyle
3、使用其他工具,例如easy python decompiler
这里我用第二种方法试试
先安装pip install uncompyle6
说明:
-o后面可以加上文件路径,代表反编译文件输出的位置,“.”表示输出到当前文件夹
反编译成功后会返回“# Successfully decompiled file”
可以使用uncompyle6 --help查看其他参数
然后我发现我是3.9。。。
试了很多种方法都不适合3.9.。。版本太高不是啥好事
这里劝各位小伙伴,做实验的时候用低版本的python吧。。。