Loading... # 什么是导入表 导入表记录当前程序使用了哪些模块的哪些函数,如果使用了多个模块,相应的也会存在多张导入表,最终会以一个全0 的导入表结构结尾。当文件没有加载到内存时,IAT和 INT中保存的通常序号或导入名称结构的RVA,一旦程序被加载到内存中,PE加载器就会为 IAT填充函数的真实地址 # 导入表结构体 记录可执行文件模块函数信息,就是导入表。 IMAGE_IMPORT_DESCRIPTOR 数组开始第二个。 ![对照表](http://www.irohane.top/usr/uploads/2020/12/3596477108.png) OriginalFirstThunk-FirstThunk 在文件中所指向的是一致的(相同的)。 ```cpp typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; DWORD OriginalFirstThunk;//指向导入名称表(INT)的RVA|是一个结构数组 } DWORD TimeDateStamp; //时间标识 DWORD ForwarderChain; //转发链,不转发此值为0|在程序引用DLLAPI而这个API又引用其API DWORD Name; //指向输入表DLL的字符串|以00为结尾 DWORD FirstThunk; //指向导入地址表(IAT)的RVA } typedef struct _IMAGE_THUNK_DATA32 { union { PBYTE ForwarderString; //指向一个转向者字符串(加密4看到的-.-) PDWORD Funtion; //被导入函数的地址 DWORD Ordinal; //被导入函数的序号 PIMAGE_IMPORT_BY_NAME AddressOfData; //指向输入名称表 }u1; } ``` _IMAGE_THUNK_DATA32 1. 最高位为1的时候,表示函数以序号方式输入。这时候低32位(64位-》低63位)被看成一个函数序号。 2. 当双字节最高位为0时,表示函数以字符串类型函数名输入,这时双字节的值是一个RVA,指向一个IMAGE_IMPORT_BY_NAME结构。【这里应该根据RVA转FOA计算】 IMAGE_IMPORT_BY_NAME结构仅有1个字节大小,存储了一个输入函数的相关信息。 ```cpp IMAGE_IMPORT_BY_NAME { Hint WORD ? Name Byte ? } ``` Hint:本函数在其所驻留DLL的输出表中的序号。该域被PE装载器用来在DLL的输出表里快速查询,不是必要的,有些连接器设置为0 Name:输入函数的函数名,ASCII字符串,以NULL结尾。 ![image.png](http://www.irohane.top/usr/uploads/2021/03/735816002.png) ```cpp #include <iostream> #include <Windows.h> #define path L"C:\\Users\\Administrator\\source\\repos\\测试\\Debug\\测试.exe" char* buf; PIMAGE_DOS_HEADER GET_DOS(char* buf); PIMAGE_NT_HEADERS GET_NT(char* buf); BOOL IS_PEFILE(char* buf); BOOL IS_PEFILE(char* buf) { if (GET_DOS(buf)->e_magic == IMAGE_DOS_SIGNATURE && GET_NT(buf)->Signature == IMAGE_NT_SIGNATURE) { printf("是PE文件\n"); return TRUE; } else { printf("不是PE文件\n"); return FALSE; } } DWORD RvaToFoa(char* lpImage, DWORD dwRva) { //1 获取区段表的起始位置 PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage; PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + lpImage); PIMAGE_SECTION_HEADER pHeader = IMAGE_FIRST_SECTION(pNt); if (dwRva < pNt->OptionalHeader.SizeOfHeaders) { return dwRva; } //2 循环判断RVA落在了哪个区段中 for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++) { DWORD dwSectionRva = pHeader[i].VirtualAddress; DWORD dwSectionEndRva = dwSectionRva + pHeader[i].SizeOfRawData; DWORD dwSectionFOA = pHeader[i].PointerToRawData; if (dwRva >= dwSectionRva && dwRva < dwSectionEndRva) { pHeader[i].VirtualAddress; DWORD dwFOA = dwRva - dwSectionRva + dwSectionFOA; return dwFOA; } } return -1; } PIMAGE_DOS_HEADER GET_DOS(char* buf) { return PIMAGE_DOS_HEADER(buf); } PIMAGE_NT_HEADERS GET_NT(char* buf) { return (PIMAGE_NT_HEADERS)(GET_DOS(buf)->e_lfanew+buf); } PIMAGE_SECTION_HEADER GET_SECTION(char* buf) { PIMAGE_NT_HEADERS pNt = GET_NT(buf); return IMAGE_FIRST_SECTION(pNt); }; //枚举区段表 BOOL EnumSection(char* buf) { PIMAGE_SECTION_HEADER pSection = GET_SECTION(buf); //解析 for (int i = 0;i < GET_NT(buf)->FileHeader.NumberOfSections;i++) { printf("【区块NAME 】IMAMEG_NAME:%s\n", pSection[i].Name); printf("【装载到内存RVA 】VirtualAddress:0x%08X\n", pSection[i].VirtualAddress); printf("【装载到内存大小 】SizeofRawData:0x%d\n", pSection[i].SizeOfRawData); printf("【在磁盘文件中RVA 】PointerToRawData:0x%08X\n", pSection[i].PointerToRawData); printf("【属性 】Characteristice:0x%08X\n\n", pSection[i].Characteristics); } return TRUE; } //枚举导出表 BOOL EnumExport(char* buf) { PIMAGE_NT_HEADERS pNt = GET_NT(buf); DWORD ExportRva = RvaToFoa(buf, pNt->OptionalHeader.DataDirectory[0].VirtualAddress); // VirtualAddress 是加载到内存之后的相对地址需要对其转换 PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)(ExportRva + buf); //表的总大小 导出表函数总数 + 导出表名称总数 DWORD dwSize = pExport->NumberOfFunctions + pExport->NumberOfNames; //地址表 PDWORD dwAddrTab = (PDWORD)(buf + RvaToFoa(buf, pExport->AddressOfFunctions)); //名称表 PDWORD dwNameTab = (PDWORD)(buf + RvaToFoa(buf, pExport->AddressOfNames)); //序号表 PWORD dwNumTab = (PWORD)(buf + RvaToFoa(buf, pExport->AddressOfNameOrdinals)); for (int i = 0;i < dwSize;i++) { // 序号表 for (int num = 0;num < pExport->NumberOfFunctions;num++) { //判断地址是否有效 if (dwAddrTab[i] == 0) { continue; } //------------------------------------检测是是否有名称 int j = 0; int nSign = FALSE; for (;j < pExport->NumberOfNames;j++) { if (i == dwNumTab[j]) //序号表里 存储名称表的下表 名称表和地址表下表是一样的 { nSign = TRUE; break; } } //---------------------------------- if (nSign == TRUE) { //名称表存储的是RVA DWORD dwFunNameFoa = RvaToFoa(buf, dwNameTab[j]); char* pFunName = buf + dwFunNameFoa; printf("0x%08X = %s\n", dwAddrTab[i], pFunName); break; } else { //什么都没找到那就是只有序号 //printf("%x\n", dwNumTab[i]); } } } return TRUE; } //枚举导入表 BOOL EnumImport(char* lpImage) { //1 获取到导入表的结构 PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImage; PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + lpImage); //1 获取到导入表的数据目录结构 PIMAGE_DATA_DIRECTORY dwImportDir = &pNt->OptionalHeader.DataDirectory[1]; //1 已经得到了FOA,直接就能够找到导入表的结构 PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)(lpImage + RvaToFoa(buf,dwImportDir->VirtualAddress)); if (pImportTable->Name == 0xffff) { printf("没有找到导入函数!\n"); return 1; } //2 开始解析 while (pImportTable->Name != 0) { //2.1 先解析DLL的名字 char* pDllName = (char*)(RvaToFoa(lpImage,pImportTable->Name) + lpImage); printf("\nDllName:%s\n", pDllName); //2.2 解析函数名字,选择用什么去解析 PIMAGE_THUNK_DATA32 pNameTable = (PIMAGE_THUNK_DATA32)(lpImage + RvaToFoa(lpImage, pImportTable->OriginalFirstThunk)); //2.3 开始解析名字 while (pNameTable->u1.Ordinal != 0) { //2.3.1 判断最高位是不是1 if (IMAGE_SNAP_BY_ORDINAL32(pNameTable->u1.Ordinal) == 1) { //只有序号 printf(" 序号:%x,名称:NULL\n", pNameTable->u1.Ordinal & 0x7FFFFFFF); } else { //既有名字,又有序号 PIMAGE_IMPORT_BY_NAME pName = (PIMAGE_IMPORT_BY_NAME)(RvaToFoa(lpImage, pNameTable->u1.AddressOfData) + lpImage); printf(" 序号:%x,名称:%s\n", pName->Hint, pName->Name); } pNameTable++; } printf("----------------------------\n"); pImportTable++; } return 0; } int main() { DWORD dwrealSize; // 打开文件获取句柄 HANDLE hFile = CreateFile(path, GENERIC_ALL, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); //获取文件大小 DWORD dwFileSize = GetFileSize(hFile, NULL); //申请空间并初始化 buf = new char[dwFileSize] {0}; //读取文件 ReadFile(hFile, buf, dwFileSize, &dwrealSize, NULL); //判断是否是PE文件 IS_PEFILE(buf); //枚举区段 //EnumSection(buf); //枚举导出表 //EnumExport(buf); //枚举导入表 EnumImport(buf); CloseHandle(hFile); delete[] buf; } ``` 最后修改:2021 年 03 月 01 日 © 允许规范转载 赞 0 如果觉得我的文章对你有用,请随意赞赏