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

手动加载PE导入表时解析PIMAGE_IMPORT_DESCRIPTOR出现数值错误

问题根因
  • 核心错误是把PE结构里的相对虚拟地址(RVA)直接当成文件偏移使用。
  • PIMAGE_DATA_DIRECTORY中存储的VirtualAddress是PE被系统加载器映射到内存后,相对于模块基址的偏移量,不是PE文件在磁盘上存储时的文件偏移。你当前是把DLL完整文件以字节数组形式读入内存,没有做节区对齐展开,直接拿RVA加文件缓冲区基址寻址,必然会读到错误的内存位置。
  • 你打印出的错误值ForwarderChain: 74737973按小端序转ASCII是syst,明显是导入表中的DLL名字符串内容,刚好可以佐证寻址位置完全偏离了IMAGE_IMPORT_DESCRIPTOR结构的实际存储位置。
  • 另外你的代码存在笔误:定义的NT头变量是小写开头的ntHeader,后续取数据目录时写成了大写开头的NtHeader,会直接触发编译错误,需要先修正。
修复方案
  • 首先实现RVA到文件偏移的转换函数,逻辑是遍历节表找到目标RVA所属的节区,通过节区的虚拟地址和原始数据偏移计算出对应的文件偏移:
DWORD RvaToFileOffset(PIMAGE_NT_HEADERS32 ntHdr, DWORD rva)
{
    PIMAGE_SECTION_HEADER secHeader = IMAGE_FIRST_SECTION(ntHdr);
    WORD sectionCount = ntHdr->FileHeader.NumberOfSections;
    for (WORD i = 0; i < sectionCount; i++)
    {
        DWORD secVaStart = secHeader[i].VirtualAddress;
        DWORD secVaEnd = secVaStart + max(secHeader[i].Misc.VirtualSize, secHeader[i].SizeOfRawData);
        if (rva >= secVaStart && rva < secVaEnd)
        {
            return rva - secVaStart + secHeader[i].PointerToRawData;
        }
    }
    // PE头区域的RVA和文件偏移一致,直接返回
    return rva;
}
  • 修正原有导入表解析逻辑,所有从PE结构中读取到的RVA,都要先转成文件偏移再和缓冲区基址相加寻址:
PIMAGE_NT_HEADERS32 ntHeader = (PIMAGE_NT_HEADERS32)((DWORD)dllFile32 + ((PIMAGE_DOS_HEADER)dllFile32)->e_lfanew);
PIMAGE_DATA_DIRECTORY importDir = &ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];

if (importDir->VirtualAddress > 0 && importDir->Size > sizeof(IMAGE_IMPORT_DESCRIPTOR))
{
    DWORD importTableOffset = RvaToFileOffset(ntHeader, importDir->VirtualAddress);
    PIMAGE_IMPORT_DESCRIPTOR iid = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)dllFile32 + importTableOffset);
    
    // 后续遍历IID时,OriginalFirstThunk、Name、FirstThunk字段都是RVA,使用前都需要做同样的偏移转换
    printf("    VirtualAddress: %08X\n", importDir->VirtualAddress);
    printf("              Size: %08X\n", importDir->Size);
    printf("OriginalFirstThunk: %08X\n", iid->OriginalFirstThunk);
    printf("     TimeDateStamp: %08X\n", iid->TimeDateStamp);
    printf("    ForwarderChain: %08X\n", iid->ForwarderChain);
}
注意事项
  • 只有当PE被系统加载器完整映射到内存、所有节区按内存对齐要求展开后,RVA才能直接加模块基址寻址。手动读取磁盘文件做解析时,所有RVA都必须先转成文件偏移才能正确定位数据。
  • 遍历导入表时,终止条件是读到OriginalFirstThunk和FirstThunk都为0的IID结构,不能直接用数据目录的Size硬编码遍历长度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 03:57:21