Hook进程IAT表

这两天研究下IAT表的hook,看来很多帖子,也试过很多代码,但是都会遇到一些问题。下面我结合被人的东西还有一些自己修改,写了一个简单的hook IAT表。首先自己写一个IAThook,要先对PE有一定的了解,知道IAT做什么的,IAT在PE文件中的位置,当你有了这些知识之后就可以对IAT做hook了。这里我就不对IAT表做什么的和PE文件做介绍了。下面介绍一下hook原理,当你要hook一个函数的时候首先应该是替换这个函数的地址,之后再你的函数中再去调用这个函数。这样你就可以hook这个函数。其实对IAT表的hook也是一样的,IAT是存动态库的导入函数地址,我们只要把IAT表的函数地址改成我们自己的函数名就可以实现对IAT表的hook,听起来是不是很简单。下面我通过代码分析来讲解如何对IAT表的hook。

DWORD g_oldaddr = (DWORD)GetProcAddress((HMODULE)LoadLibraryA("User32.dll"), "MessageBoxA");

这行代码是获取MessageboxA函数的地址,也是我们要替换的函数地址。

int WINAPI MyMessageboxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
	typedef int (WINAPI *_MessageBoxA)(
		_In_opt_ HWND hWnd,
		_In_opt_ LPCSTR lpText,
		_In_opt_ LPCSTR lpCaption,
		_In_ UINT uType
		);
	OutputDebugStringA(lpText);
	int ret = ((_MessageBoxA)g_oldaddr)(hWnd, lpText, lpCaption, uType);

	return ret;
}

这个是我用用来替换的函数,也就是所谓的hook函数,函数实现就是调用下以前的函数地址,让程序继续进行。

bool CInjection::SetIATHook(DWORD oldaddr, DWORD newaddr)
{
	bool Is64 = false;
	bool bflag = false;
	DWORD dwImageBase = 0;
	PDWORD pFuncAddr = NULL;
	PIMAGE_NT_HEADERS pNtHeader = NULL;
	PIMAGE_IMPORT_DESCRIPTOR pImportDesciptor = NULL;
	PIMAGE_NT_HEADERS64 pNtHeader64;

	dwImageBase = (DWORD)GetModuleHandle(NULL);
	pNtHeader = (PIMAGE_NT_HEADERS)(dwImageBase + ((PIMAGE_DOS_HEADER)dwImageBase)->e_lfanew);//获取Nt头
	if (pNtHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
	{
		Is64 = true;
		pNtHeader64 = (PIMAGE_NT_HEADERS64)(dwImageBase + ((PIMAGE_DOS_HEADER)dwImageBase)->e_lfanew);
		pImportDesciptor = (PIMAGE_IMPORT_DESCRIPTOR)(dwImageBase + pNtHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
	}
	else if (pNtHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) //32bit 
	{
		pImportDesciptor = (PIMAGE_IMPORT_DESCRIPTOR)(dwImageBase + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);//获取导入表位置
	}

	while (pImportDesciptor->FirstThunk != 0 && bflag == false)
	{
		pFuncAddr = (PDWORD)(dwImageBase + pImportDesciptor->FirstThunk);//获取函数地址
		while (*pFuncAddr)
		{
			if (oldaddr == *pFuncAddr)
			{
				DWORD ierror;
				MEMORY_BASIC_INFORMATION mbi;
				VirtualQueryEx(GetCurrentProcess(), pFuncAddr, &mbi, sizeof(MEMORY_BASIC_INFORMATION));//查询当前内存块的保护属性
				VirtualProtectEx(GetCurrentProcess(), pFuncAddr, 4, PAGE_EXECUTE_READWRITE, &mbi.Protect);//修改当前内存块保护属性为可读可写
				//VirtualQueryEx(GetCurrentProcess(), pFuncAddr, &mbi, sizeof(MEMORY_BASIC_INFORMATION));//查询下Protect 是否还是保护
				if (!WriteProcessMemory(GetCurrentProcess(), pFuncAddr, &newaddr, sizeof(pFuncAddr), NULL))//修改IAT表函数地址
				{
					ierror = GetLastError();
				}
				DWORD oldProtect;
				VirtualProtectEx(GetCurrentProcess(), pFuncAddr, 4, mbi.Protect, &oldProtect);//写完后修改回可读保护属性
				bflag = true;
				break;
			 }
			pFuncAddr++;
		}
		pImportDesciptor = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pImportDesciptor + sizeof(IMAGE_IMPORT_DESCRIPTOR));//获取下一个IAT表
	}
	
	return true;
}
这个函数就是具体实现的函数了,首先获取进程的基地址也就是我们常说的imagebase。之后获取NT头,和PE可选头的地址,这些需要你对PE文件有一定了解,才能理解为什么这么写。之后循环遍历IAT表的函数地址。如果发现和MessageA的函数地址一样说明我们已经找到了MessageA在IAT表中的位置。我们只需要替换
*pFuncAddr = newaddr;发现报错 这是因为内存访问没有权限,这块内存只有读取的权限

所以需要VirtualProtectEx修改内存的权限改为读写权。之后我们通过WriteProcessMemory函数把新的函数地址写入到IAT表中

当我们运行MessageBosA函数时,你会发现先进入我们的MyMessageboxA函数中,是不是很容易理解。



猜你喜欢

转载自blog.csdn.net/u011569253/article/details/80270775