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

x64 DLL注入器如何获取x86进程所需的WinAPI函数地址?

在x64程序中获取x86函数地址的解决方案

你遇到的问题本质是:x64进程中默认加载的系统DLL都是64位版本,所以GetProcAddress返回的自然是64位函数地址。要拿到32位函数的地址,你不能依赖系统的加载器,得手动读取32位DLL的文件镜像,解析它的导出表来获取函数的相对虚拟地址(RVA),再结合目标x86进程中该DLL的实际加载基址计算出最终的32位函数地址。

下面是具体的实现步骤和要点:


核心思路

x64进程无法通过LoadLibrary加载32位DLL(会直接报错),所以我们需要:

  1. 直接读取32位DLL的磁盘文件到内存
  2. 解析32位PE结构,定位导出表
  3. 根据函数名或序号找到目标函数的RVA
  4. 在注入x86进程时,用目标进程中该32位DLL的加载基址 + RVA,得到目标进程中的实际函数地址

具体实现步骤

1. 读取32位DLL文件到内存

用文件操作API打开32位DLL(比如C:\Windows\SysWOW64\kernel32.dll,这是32位版本的kernel32),然后映射到内存:

HANDLE hFile = CreateFileW(L"C:\\Windows\\SysWOW64\\kernel32.dll", GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile == INVALID_HANDLE_VALUE) { /* 处理错误 */ }

HANDLE hMap = CreateFileMapping(hFile, nullptr, PAGE_READONLY, 0, 0, nullptr);
if (!hMap) { /* 处理错误 */ }

void* pDllBase = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
if (!pDllBase) { /* 处理错误 */ }

2. 解析32位PE结构

要使用32位的PE结构体(因为我们处理的是32位DLL):

IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)pDllBase;
IMAGE_NT_HEADERS32* pNtHeaders = (IMAGE_NT_HEADERS32*)((uintptr_t)pDllBase + pDosHeader->e_lfanew);

// 验证是否是32位PE文件
if (pNtHeaders->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
    // 不是32位DLL,处理错误
}

3. 定位并解析导出表

找到导出表的位置,然后遍历导出表查找目标函数:

IMAGE_DATA_DIRECTORY* pExportDir = &pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
IMAGE_EXPORT_DIRECTORY* pExportTable = (IMAGE_EXPORT_DIRECTORY*)((uintptr_t)pDllBase + pExportDir->VirtualAddress);

// 获取导出表的关键数组
DWORD* pNames = (DWORD*)((uintptr_t)pDllBase + pExportTable->AddressOfNames);
WORD* pNameOrdinals = (WORD*)((uintptr_t)pDllBase + pExportTable->AddressOfNameOrdinals);
DWORD* pFunctions = (DWORD*)((uintptr_t)pDllBase + pExportTable->AddressOfFunctions);

// 遍历查找目标函数(比如"GetProcAddress")
for (DWORD i = 0; i < pExportTable->NumberOfNames; i++) {
    const char* pFuncName = (const char*)((uintptr_t)pDllBase + pNames[i]);
    if (strcmp(pFuncName, "GetProcAddress") == 0) {
        WORD ordinal = pNameOrdinals[i];
        DWORD funcRva = pFunctions[ordinal];
        
        // 这里的funcRva就是32位函数在DLL中的相对虚拟地址
        break;
    }
}

4. 计算目标进程中的实际函数地址

在注入x86进程时,你需要先获取目标进程中该32位DLL的加载基址(比如通过EnumProcessModulesEx在x86进程中查询,或者在手动映射DLL时自己指定基址),然后:

// 假设targetBase是目标x86进程中32位kernel32.dll的加载基址
uintptr_t targetFuncAddr = (uintptr_t)targetBase + funcRva;

额外注意事项

  • 处理转发函数:有些函数的导出是转发到其他DLL的(比如CreateFile可能转发到kernelbase.dll),此时funcRva会是一个指向转发字符串的RVA,你需要读取这个字符串(格式为DLLName.FuncName),然后递归解析对应的32位DLL的导出表。
  • 基址重定位:如果目标进程中32位DLL的加载基址和它的默认基址(在PE文件中指定的ImageBase)不同,系统会自动处理重定位,但如果你是手动映射DLL到目标进程,需要自己处理基址重定位表。
  • SysWOW64路径:32位系统DLL都在C:\Windows\SysWOW64目录下,x64版本在C:\Windows\System32,不要搞混路径。

内容的提问来源于stack exchange,提问作者lotsch

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:14:39