数据目录项 在PIMAGE_OPTIONAL_HEADER
结构下的最后两个成员NumberOfRvaAndSizes
标识了目录项的数量(为16),IMAGE_DATA_DIRECTORY
是一个数组,其每个数组成员标识了表的VA和大小,每一个模块都有一个IMAGE_DATA_DIRECTORY成员,结构如下
1 2 3 4 struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; };
按顺序分别是:导出表、导入表、资源表、异常信息表、安全证书表、重定位表、调试信息表、版权所有表、全局指针表、TLS表、加载配置表、绑定导入表、IAT表、延迟导入表、COM信息表,最后一个保留未使用。
遍历目录项数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <Windows.h> #include <stdio.h> #include <stdlib.h> #include <PETool.h> #pragma comment(lib,"PETool.lib" ) #define FILEPATH TEXT("D:\\absolution\\temporary\\test.exe" ) #define OUTFILEPATH TEXT("D:\\absolution\\temporary\\output.exe" ) int main () { LPVOID lpFileBuffer = createFileBuffer(FILEPATH); if (!lpFileBuffer) { return 0 ; } DWORD dwSize; LPVOID lpImageBuffer = file2Image(lpFileBuffer, &dwSize); PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpImageBuffer; PIMAGE_NT_HEADERS pNT = (PIMAGE_NT_HEADERS)((DWORD)lpImageBuffer + pDos->e_lfanew); PIMAGE_FILE_HEADER pFile = (PIMAGE_FILE_HEADER)((DWORD)pNT + 4 ); PIMAGE_OPTIONAL_HEADER pOption = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFile + IMAGE_SIZEOF_FILE_HEADER); PIMAGE_SECTION_HEADER pFirstSection = (PIMAGE_SECTION_HEADER)((DWORD)pOption + pFile->SizeOfOptionalHeader); for (int i = 0 ; i < pOption->NumberOfRvaAndSizes; i++) { IMAGE_DATA_DIRECTORY IDD = pOption->DataDirectory[i]; printf ("IMAGE_DATA_DIRECTORY:%d \n%X\n%X\n---------\n" , i, IDD.VirtualAddress, IDD.Size); } free (lpImageBuffer); free (lpFileBuffer); getchar(); }
导出表 导出表结构 1 2 3 4 typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; }
第一个成员指向的地址为导出表,结构如下,总共40个字节。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; DWORD Name; DWORD Base; DWORD NumberOfFunctions; DWORD NumberOfNames; DWORD AddressOfFunctions; DWORD AddressOfNames; DWORD AddressOfNameOrdinals; } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
NumberOfFunctions是通过导出序号计算出来的,如果序号有断档函数个数也会增加,序号不一定是连续的整数,序号最大的数 - 最小的数 + 1得到NumberOfFunctions
名称表和地址表地址指向的数据占4字节,序号表占2字节。
GetProcAddress如何找到函数地址
名字导出 GetProcAddress函数调用dll时,如果传入函数名则会检索函数名称表,然后通过在函数名称表中的顺序(下标)在序号表中寻找,通过序号表找到的序号(作为下标)在地址表中检索
序号导出序号导出只需要用到地址表 ,导出表的Base成员标识了导出函数的起始序号,序号表中的值 + Base 得到真正的导出序号。如果传入序号则直接在地址表中检索到函数地址 :导出序号 - Base 得到地址表的下标所以实际上导出函数里NONAME的函数是不计算在名称表和序号表的,序号表只是给名称表提供下标用的
综上所诉,如果导出序号不是连续的,地址表中会用0填充以保证使用下标可以检索到函数地址
遍历导出表 通过程序遍历导出表的练习,并编写函数模拟GetProcAddress的功能实现2种方式获取函数地址。GetProcAddress中的hModel实际上是ImageBase,模拟时使用fileBuffer,在函数内部转换。
需要注意一点,名称表里储存的依然是RVA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 #include <Windows.h> #include <stdio.h> #include <stdlib.h> #define FILEPATH TEXT("D:\\absolution\\temporary\\test.exe" ) #define OUTFILEPATH TEXT("D:\\absolution\\temporary\\output.exe" ) #define DLLPATH TEXT("D:\\absolution\\temporary\\myDll.dll" ) #include <PETool.h> #pragma comment(lib,"PETool.lib" ) DWORD getFunctionAddressByName (LPVOID lpFileBuffer, LPCTSTR checkName) { PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpFileBuffer; PIMAGE_NT_HEADERS pNT = (PIMAGE_NT_HEADERS)((DWORD)lpFileBuffer + pDos->e_lfanew); PIMAGE_FILE_HEADER pFile = (PIMAGE_FILE_HEADER)((DWORD)pNT + 4 ); PIMAGE_OPTIONAL_HEADER pOption = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFile + IMAGE_SIZEOF_FILE_HEADER); PIMAGE_SECTION_HEADER pFirstSection = (PIMAGE_SECTION_HEADER)((DWORD)pOption + pFile->SizeOfOptionalHeader); PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)(RVA2FOA(lpFileBuffer, pOption->DataDirectory[0 ].VirtualAddress) + (DWORD)lpFileBuffer); PDWORD lpAddOfFns = (PDWORD)(RVA2FOA(lpFileBuffer, (DWORD)pExportDir->AddressOfFunctions) + (DWORD)lpFileBuffer); PDWORD lpAddOfNames = (PDWORD)(RVA2FOA(lpFileBuffer, (DWORD)pExportDir->AddressOfNames) + (DWORD)lpFileBuffer); PWORD lpAddOfOrdinals = (PWORD)(RVA2FOA(lpFileBuffer, (DWORD)pExportDir->AddressOfNameOrdinals) + (DWORD)lpFileBuffer); DWORD FnAddr = 0 ; for (int i = 0 ; i < pExportDir->NumberOfNames; i++) { LPCTSTR fnName = (LPCTSTR)(RVA2FOA(lpFileBuffer, lpAddOfNames[i]) + (DWORD)lpFileBuffer); if (!strcmp (fnName, checkName)) { DWORD fnOrdinal = lpAddOfOrdinals[i]; FnAddr = lpAddOfFns[fnOrdinal]; return FnAddr; } } } DWORD getFunctionAddressByOrdinals (LPVOID lpFileBuffer, DWORD checkOrdinal) { PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpFileBuffer; PIMAGE_NT_HEADERS pNT = (PIMAGE_NT_HEADERS)((DWORD)lpFileBuffer + pDos->e_lfanew); PIMAGE_FILE_HEADER pFile = (PIMAGE_FILE_HEADER)((DWORD)pNT + 4 ); PIMAGE_OPTIONAL_HEADER pOption = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFile + IMAGE_SIZEOF_FILE_HEADER); PIMAGE_SECTION_HEADER pFirstSection = (PIMAGE_SECTION_HEADER)((DWORD)pOption + pFile->SizeOfOptionalHeader); PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)(RVA2FOA(lpFileBuffer, pOption->DataDirectory[0 ].VirtualAddress) + (DWORD)lpFileBuffer); PDWORD lpAddOfFns = (PDWORD)(RVA2FOA(lpFileBuffer, (DWORD)pExportDir->AddressOfFunctions) + (DWORD)lpFileBuffer); PDWORD lpAddOfNames = (PDWORD)(RVA2FOA(lpFileBuffer, (DWORD)pExportDir->AddressOfNames) + (DWORD)lpFileBuffer); PWORD lpAddOfOrdinals = (PWORD)(RVA2FOA(lpFileBuffer, (DWORD)pExportDir->AddressOfNameOrdinals) + (DWORD)lpFileBuffer); DWORD ordinal = checkOrdinal - pExportDir->Base; if (ordinal >= pExportDir->NumberOfFunctions || ordinal < 0 ) { return 0 ; } DWORD fnAddr = lpAddOfFns[ordinal]; return fnAddr; } int main () { LPVOID lpFileBuffer = createFileBuffer(DLLPATH); PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)lpFileBuffer; PIMAGE_NT_HEADERS pNT = (PIMAGE_NT_HEADERS)((DWORD)lpFileBuffer + pDos->e_lfanew); PIMAGE_FILE_HEADER pFile = (PIMAGE_FILE_HEADER)((DWORD)pNT + 4 ); PIMAGE_OPTIONAL_HEADER pOption = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFile + IMAGE_SIZEOF_FILE_HEADER); PIMAGE_SECTION_HEADER pFirstSection = (PIMAGE_SECTION_HEADER)((DWORD)pOption + pFile->SizeOfOptionalHeader); PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)(RVA2FOA(lpFileBuffer, pOption->DataDirectory[0 ].VirtualAddress) + (DWORD)lpFileBuffer); PDWORD lpAddOfFns = (PDWORD)(RVA2FOA(lpFileBuffer, (DWORD)pExportDir->AddressOfFunctions) + (DWORD)lpFileBuffer); PDWORD lpAddOfNames = (PDWORD)(RVA2FOA(lpFileBuffer, (DWORD)pExportDir->AddressOfNames) + (DWORD)lpFileBuffer); PWORD lpAddOfOrdinals = (PWORD)(RVA2FOA(lpFileBuffer, (DWORD)pExportDir->AddressOfNameOrdinals) + (DWORD)lpFileBuffer); for (int i = 0 ; i < pExportDir->NumberOfFunctions; i++) { if (lpAddOfFns[i]) { printf ("地址:0x%X\n" , lpAddOfFns[i]); } } for (int i = 0 ; i < pExportDir->NumberOfNames; i++) { printf ("序号:0x%x\n" , lpAddOfOrdinals[i] + pExportDir->Base); } for (int i = 0 ; i < pExportDir->NumberOfNames; i++) { printf ("名称:%s\n" , RVA2FOA(lpFileBuffer, (DWORD)lpAddOfNames[i]) + (DWORD)lpFileBuffer); } DWORD fnAddr; fnAddr = getFunctionAddressByName(lpFileBuffer, "div" ); if (fnAddr) { printf ("div地址为:0X%X\n" , fnAddr); } else { printf ("未找到名为\"div\"的函数\n" ); } fnAddr = getFunctionAddressByOrdinals(lpFileBuffer, 7 ); if (fnAddr) { printf ("mul地址为:0X%X\n" , fnAddr); } else { printf ("未找到导出序号为\"7\"的函数\n" ); } free (lpFileBuffer); getchar(); }