PE文件结构

发布时间 2023-11-09 09:43:42作者: 乘舟凉

导入表

image

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 0 for terminating null import descriptor
        DWORD   OriginalFirstThunk;         // RVA 指向 INT (PIMAGE_THUNK_DATA结构数组)
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;                  // 0 if not bound,
                                            // -1 if bound, and real date\time 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;							//RVA指向dll名字,以0结尾
    DWORD   FirstThunk;                     // RVA 指向 IAT (PIMAGE_THUNK_DATA结构数组)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint; //可能为空,编译器决定,如果不为空,是函数在导出表的索引
    BYTE    Name[1]; //函数名称,以0结尾
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

导入表是一个数组,每一个元素都是一个IMAGE_IMPORT_DESCRIPTOR(最后一个元素为空来表示数组的结束,后面的数组都是这样的),每一个IMAGE_IMPORT_DESCRIPTOR都代表了一个dll,Name存放着dll名称的地址,OriginalFirstThunk存放着INT的地址,FirstThunk存放着IAT的地址。

INT也是一个数组,每一个元素都是一个ULONGLONG AddressOfData;,AddressOfData存放着IMAGE_IMPORT_BY_NAME的地址。

IAT也是一个数组,每一个元素都是一个ULONGLONG Function;,Function存放着导入函数的地址。

INTIAT的元素是根据索引一一对应的

IAT填充

下面介绍在PE加载过程中操作系统如何对IAT进行填充

  • 获取模块句柄

通过IMAGE_IMPORT_DESCRIPTOR的成员Name获取dll名称地址调用GetModuleHandle(dll函数名称),获取该dll模块句柄

  • 获取函数地址

首先通过OriginalFirstThunk,找到INT,通过INT的元素AddressOfData找到IMAGE_IMPORT_BY_NAME,若IMAGE_IMPORT_BY_NAME.Name不为空,则通过调用GetProcAddress(IMAGE_IMPORT_BY_NAME.Name)获取该函数地址,若IMAGE_IMPORT_BY_NAME.Name为空,则通过调用GetProcAddress(IMAGE_IMPORT_BY_NAME.Hint)获取该函数地址

  • 填充IAT

通过INT的元素AddressOfData的索引,找到IAT中对应的元素Function,将获取到的函数地址填充到Function