32位Python查找ntdll.dll中jmp esp gadget遇访问冲突求助
解决32位Python访问ntdll.dll内存崩溃及获取Gadget偏移的问题
问题根源
64位Windows系统中,32位进程运行在WoW64(Windows on Windows 64)兼容层下,默认加载的ntdll.dll是64位版本,其内存地址超出32位进程的4GB地址空间范围。直接读取该模块内存会触发访问冲突(退出码-1073741819即0xC0000005,内存访问违规)。
解决内存崩溃的方法
1. 加载32位版本的ntdll.dll
64位Windows的32位系统库存放在C:\Windows\SysWOW64目录下,修改脚本加载该路径下的ntdll.dll,确保模块地址落在32位进程的地址空间内。
2. 仅读取模块的代码段(而非全量内存)
直接读取整个模块内存容易触发权限或范围问题,正确做法是解析PE头,仅读取存放指令的.text段(代码段)内存。
完整修正脚本示例
import ctypes from ctypes import wintypes # 定义Windows API函数 kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) GetModuleHandleW = kernel32.GetModuleHandleW GetModuleHandleW.argtypes = [wintypes.LPCWSTR] GetModuleHandleW.restype = wintypes.HMODULE ReadProcessMemory = kernel32.ReadProcessMemory ReadProcessMemory.argtypes = [wintypes.HANDLE, wintypes.LPCVOID, wintypes.LPVOID, wintypes.SIZE_T, wintypes.PSIZE_T] ReadProcessMemory.restype = wintypes.BOOL # 加载32位ntdll.dll hModule = GetModuleHandleW(r'C:\Windows\SysWOW64\ntdll.dll') if not hModule: raise ctypes.WinError(ctypes.get_last_error()) # 读取DOS头,定位NT头 dos_header = wintypes.IMAGE_DOS_HEADER() bytes_read = wintypes.SIZE_T() ReadProcessMemory(wintypes.HANDLE(-1), hModule, ctypes.byref(dos_header), ctypes.sizeof(dos_header), ctypes.byref(bytes_read)) nt_headers_addr = hModule + dos_header.e_lfanew nt_headers = wintypes.IMAGE_NT_HEADERS() ReadProcessMemory(wintypes.HANDLE(-1), nt_headers_addr, ctypes.byref(nt_headers), ctypes.sizeof(nt_headers), ctypes.byref(bytes_read)) # 获取代码段(.text)的虚拟地址和大小 text_section = nt_headers.OptionalHeader.DataDirectory[0] text_virtual_addr = text_section.VirtualAddress text_size = text_section.Size text_mem_addr = hModule + text_virtual_addr # 读取代码段内存 text_data = (ctypes.c_ubyte * text_size)() ReadProcessMemory(wintypes.HANDLE(-1), text_mem_addr, text_data, text_size, ctypes.byref(bytes_read)) # 查找jmp esp的机器码(0xFF E4) jmp_esp_opcode = b'\xFF\xE4' offset_in_text = 0 while offset_in_text <= len(text_data) - 2: if bytes(text_data[offset_in_text:offset_in_text+2]) == jmp_esp_opcode: # 计算相对于DLL基地址的偏移 gadget_offset = text_virtual_addr + offset_in_text gadget_full_addr = text_mem_addr + offset_in_text print(f"找到jmp esp Gadget: 地址 = 0x{gadget_full_addr:X}, 相对于DLL基地址的偏移 = 0x{gadget_offset:X}") offset_in_text += 1
获取Gadget偏移的逻辑
Gadget相对于DLL起始位置的偏移计算公式为:
gadget偏移 = (Gadget内存地址 - DLL基地址)
在脚本中,我们通过代码段的虚拟地址加上其内部偏移,直接得到相对于DLL基地址的偏移,无需额外计算。
额外说明
如果需要查找64位ntdll.dll的Gadget(如jmp rsp,机器码同样为0xFF E4),32位Python无法直接访问内存中的64位模块,但可以直接读取磁盘上的C:\Windows\System32\ntdll.dll文件,解析PE文件结构后在文件内容中查找对应机器码,计算虚拟偏移(需考虑PE节的对齐规则)。
内容的提问来源于stack exchange,提问作者Tal Afek
相关产品推荐
相关产品推荐

