当附加到另一个进程时,GetModuleHandleA 无法获取 python.exe 未使用的模块
GetModuleHandleA fails to get modules not used by python.exe when attached to another process
我目前正在编写 Gray Hat Python 这本书。它描述了如何在 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 进程中的模块会失败。也许这可能与 OS Windows 7(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
这是 ERROR_PARTIAL_COPY,如果我们试图从 32 位进程检索 64 位进程,就会发生这种情况。我有 32 位 python。我的 OS 是 64 位的。我使用 mingw 32 位编译器编译了 testprog.exe。我现在收到此错误是怎么回事?
对于 TH32CS_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
根据 System Error Codes, error code 126 is ERROR_MOD_NOT_FOUND
. You might want to review the DLL Search Path 上的文档确保 DLL 安装在正确的位置。 opengl32.dll 虽然很常见,所以我希望它可用。
另一种可能是您的代码正在调用 GetModuleHandleA
(Windows 代码页或函数的 "ANSI" 版本),但传递的是宽字符 Unicode 字符串。 GetModuleHandleA
将无法正确解释 Unicode 字符串,因此它会搜索错误的模块。如果是这种情况,则解决方法是更改您的代码以调用 GetModuleHandleW
。 Python 3 特别对字符串使用 Unicode,因此如果您是 运行 Python 3,那么这可能是相关的。
Unicode in the Windows API 文档对函数的 A
与 W
命名约定以及能够处理 Windows 代码页的函数和函数之间的区别进行了更多讨论能够处理 Unicode。
上一个问题看起来很相似。
Call to GetModuleHandle on kernel32 using Python C-types
GetModuleHandle
在当前进程中查找模块。要在另一个进程中查找模块,您需要使用 PSAPI 函数 EnumProcessModulesEx
& GetModuleBaseName
或工具帮助函数 CreateToolhelp32Snapshot
、Module32First
、& Module32Next
.
如果目标进程与当前进程具有相同的体系结构,则可以在其加载的DLL 中间接找到过程地址。首先,通过LoadLibraryEx
和DONT_RESOLVE_DLL_REFERENCES
加载当前进程中的DLL。然后用这个本地HMODULE
调用GetProcAddress
得到本地地址。最后,调整目标进程中相对于模块基地址的本地地址。记得调用FreeLibrary
从当前进程中卸载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 事件对象等待子进程完成加载,然后再尝试读取其模块 table。这避免了 ERROR_PARTIAL_COPY
的问题。如果目标是带有消息队列的 GUI 进程,您可以改为使用 WaitForInputIdle
.
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()
我目前正在编写 Gray Hat Python 这本书。它描述了如何在 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 进程中的模块会失败。也许这可能与 OS Windows 7(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
这是 ERROR_PARTIAL_COPY,如果我们试图从 32 位进程检索 64 位进程,就会发生这种情况。我有 32 位 python。我的 OS 是 64 位的。我使用 mingw 32 位编译器编译了 testprog.exe。我现在收到此错误是怎么回事?
对于 TH32CS_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
根据 System Error Codes, error code 126 is ERROR_MOD_NOT_FOUND
. You might want to review the DLL Search Path 上的文档确保 DLL 安装在正确的位置。 opengl32.dll 虽然很常见,所以我希望它可用。
另一种可能是您的代码正在调用 GetModuleHandleA
(Windows 代码页或函数的 "ANSI" 版本),但传递的是宽字符 Unicode 字符串。 GetModuleHandleA
将无法正确解释 Unicode 字符串,因此它会搜索错误的模块。如果是这种情况,则解决方法是更改您的代码以调用 GetModuleHandleW
。 Python 3 特别对字符串使用 Unicode,因此如果您是 运行 Python 3,那么这可能是相关的。
Unicode in the Windows API 文档对函数的 A
与 W
命名约定以及能够处理 Windows 代码页的函数和函数之间的区别进行了更多讨论能够处理 Unicode。
上一个问题看起来很相似。
Call to GetModuleHandle on kernel32 using Python C-types
GetModuleHandle
在当前进程中查找模块。要在另一个进程中查找模块,您需要使用 PSAPI 函数 EnumProcessModulesEx
& GetModuleBaseName
或工具帮助函数 CreateToolhelp32Snapshot
、Module32First
、& Module32Next
.
如果目标进程与当前进程具有相同的体系结构,则可以在其加载的DLL 中间接找到过程地址。首先,通过LoadLibraryEx
和DONT_RESOLVE_DLL_REFERENCES
加载当前进程中的DLL。然后用这个本地HMODULE
调用GetProcAddress
得到本地地址。最后,调整目标进程中相对于模块基地址的本地地址。记得调用FreeLibrary
从当前进程中卸载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 事件对象等待子进程完成加载,然后再尝试读取其模块 table。这避免了 ERROR_PARTIAL_COPY
的问题。如果目标是带有消息队列的 GUI 进程,您可以改为使用 WaitForInputIdle
.
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()