Loading... ## 什么是导出表 当dll文件提供函数给其他模块函数使用或者,_declspec(dllexport) 函数 声明导出。 用以记录本模块能够给其他模块提供的函数的信息。 函数名 函数地址 函数的序号 程序运行的时候,会检查主模块的导入表,看用了哪些其他模块,就会将此模块加载到进程空间中。加载进来之后,分析INT(IAT)得到函数名称,用这个名称去导出表中找到函数地址的RVA,RVA+模块基址,就是真正的函数地址,将此函数地址填充到IAT中,从而完成加载后的IAT功能。 一个PE文件,可以没有导出表的,比如exe文件,一般都没有。 如下图。 序号表是一个 WORD 2字节的数组 通过遍历这个数组的值 对应着地址表的下表 : 通过序号表的值获取地址表的地址,然后因为序号表和名称表是一一对应的所以说可以直接获取名称表,注意有些函数只有序号没有名称 ![image.png](http://www.irohane.top/usr/uploads/2021/03/674878923.png) ## 使用代码解析 ![请输入图片描述](http://www.irohane.top/usr/uploads/2021/03/2769148655.png) ``` #include <iostream> #include <Windows.h> #define path L"C:\\Users\\Administrator\\source\\repos\\测试\\Debug\\kernel32.dll" 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; } 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); CloseHandle(hFile); delete[] buf; } ``` 最后修改:2021 年 03 月 01 日 © 允许规范转载 赞 0 如果觉得我的文章对你有用,请随意赞赏