当前位置: 首页 > news >正文

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

// 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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.dtcms.com/a/50655.html

相关文章:

  • 设计链表 力扣707
  • Leetcode 面试150题(三)
  • 字节跳动发布 Trae AI IDE!支持 DeepSeek R1 V3,AI 编程新时代来了!
  • MWC 2025|美格智能发布基于高通®X85 5G调制解调器及射频的新一代5G-A通信模组SRM819W
  • Linux搜索---find
  • 用matplotlib构建BI看板:Superset插件开发实战
  • 历年杭州电子科技大学计算机考研复试上机真题
  • 【每日学点HarmonyOS Next知识】web滚动、事件回调、selectable属性、监听H5内部router、Grid嵌套时高度设置
  • 【JavaScript—前端快速入门】JavaScript 基础语法
  • React封装通用Table组件,支持搜索(多条件)、筛选、自动序号、数据量统计等功能。未采用二次封装调整灵活,包含使用文档
  • vulnhub靶场之【digitalworld.local系列】的development靶机
  • AI会带给我们一个什么样的未来
  • X Window---图形接口
  • Linux下测试Wifi性能——4.Wifi性能测试脚本
  • vue3中 组合式~测试深入组件:事件 与 $emit()
  • C#:LINQ学习笔记01:LINQ基础概念
  • 品佳诚邀您参加 3/12『英飞凌汽车方案引领智能座舱新纪元』在线研讨会
  • Ubuntu问题 - 在ubuntu上使用 telnet 测试远程的IP:端口是否连通
  • 软考-数据库开发工程师-3.1-数据结构-线性结构
  • 为什么要学习数据结构与算法
  • 修改hosts文件,修改安全属性,建立自己的DNS
  • 如何判断https使用了哪个版本的TLS?
  • 创建 Ubuntu 22.04 USB 启动盘
  • 游戏引擎学习第135天
  • 【RTC】 TM32 RTC(实时时钟)库函数 配置
  • 图生生AI,如何将商品主图的商品替换成自己的商品?
  • CF 452A.Eevee(Java实现)
  • 操作 Redis 常用 shell 脚本
  • 获取哔站评论
  • 【JavaScript — 前端快速入门】 JavaScript 引入方式