You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

进程注入(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 05:42:55