简介
当一个应用内置了Python解释器(一些打包工具[Pyinstaller、pyarmor]、游戏引擎[游戏逻辑依赖Python])时,可以通过这种方式注入自定义代码,从而获取到应用的Python相关源代码
尝试注入Pyinstaller打包的文件
写一个测试用的test.py然后给它打包


运行test.py

然后写一个code.py,和exe放到同一目录下,Pyinjector注入时会自动注入这个文件

然后通过Process Hacker 2工具注入Pyinjector的x64 dll


注入效果

现在有了dis就可以借助uncompyle6等工具还原Python源代码了,简单分析的话直接看dis结果也可以
Pyinjector源码分析
Pyinjector仓库
其实这个项目非常简单,只需要关注两个文件SDK.cpp和dllmain.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| void SDK::InitCPython() { HMODULE hPython = 0x0; const char* pythonVersions[] = { "37", "38", "39", "310", "311", "312" }; const int numVersions = sizeof(pythonVersions) / sizeof(pythonVersions[0]);
for (int i = 0; i < numVersions; ++i) { char pythonDllName[15]; snprintf(pythonDllName, sizeof(pythonDllName), "Python%s.dll", pythonVersions[i]);
hPython = GetModuleHandleA(pythonDllName);
if (hPython) break; }
Py_SetProgramName = (_Py_SetProgramName)(GetProcAddress(hPython, "Py_SetProgramName")); PyEval_InitThreads = (_PyEval_InitThreads)(GetProcAddress(hPython, "PyEval_InitThreads")); PyGILState_Ensure = (_PyGILState_Ensure)(GetProcAddress(hPython, "PyGILState_Ensure")); PyGILState_Release = (_PyGILState_Release)(GetProcAddress(hPython, "PyGILState_Release")); PyRun_SimpleStringFlags = (_PyRun_SimpleStringFlags)(GetProcAddress(hPython, "PyRun_SimpleStringFlags")); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| DWORD WINAPI MainThread(HMODULE hModule) { sdk.InitCPython(); Py_SetProgramName(sdk.random_string(10).c_str()); PyEval_InitThreads();
PyGILState_STATE s = PyGILState_Ensure(); PyRun_SimpleString(sdk.ReadFile("code.py").c_str()); PyGILState_Release(s); FreeLibraryAndExitThread(hModule, 0); CloseHandle(hModule); }
|
分析完上面的关键代码,我认为有两个地方需要思考:
- pythonVersions能否扩大支持范围?
- 我认为是可以扩大支持范围的,程序的原理是从Python[3.x].dll中获取导出函数的指针,那么假如Python2.7中含有PyRun_SimpleStringFlags导出函数,它理应可以获取到指针。在扩大范围的时候要先去Python对应版本源码中是否包含这个导出函数
- Py_SetProgramName、PyEval_InitThreads、PyGILState_Ensure、PyGILState_Release是否是必要的?毕竟我们需要的功能只是执行代码
- 我认为这些函数都是不必要的,这些应该只是会影响程序的稳定性,假如不新建一个线程来执行代码的话,它可能会在主线程中被执行。当然,这个需要编译一下项目再实测一下,但我暂时没有编译环境
第二个问题对我们的逆向工作影响很大。对于一些特殊的打包环境,比如游戏引擎,它会将Python编译进它的引擎中,自然就找不到Python[version].dll。这时我们应该通过IDA等方式直接去找PyRun_SimpleStringFlags的函数地址,通过修改这个项目的相关代码就可以实现对特殊打包EXE的代码注入
总结
刚刚我们探讨了Pyinjector对Windows下exe的注入,那么能否对其它平台的可执行文件进行注入呢?其它平台也有一些动态链接库的注入工具,我认为原理上是可行的,但凭借这个项目肯定是不能