avatar

IAT结构分析

IAT结构分析

IAT介绍

解决兼容问题(不同机器的dll版本不同,地址自然不同),操作系统就必须提供一些措施来确保可以在其他版本的Windows操作系统,以及DLL版本下也能正常运行。

这时IAT(Import Address Table:输入函数地址表)就应运而生了。

IAT定位

手工寻找

  • 利用PEview找到程序的NT头的可选头内有个import table此处记录了其RVA。
  • 根据结构体 IMAGE_IMPORT_DESCRIPTOR即导入表结构确定dll名称和IAT表的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
typedef struct _IMAGE_IMPORT_DESCRIPTOR {

union {

DWORD Characteristics;

DWORD OriginalFirstThunk;//该字段指向导入名称表(INT),该RVA是一个IMAGE_THUNK_DATA结构体0x01b218

};

DWORD TimeDateStamp;//可以忽略,一般为0

DWORD ForwarderChain;//一般为0

DWORD Name;//指向DLL的名称的RVA地址

DWORD FirstThunk;//该字段包含导入地址表(IAT)的RVA,IAT是一个IMAGE_THUNK_DATA结构体数组 0x01b000 0x01b3e0

} IMAGE_IMPORT_DESCRIPTOR;
  • 根据IAT表的指针 找到导入函数的序数和该函数的名字
1
2
3
4
5
6
7
typedef struct _IMAGE_IMPORT_BY_NAME {

WORD Hint; //该函数的导出序数

BYTE Name[1]; // 该函数的名字

} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

程序实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include<windows.h>
#include<stdio.h>
BOOL ImportAddressTablefind(IN HMODULE hModule)
{
IMAGE_DOS_HEADER* pImageDosHearder = (IMAGE_DOS_HEADER*)hModule;
IMAGE_OPTIONAL_HEADER* pImageOptionalHeader = (IMAGE_OPTIONAL_HEADER*)((DWORD)hModule + pImageDosHearder->e_lfanew + 24);//获取可选头的地址
IMAGE_IMPORT_DESCRIPTOR* pImageImportDescriptor = (IMAGE_IMPORT_DESCRIPTOR*)
((DWORD)hModule + pImageOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);//导入表地址

while (pImageImportDescriptor != 0)
{
LPCSTR base = (PSTR)(PBYTE)GetModuleHandle(NULL);
LPCSTR pszModName = (PSTR)(base + pImageImportDescriptor->Name);//导入表动态链接库名称
IMAGE_THUNK_DATA* pImageThunkData = (IMAGE_THUNK_DATA*)((DWORD)hModule + pImageImportDescriptor->FirstThunk);//获取动态链接库中的IAT函数地址0x1b000
IMAGE_IMPORT_BY_NAME ** Original =(IMAGE_IMPORT_BY_NAME **) ((DWORD)hModule + pImageImportDescriptor->OriginalFirstThunk);//获取0x1b214//0x
while (pImageThunkData->u1.Function)
{

unsigned int fun = (unsigned int)(PBYTE)(*Original)->Name;
LPCSTR funcname =base+ fun;
LPDWORD lpFunctionAddress = (LPDWORD)&(pImageThunkData->u1.Function);
__try
{
printf("dllname:%s funcname:%s addr:%x\n", pszModName, funcname, *lpFunctionAddress);
}
__except (1)
{
system("pause");
}
pImageThunkData++;
*Original++;
}


pImageImportDescriptor++;
}
return true;
}
int main()
{
ImportAddressTablefind(::GetModuleHandle(NULL));
system("pause");
}

IAT脱壳修复

原因

我们运行程序,利用OD将其直接dump,此时我们发现,依据导入表找到IAT指针,发现指针为0。但是脱壳结束时IAT地址已经被写好。如果我们直接dump所运行程序,因为INT指针缺失,程序肯定运行不了。

修复

起初利用工具ImpREC自动查找IAT,查找的IAT有点错误,我们根据OD来看找到正确IAT表的RVA和size最后还有OEP,但是多出两个无效指针,看了一下不是动态链接库函数地址,将其删除即可。

修复后的PE结构

程序新建了一个区段,程序的导入表并且与导入表相关都在在这个区段,并且INT不在为0,为结构体 _IMAGE_IMPORT_BY_NAME 的双重指针。这样就能重新给IAT赋值。

PE重定位

由于部分程序开启了ASLR,利用IAT时的地址也会随着加载基址变化而变化,导致硬编码在程序中的内存地址随当前加载地址变化而改变的处理就是重定位。

重定位表一般位于可选头的BASE RELOCATION TABLE,这是一个记录硬编码地址偏移的列表。其中表中前两个为基准地址和大小后面为typeoffset。

type为4位,offset为12为,offset+基准地址+程序基址=程序该改变硬编码的VA

Author: L0x1c
Link: https://l0x1c.github.io/2020/04/26/IAT%E7%BB%93%E6%9E%84%E5%88%86%E6%9E%90/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信
  • 支付寶
    支付寶