首页 > 其他分享 >逆向开发-hook技术

逆向开发-hook技术

时间:2023-07-31 19:34:44浏览次数:23  
标签:逆向 MessageBoxW 函数 钩子 OldProtect hook 地址 开发

Hook(钩子)

简介

Hook:意为钩子
在技术中用途多样化,但多数都用来改变原有程序执行顺序或拦截原程序指定信息、代码或数据。

  • 这技术其实无处不在,比如我们所熟知的任意杀毒软件中。这些杀软在各种敏感的系统函数中,下了各种钩子。当有程序或病毒调用该函数时,首先会经过杀软的检测函数。如果正常则放行,如不正常则阻止并提示。(当然这只是部分应用,真实场景下对抗是很复杂的)
  • 或在我们熟知的外挂中,通过各种hook技术达到无敌、锁头等功能实现。(外挂其实是在调用当前游戏自己的函数功能,而不是外挂作者自己去实现一个具体功能)
    本篇文章复现及讲解几个主要的hook技术。
  • IATHook(IAT表Hook)
  • inlineHook(内联Hook)
  • MessageHook(系统消息钩子)

IATHook

通过修改导入表中函数地址达到Hook目的,一般用来修改指定函数。
缺点就是局限性较高,只能修改对应一些函数地址。
本次目标在windows弹窗函数上下钩子:Messagebox
代码实现:

//初始化钩子(解析IAT表保存原函数地址)
void InitHook()
{
	HMODULE exe_model = GetModuleHandle(NULL);//获得本模块句柄

	char* base = (char*)exe_model;//该模块进程的首地址(相当于是ImageBase)

	PIMAGE_DOS_HEADER dosHead = (PIMAGE_DOS_HEADER)base;//获得Dos头

	PIMAGE_NT_HEADERS ntHead = (PIMAGE_NT_HEADERS)(dosHead->e_lfanew + base);//获得NT头

	PIMAGE_OPTIONAL_HEADER optHead = &ntHead->OptionalHeader;//获得可选PE头

	PIMAGE_DATA_DIRECTORY import_directory = &optHead->DataDirectory[1];//拿到IAT表的内存偏移(RVA)

	//获得IAT表的首地址
	PIMAGE_IMPORT_DESCRIPTOR import_table = (PIMAGE_IMPORT_DESCRIPTOR)(import_directory->VirtualAddress + base);

	while (import_table->Name != 0)//循环表内所有模块的名字,以0结尾
	{
		PCHAR dllname = (PCHAR)(import_table->Name + base);//获得表内dll的名字的首地址

		if (strcmp(dllname, "USER32.dll") == 0)//判断是否存在USER32这个dll
		{
			PDWORD iat = (PDWORD)(import_table->FirstThunk + base);//获得USER32内的函数名称地址

			for (size_t i = 0; iat[i] != 0; i++)//循环,函数名称地址以0结尾
			{
				if (iat[i] == (DWORD)MessageBoxW)//判断是否是MessageBoxW这个函数
				{
					OldMessagebox = iat + i;//将MessageBoxW的地址保存下来
					break;
				}
			}
			break;
		}
		import_table++;
	}
}
//重新实现一个自己的messagebox函数
int WINAPI MyMessageBoxW(
	_In_opt_ HWND hWnd,
	_In_opt_ LPCWSTR lpText,
	_In_opt_ LPCWSTR lpCaption,
	_In_ UINT uType)
{
	lpText = L"IATHook	-by、my";//弹窗内容修改
	lpCaption = L"提示";//修改弹窗的标题
	OffHook();    //卸载一次钩子
	int result = MessageBoxW(hWnd, lpText, lpCaption, uType);
	SetHook();   //将钩子设置回去
	return result;  //让返回值正常返回
}
//设置钩子
void SetHook()
{
	PUCHAR Address = (PUCHAR)OldMessagebox;//PUCHAR无符号char类型
	DWORD OldProtect = 0;//定义一个属性,用来保存旧属性
	VirtualProtect(Address, 5, PAGE_EXECUTE_READWRITE, &OldProtect);//将虚拟内存的属性修改为可读可写
	*OldMessagebox = (DWORD)MyMessageBoxW;//将系统MessageBox的地址变成我自己的函数地址
	VirtualProtect(Address, 5, OldProtect, &OldProtect);//将内存恢复回原来的属性
}
//卸载钩子
void OffHook()
{
	DWORD OldProtect = 0;//修改保护属性
	VirtualProtect((LPVOID)OldMessagebox, 5, PAGE_EXECUTE_READWRITE, &OldProtect);
	*OldMessagebox = (DWORD)MessageBoxW;
	//还原保护属性
	VirtualProtect((LPVOID)OldMessagebox, 5, OldProtect, &OldProtect);
}

最终hook成功效果

hook前提示

hook后提示

解析

根据代码,我们做了三件事。
1、保存原来的系统函数(messagebox)地址
2、重写一个我们自己的messagebox函数
3、将我们自己的messagebox函数地址替换到IAT表中
分析一下看看具体:
Hook前
Hook后

inlineHook(内联钩子)

内联钩子,也称自定义钩子。
自由度高,可以在任何地址上下钩子。缺点就是会修改原程序字节,可能会被检查到。
步骤:

  • 组装需要跳转的数据(JMP xxx)
  • 计算偏移
  • 修改目标地址内存属性
  • 写入自己的代码
  • 还原代码段属性
  • 重新调用被覆盖的代码
    这里还是以系统弹窗函数messagebox为例。将messagebox的头几个字节修改为跳转到我们自己的函数地址上
    代码实现:
// 目标 钩住 MessageBoxW 这个函数
int WINAPI MyMessageBoxW(
	_In_opt_ HWND hWnd,
	_In_opt_ LPCWSTR lpText,
	_In_opt_ LPCWSTR lpCaption,
	_In_ UINT uType)
{
	// 修改了参数
	lpText = L"被InlineHook了";
	lpCaption = L"提示";
	// 如果不还原原来的字节码,先还原原来的数据
	OffHook();
	// call MessageBoxW 还是跳回当前函数,就无限套娃了
	int result = MessageBoxW(hWnd, lpText, lpCaption, uType);
	// 继续加钩子,再吧 MessageBoxW 的前五个字节修改
	OnHook();
	return result;
}
// 初始化函数
// void InitHook(char* dllname,char* funname,int loc,int funadd)
void InitHook()
{
	// 保存原来的函数地址
	OldMessageBox = (DWORD)MessageBoxW;
	// 计算偏移
	*((PDWORD)Offset) = (DWORD)MyMessageBoxW - OldMessageBox - 5;
	// 保存原来的数据
	memcpy_s(OldOpcode, 5, (PUCHAR)OldMessageBox, 5);
}
// 安装钩子函数
void OnHook()
{
	// 找到 MessageBoxW 修改前五个字节
	PUCHAR Address = (PUCHAR)OldMessageBox;
	// 修改成 jmp MyMessageBoxW
	// 之后 MessageBox 的前五个字节就是 jmp MyMessageBoxW
	// call MessageBoxW 跳转到我自己的函数中
	// 修改保护属性
	DWORD OldProtect = 0;
	VirtualProtect(Address, 5, PAGE_EXECUTE_READWRITE, &OldProtect);

	// 修改字节
	Address[0] = 0xE9;
	Address[1] = Offset[0];
	Address[2] = Offset[1];
	Address[3] = Offset[2];
	Address[4] = Offset[3];

	// 还原保护属性
	VirtualProtect(Address, 5, OldProtect, &OldProtect);

}
// 卸载钩子函数
void OffHook()
{
	// 修改保护属性
	DWORD OldProtect = 0;
	VirtualProtect((LPVOID)OldMessageBox, 5, PAGE_EXECUTE_READWRITE, &OldProtect);

	memcpy_s((PUCHAR)OldMessageBox, 5, OldOpcode, 5);

	// 还原保护属性
	VirtualProtect((LPVOID)OldMessageBox, 5, OldProtect, &OldProtect);
	
}

Hook成功效果

Hook前提示


Hook后提示

内存解析

查看内存,在我们hook瞬间内存里发生了什么
未hook前地址指向正常的系统函数


hook后,我们修改了函数头几个字节。在正常调用messagebox函数时直接跳转到我们函数上


跟进一步查看




这样下来内联hook已经完成了
试想一下,我们既然可以破坏修改任意字节。那么如果这个函数是一个游戏里的扣血函数或是其他功能函数,我们是不是可以把扣除血量改为加血量。无敌效果是不是就实现了(当然不会这么简单)

MessageHook

系统提供的消息Hook机制
Windows操作系统是以事件驱动的。事件被包装成了消息发送给窗口,比如点击菜单,按钮,移动窗口,按下键盘,正常消息:
● 当按下键盘,产生一个消息,按键消息加入到系统消息队列
● 操作系统从消息队列取出消息,添加到相应的程序的消息队列中
● 应用程序使用消息泵从自身的消息队列中取出消息 WM_KEYDOWN,调用消息处理函数
● 我们可以在系统消息队列到程序消息队列之间添加消息钩子,从而使得在系统消息队列消息发给应用程序之前捕获到消息。
● 可以多次添加钩子,从而形成一个钩子链,可以依次调用函数。
● 消息钩子是windows操作系统提供的机制,SPY++截获窗口消息的功能就是基于这样的机制
这里我们实现一个监控键盘输入的程序
代码实现:

// 消息钩子的回调函数
LRESULT CALLBACK HookProc(int code, WPARAM wParam, LPARAM lParam)
{
	// 判断是否wParam与lParam都有键盘消息,是的话则执行打印操作
	if (code == HC_ACTION) {
		// 将256个虚拟键的状态拷贝到指定的缓冲区中,如果成功则继续
		BYTE KeyState[256] = { 0 };
		if (GetKeyboardState(KeyState)) {
			// 得到第16–23位,键盘虚拟码
			LONG  KeyInfo = lParam;
			UINT  keyCode = (KeyInfo >> 16) & 0x00ff;
			WCHAR wKeyCode = 0;
			ToAscii((UINT)wParam, keyCode, KeyState, (LPWORD)&wKeyCode, 0);
			// 将其打印出来
			WCHAR szInfo[512] = { 0 };
			swprintf_s(szInfo, _countof(szInfo), L"Hook_%c", (char)wKeyCode);
			OutputDebugString(szInfo);
			return 0;
		}
	}
	return CallNextHookEx(g_hook, code, wParam, lParam);

}
// 安装钩子的函数
BOOL InstallHook(DWORD threadid)
{
	g_hook = SetWindowsHookEx(
		WH_KEYBOARD,		// 要钩取的消息类型
		HookProc,			// 消息钩子的回调函数
		g_module,			// 包含回调函数的模块句柄
		threadid			// 要钩取的线程ID,如果填 0 就是钩住所有线程
	);
	if (g_hook == NULL)
	{
		return FALSE;
	}
	else
	{
		return TRUE;
	}
}
// 卸载钩子函数
BOOL UnInstallHook()
{
	if (g_hook == NULL)
	{
		return FALSE;
	}
	else
	{
		return UnhookWindowsHookEx(g_hook);
	}
}

Hook成功效果

利用debugview等系统消息捕捉工具,实现监控我们的键盘输入。

结尾

既然能攻击,那防守肯定是没问题的。解决问题的方法很多种
其中一种解决办法:如果我们把关键的代码去计算一个crc32值单独开一个线程不断去扫去校验。只要crc32值不对劲,立马终止程序。其实就解决了这个问题
技术是个双刃剑,怎么用取决自己。

  • 本篇文章不鼓励任何破坏行为,仅做参考和学习

标签:逆向,MessageBoxW,函数,钩子,OldProtect,hook,地址,开发
From: https://www.cnblogs.com/mykr3/p/17587681.html

相关文章

  • 【雕爷学编程】Arduino动手做(176)---UFUN2.0 开发板
    37款传感器与执行器的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的,这里准备逐一动手尝试系列实验,不管成功(程序走通)与否,都会记录下来—小小的进步或是搞......
  • 开发环境搭配_后端环境搭建_前后端联调_Nginx反向代理和负载均衡配置
       ......
  • 微信开发机器人
    请求URL:http://域名地址/addChatRoomMember请求方式:POST请求头Headers:Content-Type:application/jsonAuthorization:login接口返回参数:参数名必选类型说明wId是string登录实例标识chatRoomId是String群userList是String群成员微信id,多个已","分割返回数据:参数名类型说明codestring1......
  • 微商城小程序怎么开发?(微商城小程序开发流程)
    微商城小程序作为一个便捷的电商平台,成为越来越多企业和个人的选择。它不仅为商家提供了一个在线销售的渠道,也为消费者提供了更方便快捷的购物体验。那么,如何开发一个成功的微商城小程序呢?接下来广州名锐讯动将介绍微商城小程序的开发流程,并分享一些关键步骤和注意事项。第一步:需求......
  • 抽奖小程序开发如何提高用户参与度?
    抽奖活动作为一种具有互动性和娱乐性的营销方式,被越来越多的企业和个人采用。而随着移动互联网的发展,抽奖小程序的兴起为抽奖活动的展开提供了便利和创新。然而,如何提高用户参与度成为抽奖小程序开发中的关键问题。接下来广州名锐讯动将从设计策略、奖励机制和用户体验三个方面进行......
  • 安卓4G核心板开发板_MTK6785/MT6785(Helio G95)安卓手机主板方案
    联发科MTK6785(HelioG95)安卓核心板采用八核CPU具有两个强大的ArmCortex-A76处理器内核,主频高达2.05GHz,外加六个Cortex-A55高效处理器。其强大的图形性能由ArmMali-G76MC4提供,速度可提升至900MHz。MT6785器件集成了蓝牙、FM、WLAN和GPS模块,是一个高度集成的基带平台,结......
  • 开发环境搭建_后端环境搭建_前后端联调
        ......
  • 开发环境搭配_后端环境搭建_前后端联调_Nginx反向代理和负载均衡概念
        ......
  • 正点原子ARM裸机开发001---开发环境的搭建
    一、Ubuntu与Windows的文件互传直接通过复制粘贴,但仅限于在虚拟机上使用FTP服务进行互传:开启Ubuntu下的FPT服务打开Ubuntu终端窗口,输入以下命令:sudoapt-getinstallvsftpd等待安装完成后,输入以下命令sudovi/etc/vsftpd.conf打开vsftpd.conf文件以后,找到以......
  • 【独家】各大开发商频“触网” …
    随着房地产开发的升级和市场大势下行,越来越多的房地产企业开始通过开发社区APP来实现服务延伸,力图以增值服务来换市场和发展。如今,业主专属社区APP开发已经蔚为大观,对房地产开发也正逐渐形成着缓慢而深刻的影响。现象:众多开发商纷纷触网,抢占业主专属社区APP滩头近段时间来,各大开......