2009年11月30日 星期一 11:31
我在编写一个C++程序,这个C++程序执行过程中会调用Python脚本,而在这个Python脚本中,又需要调用我用C++编写的提供给Python的扩展。在调试中遇到了一个棘手的问题,C++导入Python脚本失败,也就是PyImport_Import函数返回失败,注释Python脚本分块定位,证实是Python脚本中的import出错的,无法导入我用C++编写的Python扩展,甚至连import win32api都会出错,而相同的脚本在PythonWin的Interactive Window中却可以被正常运行。 -------------------------------------------------------------------------------- C++程序中导入Python脚本的部分: Py_Initialize(); nRetCode = Py_IsInitialized(); Z_PROCESS_ERROR(nRetCode); g_ppyName = PyString_FromString(lpszSource); Z_PROCESS_ERROR(g_ppyName); g_ppyModule = PyImport_Import(g_ppyName); Z_PROCESS_ERROR(g_ppyModule); g_ppyDict = PyModule_GetDict(g_ppyModule); Z_PROCESS_ERROR(g_ppyDict); -------------------------------------------------------------------------------- C++程序中调用Python脚本的部分: //get the function from python scripts. ppyFunction = PyDict_GetItemString( g_ppyDict, CT2A(itrPythonProc->second->szProc)); Z_PROCESS_ERROR(ppyFunction); nRetCode = PyCallable_Check(ppyFunction); Z_PROCESS_ERROR(nRetCode); //set parameters. ppyArgument = PyTuple_New(UI_PARAM_COUNT); nRetCode = PyTuple_SetItem( ppyArgument, 0, Py_BuildValue("l", dwAddress)); nRetCode = PyTuple_SetItem( ppyArgument, 1, Py_BuildValue( "l", pstRegister->stAllDouble.dwEax)); /* ... */ nRetCode = PyTuple_SetItem( ppyArgument, 9, Py_BuildValue( "l", pstRegister->dwFlagsDouble)); //call the function. ppyResult = PyObject_CallObject(ppyFunction, ppyArgument); -------------------------------------------------------------------------------- Python脚本部分: #sensor.py import SensorHelper def detoured_test(eip, eax, ecx, edx, ebx, esp, ebp, esi, edi, flags): SensorHelper.output_debug_string('breaked at address 0x%08x' % eip) SensorHelper.output_debug_string('eax=0x%08x\necx=0x%08x\nedx=0x%08x\nebx=0x%08x\nesp=0x%08x\nebp=0x%08x\nesi=0x%08x\nedi=0x%08x\nflags=0x%08x\n' % (eax, ecx, edx, ebx, esp, ebp, esi, edi, flags)) SensorHelper.output_debug_string('%s' % esp+4) SensorHelper.output_debug_string('%s' % esp+8) # ... return -------------------------------------------------------------------------------- C++为Python提供的扩展: PyObject* OutputDbgStr(PyObject *ppySelf, PyObject *ppyArgs) { const TCHAR SZ_DBGSTR[] = _T("An error occurred in output_debug_string"); PyObject *pyResult = NULL; BOOL bRetCode = FALSE; LPSTR lpszDbgStr = NULL; bRetCode = PyArg_ParseTuple(ppyArgs, "s", &lpszDbgStr;); Z_PROCESS_ERROR(bRetCode); /* ... */ pyResult = Py_BuildValue("s", lpszDbgStr); Exit0: if (!pyResult) { ::OutputDebugString(SZ_DBGSTR); pyResult = Py_BuildValue(""); } return pyResult; } ////////////////////////////////////////////////////////////////////////// // Export function registration static PyMethodDef AllMyMethods[] = { /* ... */ {"output_debug_string", OutputDbgStr, METH_VARARGS, "send a debug string"}, {NULL, NULL} }; PyMODINIT_FUNC initSensorHelper() { PyObject *ppyModule = Py_InitModule("SensorHelper", AllMyMethods); return; } -------------------------------------------------------------------------------- 在排除了路径和文件等会直接想到的原因后,通过Google搜索相关内容却仍然没有解决,无奈之下,既然是import失败,就从PyImport_Import开始用OllyDbg跟踪,跟进去后,层次颇深,跟到import的位置,拼接字符串时一个"_d.pyd"的字串被看到时,恍然大悟。 原来,在Debug配置下构建,Python在import一个库的时候,会尝试找库名拼接"_d.pyd"的文件,而不是库名拼接".pyd"的文件 -------------------------------------------------------------------------------- 代码 1E0E116E E8 4B670D00 call1E0E1173 83C4 08 add esp, 8 1E0E1176 833D 0878321E 0>cmp dword ptr [Py_VerboseFlag], 1 1E0E117D 7E 11 jle short 1E0E1190 1E0E117F 8B55 14 mov edx, dword ptr [ebp+14] 1E0E1182 52 push edx 1E0E1183 68 509F2C1E push 1E2C9F50 ; ASCII "# trying %s",LF 1E0E1188 E8 53E70900 call PySys_WriteStderr 1E0E118D 83C4 08 add esp, 8 1E0E1190 8B45 E4 mov eax, dword ptr [ebp-1C] 1E0E1193 8B48 04 mov ecx, dword ptr [eax+4] 1E0E1196 894D F4 mov dword ptr [ebp-C], ecx 1E0E1199 8B55 F4 mov edx, dword ptr [ebp-C] 1E0E119C 0FBE02 movsx eax, byte ptr [edx] 1E0E119F 83F8 55 cmp eax, 55 1E0E11A2 75 07 jnz short 1E0E11AB 1E0E11A4 C745 F4 609F2C1>mov dword ptr [ebp-C], 1E2C9F60 ; ASCII "rb" 1E0E11AB 8B4D F4 mov ecx, dword ptr [ebp-C] 1E0E11AE 51 push ecx 1E0E11AF 8B55 14 mov edx, dword ptr [ebp+14] 1E0E11B2 52 push edx 1E0E11B3 FF15 D8911B1E call dword ptr [<&MSVCR80D.fopen;>] ; msvcr80d.fopen -------------------------------------------------------------------------------- 堆栈 00F5F4C0 00F5F7E8 |dest = 00F5F7E8 00F5F4C4 1E2B1940 \src = "_d.pyd" 00F5F4C8 00F5FCF4 00F5F4CC 00000000 00F5F4D0 0104D162 00F5F4D4 01021C52 00F5F4D8 /00F5F4F8 00F5F4DC |1023E11D 返回到 msvcr80d.1023E11D 来自 msvcr80d.1023DA60 ... 00F5F4C0 00F5F7D4 |path = "D:\Python26\Lib\site_d.pyd" 00F5F4C4 1E2B1948 \mode = "rb" -------------------------------------------------------------------------------- 出现问题的细节原因找到后,回想了一下为什么会这样。我在Windows下安装的Python环境是ActivePython 2.6,它并没有提供debug配置构建的库文件,其实包括Python官方发布版本在内,都是没有提供debug配置构建的库文件,我在看到程序编译错误提示后,直接把python26.lib复制了一份为python26_d.lib以使得我的程序可以在debug配置下构建成功并调试,但却忽略了在debug配置下,对于Python的pyd库文件,是回去搜索以_d.pyd文件名结尾的文件。 这个问题距离现在已经两周了,当时犯懒没有发帖,后来在哲思网站上看到了那篇的《Richard Stallman和自由软件运动》,尽管过去对RMS大叔也有所了解,但了解了他的更多后非常感动。专有软件开发中大量的重复劳动,无处不在的技术封锁,以及因此滋生出的逆向工程,它们吞噬着大量的资源,这些资源本不应该被消耗,这一切在阻碍着人类的进步。即使自己已经深陷专有软件的泥潭,为了自由,为支持自由软件运动行动。
Zeuux © 2024
京ICP备05028076号