WindowsAPI解析IAT地址

#include <stdio.h>
#include <windows.h>

int main(int argc,char* argv[]){
    /*
    typedef struct _IMAGE_IMPORT_DESCRIPTOR {
        union{
            DWORD   Characteristics;
            DWORD   OriginalFirstThunk;
        };
        DWORD   TimeDateStamp;
        DWORD   ForwarderChain;      
        DWORD   Name; // DLL名称的RVA偏移
        DWORD   FirstThunk; // IAT真实地址的RVA偏移
    }IMAGE_IMPORT_DESCRIPTOR;
    */

    /*
    typedef struct _IMAGE_THUNK_DATA32{
        union{
            DWORD ForwarderString;
            DWORD Function;
            DWORD Ordinal;
            DWORD AddressOfData; // RVA指向_IMAGE_IMPORT_BY_NAME 
        }u1;
    }IMAGE_THUNK_DATA32;
    */

    /*
   typedef struct _IMAGE_IMPORT_BY_NAME{
        WORD    Hint;
        BYTE    Name[1];
    }IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
    */
    HMODULE hmod;
    PIMAGE_DOS_HEADER pdos;
    PIMAGE_NT_HEADERS pnt;
    PIMAGE_OPTIONAL_HEADER popt;
    PIMAGE_IMPORT_DESCRIPTOR piid;
    PIMAGE_THUNK_DATA pfuncname_addr,pfunc_addr;
    
    hmod = GetModuleHandle(NULL); // HMODULE类型不能直接参与运算,要强制转换为字节流
    pdos = (PIMAGE_DOS_HEADER)hmod;
    pnt = (PIMAGE_NT_HEADERS)((BYTE *)hmod + pdos->e_lfanew); // NT_offset = dos.e_lfanew
    popt = (PIMAGE_OPTIONAL_HEADER)(&(pnt->OptionalHeader));
    piid = (PIMAGE_IMPORT_DESCRIPTOR)((BYTE *)hmod+popt->DataDirectory[1].VirtualAddress); // Base_addr + RVA_addr
    while(piid->FirstThunk){
        pfuncname_addr = (PIMAGE_THUNK_DATA)((BYTE *)hmod + piid->OriginalFirstThunk);
        pfunc_addr = (PIMAGE_THUNK_DATA)((BYTE *)hmod + piid->FirstThunk);
        char* pdll_name = (char *)((BYTE *)hmod + piid->Name);
        printf("DLL:%s
",pdll_name);
        printf("|
");
        while(pfuncname_addr->u1.Function){
            char* func_name = (char *)((BYTE *)hmod + pfuncname_addr->u1.AddressOfData+2);
            DWORD lpAddr = pfunc_addr->u1.AddressOfData;
            printf("Func_Name:%s	",func_name); // OriginalFirstThunk对应一个结构体数组,分别是每个函数的名称
            printf("Addr:0x%04x
",lpAddr); // FirstThunk对应一个结构体数组,开始与OriginalFirstThunk相同,之后填入函数的真实地址
            pfuncname_addr++;
            pfunc_addr++;
        }
        printf("
");
        piid++; // 每个DLL对应一个IID项
    }
    getchar();
    return 0;
}
简单解释

通过获取本进程的模块句柄,从而定位到文件载入的内存区域!再用 DOS 头的 e_lfanew 偏移到 NT 头,用 NT 头找到 Optional 头。其中 Optional 的 DataDirectory 的第一个索引项就对应第一个 IID 项,再通过 IID 定位到 OriginalFirstThunk 和 FirstThunk,循环获取每一个函数的名称与内存地址!再外层循环每一个 DLL!最终打印出所有 DLL 的 IAT 函数名称与内存地址!

注意

OriginalFirstThunk指向的 u1.AddressOfData 是 Hint/Name 所在的结构体
FirstThunk指向的 u1.AddressOfData 此时已经填入函数在内存的真实地址

原文地址:https://www.cnblogs.com/laohaozi/p/12537624.html