需手动解析PE导入表:先校验DOS/NT头并定位IMAGE_DIRECTORY_ENTRY_IMPORT,将RVA转FOA后遍历IMAGE_IMPORT_DESCRIPTOR数组(以全0项终止),再分别处理INT(OriginalFirstThunk)与IAT(FirstThunk)获取函数名或地址;读取IMAGE_IMPORT_BY_NAME时须按节边界截断Name字段并补\0,且所有结构体必须#pragma pack(push,1)避免对齐偏差。

怎么用C++解析PE文件的导入表(IMAGE_IMPORT_DESCRIPTOR)
直接读取PE导入表,核心是按结构体偏移手动解析,不能依赖Windows API的LoadLibrary或GetProcAddress——它们加载后就看不到原始导入表布局了。你得自己定位IMAGE_DATA_DIRECTORY第1项(IMAGE_DIRECTORY_ENTRY_IMPORT),再算出IMAGE_IMPORT_DESCRIPTOR数组起始地址。
常见错误现象:ReadProcessMemory读不到、指针解引用崩溃、导入表地址全为0——基本都是没做DOS/NT头校验、没转VA到FOA、或忽略节对齐(SectionAlignment vs FileAlignment)。
- 先用
MapViewOfFile映射文件为只读内存,别用fopen逐字节读,否则VA→FOA换算会错 -
IMAGE_NT_HEADERS位置不是固定0x3C,必须先读e_lfanew字段;NT头里OptionalHeader.DataDirectory[1].VirtualAddress是RVA,要减去映像基址(通常是0)再转FOA - 每个
IMAGE_IMPORT_DESCRIPTOR以全0结尾,遍历时必须检查FirstThunk == 0 && OriginalFirstThunk == 0来终止,不能硬算数量
为什么IMAGE_THUNK_DATA里的函数名地址要查INT或IAT两遍
因为导入表实际存了两套并行指针数组:INT(Import Name Table,含函数名RVA)和IAT(Import Address Table,运行时被填充为真实函数地址)。静态分析时只能看INT;但某些加壳/混淆PE会清空INT、只留IAT,此时就得回溯DLL名称再靠字符串匹配猜函数——这是逆向中容易卡住的点。
使用场景:你写的是静态解析器(比如扫描恶意软件特征),就必须同时处理INT存在/缺失两种情况;如果只是调试时查当前进程,直接读IAT更稳。
立即学习“C++免费学习笔记(深入)”;
- INT入口在
OriginalFirstThunk字段,指向IMAGE_THUNK_DATA数组,每个元素是函数名字符串的RVA(低字节为0表示序号导入) - IAT入口在
FirstThunk字段,通常和INT内容一样,但运行后会被系统覆写为函数真实地址 - 若
OriginalFirstThunk == 0,说明INT被抹掉,只能从IAT反查——但此时你得不到函数名,只能看到地址,需配合GetModuleHandle+EnumProcessModules穷举比对
读取函数名字符串时最容易踩的地址越界坑
IMAGE_IMPORT_BY_NAME结构前面2字节是Hint,后面才是char Name[],但很多代码直接当C字符串用strlen,结果读到相邻节的垃圾数据甚至触发AV。Windows PE规范没保证Name以\0结尾,必须按节边界截断。
性能影响:不检查边界会导致解析器在遇到坏PE时随机崩溃,而兼容性上,Win10+的某些ASLR强化PE还会故意在Name后填随机字节干扰解析。
- 获取函数名前,先确认该
IMAGE_IMPORT_BY_NAME结构落在哪个节内——用AddressOfRawData和SizeOfRawData判断RVA是否在节FOA范围内 - 读取
Name时,最多读到节末尾,且要手动补\0;别信strlen返回值 - 有些编译器生成的导入表会把多个
IMAGE_IMPORT_BY_NAME紧凑排列,中间无填充,所以不能假设每个Name都独立对齐
VC++编译时要注意结构体默认对齐导致的字段偏移错乱
MSDN文档里IMAGE_IMPORT_DESCRIPTOR各字段是4字节对齐的,但VC++默认结构体对齐是8字节(x64)或16字节(带/Zp),会导致sizeof(IMAGE_IMPORT_DESCRIPTOR)变成24或32字节,后续所有RVA计算全偏移。
这问题在MinGW或Clang下不明显,但VC++项目里一跑就崩——尤其当你用#pragma pack(2)却忘了重置,整个解析链全废。
- 必须显式用
#pragma pack(push, 1)包裹所有PE结构体定义,解析完再#pragma pack(pop) - 别用
sizeof算结构体大小来跳转,直接按文档写死偏移:比如OriginalFirstThunk永远是偏移0,Name是偏移12(x86)或16(x64) - VS工程属性里“结构体成员对齐”设为“1 字节”只是影响全局,默认还是不生效,必须代码级控制
真正麻烦的是不同PE版本(如.NET Core的Portable Executable v3)会扩展IMAGE_DATA_DIRECTORY长度,但导入表结构本身没变——所以只要老老实实按PE32/PE32+规范走,别碰扩展字段,就能覆盖99%的常规可执行文件。



















