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;
};
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk;
} 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); IMAGE_IMPORT_BY_NAME ** Original =(IMAGE_IMPORT_BY_NAME **) ((DWORD)hModule + pImageImportDescriptor->OriginalFirstThunk); 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