导入表的结构如下
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为空,参考下篇文章)
