您的位置:首页 > 编程语言

20171011WindowsPrj08_03IAT Hook

2017-10-11 22:51 141 查看

IAT Hook:

    IAT:就是一张函数导入表,DLL的加载分为动态加载和静态加载,静态加载是不需要指定DLL,它包含在PE结构中,里面指出了这个EXE需要使用哪些DLL名,DLL里面的那些函数。下面为PE结构表:如果看不清,可以将图片另存到自己电脑,然后打开看。



PE结构:

1:每个EXE和DLL文件都有自己的PE头,里面包含了DOS头和PE头,PE头里面包含了NT头和OPTIONAL头,这是PE头里面最重要的,里面记录了非常重要的信息,包括程序入口,其中最后一个是数据目录表(IMAGE_OPTIONAL_HEADER),里面共计15项(版本不同可能会有区别),每一项都是一个IMAGE_DATA_DIRECTORY结构体,里面包含两项,如下:

typedef struct _IMAGE_DATA_DIRECTORY {
DWORD   VirtualAddress;//数据偏移地址(相对于模块首地址)
DWORD   Size;//数据大小
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;


2:数据目录表:里面记录了很多东西,共包含15项(版本不同可能会有区别),例如:导出表,导入表,资源,异常,安全等。这个15项都是代表其表头,而非实际的表,他本身是一个指针,指向各个表。每个表里面可能有很多项(模块),例如导入表中,有很多的DLL。

    特别注意:在PE结构里面,所有的地址记录都是记录的相对地址,相对于模块基地址的偏移地址。

按如下方法可以定位到导入表:

HMODULE hMod = GetModuleHandle(nullptr);	//获取到当前进程的模块

IMAGE_DOS_HEADER *pDosHeader = (IMAGE_DOS_HEADER*)hMod;
IMAGE_OPTIONAL_HEADER *pOptHeader = (IMAGE_OPTIONAL_HEADER*)
(((BYTE*)hMod) + pDosHeader->
4000
e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER));//取到IMAGE_OPTIONAL_HEADER的位置

IMAGE_IMPORT_DESCRIPTOR* pImportDesc = (IMAGE_IMPORT_DESCRIPTOR*)
((BYTE*)hMod + pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);//取到IMAGE_IMPORT_DESCRIPTOR的位置


3:导入表里面就是以模块的形式存在于里面的,每一个模块都有这样一个结构体(IMAGE_IMPORT_DESCRIPTOR),其结构如下:

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\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;
DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;


    这个结构体是连续存储的,我们可以直接用指针++,移到下一个模块,从而遍历所有模块,并对特定的模块里面特定的API做特定的改变,达到对特定的API进行Hook的目的。

4:在上面所说的这个导入表的IMAGE_IMPORT_DESCRIPTOR结构体里面,Name指向的是当前DLL的名称,FirstThunk指向的是一个连续的内存空间,保存了这个DLL里面每个API的信息。

IAT Hook实练:

1:

APIHook.h:

#ifndef MESSAGEBOX_HOOK_H_
#define MESSAGEBOX_HOOK_H_

#include <windows.h>

#ifndef APIHOOK_EXPORTS
#define API_HOOK _declspec(dllimport)
#else
#define API_HOOK _declspec(dllexport)
#endif//!APIHOOK_EXPORTS

extern"C" BOOL API_HOOK SetHook(char* FuncName, char* ModName, char* NewFunc);
extern"C" int API_HOOK WINAPI MyMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType);

#ifndef APIHOOK_EXPORTS
#pragma comment(lib, "APIHook.lib")
#endif

#endif//!MESSAGEBOX_HOOK_H_


APIHook.cpp:

int WINAPI MyMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType)
{
printf("Hook成功!");
return 0;
}

BOOL SetHook(char* FuncName, char* ModName, char* NewFunc)
{
bool bRet = false;
//EXE模块的基地址
HMODULE hMod = GetModuleHandle(nullptr);	//获取到当前进程的模块

IMAGE_DOS_HEADER *pDosHeader = (IMAGE_DOS_HEADER*)hMod;
IMAGE_OPTIONAL_HEADER *pOptHeader = (IMAGE_OPTIONAL_HEADER*)
(((BYTE*)hMod) + pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS) - sizeof(IMAGE_OPTIONAL_HEADER));//取到IMAGE_OPTIONAL_HEADER的位置

IMAGE_IMPORT_DESCRIPTOR* pImportDesc = (IMAGE_IMPORT_DESCRIPTOR*)
((BYTE*)hMod + pOptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);//取到IMAGE_IMPORT_DESCRIPTOR的位置
//IMAGE_IMPORT_DESCRIPTOR在IMAGE_OPTIONAL_HEADER的DataDirectory表里面第一个(导出表)的VirtualAddress指向的地方

while (pImportDesc->FirstThunk)//每一个DLL都对应一个输入表,输入表地址存在
{
char* pstrDllName = (char*)((BYTE*)hMod + pImportDesc->Name);//保存的地址都是相对地址,Name指向的DLL的名字
if (!strcmp(pstrDllName, ModName))
//找到USER32这个DLL,然后找里面的MessageBoxW函数,对他进行挂钩,对其他API也是可以的,只要知道所在的DLL名
{
break;
}
++pImportDesc;//对输入表遍历
}

if (pImportDesc)//此时,pImportDesc指向的是USER32.dll输入表,需要找到名为MessageBoxW的函数
{
IMAGE_THUNK_DATA *pThunkData = (IMAGE_THUNK_DATA *)((BYTE*)hMod + pImportDesc->FirstThunk);
DWORD dwFunAddr = (DWORD)MessageBoxW;/*(DWORD)GetProcAddress(hMod, FuncName);*/
while (pThunkData->u1.Function)
{
if (pThunkData->u1.Function == dwFunAddr)
{
//需要更改能够有写权限
VOID* lpAddr = &(pThunkData->u1.Function);
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(lpAddr, &mbi, sizeof(mbi));
DWORD OldProtect;//保存旧的权限
VirtualProtect(lpAddr, sizeof(DWORD), PAGE_READWRITE, &OldProtect);
pThunkData->u1.Function = (DWORD)MyMessageBoxW;/*(DWORD)GetProcAddress(hMod, NewFunc);*///更改执行函数
VirtualProtect(lpAddr, sizeof(DWORD), OldProtect, nullptr);//还原权限
bRet = true;
break;
}
++pThunkData;//对一个DLL里面存在的所有API遍历
}
}
return bRet;
}


测试代码:

int _tmain(int argc, _TCHAR* argv[])
{
MessageBox(nullptr, L"123", nullptr, MB_OK);
SetHook("MessageBoxW", "USER32.dll", "MyMessageBoxW");
MessageBox(nullptr, L"123", nullptr, MB_OK);

return 0;
}


2:上面的代码,实现了将IAT Hook简单封装在了dll里面,并通过加载这个DLL,并调用SetHook,实现了对MessageBoxW的Hook,每次调用MessageBoxW的时候,就会打印一次Hook成功!

3:代码中,末尾部分对调用函数地址的改变涉及到内存操作,需要对已映射的DLL的内存部分进行修改,需要更改内存权限为可读写,并还原权限。此外,还有UnSetHook函数有待完善。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息