我所说的是windows下的inline hook,总所周知inline hook是windows平台下很灵活好用的一种hook方式, 但inline hook是非线程稳定的,也就是说在多线程的程序中,频繁的进行inline hook有可能会导致程序崩溃,不稳定。
之前在为了解决这个问题,只找到了Hook七个字节的方法,而这两天空余时间研究了下inline hook中线程同步的方法,下面我就分享下,如有不对之处,欢迎大佬指出批评。
在inline hook中实现线程同步,下面我介绍自己所知的3种方法:
首先进行一下普通inline hook观察效果。注入程序我就不介绍了,之后附上源码。 我这里hook的是MessageBoxW对话框函数,每当被hook的程序调用了MessageBoxW函数,弹出的对话框里面的字符串就会被我修改,下图是hook前和hook后弹框的对比图。 下面附上inline hook的源代码,核心点是安装钩子的函数,之后的原子操作,信号量,修改7字节都是在此代码的基础上稍加修改:
简单形容一下就是:一个线程中的原子操作在更改一个变量时,其它所有线程相关的操作都处于暂停状态,这就是所谓的互斥。 在inline hook中写入5个字节的地方我们使用原子操作,这就可以杜绝其它线程的访问,从而使程序稳定的运行。 在这里使用到一个宏,他会以原子方式把新值赋给旧值。:
那么在inline hook中写入数据的时候我们改为原子操作就能达到优化的效果,主要就是安装钩子函数,更改如下图:
信号量是一个能跨进程使用的好东西! 简而言之,假设创建了一个信号量,设置它的信号数为1,使用WaitForSingleObject()时该信号数减1,也就说这个信号数此时为0了,此时其他线程中再有代码使用WaitForSingleObject()时那个线程就处于阻塞状态,必须等到信号数非0了,他才能继续运行下去。使用ReleaseSemaphore()函数可把信号数加1。
利用信号量的这个特性貌似也起到优化inline hook的效果,前提条件是在其他任意程序中创建一个信号量,因为它是跨进程的:
然后HOOK代码如下: 信号量的作用不止于此,在内联hook中,之前我在15PB的老师"WM"发掘出用信号量传递进程PID,当真是好用之极,让我大开眼界。
修改7字节 inline hook这种技术我在《逆向工程核心原理》一书中看到作者称之为"热补丁",那我也就称它就hook 热补丁吧。为了了解他先做一些准备工作。 首先使用OD观察普通API函数(大多数API都一样)的汇编代码,第一行指令是mov edi,edi 这条2个字节的命令显然没有任何作用完全可以忽略。 再观察普通的inline hook后MessageBoxW函数地址的汇编代码(注意这里要选运行程序注入后,使用OD附加调试)。它直接跳转到我们自己的那个函数中去。jmp(0xE9)后面是4个字节,所以破坏了原函数的可调用性,如果之占据mov edi,edi 这条2个字节的就是实现跳转的话那么从此以后就不必再频繁修改该函数地址上的数据了。注意观察大多数函数地址上面都有5个或以上的无用字节 头两个字节改为0xEB,0xF9,实现了跳转到此地址之上的5个字节的位置,在这5个字节中在构造跳转到函数的代码,那么就完美解决了hook时多线程不稳定的问题。修改7字节时如下: 修改7字节的热补丁技术,主要就是安装钩子和实现自己的函数值得注意:
最后把附上源码,里面还有远程线程注入代码。
#include "stdafx.h"
// 原函数的地址数据
BYTE g_OldData[5];
// 替换后的数据
BYTE g_NewData[5] = { 0xE9 };
// 声明自己的函数
int
WINAPI
MyMsg(_In_opt_ HWND hWnd,
_In_opt_ LPCWSTR lpText,
_In_opt_ LPCWSTR lpCaption,
_In_ UINT uType);
// 安装钩子
void InHook()
{
// 保存原函数地址
memcpy(g_OldData, MessageBoxW, 5);
// 计算出要跳转的偏移(自己的函数地址 - 原函数地址 - 5)
DWORD Offset = (DWORD)MyMsg - (DWORD)MessageBoxW - 5;
*(DWORD*)(g_NewData + 1) = Offset;
// 修改内存属性
DWORD Protect;
VirtualProtect(MessageBoxW, 5, PAGE_EXECUTE_READWRITE, &Protect);
// 替换函数地址
memcpy(MessageBoxW, g_NewData, 5);
// 还原内存属性
VirtualProtect(MessageBoxW, 5, Protect, &Protect);
}
// 卸载钩子
void UnHook()
{
DWORD Protect;
VirtualProtect(MessageBoxW, 5, PAGE_EXECUTE_READWRITE, &Protect);
memcpy(MessageBoxW, g_OldData, 5);
VirtualProtect(MessageBoxW, 5, Protect, &Protect);
}
// 自己的Hook函数
int
WINAPI
MyMsg(
_In_opt_ HWND hWnd,
_In_opt_ LPCWSTR lpText,
_In_opt_ LPCWSTR lpCaption,
_In_ UINT uType)
{
// 替换字符串
lpText = L"九阳道人内联Hook成功!";
lpCaption = L"九阳道人";
// 先卸载钩子
UnHook();
// 在调用真正的MessageBoxW函数
int nRet = MessageBoxW(hWnd, lpText, lpCaption, uType);
// 最后在把钩子装上
InHook();
return nRet;
}
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
上传的附件: