pe文件导入表需通过image_data_directory中image_directory_entry_import索引定位,先解析dos_header和nt_headers,再将rva转为文件偏移;修改iat易因未修复重定位、忽略绑定导入或未更新校验和导致崩溃。

PE 文件导入表(IAT)怎么定位和解析
Windows 可执行文件(.exe/.dll)的导入表不是固定偏移,得靠 IMAGE_DATA_DIRECTORY 中的 IMAGE_DIRECTORY_ENTRY_IMPORT 索引去找。直接从文件头硬跳 0x80 是错的——PE 头大小可变(有无签名、扩展头),节对齐方式也影响后续布局。
实操要点:
- 先读
DOS_HEADER,再跳到e_lfanew指向的NT_HEADERS - 从
OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]拿到 RVA 和 Size - 把 RVA 转成文件偏移:需遍历节表(
IMAGE_SECTION_HEADER),找到包含该 RVA 的节,再用公式file_offset = section.PointerToRawData + (rva - section.VirtualAddress) - 导入描述符数组以全零项结尾,不能只按 Size 字段硬读——Size 是整个目录占用字节数,可能含 padding
修改 IAT 时为什么程序一运行就崩溃
改了导入表却不修复重定位或忽略绑定导入(Bound Import),大概率触发访问违规。IAT 在内存中是可写的(默认页属性为 PAGE_READWRITE),但 PE 加载器会在加载后把 IAT 所在页设为只读(除非你手动 VirtualProtect 改回来);更关键的是,如果目标函数地址已由绑定导入固化(IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT),系统会跳过 IAT 解析直接跳转,你改了也没用。
常见错误现象:
- 修改后程序启动报
0xC0000005(访问冲突),多半是 IAT 地址写到了只读页 - 函数调用没走你的 hook,是因为绑定导入存在且时间戳更新,系统绕过了 IAT 查找
- 改完保存文件再运行失败,常因没同步更新校验和(
OptionalHeader.CheckSum),某些安全软件或 Windows Defender 会拒载
导出表(EAT)修改后 LoadLibrary 失败怎么办
导出表结构比导入表更敏感:名字序号表(AddressOfNames)、序号表(AddressOfNameOrdinals)、地址表(AddressOfFunctions)三者长度必须严格一致,且所有 RVA 都必须指向节内有效位置。哪怕一个指针差 1 字节,GetProcAddress 就返回 NULL,LoadLibrary 却不报错——它只检查结构完整性,不验证函数地址是否可执行。
使用场景注意点:
- 新增导出函数时,必须确保新函数代码位于可执行节(如
.text),且其 RVA 被正确填入AddressOfFunctions - 修改函数名字符串要小心:名字表是 ASCIIZ 数组,末尾必须有
\0,且不能跨节边界(否则加载器读串时越界) - 导出序号(ordinal)不是下标——它对应
AddressOfNameOrdinals中的值,而该值是AddressOfFunctions的索引,不是名字数组索引
用 SetFilePointer + WriteFile 直接覆写二进制文件会丢数据吗
会。PE 文件里大量字段是 RVA(相对虚拟地址),不是文件偏移;你只改了磁盘上某几个字节,却没更新校验和、没调整节表的 SizeOfRawData、没处理对齐填充,下次加载时校验失败或节映射错位,结果就是程序无法启动或行为异常。
安全做法:
- 必须用完整 PE 解析库(如
pe-parse或手写完整节解析)重新计算所有依赖字段 - 修改后调用
ImageNtHeader验证结构合法性,再用CheckSumMappedFile更新校验和 - 不要用文本编辑器或十六进制工具“肉眼”改——
IMAGE_IMPORT_DESCRIPTOR的FirstThunk和OriginalFirstThunk指向不同数组,改错一个就导致延迟加载失效
底层操作真正麻烦的从来不是“怎么找到那块内存”,而是“改完之后,整个 PE 结构还能被 Windows 加载器认作合法映像”。每个字段都可能牵连其他 3 个字段,漏掉任意一个,文件就废了。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











