Windows基础知识之.idata节区详解
Aking9 发表于 河南 二进制安全 630浏览 · 2024-07-19 15:47

.idata详解

前置信息

.idata节区是可执行文件中的一个常见节区,用于存储导入表(Import Table)的相关信息。

在可执行文件中,导入表用于记录程序需要从外部导入的函数和符号的信息。这些函数和符号通常是由其他模块(如动态链接库)提供的,程序在运行时需要通过导入表来获取这些函数和符号的地址,并进行调用或引用。

IMAGE_DATA_DIRECTORY

各节区的信息首次出现在IMAGE_NT_HEADERS.IMAGE_OPTIONAL_HEADER32.DataDirectory[]

我们可以得到Import Table-IT的基本信息:

  • 相对地址:
    • RVA:00 30 00 003000h
    • FOA:0xE00
  • 导入表大小:70 06 00 00 ,即0x670 --> 1648

在OllyDbg中的展示

DataDirectory:由IMAGE_DATA_DIRECTORY结构体组成的数组

struct _IMAGE_DATA_DIRECTORY
{
    ULONG VirtualAddress;                                                   //0x0
    ULONG Size;                                                             //0x4
};
  • VirtualAddress:数据目录的起始地址,以相对虚拟地址(Relative Virtual Address, RVA)的形式表示
  • Size:数据目录的大小,以字节为单位。表示从 VirtualAddress 开始的数据段的总长度。

DataDirectory列表i信息如下

#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory
//      IMAGE_DIRECTORY_ENTRY_COPYRIGHT       7   // (X86 usage)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor
#define Reserved                             15   //系统保留

_IMAGE_SECTION_HEADER

节区表的基本信息在_IMAGE_SECTION_HEADER头中

这里面主要信息如下:

  • Name:长度为8的ASCII节区名.idata

  • VirtualSize.idata节区大小 4096,未对齐处理前节区实际使用的大小

  • VirtualAddress:内存中节区起始地址3000(RVA),与IMAGE_DATA_DIRECTORY照应。

  • SizeOfRawData:磁盘文件中节区所占大小800,前面我们得知导入表的大小为0x670

    这里是0x800h,是因为在文件中以200h为一个单位来存储

  • PointerToRawData:磁盘文件中的起始地址E00h(FOA)

在OllDbg中的展示:

节表的结构如下,整体为40个字节

//0x28 bytes (sizeof)
struct _IMAGE_SECTION_HEADER
{
    UCHAR Name[8];                                                          //0x0
    union
    {
        ULONG PhysicalAddress;                                              //0x8
        ULONG VirtualSize;                                                  //0x8
    } Misc;                                                                 //0x8
    ULONG VirtualAddress;                                                   //0xc
    ULONG SizeOfRawData;                                                    //0x10
    ULONG PointerToRawData;                                                 //0x14
    ULONG PointerToRelocations;                                             //0x18
    ULONG PointerToLinenumbers;                                             //0x1c
    USHORT NumberOfRelocations;                                             //0x20
    USHORT NumberOfLinenumbers;                                             //0x22
    ULONG Characteristics;                                                  //0x24
};

重要成员:

  • Name:长度为8的ASCII节区名(比如.text)。
  • VirtualSize-0x08:未对齐处理前节区实际使用的大小
  • VirtualAddress-0x0C:内存中节区起始地址(RVA)
  • SizeOfRawData-0x10:磁盘文件中节区所占大小
  • PointerToRawData-0X14:磁盘文件中的起始地址FOA
  • Charateristics-0x24:节区属性(bit OR)

结构详解

导入表的数据结构包含以下几个主要部分:

  1. 导入描述符(Import Descriptor):导入描述符是一个指向导入表中一个DLL模块的指针。每个导入描述符对应一个被依赖的DLL模块,它包含了该模块的名称、导入地址表(Import Address Table)的位置的位置等信息。
  2. 导入地址表(Import Address Table):导入地址表是一个指针数组,用于存储被依赖的外部函数或符号的地址。每个指针指向相应的外部函数或符号在内存中的地址。在程序加载时,这些指针会被动态地填充为正确的地址。
  3. 导入名称表(Import Name Table):导入名称表是一个存储外部函数或符号名称的表格,用于与导入地址表进行对应。每个表项包含了一个被依赖的外部函数或符号的名称。
  4. 段定位表(Thunk Table):段定位表是一个指针数组,用于存储导入地址表中每个表项的位置。它指示了导入地址表中每个表项的地址或名称的位置。

导入描述符(Import Descriptor)

.idata的起始部分是导入描述符

关键信息:

  • OriginalFirstThunk:,里面的内容是,导入名称表(Import Name Table)存储的API的Name的指针。
    • 虚拟偏移地址3078h(RVA)
    • 文件偏移地址0xE78(FOA)
  • Name:要导入的DLL的文件Name的地址3290h(RVA),文件地址0x1090(FOA)。
  • FirstThunk:实际IAT的起始地址 3184h(RVA),IAT填充后里面存储的是API的实际内存地址
    • 在未导入内存中和OriginalFirstThunk内容一样
    • 导入内存中是API的实际地址。

ollyDbg中显示

我们查看两个IAT地址区别,未导入内存中

IMAGE_IMPORT_DESCRIPTOR

IMAGE_IMPORT_DESCRIPTOR是一个结构体,用于描述可执行文件中的导入表信息。它定义在Windows的PE文件格式中,用于存储导入表的相关信息。

//winnt.h
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;            // 用于终止的空导入描述符时为0
        DWORD   OriginalFirstThunk;         // 指向INT,指向原始未绑定导入地址表(PIMAGE_THUNK_DATA)的RVA
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;                  // 如果未绑定为0,已绑定为-1,实际时间戳存储在IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT中

    DWORD   ForwarderChain;                 // 如果没有前向链为-1
    DWORD   Name;                           // DLL模块名称的RVA
    DWORD   FirstThunk;                     // 导入地址表(IAT)的RVA
} IMAGE_IMPORT_DESCRIPTOR, *PIMAGE_IMPORT_DESCRIPTOR;
  • Characteristics/OriginalFirstThunk:这是一个联合体,用于表示导入描述符的特征或原始未绑定的导入地址表(INT)的RVA(相对虚拟地址)。
  • TimeDateStamp:用于表示DLL模块的时间戳。如果DLL模块没有绑定,该值为0;如果已绑定,该值为-1,并且实际的时间戳存储在IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT中。
  • ForwarderChain:表示导入表的前向链的索引。如果没有前向链,该值为-1。
  • Name:表示DLL模块的名称的RVA。
  • FirstThunk:表示导入地址表(Import Address Table,IAT)的RVA。如果已绑定,该IAT包含实际的地址。

IMAGE_THUNK_DATA32

IMAGE_THUNK_DATA32是一个结构体,用于描述导入表和延迟加载表中的函数地址。它定义在Windows的PE文件格式中,用于存储函数地址或函数名称的相关信息。

typedef struct _IMAGE_THUNK_DATA32 {
    union {
        DWORD ForwarderString;      // 如果是转发函数,指向转发字符串的RVA
        DWORD Function;             // 函数地址或函数名称的RVA
        DWORD Ordinal;              // 函数的序号
        DWORD AddressOfData;        // 指向IMAGE_IMPORT_BY_NAME结构体的RVA
    } u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

导入地址表(Import Address Table)

我们前面得知IMAGE_IMPORT_DESCRIPTOR.FirstThunk存储的是IAT的RVA。

导入地址表(Import Address Table):导入地址表是一个指针数组,用于存储被依赖的外部函数或符号的地址。每个指针指向相应的外部函数或符号在内存中的地址。

  • 程序未运行时:IAT存储的是API的Name指针
  • 在程序加载时,这些指针会被动态地填充为正确的地址。

我们以CRACKME.EXE为案例,IAT的FOA0xF84

我们在OllyDbg中查看,导入内存中

我们以KillTimer为案例

  • [F84] == CC 32 00 00,即KillTimer的Name指针

    ImageBase + [F84]存储是..KillTimer

  • 操作系统可以根据这个指针,定位到相应的API函数名称,然后通过调用GetProcAddress来获取MessageBoxA的地址0x75FC2B30

    然后将其覆盖FAC的内容 50 1A A5 75

因此我们可以这样理解

  • OriginalFirstThunk:原始IAT的起始地址3078h(RVA),里面的内容是,导入名称表(Import Name Table)存储的API的Name
  • Name:要导入的DLL的文件Name的地址3290h(RVA),文件地址0x1090(FOA)。
  • FirstThunk:实际IAT的起始地址 3184h(RVA),IAT填充后里面存储的是API的实际内存地址

导入名称表(Import Name Table)

我们通过010 Editor查看得知,在DLL_Name后面就是各DLL的API函数名称

导入名称表(Import Name Table):导入名称表是一个存储外部函数或符号名称的表格,用于与导入地址表进行对应。每个表项包含了一个被依赖的外部函数或符号的名称。

IMAGE_IMPORT_BY_NAME是一个结构体,用于描述导入表中每个函数的信息。它定义在Windows的PE文件格式中,用于存储导入函数的名称和序号。

typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD    Hint;       // 函数的序号
    BYTE    Name[1];    // 函数的名称
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
  • Hint:函数的序号,用于快速查找函数。在导入表中,函数的名称通常会与其序号一起存储,以提高查找效率。
  • Name:函数的名称。由于结构体中的Name成员只定义了一个字节,实际上,函数的名称是一个以null结尾的字符串,该字符串紧随Hint成员之后。

IAT 填充过程

  1. 解析PE文件头

    当一个可执行文件(EXE或DLL)加载到内存中时,操作系统的PE(Portable Executable)加载器首先解析PE文件头,以找到导入表(Import Table)的位置。

  2. 定位导入表

    导入表包含了程序需要的所有DLL及其导入的函数名称。导入表的结构包括一个或多个导入描述符,每个描述符对应一个DLL。

  3. 加载DLL

    对于每个导入描述符,加载器会加载相应的DLL文件到内存中。如果DLL文件尚未加载,则加载器调用LoadLibrary函数加载它。

  4. 获取函数地址

    加载器解析导入描述符中的函数名称,并调用GetProcAddress获取每个导入函数的实际内存地址。

  5. 填充IAT

    一旦函数地址被解析,加载器将这些地址填充到IAT中。此时,IAT中的每个条目都指向实际的DLL函数地址。

附件:
0 条评论
某人
表情
可输入 255