导入表的结构如下
xxxxxxxxxxtypedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; DWORD OriginalFirstThunk; } DUMMYUNIONNAME; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; DWORD FirstThunk;} IMAGE_IMPORT_DESCRIPTOR;需要关注的三个字段为:
OriginalFirstThunk是指向INT表的RVA,FirstThunk是指向IAT表的RVA
INT表和IAT表在文件中是完全一样的(前提是没有绑定导入)

IMAGE_THUNK_DATA是一个4字节的数据,如果最高位是1,那么低31位就是函数的导出序号;如果最高位是0,那么它的值是一个RVA,指向一个IMAGE_IMPORT_BY_NAME结构
IMAGE_THUNK_DATA结构(不需要把它当成struct)
xxxxxxxxxxtypedef struct _IMAGE_THUNK_DATA32 { union { DWORD ForwarderString; DWORD Function; DWORD Ordinal; DWORD AddressOfData; } u1;} IMAGE_THUNK_DATA32;IMAGE_IMPORT_BY_NAME结构
xxxxxxxxxxtypedef struct _IMAGE_IMPORT_BY_NAME { WORD Hint; CHAR Name[1];} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;其中低地址的Hint是导出序号,然而这个值可能并不准确,有些编译器会把它设置成0,我们只需要关注 Name,这个是一个以0结尾的字符串,表示函数名
如果没有提前绑定函数地址,IAT表和INT表在文件中存储的数据是完全一样的

在PE加载前后IAT表变化

参考之前的文章,拿到导出表的FOA和RVA
其中IMAGE_DIRECTORY_ENTRY_IMPORT是宏定义的1,导入表位于数据目录第二个
xxxxxxxxxxpDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;if (*((PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE){ return;}pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 0x04);pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);pDataDirectory = (PIMAGE_DATA_DIRECTORY)pOptionHeader->DataDirectory;
pDataDirectoryImportTable = &pDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
if (!pDataDirectoryImportTable->VirtualAddress){ printf("这个程序不存在导入表\n"); return;}
printf("Import Table RVA: %#010x\n", pDataDirectoryImportTable->VirtualAddress);DWORD FoaImportTable = RvaToFileOffset(pFileBuffer, pDataDirectoryImportTable->VirtualAddress);printf("Import Table FOA: %#010x\n", FoaImportTable);回顾PE的加载过程:
循环所有导出表(如果名字为空认为读取结束)
xxxxxxxxxxpImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pDosHeader + FoaImportTable);
while (pImportTable->Name != 0){ // ... pImportTable++;}拿到DLL名称,注意转为FOA
xxxxxxxxxxDWORD FoaDllName = RvaToFileOffset(pFileBuffer, pImportTable->Name);PDWORD pFoaDllName = (PDWORD)((DWORD)pFileBuffer + FoaDllName);printf("%s\n", (LPSTR)pFoaDllName);拿到OriginalFirstThunk结构体进行遍历
xxxxxxxxxxDWORD FoaOriginalFirstThunk = RvaToFileOffset(pFileBuffer, pImportTable->OriginalFirstThunk);PDWORD pFoaOriginalFirstThunk = (PDWORD)((DWORD)pFileBuffer + FoaOriginalFirstThunk);pOriginalFirstThunk = (PIMAGE_THUNK_DATA)pFoaOriginalFirstThunk;xxxxxxxxxxwhile (*(PDWORD)pOriginalFirstThunk){ // ... pOriginalFirstThunk++;}
参考开头部分的内容:IMAGE_THUNK_DATA是一个4字节的数据,如果最高位是1,那么低31位就是函数的导出序号;如果最高位是0,那么它的值是一个RVA,指向一个IMAGE_IMPORT_BY_NAME结构
拿到这个值的最高位(其中IMAGE_ORDINAL_FLAG32是最高位为1的DWORD)
xxxxxxxxxxDWORD value = *(PDWORD)pOriginalFirstThunk;DWORD cntf = (value & IMAGE_ORDINAL_FLAG32) >> 31;最高位为1时输出,为0时转FOA后计算偏移转IMAGE_IMPORT_BY_NAME结构体拿到名称
xxxxxxxxxxif (cntf){ value -= IMAGE_ORDINAL_FLAG32; printf("Import Ordinal --> %#010x\n", value);}else{ DWORD FoaImportByName = RvaToFileOffset(pFileBuffer, value); PDWORD pFoaImportByName = (PDWORD)((DWORD)pFileBuffer + FoaImportByName); pImportByName = (PIMAGE_IMPORT_BY_NAME)pFoaImportByName; printf("Import Hint Name --> %#-028s Import Address --> %#010x\n", pImportByName->Name, pImportByName->Hint);}
打印FirstThunk部分代码逻辑类似,整体代码如下
xxxxxxxxxxVOID LogImportTable(IN 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_DATA_DIRECTORY pDataDirectory = NULL; PIMAGE_DATA_DIRECTORY pDataDirectoryImportTable = NULL; PIMAGE_IMPORT_DESCRIPTOR pImportTable = NULL; PIMAGE_IMPORT_BY_NAME pImportByName = NULL; PIMAGE_THUNK_DATA pOriginalFirstThunk = NULL; PIMAGE_THUNK_DATA pFirstThunk = NULL;
if (pFileBuffer == NULL) { return; } if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE) { return; } pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; if (*((PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) { return; } pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew); pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 0x04); pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER); pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader); pDataDirectory = (PIMAGE_DATA_DIRECTORY)pOptionHeader->DataDirectory;
pDataDirectoryImportTable = &pDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
if (!pDataDirectoryImportTable->VirtualAddress) { printf("这个程序不存在导入表\n"); return; }
printf("Import Table RVA: %#010x\n", pDataDirectoryImportTable->VirtualAddress); DWORD FoaImportTable = RvaToFileOffset(pFileBuffer, pDataDirectoryImportTable->VirtualAddress); printf("Import Table FOA: %#010x\n", FoaImportTable);
pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pDosHeader + FoaImportTable);
while (pImportTable->Name != 0) { DWORD FoaDllName = RvaToFileOffset(pFileBuffer, pImportTable->Name); PDWORD pFoaDllName = (PDWORD)((DWORD)pFileBuffer + FoaDllName); printf("%s\n", (LPSTR)pFoaDllName);
DWORD FoaOriginalFirstThunk = RvaToFileOffset(pFileBuffer, pImportTable->OriginalFirstThunk); PDWORD pFoaOriginalFirstThunk = (PDWORD)((DWORD)pFileBuffer + FoaOriginalFirstThunk); pOriginalFirstThunk = (PIMAGE_THUNK_DATA)pFoaOriginalFirstThunk;
while (*(PDWORD)pOriginalFirstThunk) { DWORD value = *(PDWORD)pOriginalFirstThunk; DWORD cntf = (value & IMAGE_ORDINAL_FLAG32) >> 31;
if (cntf) { value -= IMAGE_ORDINAL_FLAG32; printf("Import Ordinal --> %#010x\n", value); } else { DWORD FoaImportByName = RvaToFileOffset(pFileBuffer, value); PDWORD pFoaImportByName = (PDWORD)((DWORD)pFileBuffer + FoaImportByName); pImportByName = (PIMAGE_IMPORT_BY_NAME)pFoaImportByName; printf("Import Hint Name --> %#-028s Import Address --> %#010x\n", pImportByName->Name, pImportByName->Hint); } pOriginalFirstThunk++; }
DWORD FoaFirstThunk = RvaToFileOffset(pFileBuffer, pImportTable->FirstThunk); PDWORD pFoaFirstThunk = (PDWORD)((DWORD)pDosHeader + FoaFirstThunk); pFirstThunk = (PIMAGE_THUNK_DATA)pFoaFirstThunk; while (*(PDWORD)pFirstThunk) { DWORD value = *(PDWORD)pFirstThunk; DWORD cntf = (value & IMAGE_ORDINAL_FLAG32) >> 31; if (cntf) { value -= IMAGE_ORDINAL_FLAG32; printf("Import Ordinal --> %#010x\n", value); } else { DWORD FoaImportByName = RvaToFileOffset(pFileBuffer, value); PDWORD pFoaImportByName = (PDWORD)((DWORD)pFileBuffer + FoaImportByName); pImportByName = (PIMAGE_IMPORT_BY_NAME)pFoaImportByName; printf("Import Hint Name --> %#-028s Import Address --> %#010x\n", pImportByName->Name, pImportByName->Hint); } pFirstThunk++; } printf("\n"); pImportTable++; }}
某非WINDOWS自带EXE截图

NOTEPAD截图(为什么这里的FirstThunk内的Name为空,参考下篇文章)
