• [PE结构分析] 8.输入表结构和输入地址表(IAT)


    在 PE文件头的 IMAGE_OPTIONAL_HEADER 结构中的 DataDirectory(数据目录表) 的第二个成员就是指向输入表的。每个被链接进来的 DLL文件都分别对应一个 IMAGE_IMPORT_DESCRIPTOR (简称IID) 数组结构。

    typedef struct _IMAGE_IMPORT_DESCRIPTOR {
        union {
            DWORD   Characteristics;            // 0 for terminating null import descriptor
            DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
        } DUMMYUNIONNAME;
        DWORD   TimeDateStamp;                  // 0 if not bound,
                                                // -1 if bound, and real date	ime stamp
                                                // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                                // O.W. date/time stamp of DLL bound to (Old BIND)
    
        DWORD   ForwarderChain;                 // -1 if no forwarders
        DWORD   Name;
        DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
    } IMAGE_IMPORT_DESCRIPTOR;
    typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

    在这个 IID数组中,并没有指出有多少个项(就是没有明确指明有多少个链接文件),但它最后是以一个全为NULL(0) 的 IID 作为结束的标志。

    下面只摘录比较重要的字段:

    OriginalFirstThunk

    它指向first thunk,IMAGE_THUNK_DATA,该 thunk 拥有 Hint 和 Function name 的地址。

    Name

    它表示DLL 名称的相对虚地址(译注:相对一个用null作为结束符的ASCII字符串的一个RVA,该字符串是该导入DLL文件的名称。如:KERNEL32.DLL)。

    FirstThunk

    它包含由IMAGE_THUNK_DATA定义的 first thunk数组的虚地址,通过loader用函数虚地址初始化thunk。

    在Orignal First Thunk缺席下,它指向first thunk:Hints和The Function names的thunks。

    下面来解释下OriginalFirstThunk和FirstThunk。就个人理解而言:

    1. 在文件中时,他们都分别指向一个RVA地址。这个地址转换到文件中,分别对应两个以 IMAGE_THUNK_DATA 为元素的的数组,这两个数组是以一个填充为 0 的IMAGE_THUNK_DATA作为结束标识符。虽然他们这两个表位置不同,但实际内容是一模一样的。此时,每个 IMAGE_THUNK_DATA 元素指向的是一个记录了函数名和相对应的DLL文件名的 IMAGE_IMPORT_BY_NAME结构体。

    2. 为什么会有两个一模一样的数组呢?是有原因的:

    OriginalFirstThunk 指向的数组通常叫做  hint-name table,即 HNT ,他在 PE 加载到内存中时被保留了下来且永远不会被修改。但是在 Windows 加载过 PE 到内存之后,Windows 会重写 FirstThunk 所指向的数组元素中的内容,使得数组中每个 IMAGE_THUNK_DATA 不再表示指向带有函数描述的 IMAGE_THUNK_DATA 元素,而是直接指向了函数地址。此时,FirstThunk 所指向的数组就称之为输入地址表(Import Address Table ,即经常说的 IAT)。

    重写前:

    重写后:

     

    (以上两张图片来自:http://www.dematte.org/2006/03/04/InterceptingWindowsAPIs.aspx

    typedef struct _IMAGE_THUNK_DATA32 {
        union {
            DWORD ForwarderString;      // PBYTE  指向一个转向者字符串的RVA
            DWORD Function;             // PDWORD 被输入的函数的内存地址
             DWORD Ordinal;              // 被输入的 API 的序数值
             DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME   指向 IMAGE_IMPORT_BY_NAME
        } u1;
    } IMAGE_THUNK_DATA32;
    typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

    根据 _IMAGE_THUNK_DATA32 所指虚拟地址转到文件地址可以得到实际的 _IMAGE_IMPORT_BY_NAME 数据

    typedef struct _IMAGE_IMPORT_BY_NAME {
        WORD   Hint;     // 序号 
        CHAR   Name[1];  // 实际上是一个可变长的以0为结尾的字符串
    } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

    例如有程序:

    00 源代码图片

    文字版:

    #include <windows.h>
    int WINAPI WinMain(_In_ HINSTANCE hInstance, 
        _In_opt_ HINSTANCE hPrevInstance,
        _In_ LPSTR lpCmdLine,
        _In_ int nShowCmd)
    {
        MessageBoxA(0, "hello", "my message", MB_OK);
        SetWindowTextA(0, "Si Wang");
        
        return 0;
    }

    此程序使用了两个 Windows API : MessageBoxA 和 SetWindowTextA

    编译得到程序(为简化说明,区段位置由软件计算出):

    0001c

    0001a

    我们试着找出 MessageBoxA。首先分析 PE 头文件,找到导出表在文件中的位置:

    001 输入表地址

    输入表位置在 .rdata 区段内, 0x2264 – 0x2000 = 0x0264 得到偏移量。加上文件地址 0x0E00 得到实际文件偏移量(0x0E00 + 0x264 = 0x1064):0x1064。

    接下来查看 0x1064 处:

    002 dll表

    可以得到三个 DLL 的描述,最后一个_IMAGE_IMPORT_DESCRIPTOR 以0填充表示结束:

    那么只要一个个查看每个DLL对应的数据就能找到,不过之前我把所有的数据都看了下,在第一个DLL中

    根据第一个DLL描述的 OriginalFirstThunk 的 0x2350 转换可以知道,_IMAGE_THUNK_DATA32 在文件的 0x1150处,FirstThunk 指向的数据相同:

    003 IMAGE_THUNK_DATA 表

    于是就得到了文件中的 MessageBoxA 的信息。

    最后,在内存中 FirstThunk 所指位置上的_IMAGE_THUNK_DATA32 数组被 Windows 加载后被重写后就成了传说中的 IAT ,Import Address Table,输入地址表。使用 OllyDbg 查看运行时情况:

    内存情况

  • 相关阅读:
    [php]php设计模式 (总结)
    MySql常用命令总结
    mysql常用命令
    搜集几个API接口
    c语言 11-7
    c语言中转换字符串函数 atoi函数
    c语言中 strncmp函数, 函数原型和头文件。
    c语言中strcmp函数,函数原型和函数头文件
    c语言中strncat函数,函数原型以头文件
    c语言中strcat函数,函数原型和函数头文件
  • 原文地址:https://www.cnblogs.com/night-ride-depart/p/5776107.html
Copyright © 2020-2023  润新知