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 call
1E0E1173 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 © 2025
京ICP备05028076号