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成功了:

相关推荐
vfvfb5 小时前
bat批量去掉本文件夹中的文件扩展名
服务器·windows·批处理·删除扩展名·bat技巧
我命由我1234511 小时前
VSCode - VSCode 放大与缩小代码
前端·ide·windows·vscode·前端框架·编辑器·软件工具
PT_silver11 小时前
tryhackme——Abusing Windows Internals(进程注入)
windows·microsoft
爱炸薯条的小朋友12 小时前
C#由于获取WPF窗口名称造成的异常报错问题
windows·c#·wpf
Lw老王要学习13 小时前
VScode 使用 git 提交数据到指定库的完整指南
windows·git·vscode
CodeOfCC19 小时前
c语言 封装跨平台线程头文件
linux·c语言·windows
momo卡20 小时前
MinGW-w64的安装详细步骤(c_c++的编译器gcc、g++的windows版,win10、win11真实可用)
c语言·c++·windows
南林yan1 天前
DLL动态库实现文件遍历功能(Windows编程)
windows
Mike_6661 天前
win10安装WSL2、Ubuntu24.04
windows·ubuntu·wsl2
liulun1 天前
Skia如何绘制几何图形
c++·windows