进程注入(Process Hollowing)获取PEB地址异常求助
32位Process Hollowing中PEB读取与镜像基址获取问题排查与修复
核心问题分析
你遇到的"Invalid DOS signature"错误,主要来自以下几个关键问题:
- PEB偏移使用错误(32位与64位偏移混淆)
- 读取镜像基址的变量类型与打印方式错误
- NtQueryInformationProcess参数传递错误
- NTSTATUS错误处理逻辑错误
具体修复措施
1. 修正PEB镜像基址偏移
32位系统中,PEB结构体的ImageBaseAddress偏移是0x08,而0x10是64位系统下的偏移。你之前尝试+0x8但未解决问题,是因为其他错误掩盖了这个修正效果。
2. 修正镜像基址的存储与读取逻辑
- 32位程序中,进程镜像基址是32位指针,应使用
DWORD_PTR或PVOID类型存储,而非BYTE数组(数组会导致指针解析错误)。 - 打印镜像基址时,要打印读取到的数值,而非数组本身的地址。
3. 修正NtQueryInformationProcess的参数
第五个参数retLen需要传递指针(&retLen),而非值,否则函数无法返回实际读取的结构体长度,导致PROCESS_BASIC_INFORMATION未被正确填充,进而得到错误的PEB地址。
4. 修正NTSTATUS错误处理
NtQueryInformationProcess返回的是NTSTATUS类型,不能用GetLastError()获取错误信息,应使用RtlNtStatusToDosError()将其转换为Win32错误码,或直接打印NTSTATUS值。
5. 修正PE头读取逻辑
目标进程的内存空间无法被当前进程直接访问,不能直接将读取到的镜像基址转换为PIMAGE_DOS_HEADER,需要在当前进程分配临时内存,再读取目标进程的PE头数据到该内存中进行验证。
修复后的完整代码
#include <Windows.h> #include <stdio.h> #include <winternl.h> // 链接ntdll.lib,确保NtQueryInformationProcess可调用 #pragma comment(lib, "ntdll.lib") int main(int argc, char* argv[]) { STARTUPINFOW si = { 0 }; PROCESS_INFORMATION pi = { 0 }; si.cb = sizeof(si); // 必须初始化cb字段,否则CreateProcess可能失败 // 创建挂起的32位notepad进程(64位系统下需指定SysWOW64路径) BOOL processCreationSuccess = CreateProcessW( L"C:\\Windows\\SysWOW64\\notepad.exe", NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi ); if (!processCreationSuccess) { printf("进程创建失败,错误码: %d\n", GetLastError()); return 1; } printf("挂起状态进程创建成功\n"); PROCESS_BASIC_INFORMATION pbi; UINT retLen = 0; NTSTATUS pebGotten = NtQueryInformationProcess( pi.hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), &retLen // 传递指针而非值 ); if (!NT_SUCCESS(pebGotten)) { printf("获取PEB失败,NTSTATUS: 0x%X,Win32错误码: %d\n", pebGotten, RtlNtStatusToDosError(pebGotten)); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 1; } printf("PEB基地址: %p\n", pbi.PebBaseAddress); DWORD_PTR imageBase = 0; SIZE_T bytesRead = 0; // 32位PEB中ImageBaseAddress的偏移是0x08 BOOL readSuccess = ReadProcessMemory( pi.hProcess, (PBYTE)pbi.PebBaseAddress + 0x08, &imageBase, sizeof(imageBase), &bytesRead ); if (!readSuccess || bytesRead != sizeof(imageBase)) { printf("读取镜像基地址失败,错误码: %d\n", GetLastError()); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 1; } printf("镜像基地址: %p\n", (PVOID)imageBase); // 在当前进程分配内存读取目标进程的DOS头 PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)LocalAlloc(LPTR, sizeof(IMAGE_DOS_HEADER)); if (!dosHeader) { printf("内存分配失败\n"); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 1; } readSuccess = ReadProcessMemory( pi.hProcess, (PVOID)imageBase, dosHeader, sizeof(IMAGE_DOS_HEADER), &bytesRead ); if (!readSuccess || bytesRead != sizeof(IMAGE_DOS_HEADER)) { printf("读取DOS头失败,错误码: %d\n", GetLastError()); LocalFree(dosHeader); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 1; } if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) { printf("无效DOS签名\n"); LocalFree(dosHeader); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 1; } printf("DOS签名验证通过\n"); // 读取NT头 PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)LocalAlloc(LPTR, sizeof(IMAGE_NT_HEADERS)); if (!ntHeaders) { printf("内存分配失败\n"); LocalFree(dosHeader); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 1; } readSuccess = ReadProcessMemory( pi.hProcess, (PBYTE)imageBase + dosHeader->e_lfanew, ntHeaders, sizeof(IMAGE_NT_HEADERS), &bytesRead ); if (!readSuccess || bytesRead != sizeof(IMAGE_NT_HEADERS)) { printf("读取NT头失败,错误码: %d\n", GetLastError()); LocalFree(ntHeaders); LocalFree(dosHeader); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 1; } if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) { printf("无效NT签名\n"); LocalFree(ntHeaders); LocalFree(dosHeader); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 1; } printf("NT签名验证通过\n"); if (ntHeaders->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) { printf("无效可选头Magic值\n"); LocalFree(ntHeaders); LocalFree(dosHeader); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 1; } printf("可选头Magic验证通过\n"); DWORD entryPoint = ntHeaders->OptionalHeader.AddressOfEntryPoint; printf("入口点RVA: %p\n", (PVOID)entryPoint); printf("入口点实际地址: %p\n", (PBYTE)imageBase + entryPoint); // 清理资源 LocalFree(ntHeaders); LocalFree(dosHeader); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 0; }
额外注意事项
- 在64位系统上编译32位程序时,必须调用32位版本的notepad(路径为
C:\Windows\SysWOW64\notepad.exe),否则会创建64位进程,导致32位程序读取64位PEB时出现偏移错误。 - 必须初始化
STARTUPINFOW的cb字段,否则CreateProcessW可能因为结构体未正确初始化而失败。 - 目标进程的内存空间属于独立地址空间,当前进程无法直接访问,必须通过
ReadProcessMemory读取到本地内存后再进行PE头验证。
内容的提问来源于stack exchange,提问作者preno1221
相关产品推荐
相关产品推荐

