windows 导入表/导出表 hook

参考:黑客编程   

以CreateFileW为例演示iat hook,代码如下:

#include<Windows.h>
#include<winnt.h>
#include<stdio.h>

#pragma warning(disable:4996)

typedef HANDLE(WINAPI *CREATEFILEW)(
	_In_ LPCWSTR lpFileName,
	_In_ DWORD dwDesiredAccess,
	_In_ DWORD dwShareMode,
	_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	_In_ DWORD dwCreationDisposition,
	_In_ DWORD dwFlagsAndAttributes,
	_In_opt_ HANDLE hTemplateFile
);

CREATEFILEW dwCreateFileWAddr = NULL;

HANDLE WINAPI MyCreateFileW(
	_In_ LPCWSTR lpFileName,
	_In_ DWORD dwDesiredAccess,
	_In_ DWORD dwShareMode,
	_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
	_In_ DWORD dwCreationDisposition,
	_In_ DWORD dwFlagsAndAttributes,
	_In_opt_ HANDLE hTemplateFile
)
{
	wprintf(L"lpFileName:%s\n", lpFileName);

	return dwCreateFileWAddr(lpFileName,
		dwDesiredAccess,
		dwShareMode,
		lpSecurityAttributes,
		dwCreationDisposition,
		dwFlagsAndAttributes,
		hTemplateFile);
}


/*
* moduleToHook 需要hook的模块      字符串
* funcToHook   需要hook的函数名称  字符串
* oriFuncAddr  用来保存被hook函数的原始地址  二级指针
* newFuncAddr  用来接管被hook函数的新函数地址  
*/

DWORD hookIat(char* moduleToHook,char* funcToHook,PVOID* oriFuncAddr,PVOID newFuncAddr)
{
	DWORD result = 0;

	do
	{
		if (moduleToHook == NULL || funcToHook == NULL)
		{
			result = 1;
			break;
		}
		HMODULE hMod = GetModuleHandleA(moduleToHook);
		if (hMod == NULL)
		{
			printf("fails to get %s module!\n", moduleToHook);
			result = 2;
			break;
		}
		ULONG64 dwFuncAddr = (ULONG64)GetProcAddress(hMod, funcToHook);
		CloseHandle(hMod);

		//获取当前进程模块基地址
		HMODULE hModule = GetModuleHandleA(NULL);//调试的时候这个地方会抛出异常,0xc000008:an invalid handle was specified

		if (dwFuncAddr)
		{
			printf("dwFuncAddr:%llx\n", dwFuncAddr);
		}
		if (hModule)
		{
			printf("hModule:%p\n", (PVOID)hModule);
		}

		//定位PE结构
		PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hModule;
		PIMAGE_NT_HEADERS64 pNtHdr = (PIMAGE_NT_HEADERS64)((ULONGLONG)hModule + pDosHdr->e_lfanew);

		//保存映像基地址及导入表的rva
		ULONGLONG dwImageBase = pNtHdr->OptionalHeader.ImageBase;
		ULONG64 dwImpRva = pNtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;

		//导入表的va
		PIMAGE_IMPORT_DESCRIPTOR pImgDes = (PIMAGE_IMPORT_DESCRIPTOR)(dwImageBase + dwImpRva);

		PIMAGE_IMPORT_DESCRIPTOR pTmpImpDes = pImgDes;
		BOOL bFound = FALSE;

		//查找欲hook函数所在的模块名
		while (pTmpImpDes->Name)
		{
			ULONG64 dwNameAddr = dwImageBase + pTmpImpDes->Name;
			char szName[MAXBYTE] = { 0 };
			strcpy(szName, (char*)dwNameAddr);
			//查找模块
			if (strcmp(strlwr(szName), moduleToHook) == 0)
			{
				bFound = TRUE;
				break;
			}

			pTmpImpDes++;
		}
		//判断查找结果
		if (bFound == TRUE)
		{
			//
			printf("%s base:%llx\n", moduleToHook, dwImageBase);
			bFound = FALSE;

			//
			//逐个遍历模块的iat地址
			//注意iat中OriginalFirstThunk和FirstThunk的区别
			//
			PIMAGE_THUNK_DATA64  pThunk = (PIMAGE_THUNK_DATA64)(pTmpImpDes->FirstThunk + dwImageBase);
			PIMAGE_THUNK_DATA64   pOriFirstThunk = (PIMAGE_THUNK_DATA64)(pTmpImpDes->OriginalFirstThunk + dwImageBase);

			while (pThunk->u1.Function)
			{
				ULONG64* pAddr = (ULONG64*)&(pThunk->u1.Function);
				PIMAGE_IMPORT_BY_NAME pImgImpName = (PIMAGE_IMPORT_BY_NAME)(pOriFirstThunk->u1.AddressOfData + dwImageBase);

				//打印导入的函数名
				if (strlen(pImgImpName->Name))
					printf("name:%s\n", pImgImpName->Name);
				//
				if (*pAddr == dwFuncAddr)
				{
					//
					printf("addr of function %s:%llx\n", funcToHook, dwFuncAddr);
					bFound = TRUE;
					*oriFuncAddr = (PVOID)*pAddr;
					ULONG64 dwMyHookAddr = (ULONG64)newFuncAddr;
					DWORD oldProtect = 0;

					//正常情况下导入表所在的内存页是无法写入数据的
					//但是可以通过修改其页属性,然后再写入数据
					VirtualProtect(pAddr, sizeof(ULONG64), PAGE_READWRITE, &oldProtect);
					//修改为hook函数的地址
					if (FALSE == WriteProcessMemory(GetCurrentProcess(),
						(LPVOID)pAddr,
						&dwMyHookAddr,
						sizeof(ULONG64),
						NULL))
					{
						printf("WriteProcessMemory fails,err:%d\n", GetLastError());
					}
					VirtualProtect(pAddr, sizeof(ULONG64), oldProtect, NULL);
					break;
				}
				pThunk++;
				pOriFirstThunk++;
			}
			//
			break;
		}
		else
		{
			result = 3;
			printf("do not find module  %s in iat!\n", moduleToHook);
			break;
		}
	}while (0);

	printf("done!\n");

	return result;
}


//导入表hook
//
// 使用情景
// 
// exe或dll通过隐式调用某个api,例如CreateFileA/CloseHandle,
// 这些api通常直接调用;然后,编译生成pe文件
//之后,可以发现pe文件导入表中有相应的模块及函数。在这种情况下是可以使用iat hook的。
// 
// 
// 但是,如果是通过GetProcAddress函数来动态获取api地址,也就是显示调用,
// 编译生成的pe文件导入表中是没有相关api的信息,iat hook也就无法排上用场。
// 
// 这个时候就可以考虑eat(导出表)hook
//
void testhook()
{
	CreateFileW(L"110.txt", 0, 0, 0, 0, 0, 0);

	printf("hookIat result:%d\n",hookIat("kernel32.dll", "CreateFileW", (PVOID*)&dwCreateFileWAddr, MyCreateFileW));

	printf("hookIat result:%d\n", hookIat("Advapi32.dll", "CreateFileW", (PVOID*)&dwCreateFileWAddr, MyCreateFileW));

	//触发被hook的函数
	CreateFileW(L"___???.txt", 0, 0, 0, 0, 0, 0);
}

相对于导入表hook,针对的某个pe文件的导入表里面的某个函数,不会影响其他也导入了 该函数的pe文件。导出表hook,就有一个全局的作用,在其他文件中,只要导入了该导出函数就会受影响。由于导出表数据结构的限制,使得其在64位环境下操作起来不像iat hook那么简便,不过eat hook在某些特殊的场景下还是非常适合的。下面是eat hood的基本流程:

			ULONG64 eatAddr = 0;
			if ((ULONG64)newFuncAddr >dwImageBase)
			{
				eatAddr = (ULONG64)newFuncAddr - dwImageBase;
			}
			else
			{
				eatAddr = dwImageBase - (ULONG64)newFuncAddr;
			}

			printf("eatAddr:%lld\n", eatAddr);

			if (FALSE == WriteProcessMemory(GetCurrentProcess(),
				(LPVOID)(tmpAddr+oridinals),
				&eatAddr,
				sizeof(DWORD),
				NULL))
			{
				printf("WriteProcessMemory fails,err:%d\n", GetLastError());
			}
			else
			{
				printf("after eathook addr of %s is %llx\n", funcToHook, dwImageBase + tmpAddr[oridinals]);
			}

硬件hook,

PVOID kerCreateFileW = NULL;//kernel32.dll  导出的CreateFileW
PVOID baseCreateFileW = NULL;//kernelbase.dll   导出的CreateFileW

DWORD NTAPI ExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
{
	if (pExceptionInfo->ExceptionRecord->ExceptionAddress == kerCreateFileW)
	{
		 已经处理了异常,不需要调用下一个异常处理来处理该异常
		//
		// 在当前系统版本10.0.19045.3086下,kernel32!CreateFileW 函数内部是一个跳转指令。
		//
		// 直接跳到kernelbase!CreateFileW。所以出来被hook的执行流程是直接将Rip设置为kernelbase!CreateFileW
		// 
		// 其他的应用场景,需要做一定的调整
		//
		pExceptionInfo->ContextRecord->Rip =(ULONG64)baseCreateFileW;

		wprintf(L"(rcx):%s\n", (WCHAR*)(pExceptionInfo->ContextRecord->Rcx));

		return EXCEPTION_CONTINUE_EXECUTION;
	}
	return EXCEPTION_CONTINUE_SEARCH;
}

//
// hardHook根据参数pointToHook,对指定的地址进行硬件hook(硬件断点的类型,长度省略)
//
// 首先,通过CreateThread创建一个线程,在线程内部会循环调用待hook的函数
// 
// 然后,暂停线程,设置硬件断点来hook pointToHook对于的地址
// 
// 最后,恢复线程执行,触发硬件hook
//
DWORD hardHook(PVOID pointToHook)
{
	CONTEXT context;
	HANDLE hThread = INVALID_HANDLE_VALUE;
	DWORD threadId = 0;

	//设置异常出来函数
	SetUnhandledExceptionFilter(ExceptionHandler);

	//创建线程,然后通过硬件断点来hook CreateFileW函数
	//
	//由于硬件断点的使用是与具体的线程相关的,所以在没有设置硬件hook的线程中,
	// CreateFileW的执行流程是不会被劫持的
	//
	hThread = CreateThread(NULL, 0, ThreadRoutine, NULL, 0, &threadId);

	//暂停线程
	SuspendThread(hThread);

	//获取线程环境
	context.ContextFlags = CONTEXT_ALL;

	if (GetThreadContext(hThread, &context) == TRUE)
	{
		//设置硬件断点,使用dr0寄存器
		context.Dr7 = (DWORD)1;
		
		//设置线性地址
		context.Dr0 = (ULONG64)pointToHook; 
		// 设置硬件断点	
		if (SetThreadContext(hThread, &context) == TRUE)
		{
			printf("new thread context is set!\n");
		}
	}
	else
	{
		printf("GetThreadContext fails,error:%d\n", GetLastError());
	}

	//唤醒线程
	ResumeThread(hThread);


	CreateFileW(L"___?00o??.txt", 0, 0, 0, 0, 0, 0);


	WaitForSingleObject(hThread, INFINITE);

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值