Python论坛  - 讨论区

标题:[zeuux-py:188] Windows下C++调用的Python脚本中Import库失败的问题和解决

2009年11月30日 星期一 11:31

X Xseclab xseclab在gmail.com
星期一 十一月 30 11:31:39 CST 2009

我在编写一个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大叔也有所了解,但了解了他的更多后非常感动。专有软件开发中大量的重复劳动,无处不在的技术封锁,以及因此滋生出的逆向工程,它们吞噬着大量的资源,这些资源本不应该被消耗,这一切在阻碍着人类的进步。即使自己已经深陷专有软件的泥潭,为了自由,为支持自由软件运动行动。

[导入自Mailman归档:http://www.zeuux.org/pipermail/zeuux-python]

如下红色区域有误,请重新填写。

    你的回复:

    请 登录 后回复。还没有在Zeuux哲思注册吗?现在 注册 !

    Zeuux © 2024

    京ICP备05028076号