GetModuleHandleA在附加到另一个进程时无法获取python.exe未使用的模块

GetModuleHandleA在附加到另一个进程时无法获取python.exe未使用的模块,python,code-injection,ctypes,kernel32,Python,Code Injection,Ctypes,Kernel32,目前我正在使用Grey Hat Python book。它描述了如何在python中创建调试器。到目前为止,我的调试器能够启动进程并附加到它。当我试图从进程中检索模块句柄时,就会出现问题。根据OllyDbg,DLL存在于程序中,但GetModuleHandleA无法获得句柄。我对书中的一段代码做了一点改进,以便在GetModuleHandleA无法检索句柄的情况下,该函数将尝试创建一个远程线程,并强制将该模块加载到进程中。但即便如此,它GetModuleHandleA还是失败了(其他一切都正常工

目前我正在使用Grey Hat Python book。它描述了如何在python中创建调试器。到目前为止,我的调试器能够启动进程并附加到它。当我试图从进程中检索模块句柄时,就会出现问题。根据OllyDbg,DLL存在于程序中,但
GetModuleHandleA
无法获得句柄。我对书中的一段代码做了一点改进,以便在
GetModuleHandleA
无法检索句柄的情况下,该函数将尝试创建一个远程线程,并强制将该模块加载到进程中。但即便如此,它
GetModuleHandleA
还是失败了(其他一切都正常工作)。所以也许有人可以快速浏览一下代码,看看其中的问题

def func_resolve(self,dll,function):
    handle  = kernel32.GetModuleHandleA(dll)
    print "%s module handle is at 0x%08x" % (dll, handle)
    error = kernel32.GetLastError()
    if error:
        print "There was an error in func_resolve::GetModuleHandleA(%s): %d" % (dll, error)
        print "Loading library into the process"
        pLibRemote = kernel32.VirtualAllocEx(self.h_process, 0, len(dll), 0x00001000, 0x04)
        print "Allocated %d bytes of memory at 0x%08x" % (len(dll), pLibRemote)
        written = c_int(0)
        kernel32.WriteProcessMemory(self.h_process, pLibRemote, dll, len(dll), byref(written))
        print "Written %d bytes" % written.value
        handle  = kernel32.GetModuleHandleA("kernel32.dll")
        print "Kernel module handle is 0x%08x" % handle
        address = kernel32.GetProcAddress(handle, "LoadLibraryA")
        print "LoadLibraryA address is 0x%08x" % address
        thread_id = c_ulong(0)
        kernel32.CreateRemoteThread(self.h_process, None, 0, address, pLibRemote, 0, byref(thread_id))
        print "Created thread %d" % thread_id.value
    handle  = kernel32.GetModuleHandleA(dll)
    address = kernel32.GetProcAddress(handle, function)
    kernel32.CloseHandle(handle)
    return address
输出如下所示:

[*] We have successfully launched the process!
[*] The Process ID I have is: 10380
Proces handle is 228
opengl32.dll module handle is at 0x00000000
There was an error in func_resolve::GetModuleHandleA(opengl32.dll): 126
Loading library into the process
Allocated 12 bytes of memory at 0x002c0000
Written 12 bytes
Kernel module handle is 0x772c0000
LoadLibraryA address is 0x772d498f
Created thread 11136
[*] Address of func: 0x00000000
[*] Setting breakpoint at: 0x00000000
如果python.exe使用模块句柄(在python.exe进程的导入列表中),则可以很好地检索模块句柄。但是不在python.exe进程中的模块会失败。也许这可能与操作系统Windows7(64位)有关,但我测试的应用程序仍然是用32位编译器编译的

更新2:根据评论中的建议,我编写了自己的函数:

def my_func_resolve(self, dll, function):
    module32 = MODULEENTRY32()
    CreateToolhelp32Snapshot = kernel32.CreateToolhelp32Snapshot
    CreateToolhelp32Snapshot.restype = HANDLE
    CreateToolhelp32Snapshot.argtypes = [DWORD, DWORD]
    Module32First = kernel32.Module32First
    Module32First.restype = BOOL
    Module32First.argtypes = [HANDLE, POINTER(MODULEENTRY32)]
    Module32Next = kernel32.Module32Next
    Module32Next.restype = BOOL
    Module32Next.argtypes = [HANDLE, POINTER(MODULEENTRY32)]
    thandle = 24
    while thandle == 24:
        thandle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, self.pid)
    if thandle == 0 or thandle == 0xFFFFFFFF:
        print "Failed to create a snapshot. Error: %d" % kernel32.GetLastError()
        exit()
    if not Module32First(thandle, byref(module32)):
        print "Module32First failed. Error: %d" % kernel32.GetLastError()
        kernel32.CloseHandle(thandle)
        exit()
    while module32:
        print "DLL %s is loaded at 0x%08x" % (module32.szModule, module32.modBaseAddr)
        Module32Next(thandle, byref(module32))
    kernel32.CloseHandle(thandle)
    return True
但它失败了

[*] We have successfully launched the process!
[*] The Process ID I have is: 9584
Proces handle is 228
Failed create snapshot. Error: 299
这是错误的部分拷贝,当我们试图从32位进程中检索64位进程时会发生。我有32位python。我的操作系统是64位的。我使用mingw 32位编译器编译了testprog.exe。我现在怎么会犯这个错误? 对于
TH32CS\u SNAPMODULE
我使用了
0x00000008
0x00000010

以防万一,流程是这样创建的:

if kernel32.CreateProcessA(path_to_exe,
                            None,
                            None,
                            None,
                            None,
                            creation_flags,
                            None,
                            None,
                            byref(startupinfo),
                            byref(process_information)):
    print "[*] We have successfully launched the process!"
    print "[*] The Process ID I have is: %d" % \
                    process_information.dwProcessId
    self.pid = process_information.dwProcessId
    self.h_process = self.open_process(process_information.dwProcessId)
    print "Proces handle is %d" % self.h_process

根据上的文档,错误代码126是
error\u MOD\u NOT\u FOUND
。您可能需要查看以确保DLL安装在正确的位置。不过opengl32.dll是相当常见的,所以我希望它可以使用

另一种可能是,您的代码正在调用
GetModuleHandleA
(Windows代码页或函数的“ANSI”版本),但传递的是宽字符Unicode字符串
GetModuleHandleA
将无法正确解释Unicode字符串,因此它将搜索错误的模块。如果是这种情况,那么修复方法就是将代码更改为调用
GetModuleHandleW
。Python3特别使用Unicode表示字符串,因此如果您使用Python3运行,那么这可能是相关的

本文档详细讨论了函数的
A
W
命名约定,以及能够处理Windows代码页的函数与能够处理Unicode的函数之间的区别

前一个问题看起来类似


GetModuleHandle
在当前进程中查找模块。要在另一个进程中查找模块,您需要使用PSAPI函数
EnumProcessModulesEx
GetModuleBaseName
或工具帮助函数
CreateToolhelp32Snapshot
Module32First
,&
Module32Next

如果目标进程与当前进程具有相同的体系结构,则可以在其加载的DLL中间接查找过程地址。首先,通过
LoadLibraryEx
don\u RESOLVE\u DLL\u引用
在当前进程中加载DLL。然后使用此本地
HMODULE
调用
GetProcAddress
,以获取本地地址。最后,在目标进程中调整相对于模块基址的本地地址。记住调用
freebrary
从当前进程卸载DLL


请注意,
HMODULE
句柄实际上是指针,因此需要为所有ctypes函数设置
restype
argtypes
。这可以防止将64位指针值截断为32位C
int

下面是一个使用工具帮助功能的示例

import os
import ctypes
from ctypes import wintypes

kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
wintypes.LPBOOL = ctypes.POINTER(wintypes.BOOL)

ERROR_NO_MORE_FILES = 0x0012
ERROR_BAD_LENGTH    = 0x0018
ERROR_MOD_NOT_FOUND = 0x007E

INVALID_HANDLE_VALUE = wintypes.HANDLE(-1).value
DONT_RESOLVE_DLL_REFERENCES = 0x00000001
MAX_PATH = 260
MAX_MODULE_NAME32 = 255
TH32CS_SNAPMODULE   = 0x00000008

class MODULEENTRY32W(ctypes.Structure):
    _fields_ = (('dwSize',        wintypes.DWORD),
                ('th32ModuleID',  wintypes.DWORD),
                ('th32ProcessID', wintypes.DWORD),
                ('GlblcntUsage',  wintypes.DWORD),
                ('ProccntUsage',  wintypes.DWORD),
                ('modBaseAddr',   wintypes.LPVOID),
                ('modBaseSize',   wintypes.DWORD),
                ('hModule',       wintypes.HMODULE),
                ('szModule',      wintypes.WCHAR * (MAX_MODULE_NAME32 + 1)),
                ('szExePath',     wintypes.WCHAR * MAX_PATH))
    def __init__(self, *args, **kwds):
        super(MODULEENTRY32W, self).__init__(*args, **kwds)
        self.dwSize = ctypes.sizeof(self)

LPMODULEENTRY32W = ctypes.POINTER(MODULEENTRY32W)

def errcheck_bool(result, func, args):
    if not result:
        raise ctypes.WinError(ctypes.get_last_error())
    return args

def errcheck_ihv(result, func, args):
    if result == INVALID_HANDLE_VALUE:
        raise ctypes.WinError(ctypes.get_last_error())
    return args

kernel32.LoadLibraryExW.errcheck = errcheck_bool
kernel32.LoadLibraryExW.restype = wintypes.HMODULE
kernel32.LoadLibraryExW.argtypes = (wintypes.LPCWSTR,
                                    wintypes.HANDLE,
                                    wintypes.DWORD)

kernel32.FreeLibrary.errcheck = errcheck_bool
kernel32.FreeLibrary.argtypes = (wintypes.HMODULE,)

kernel32.GetProcAddress.errcheck = errcheck_bool
kernel32.GetProcAddress.restype = wintypes.LPVOID
kernel32.GetProcAddress.argtypes = (wintypes.HMODULE,
                                    wintypes.LPCSTR)

kernel32.CloseHandle.errcheck = errcheck_bool
kernel32.CloseHandle.argtypes = (wintypes.HANDLE,)

kernel32.CreateToolhelp32Snapshot.errcheck = errcheck_ihv
kernel32.CreateToolhelp32Snapshot.restype = wintypes.HANDLE
kernel32.CreateToolhelp32Snapshot.argtypes = (wintypes.DWORD,
                                              wintypes.DWORD)

kernel32.Module32FirstW.errcheck = errcheck_bool
kernel32.Module32FirstW.argtypes = (wintypes.HANDLE,
                                    LPMODULEENTRY32W)

kernel32.Module32NextW.errcheck = errcheck_bool
kernel32.Module32NextW.argtypes = (wintypes.HANDLE,
                                   LPMODULEENTRY32W)

def GetRemoteProcAddress(pid, filename, procname):
    procname = procname.encode('utf-8')
    hLocal = kernel32.LoadLibraryExW(filename, None,
                                     DONT_RESOLVE_DLL_REFERENCES)
    try:
        procaddr = kernel32.GetProcAddress(hLocal, procname)
    finally:
        kernel32.FreeLibrary(hLocal)
    modname = os.path.basename(filename)
    hRemote = GetRemoteModuleHandle(pid, modname)
    return hRemote - hLocal + procaddr

def GetRemoteModuleHandle(pid, modname):
    modname = modname.upper()
    if '.' not in modname:
        modname += '.DLL'
    while True:
        try:
            hProcessSnap = kernel32.CreateToolhelp32Snapshot(
                                TH32CS_SNAPMODULE, pid)
            break
        except OSError as e:
            if e.winerror != ERROR_BAD_LENGTH:
                raise
    try:
        modentry = MODULEENTRY32W()
        kernel32.Module32FirstW(hProcessSnap,
                                ctypes.byref(modentry))
        while True:
            if modentry.szModule.upper() == modname:
                return modentry.hModule
            try:
                kernel32.Module32NextW(hProcessSnap,
                                       ctypes.byref(modentry))
            except OSError as e:
                if e.winerror == ERROR_NO_MORE_FILES:
                    break
                raise
        raise ctypes.WinError(ERROR_MOD_NOT_FOUND)
    finally:
        kernel32.CloseHandle(hProcessSnap)
下面是一个测试,它创建另一个Python进程,并验证kernel32.dll是否加载在与当前进程相同的地址;
LoadLibraryExW
在同一地址解析;前1000个字节是相等的

请注意,我使用Windows事件对象等待子进程完成加载,然后再尝试读取其模块表。这避免了
错误\u部分\u复制
的问题。如果目标是带有消息队列的GUI进程,则可以使用


所有提到的案例都与我无关,但可能会帮助其他人解决同样的问题,所以我提出了它。我已经看到过有类似问题的帖子,并验证了Unicode和dll路径不是我的原因。我从系统路径中尝试了几个不同的dll,也是我自己的dll,用于链接可执行二进制测试文件,结果总是一样的-GetModuleHandleA失败。我也使用Python2.7,所以ANSI标准适合我。上一篇文章似乎与Python 3 Unicode问题有关。
GetModuleHandle
在当前进程中查找该模块。要在另一个进程中查找模块,需要使用PSAPI函数
EnumProcessModulesEx
GetModuleBaseName
或工具帮助API函数
CreateToolhelp32Snapshot
Module32First
,&
Module32Next
。注意
HMODULE
句柄是指针,因此,您需要设置使用它们的所有函数的
restype
argtypes
,以防止将64位指针值截断为32位C
int
值。我忘了提到,您还需要使用
LoadLibraryEx
不解析DLL\u引用将DLL加载到当前进程中,并在调用
GetProcAddress
时使用此
HMODULE
。然后根据目标进程中的DLL
HMODULE
(基址)调整函数地址
if __name__ == '__main__':
    import sys
    import subprocess

    if len(sys.argv) > 1:
        # child process
        import time
        hEvent = int(sys.argv[1])
        kernel32.SetEvent(hEvent)
        time.sleep(120)
        sys.exit(0)

    wintypes.SIZE_T = ctypes.c_size_t
    kernel32.ReadProcessMemory.argtypes = (wintypes.HANDLE,
                                           wintypes.LPVOID,
                                           wintypes.LPVOID,
                                           wintypes.SIZE_T,
                                           ctypes.POINTER(wintypes.SIZE_T))

    class SECURITY_ATTRIBUTES(ctypes.Structure):
        _fields_ = (('nLength',              wintypes.DWORD),
                    ('lpSecurityDescriptor', wintypes.LPVOID),
                    ('bInheritHandle',       wintypes.BOOL))
    def __init__(self, *args, **kwds):
        super(SECURITY_ATTRIBUTES, self).__init__(*args, **kwds)
        self.nLength = ctypes.sizeof(self)

    WAIT_OBJECT_0 = 0
    CREATE_NO_WINDOW = 0x08000000

    sa = SECURITY_ATTRIBUTES(bInheritHandle=True)
    hEvent = kernel32.CreateEventW(ctypes.byref(sa), 0, 0, None)
    script = os.path.abspath(__file__)
    p = subprocess.Popen([sys.executable, script, str(hEvent)],
                         close_fds=False,
                         creationflags=CREATE_NO_WINDOW)
    try:
        result = kernel32.WaitForSingleObject(hEvent, 60 * 1000)
        if result != WAIT_OBJECT_0:
            sys.exit('wait failed')
        # kernel32 should load at the same address in a given session.
        hModule = GetRemoteModuleHandle(p.pid, 'kernel32')
        assert hModule == kernel32._handle
        remote_addr = GetRemoteProcAddress(p.pid,
                                           'kernel32',
                                           'LoadLibraryExW')
        local_addr = ctypes.c_void_p.from_buffer(
                        kernel32.LoadLibraryExW).value
        assert remote_addr == local_addr
        remote_bytes = (ctypes.c_char * 1000)()
        read = wintypes.SIZE_T()
        kernel32.ReadProcessMemory(int(p._handle),
                                   remote_addr,
                                   remote_bytes, 1000,
                                   ctypes.byref(read))
        local_bytes = ctypes.string_at(kernel32.LoadLibraryExW, 1000)
        assert remote_bytes[:] == local_bytes
    finally:
        p.terminate()