Windows系统编程(七)HotFixHook

InoolineHook需要读写两次内存(先HOOK,再还原),这种Hook方式,性能比较低,具有局限性。今天所讲的HotFixHOOK(热补丁)是InlineHook的升级版

Win32 API特殊性

Win32API的实现代码有这样的特点:起始指令为1个MOV EDI,EDI,上方有多个int3(称之为内存空白区),这些指令实际没有任何意义。

接下来我们通过观察MessageBoxA的反汇编代码来验证这个现象

如图所示,确实存在无意义的指令

HotFix HOOK

我们从上文了解到,Win32 API的实现代码中起始位置处存在一些无意义的指令,因此我们可以通过修改这些无意义的指令来实现HOOK操作。这种方法可以使得进程处于运行状态时临时更改进程内存中的库文件,因此被称为打热补丁,即HotFix HOOK

HOOK原理

如图是MessageBoxA的汇编代码实现处,我们借助这个图方便我们理解HOOK

修改Win32 API实现代码中的第一行指令为jmp,使之可以跳转到上方内存空白处。上方内存空白处修改为我们自己的代码,如执行某API。此时每当程序调用该API时,在执行第一行指令时,都会跳转到上方内存空白处执行我们自己的代码,而不会影响原API实际的函数功能实现

HOOK实现

接下来我们创建dll文件,实现HotFix HOOK

cpp 复制代码
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <Windows.h>


BOOL Hook(const char * pszModuleName, const char * pszFuncName, PROC pfnHookFunc)//HOOK
{
	BYTE ShortJmp[2] = { 0xEB,0xF9 };//用于替换函数头两个字节mov edi, edi(8B FF)实现短跳上方五个字节
	BYTE NewCodes[5] = { 0xE9,0, };//替换短跳后的五个字节:一字节指令,四字节函数地址
	HMODULE hModule = GetModuleHandleA(pszModuleName);//获取目标HOOK函数所处模块的句柄
	FARPROC FunAddress = GetProcAddress(hModule, pszFuncName);//获取目标HOOK函数地址
	DWORD dwOldProtect = 0;//保存原有的内存属性
	VirtualProtect((LPVOID)((DWORD)FunAddress - 5), 7, PAGE_EXECUTE_READWRITE, &dwOldProtect);//修改五字节空白内存读写属性,注意修改的两个头字节一共七个字节
	DWORD dwFuncAddr = ((DWORD)pfnHookFunc - (DWORD)FunAddress);//计算要跳转的函数相对地址
	//注意换算结果:跳转函数地址 - E8指令地址处 - E8指令长度 == 跳转函数地址 - (原函数地址处 -  E8指令长度) -  E8指令长度 == 跳转函数地址 -  原函数地址处
	*(DWORD *)(NewCodes + 1) = dwFuncAddr;
	memcpy((LPVOID)((DWORD)FunAddress - 5), NewCodes, 5);
	memcpy(FunAddress, ShortJmp, 2);//修改内存
	VirtualProtect((LPVOID)((DWORD)FunAddress - 5), 7, dwOldProtect, &dwOldProtect);//恢复原有的内存属性
    //此时该程序中所有的原函数都被我们修改了,只要调用该函数,都会被HOOK
	return TRUE;
}

BOOL UnHook(const char * pszModuleName, const char * pszFuncName)//卸载HOOK 
{
	BYTE ShortJmp[2] = { 0x8B,0xFF };//还原原有字节
	BYTE NewCodes[5] = { 0x90,0x90,0x90,0x90,0x90 };//恢复空白内存的一种方式:NOP
	HMODULE hModule = GetModuleHandleA(pszModuleName);
	FARPROC   = GetProcAddress(hModule, pszFuncName);
	DWORD dwOldProtect = 0;
	VirtualProtect((LPVOID)((DWORD)FunAddress - 5), 7, PAGE_EXECUTE_READWRITE, &dwOldProtect);
	memcpy((LPVOID)((DWORD)FunAddress - 5), NewCodes, 5);
	memcpy(FunAddress, ShortJmp, 2);
	VirtualProtect((LPVOID)((DWORD)FunAddress - 5), 7, dwOldProtect, &dwOldProtect);
    //此时原函数恢复原来的代码,卸载了HOOK
	return TRUE;
}

typedef int //声明函数指针
(WINAPI
* fnMessageBoxA)(
	_In_opt_ HWND hWnd,
	_In_opt_ LPCSTR lpText,
	_In_opt_ LPCSTR lpCaption,
	_In_ UINT uType);

int
WINAPI
MyMessageBoxA(
	_In_opt_ HWND hWnd,
	_In_opt_ LPCSTR lpText,
	_In_opt_ LPCSTR lpCaption,
	_In_ UINT uType)
{
	fnMessageBoxA NewFunc = (fnMessageBoxA)((DWORD)MessageBoxA + 2);//这是我们用于HOOK后执行的函数,其指向MessageBoxA并跳过头两个字节,防止被短跳而反复HOOK进入死循环。此时我们调用这个函数时,可以执行原来的MessageBoxA的功能 
	int bRet = NewFunc(hWnd, "hook", "hook", uType);//执行我们定义的函数,实际上就是执行MessageBoxA的有效部分
	return bRet;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
		Hook("user32.dll", "MessageBoxA", (PROC)MyMessageBoxA);
		break;
    case DLL_THREAD_ATTACH:
		break;
    case DLL_THREAD_DETACH:
		break;
    case DLL_PROCESS_DETACH:
		UnHook("user32.dll", "MessageBoxA");
        break;
    }
    return TRUE;
}

接下来我们将通过如下的代码,演示HotFix HOOK

cpp 复制代码
#include <iostream>
#include <Windows.h>

int main()
{
	MessageBoxA(NULL, "rkvir", "success", MB_OK);
	system("pause");
	MessageBoxA(NULL, "rkvir", "success", MB_OK);
	return 0;
}

首先运行程序,我们发现正常运行程序时,两次弹窗都是原来的窗口:

接下来我们再次重新运行程序,第一次弹窗是原来的窗口:

此时我们注入上文编写的dll文件,然后再次弹窗,发现弹窗变了,HOOK成功了:

相关推荐
aichitang202444 分钟前
QCU 2.0.0:补齐 Windows、Linux 与 GNOME Wayland
linux·运维·windows·ai·agent·ai编程
唐钰小宝B1 小时前
深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别
java·开发语言·windows
北风toto2 小时前
Windows 下用 OpenSSL 生成 SSL 密钥并配置 Nginx 教程
windows·nginx·ssl
2601_962364972 小时前
升级电脑人脸登录,选购 Windows Hello 模组常见误区
windows·安全·电脑
小陈不好吃2 小时前
Java 泛型与类型擦除:从 PECS 原则到 TypeToken 绕过
java·开发语言·windows·pecs 原则·typetoken绕过
Helix2504 小时前
WinPE 实战:从零制作纯净 Windows 安装与维护 U 盘
windows·安装·uefi·winpe·adk·u盘启动盘·dism
桌管小李6 小时前
内网终端安全再升级|迪康端点安全 2609 版本四大新功能解读
大数据·windows·安全·macos·网络安全
情 九6 小时前
Dify 本地环境搭建教程:macOS / Windows
windows·macos
Escalating_xu8 小时前
【Linux 网络编程】Windows 客户端访问 Linux:用 Winsock 跑通 UDP 回显
linux·网络·windows
biaov9 小时前
使用 Windows 自带的 VHDX + BitLocker 加密学习资料
windows·加密