PE 绑定导入表
什么是绑定导入
PE在加载前 INT表和IAT表都指向一个名称表
但是有些程序,如果你打印该程序的IAT表的时候会指向地址中发现里面是地址?
比如如下:我先正常打印发现IAT表中的地址RVA转换FOA都直接转换失败了,那么肯定是地址中的值肯定有问题
那么就不进行RVA转换到FOA,可以先尝试直接打印地址中的值进行观察:
原因:我们的PE程序在加载的时候, PE中导入表中的IAT表会根据INT表来进行填写函数地址。但是这就造成了一个问题,PE程序启动慢,因为每次启动都要给IAT表填写函数地址
如果我们事先先把IAT表中的所需要用到的DLL函数地址都填好的话,是不是能够节省程序启动的时间?
答案:是的,但是缺点是万一需要被用到的DLL的Imagebase并没有加载到符合的位置,这样就需要修复重定位表中的地址!
那么如何判断该程序是否进行了绑定导入的操作呢?
在文件中IAT表是否填写地址,依据的地方 就是 导入表中的 TimeDateStamp (时间戳)成员, 如果为0 则是这个DLL没有绑定, 如果为-1 则是这个DLL进行了绑定导入
如下图:这个是进行了绑定导入的操作,所以打印PIMPORT_DESCRIPTOR的TimeDateStamp为ffffffff,有符号也就是-1
什么是绑定导入表
怎么判断导入表中的 IAT表函数地址是否绑定 根据 TimeDataStamp进行判断. 0未绑定 -1绑定
真正的绑定时间存放在绑定导入表IMAGE_BOUND_IMPORT_DESCRIPTOR的TimeDateStamp的属性中
TimeDateStamp == 0 未绑定
TimeDateStamp == -1 已绑定
定位方法:
在PE可选头结构中的数据目录的第12项
typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR { DWORD TimeDateStamp; //真正的时间戳, WORD OffsetModuleName; //DLL的名字. PE的文件名 WORD NumberOfModuleForwarderRefs; //依赖的另外的DLL有几个 // Array of zero or more IMAGE_BOUND_FORWARDER_REF follows } IMAGE_BOUND_IMPORT_DESCRIPTOR, *PIMAGE_BOUND_IMPORT_DESCRIPTOR;
我们的一个DLL可能依赖其他的DLL, 所以导入表的最后一个成员是依赖的DLL有几个,如果有两个,那么紧跟着下面就是依赖的DLL的绑定导入表结构
关于依赖DLL的结构体如下:
typedef struct _IMAGE_BOUND_FORWARDER_REF { DWORD TimeDateStamp; WORD OffsetModuleName; WORD Reserved; // 保留 无作用 } IMAGE_BOUND_FORWARDER_REF, *PIMAGE_BOUND_FORWARDER_REF;
打印绑定导入表的代码:
void PrintBindImportTable(PVOID pFileBuffer){ PIMAGE_DOS_HEADER pDosHeader = NULL; PIMAGE_NT_HEADERS pNTHeader = NULL; PIMAGE_FILE_HEADER pPEHeader = NULL; PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL; PIMAGE_SECTION_HEADER pSectionHeader = NULL; PIMAGE_BOUND_IMPORT_DESCRIPTOR pIMAGE_BOUND_IMPORT_DESCRIPTOR = NULL; PIMAGE_BOUND_FORWARDER_REF pIMAGE_BOUND_FORWARDER_REF = NULL; char ModuleName[20] = {0}; DWORD BOUNG_IMPORT_DESCRIPTOR_TEMP = NULL; int i = 0; DWORD RVA = 0; DWORD FOA = 0; pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew); pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4); pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER); pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + IMAGE_SIZEOF_NT_OPTIONAL_HEADER); RVA_TO_FOA(pFileBuffer, pOptionHeader->DataDirectory[11].VirtualAddress,&FOA); //保存第一个DESCRIPTOR的地址 后面加OffsetModuleName来进行使用 BOUNG_IMPORT_DESCRIPTOR_TEMP = (DWORD)pFileBuffer+(DWORD)FOA; //开始进行打印操作 pIMAGE_BOUND_IMPORT_DESCRIPTOR = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer+(DWORD)FOA); while (*(PDWORD)pIMAGE_BOUND_IMPORT_DESCRIPTOR) { printf("\n"); strcpy(ModuleName, (PVOID)((DWORD)BOUNG_IMPORT_DESCRIPTOR_TEMP + (DWORD)pIMAGE_BOUND_IMPORT_DESCRIPTOR->OffsetModuleName)); printf("模块名称: %s \n",ModuleName); printf("模块的时间戳为: %x \n", pIMAGE_BOUND_IMPORT_DESCRIPTOR->TimeDateStamp); printf("当前模块引用的dll的数量为: %x\n",pIMAGE_BOUND_IMPORT_DESCRIPTOR->NumberOfModuleForwarderRefs); for(i=0;i<pIMAGE_BOUND_IMPORT_DESCRIPTOR->NumberOfModuleForwarderRefs;i++){ pIMAGE_BOUND_IMPORT_DESCRIPTOR++; pIMAGE_BOUND_FORWARDER_REF = (PIMAGE_BOUND_FORWARDER_REF)pIMAGE_BOUND_IMPORT_DESCRIPTOR; strcpy(ModuleName, (PVOID)((DWORD)BOUNG_IMPORT_DESCRIPTOR_TEMP + (DWORD)pIMAGE_BOUND_FORWARDER_REF->OffsetModuleName)); printf("\t引用的模块名称: %s \n",ModuleName); printf("\t引用的模块的时间戳: %x\n", pIMAGE_BOUND_FORWARDER_REF->TimeDateStamp); } pIMAGE_BOUND_IMPORT_DESCRIPTOR++; } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY