【Python逆向】逆向Pyinstaller打包程序源码并写在上面的保护
我们都知道,我们可以使用Pyinstaller库将.py文件编译成.exe文件并运行。 在这篇文章中,我们将把脚本编译成.exe,并将.exe的源代码内容反编译成源文件,然后讲一下如何避免被逆向。
环境工具
Python3.6:https://www.python.org/downloads/release/python-360/
Pyinstaller库:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyinstaller==4.0
pyinstxtractor.py:下载地址:https://sourceforge.net/projects/pyinstallerextractor/
WinHex编辑器:下载地址:https://down.52pojie.cn/Tools/Editors/WinHex_v19.9.zip
uncompyle库:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple uncompyle6==3.9.0
tinyaes库:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple tinyaes==1.0.3
编译器检测环境
蟒蛇-V
写脚本
测试脚本.py
import datetime
# 函数
def test():
print("====反编译源码的测试脚本====")
input_text = input("请输入你想要打印的内容:")
# 打印格式化时间和用户输入的内容
print(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S ") + input_text)
if __name__ == '__main__':
# 调用
test()
包装工
首先安装打包器使用的库 pyinstaller。 这里使用的是北大的源码,并指定了4.0版本的库。
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyinstaller==4.0
找到脚本所在文件夹,我的是C:UsersYRJDesktopacd到这个路径,输入以下打包命令:
Pyinstaller -F 测试脚本.py
命令执行后,您将听到“完成成功”。 字段,表示.exe文件生成成功,位于dist文件夹中。
逆过程
首先我们下载反编译工具pyinstxttractor.py并将其放入与我们要反编译的.exe文件同一工作目录下,如下图:
然后我们继续在命令行cd到dist文件夹,输入以下命令并执行:
python pyinstxtractor.py 测试脚本.exe
执行完成后,如果看到Successful字样,就会生成测试脚本.exe_extracted文件夹,如下图:
进入这个文件夹,里面有很多后缀为.dll和.pyd的文件,还有一个名为PYZ-00.pyz_extracted的文件夹,里面包含了程序引入的依赖库。 如果导入的是自己的其他.py文件,则可以用类似的方式反编译依赖的.py文件,如下图所示:
在该目录中,我们将找到与 .exe 文件同名的结构体和文件。 如下所示:
这两个文件是否带有.pyc后缀与你使用的pyinstxttractor.py工具的版本有关。 V2.0之前的版本会生成两个没有.pyc后缀的文件,只需手动添加.pyc后缀即可。 如下所示:
当前的测试脚本 .pyc 文件没有 Magic Number。 我们需要根据Python版本来完成。 从上图可以看出,打包后的程序的Python版本为3.6。 接下来,我们需要检查3.6版本的Magic Number。
检查Magic Number的方法1:
查一下总体对比表:
enum PycMagic {
MAGIC_1_0 = 0x00999902,
MAGIC_1_1 = 0x00999903,
MAGIC_1_3 = 0x0A0D2E89,
MAGIC_1_4 = 0x0A0D1704,
MAGIC_1_5 = 0x0A0D4E99,
MAGIC_1_6 = 0x0A0DC4FC,
MAGIC_2_0 = 0x0A0DC687,
MAGIC_2_1 = 0x0A0DEB2A,
MAGIC_2_2 = 0x0A0DED2D,
MAGIC_2_3 = 0x0A0DF23B,
MAGIC_2_4 = 0x0A0DF26D,
MAGIC_2_5 = 0x0A0DF2B3,
MAGIC_2_6 = 0x0A0DF2D1,
MAGIC_2_7 = 0x0A0DF303,
MAGIC_3_0 = 0x0A0D0C3A,
MAGIC_3_1 = 0x0A0D0C4E,
MAGIC_3_2 = 0x0A0D0C6C,
MAGIC_3_3 = 0x0A0D0C9E,
MAGIC_3_4 = 0x0A0D0CEE,
MAGIC_3_5 = 0x0A0D0D16,
MAGIC_3_5_3 = 0x0A0D0D17,
MAGIC_3_6 = 0x0A0D0D33,
MAGIC_3_7 = 0x0A0D0D42,
MAGIC_3_8 = 0x0A0D0D55,
MAGIC_3_9 = 0x0A0D0D61,
};
例如:Python 3.6的Magic Number,执行后可以得到0x0A0D0D33,对应的二进制码为33 0D 0D 0A
方法2(推荐):
查看struct.pyc文件:
用WinHex编辑器打开struct.pyc文件,其前4个字节为magic num,与方法1的二进制代码相同。如下图:
然后打开测试script.pyc和struct.pyc对比头部内容,确定需要添加的内容,如下图:
更改.pyc文件
选择struct.pyc中框出的头内容,右键编辑→复制选中的块→十六进制值。 330D0D0A7079693001010000
打开测试脚本.pyc,直接添加,上面复制的十六进制值为330D0D0A7079693001010000,光标点击第一个字母E,右键编辑→剪贴板数据→粘贴→ASCII Hex→确定,然后保存反编译qt源码,更改为完全的! 如下所示:
转换源代码
将 .pyc 使用的库 uncompyle6 安装到 .py。 这里使用的是北大的源码,并指定了3.9.0版本库。
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple uncompyle6==3.9.0
找到测试脚本.pyc所在文件夹,cd到该路径,输入以下打包命令:
uncompyle6 -o 测试脚本.py 测试脚本.pyc
命令执行后,会听到successful的数组,说明在同路径下已经成功生成了.py源代码文件。
比较源码
通过将程序的原始源代码与逆向工程得到的源代码进行对比,可以看出,简单的源代码基本相同,而比较复杂的源代码可能会有一点瑕疵。
就这个逆向程序而言,源代码注释比程序原始源代码少,更多的是关于系统反编译时的环境和版本的注释。
通过命令python test script.py执行反向源码,测试通过,发现功能正常。
防止pyinstaller反向打包
首先安装加密打包器使用的库tinyaes。 这里使用的是北大的源码,并指定了1.0.3版本的库。
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple tinyaes==1.0.3
找到脚本所在文件夹,我的是C:UsersYRJDesktopccd到这个路径,输入以下打包命令:
pyinstaller -F 测试脚本.py --key 123456
命令执行后,您将听到“完成成功”。 字段,表示.exe文件生成成功,位于dist文件夹中。
然后按照上面写的相反过程执行命令 python pyinstxttractor.py test script.exe 。 结果只有入口脚本反编译成功,依赖脚本全部被加密,无法直接反编译,如下图:
PYZ-00.pyz_extracted 非常重要。 一般稍微大一点的项目都会被分成多个py文件,甚至会依赖其他模块。 解析完之后,这些依赖文件会被加载到PYZ-00.pyz_extracted中,可以说这里就是核心代码。
您可以在 PYZ-00.pyz_extracted 文件夹中看到它。 提取的中间结果是.pyc.加密格式,无法直接反编译。 未加密和加密的相关对比如下图所示:
用常规手段很难直接反编译。 这时候如果你还想反编译反编译qt源码,就需要做底层逆向分析研究或者完整研究pyinstaller的源码,了解其加密处理机制,看看是否有逆向工程的可能。
注意:不要使用这种方法来保护只有一个.py文件的python源代码,因为main函数的入口脚本编译成功。 按照逆向流程章节操作后,将看到入口脚本为源代码。
努特卡包
首先安装打包器使用的库 nuitka。 这里使用的是北大的源码,并指定了1.3.7版本的库。
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple nuitka==1.3.7
找到脚本所在文件夹,我的是C:UsersYRJDesktopacd到这个路径,选择并输入以下你需要的打包命令:
nuitka --standalone --mingw64 测试脚本.py
nuitka --standalone --onefile 测试脚本.py #打包成一个exe单文件
nuitka --standalone --mingw64 --follow-imports 测试脚本.py
nuitka --standalone --remove-output --mingw64 测试脚本.py
nuitka --standalone --mingw64 --nofollow-imports --show-memory --show-progress --plugin-enable=qt-plugins --follow-import-to=lib --recurse-all --output-dir=o 测试脚本.py
打包过程中会提示缺少可执行程序gcc,根据要求直接安装即可。 是的,文件比较大,命令行可能下载不流畅或者不流畅。 我们可以直接复制下载位置和下载地址,如下图:
下载位置:
C:UsersYRJAppDataLocalNuitkaNuitkaCachedownloadsgccx86_6411.3.0-14.0.3-10.0.0-msvcrt-r3
下载地址:
https://github.com/brechtsanders/winlibs_mingw/releases/download/11.3.0-14.0.3-10.0.0-msvcrt-r3/winlibs-x86_64-posix-seh-gcc-11.3.0-llvm-14.0.3-mingw-w64msvcrt-10.0.0-r3.zip
使用浏览器访问下载地址并连接到下载位置的目录并解压,如下图:
接下来我们继续执行打包命令:nuitka --standalone --mingw64 test script.py。 打包过程中会提示缺少执行程序ccache和depends。 这个文件比较小,根据提示直接安装即可。 下载完成后会手动解压到路径:
下载位置:
C:UsersYRJAppDataLocalNuitkaNuitkaCachedownloadsccachev4.6ccache.exe
下载地址:
https://github.com/ccache/ccache/releases/download/v4.6/ccache-4.6-windows-32.zip
下载位置:
C:UsersYRJAppDataLocalNuitkaNuitkaCachedownloadsdependsx86_64
下载地址:
https://dependencywalker.com/depends22_x64.zip
至此,使用nuitka将python代码打包成.exe的过程就结束了。
验证执行
执行打包好的.exe程序,命令行退出出现乱码,如下图:
解决办法是:在main函数第一行添加os.system("chcp 65001 && cls")
再次打包执行打包好的.exe程序,问题解决,如下图:
但这又导致了另一个错误。 Python源码中的input(“请输入您要复制的内容:”)需要在命令行中输入。 当我输入中文和数字时,复制输出正常。 但是,当我输入英文时,会出现异常、死机的情况。 我暂时没有相关的解决方案......
输入英文会出现异常并崩溃解决办法:
将当前使用的Python3.6.0版本更改为Python3.8.0及以上即可解决问题。
我使用的环境及运行测试图如下:
C:Usersadmin>nuitka --version
1.4
Commercial: None
Python: 3.8.8 (tags/v3.8.8:024d805, Feb 19 2021, 13:18:16) [MSC v.1928 64 bit (AMD64)]
Flavor: CPython Official
Executable: C:UsersPythonPython38python.exe
OS: Windows
Arch: x86_64
WindowsRelease: 10
Version C compiler: ~AppDataLocalNuitkaNuitkaCachedownloadsgccx86_6411.3.0-14.0.3-10.0.0-msvcrt-r3mingw64bingcc.exe (gcc).
结束
您的每一次分享、点赞、观看都是我的动力,谢谢!