20171011WindowsPrj08_03IAT Hook

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->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函数有待完善。




猜你喜欢

转载自blog.csdn.net/qq_31622605/article/details/78209527