复写IAT劫持程序执行流(计算器中显示中文数字)

结果:在计算器窗口中把本来应该显示的数字替换为中文
须知:计算器显示数据的时候需要调用user32.dll里面的SetWindowText() API
过程:在程序运行的时候会加载user32.dll到内存,这个时候会把IAT里面写入成函数的真正地址(具体见文章中图),这里自己写一个dll当dll被加载会修改user32.dll!SetWindowTextW对应的IAT值达到劫持程序执行流的目的。
注意:32位程序要编译成32位dll,64位同样

#include"windows.h"
LPVOID g_porgFunc = NULL;
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
	switch (fdwReason)
	{
	case DLL_PROCESS_ATTACH:
		g_porgFunc = GetProcAddress(GetModuleHandle(L"user32.dll"), "SetWindowTextW");
		//用hookiad.MySetWindowsText()钩取user32.SetWindowTextW()
		hook_iat("user32.dll", g_porgFunc, (PROC)MySetWindowTextW);
		break;
	case DLL_PROCESS_DETACH:
		//将calc.exe的IAT恢复原位
		hook_iat("user32.dll", (PROC)MySetWindowTextW, g_porgFunc);
		break;
	}
	return True;
}

首先获取要挂钩函数的地址,
然后把地址替换实现钩取和脱钩

然后需要一个转化函数,函数完成的过程:
1.把传入的参数进行转化
2.调用原本的参数

typedef BOOL(WINAPI *PFSETWINDOWTEXTW)(HWND hWnd, LPWSTR lpString);
//cal.exe的IAT被钩取之后当代码中调用user32.SetWindowText函数时会首先调用hookiat.MyWindowTextW函数
BOOL WINAPI MySetWindowTextW(HWND hWnd,LPWSTR lpString)
{
	wchar_t* pNum = L"零一二三四五六七八九";
	wchar_t temp[2] = { 0 };
	int i = 0, nLen = 0, nIndex = 0;

	nLen = wcslen(lpString);
	for (i = 0; i < nLen; i++)
	{
		//将数字转中文
		//lpString 是宽字符版本的字符串
		if (L'0' <= lpString[i] && lpString[i] <= L'9')
		{
			temp[0] = lpString[i];
			nIndex = _wtoi(temp);//读入的字符是宽字符
			lpString[i] = pNum[nIndex];
		}
	}//转化
	//调用user32.SetWindowTextW()API
	//修改lpString缓冲区的内容
	return ((PFSETWINDOWTEXTW)g_porgFunc)(hWnd, lpString);
}

当程序正常执行SetWindowsTextW()API的时候函数会被hook_iat函数劫持为调用MySetWindowTextW函数,SetWindowsTextW的2个参数会被传递过来作为MySetWindowTextW的参数,这是对第二个参数(要显示的字符)进行修改完成后再次调用SetWindowTextW函数(不过这个时候的第二个参数已经被修改了)

那么hook_iat是怎么实现的呢?在看之前先补充一些关于IAT的知识
IAT的结构如图
在这里插入图片描述

BOOL hook_iat(LPCSTR szDllName,PROC pfnorg, PROC pfnNew)
{
	HMODULE hMod;
	LPCSTR szLibName;
	PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
	PIMAGE_THUNK_DATA pThunk;
	DWORD dwOldProtect, dwRVA;
	PBYTE pAddr;
	//hMod,pAddr=ImageBase of cal.exe , VA to IMAGE_DOS_HEADER
	hMod = GetModuleHandle(NULL);//获取最近运行exe的句柄
	pAddr = (PBYTE)hMod;
	//pAddr=VA to IMAGE_NT_HEADERS
	pAddr += *((DWORD*)&pAddr[0x3c]);

	//dwRVA=RVA to IMAGE_IMPORT_DESCRIPTOR Table
	dwRVA = *((DWORD*)&pAddr[0x80]);
	//pImportDesc=Va to Image_Import_DESCRIPTOR Table
	pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod + dwRVA);
	for (; pImportDesc->Name; pImportDesc++)
	{
		//szLibName=VA to IMAGE_IMPORT_DESCRIPTOR.Name
		szLibName = (LPCSTR)(DWORD)hMod + pImportDesc->Name;
		if (!_stricmp(szLibName, szDllName))//找到对应dll
		{
			//pThunk=IMAGE_IMPORT_DESCRIPTOR.FirstThunk
			//		=VA to IAT(Import Address Table)
			pThunk = (PIMAGE_THUNK_DATA)((DWORD)hMod + pImportDesc->FirstThunk);
			//pThunk->u1.Function=VA to API
			for (; pThunk->u1.Function; pThunk++)
			{
				if (pThunk->u1.Function == (DWORD)pfnorg)
				{
					//更改内存属性
					VirtualProtect((LPVOID)&pThunk->u1.Function, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect);
					//修改IAT的值
					pThunk->u1.Function = (DWORD)pfnNew;

					//恢复内存属性
					VirtualProtect((LPVOID)&pThunk->u1.Function, 4, dwOldProtect, &dwOldProtect);
					return TRUE;
				}
			}
		}
		
	}
	return FALSE;
}
//替换IAT中szDllName动态链接库的pfnorg函数地址为pfnNew函数的地址

过程分为这几步

  1. 找到exe文件的加载地址(GetModuleHandle(NULL)函数)
  2. 找到user32.dll的加载地址,和SetWindowTextW的加载地址
  3. 修改IAT内存的属性用VirtualProtect函数
  4. 修改IAT->Function值(这个值是函数的真正运行时地址)(dll被主调exe加载到自己的虚拟内存空间,所以直接赋值)
  5. 把内存权限修改回去

dll注入参考https://blog.csdn.net/qq_38204481/article/details/82939494

调试:用OD的附加选项
在这里插入图片描述
右击,查找,所有模块中的名称
敲入字符即可搜索到相应的API或者dll
在这里插入图片描述
64位程序可以用windbg

总结:包含3个函数,主函数负责被加载的时候调用,hook_iat函数负责修改IAT,MySetWindowTextW函数对原函数的具体修改
和调试方式的比较:调试方式对原本的exe影响较大,容易被检测,修改IAT的方式仅仅是增加一个库或者直接代码注入,简洁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值