Embedded python code crashes when calling Py_InitializeEx

Viewed 23

Using Python, I'm attempting to inject code into the given process. It works fine until I create an executable from the script using pyinstaller, which causes Py_InitializeEx(0) to fail unless the same Python version as the system compiled on is used. It is not an issue with loading the DLL or its functions due to other parts working seamlessly.

    procId = GetProcessIdByName(target_process)
    if procId == -1:
        return 1
    dll_name = f"python{sys.version_info.major}{sys.version_info.minor}.dll"

    for process in EnumProcessModules(-1):
        name = GetModuleFileNameEx(-1, process)
        if dll_name in name:
            dll_path = name
            break

    dll_path_py = dll_path[:]

    dll_path = (CHAR * MAX_PATH)(*dll_path.encode('utf8'))
    process  = OpenProcess(PROCESS_ALL_ACCESS, False, procId)
    print("Acquired handle to process at", process)
    loc = VirtualAllocEx(process, 0, MAX_PATH, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)
    print("VirtualAllocEx returned", hex(loc))
    size = sizeof(dll_path)
    dwBytesRead = SIZE_T()

    res = WriteProcessMemory(process, loc, ctypes.addressof(dll_path), size, byref(dwBytesRead))

    # Read the value (for confirmation)
    res = (CHAR * MAX_PATH)()
    ReadProcessMemory(process, loc, res, size, byref(dwBytesRead))
    print("Wrote DLL location", res.value, "at", hex(loc), f"({dwBytesRead.value} bytes)")

    ID = DWORD()

    # Find the address of LoadLibraryA
    LoadLibraryA_addr = GetProcAddress(GetModuleHandle("kernel32.dll"), b"LoadLibraryA")
    thread = CreateRemoteThread(process, 0, 0, LoadLibraryA_addr, loc, 0, byref(ID))

    # Wait for thread to finish
    WaitForSingleObject(thread, -1))
    VirtualFreeEx(process, loc, 0, MEM_RELEASE)
    print("Thread", ID.value, "successfully finished")
    CloseHandle(thread)
    name = ''

    while name != dll_path_py:
        for hmod in EnumProcessModules(process):
            name = GetModuleFileNameEx(process, hmod)
            if dll_path_py == name:
                break
    modinfo = MODULEINFO()
    GetModuleInformation(process, hmod, byref(modinfo), sizeof(MODULEINFO))
    dll_self = GetModuleHandle(dll_path_py)
    if not dll_self:
        CloseHandle(process)
        raise ctypes.WinError()
    print("Found module at", hmod, "DLL base:", hex(modinfo.lpBaseOfDll))
    Py_InitializeEx_ptr    = GetProcAddress(hmod, b"Py_InitializeEx"    )
    PyRun_SimpleString_ptr = GetProcAddress(hmod, b"PyRun_SimpleString" )

    print("Found Py_InitializeEx at address", Py_InitializeEx_ptr, "with offset", Py_InitializeEx_ptr - hmod)
    print("Found PyRun_SimpleString at address", PyRun_SimpleString_ptr, "with offset", PyRun_SimpleString_ptr - hmod)
    Py_InitializeEx = CFUNCTYPE(None, INT)(Py_InitializeEx_ptr)
    PyRun_SimpleString = CFUNCTYPE(INT, ctypes.c_char_p)(PyRun_SimpleString_ptr)
    initsigs = ctypes.c_int(0)
    initsigs_remote = VirtualAllocEx(process, 0, sizeof(ctypes.c_int), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)
    if not initsigs_remote:
        print("Failed to allocate initsigs!")
        CloseHandle(process)
        raise ctypes.WinError()
    if not WriteProcessMemory(process, initsigs_remote, byref(initsigs), sizeof(ctypes.c_int), byref(dwBytesRead)):
        CloseHandle(process)
        print("Failed to write initsigs!")
        raise ctypes.WinError()
    print("Wrote", dwBytesRead.value, "bytes")
    initsigs_res = INT()
    if not ReadProcessMemory(process, initsigs_remote, byref(initsigs_res), sizeof(ctypes.c_int), byref(dwBytesRead)):
        CloseHandle(process)
        print("Failed to read initsigs")
        raise ctypes.WinError()
    print("Read", dwBytesRead.value, "bytes from initsigs, value:", initsigs_res.value)
    dwRes = DWORD()

    # This crashes the target application when running embedded even though 0 is given as initsigs
    thread_init = CreateRemoteThread(process, 0, 0, Py_InitializeEx_ptr, initsigs_remote, 0, None)
    print(WaitForSingleObject(thread_init, -1))
    GetExitCodeThread(thread_init , byref(dwRes))

    # This returns 1 when the process crashes
    print("Py_InitializeEx returned", bool(dwRes), "({})".format(dwRes.value))

    code = (CHAR * (len(inject_code) + 1))(*inject_code)
    nLoc = VirtualAllocEx(process, 0, len(inject_code), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)
    if not nLoc:
        print("Failed to allocate code!")
        CloseHandle(process)
        raise ctypes.WinError()
    if not WriteProcessMemory(process, nLoc, ctypes.addressof(code), len(inject_code) + 1, byref(dwBytesRead)):
        print("Failed to write code!")
        CloseHandle(process)
        raise ctypes.WinError()

    print("Wrote code at address", hex(nLoc), f"({dwBytesRead.value} bytes)")
    WaitForSingleObject(thread_init, INFINITE))
    VirtualFreeEx(process, initsigs_remote, 0, MEM_RELEASE)
    dwRes = DWORD()
    GetExitCodeThread(thread_init, byref(dwRes))
    print("Thread finished with exit status", dwRes.value)
    print("Executing injected code...")
    exec_thread = CreateRemoteThread(process, 0, 0, PyRun_SimpleString_ptr, nLoc, 0, None)
    print("WaitForSingleObject returned", hex(WaitForSingleObject(exec_thread, INFINITE)))
    print("Execution finished successfully")

    CloseHandle(exec_thread)
    CloseHandle(thread_init)
    CloseHandle(process)

Using exclusively Windows API or pywin32 features, could you help me resolve the specific issue mentioned?

0 Answers
Related