【Windows系统编程】06.HotFixHook与进程通信(详解HotFixHook)

上一讲讲到的InlineHook,每次Hook的时候,都要读写两次内存(先Hook,再还原)这种Hook方式,性能比较低,今天我们讲的这种Hook方式,可以说是InlineHook的升级版本

HotFix(热补丁)

我们先来讲讲原理:

我们继续来看看目标程序反汇编:

770A8E19 | CC                       | int3                                    |
770A8E1A | CC                       | int3                                    |
770A8E1B | CC                       | int3                                    |
770A8E1C | CC                       | int3                                    |
770A8E1D | CC                       | int3                                    |
770A8E1E | CC                       | int3                                    |
770A8E1F | CC                       | int3                                    |
770A8E20 | 8BFF                     | mov edi,edi                             |
770A8E22 | 55                       | push ebp                                |
770A8E23 | 8BEC                     | mov ebp,esp                             |
770A8E25 | 833D 8C8C0D77 00         | cmp dword ptr ds:[770D8C8C],0           |
770A8E2C | 74 22                    | je user32.770A8E50                      |
770A8E2E | 64:A1 18000000           | mov eax,dword ptr fs:[18]               |
770A8E34 | BA 18930D77              | mov edx,user32.770D9318                 | edx:"榍\f"
770A8E39 | 8B48 24                  | mov ecx,dword ptr ds:[eax+24]           | ecx:"榍\f"
770A8E3C | 33C0                     | xor eax,eax                             |
770A8E3E | F0:0FB10A                | lock cmpxchg dword ptr ds:[edx],ecx     | edx:"榍\f", ecx:"榍\f"
770A8E42 | 85C0                     | test eax,eax                            |
770A8E44 | 75 0A                    | jne user32.770A8E50                     |
770A8E46 | C705 288D0D77 01000000   | mov dword ptr ds:[770D8D28],1           |
770A8E50 | 6A FF                    | push FFFFFFFF                           |
770A8E52 | 6A 00                    | push 0                                  |
770A8E54 | FF75 14                  | push dword ptr ss:[ebp+14]              |
770A8E57 | FF75 10                  | push dword ptr ss:[ebp+10]              |
770A8E5A | FF75 0C                  | push dword ptr ss:[ebp+C]               |
770A8E5D | FF75 08                  | push dword ptr ss:[ebp+8]               |
770A8E60 | E8 0BFEFFFF              | call <user32.MessageBoxTimeoutW>        |
770A8E65 | 5D                       | pop ebp                                 |
770A8E66 | C2 1000                  | ret 10                                  |

我们发现呢,在MessageBox API之前,还有一堆int3,而这些int3是没用的,我们是否能用这点空余的内存,来构造一个jmp指令,来跳转到我们HOOK的函数?

很明显,jmp(E9)指令占一个字节,32位环境下,地址也占4个字节,而这里int3的数目,足够让我们来构造一个指令了

  • 思路:

    MessageBox函数第一句指令,mov edi,edi 实际上在32位环境下并没有什么意义,我们利用这个指令的两个字节,跳转到他上面的5个字节,构造跳转到我们自己的函数地址

    这样的话,我们就要修改7个字节的数据

  • 实操演示:

    我们还是利用动态库,注入的方式完成Hook:

    dll:

    // dllmain.cpp : 定义 DLL 应用程序的入口点。
    #include "pch.h"BOOL hotFixHook(const WCHAR* pszModuleName, const char* pszFuncName, PROC pHookFunc) {//构造段跳转指令:BYTE ShortJmp[2] = { 0xEB,0xF9 };//替换5个int3的jmp指令:BYTE JmpCode[5] = { 0xE9,0, };HMODULE hModule = GetModuleHandle(pszModuleName);if (hModule == INVALID_HANDLE_VALUE) {MessageBox(NULL, L"打开进程失败", L"错误", NULL);return FALSE;}//获取函数地址FARPROC pFuncAddr = GetProcAddress(hModule, pszFuncName);//修改内存属性DWORD dwOldProcAddrProtect = 0;VirtualProtectEx(GetCurrentProcess(), (LPVOID)((DWORD)pFuncAddr - 5), 7, PAGE_EXECUTE_READWRITE, &dwOldProcAddrProtect);DWORD JmpAddr = (DWORD)pHookFunc - (DWORD)pFuncAddr;*(DWORD*)(JmpCode + 1) = JmpAddr;//修改内存memcpy((LPVOID)((DWORD)pFuncAddr - 5), JmpCode, 5);memcpy(pFuncAddr, ShortJmp, 2);//改回内存属性VirtualProtectEx(GetCurrentProcess(), pFuncAddr, 7, dwOldProcAddrProtect, &dwOldProcAddrProtect);return TRUE;
    }BOOL UnHook(const WCHAR* pszModuleName, const char* pszFuncName) {//构造段跳转指令:BYTE ShortJmp[2] = { 0x8B,0xFF };//替换5个int3的jmp指令:BYTE JmpCode[5] = { 0x90,0x90,0x90,0x90,0x90 };HMODULE hModule = GetModuleHandle(pszModuleName);if (hModule == INVALID_HANDLE_VALUE) {MessageBox(NULL, L"打开进程失败", L"错误", NULL);return FALSE;}//获取函数地址FARPROC pFuncAddr = GetProcAddress(hModule, pszFuncName);//修改内存属性DWORD dwOldProcAddrProtect = 0;VirtualProtectEx(GetCurrentProcess(), (LPVOID)((DWORD)pFuncAddr - 5), 7, PAGE_READWRITE, &dwOldProcAddrProtect);//修改内存memcpy((LPVOID)((DWORD)pFuncAddr - 7), JmpCode, 5);memcpy(pFuncAddr, ShortJmp, 2);//改回内存属性VirtualProtectEx(GetCurrentProcess(), pFuncAddr, 7, dwOldProcAddrProtect, &dwOldProcAddrProtect);return TRUE;
    }typedef int
    (WINAPI*fnMyMessageBox)(_In_opt_ HWND hWnd,_In_opt_ LPCWSTR lpText,_In_opt_ LPCWSTR lpCaption,_In_ UINT uType);int WINAPI
    MyMessageBox(_In_opt_ HWND hWnd,_In_opt_ LPCWSTR lpText,_In_opt_ LPCWSTR lpCaption,_In_ UINT uType) {//如果我们还是通过正常流程调用MessageBOx,就会进入死循环,这里通过函数指针的方式调用,跳过前两字节(我们构造的短跳转指令)fnMyMessageBox Func = (fnMyMessageBox)((DWORD)MessageBox + 2);int nRet = Func(NULL, L"Hook", L"Hook", NULL);return nRet;
    }BOOL APIENTRY DllMain( HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved)
    {switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:hotFixHook(L"User32.dll", "MessageBoxW", (PROC)MyMessageBox);break;case DLL_THREAD_ATTACH:break;case DLL_THREAD_DETACH:break;case DLL_PROCESS_DETACH:break;}return TRUE;
    }

Hook测试:

运行目标程序:

运行目标程序

注入dll:

注入dll

Hook成功:

Hook成功

进程通信

1.文件映射

进程一:

// Process1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include <iostream>
#include <Windows.h>#define BUF_SIZE 256
TCHAR szName[] = L"WdigObject";int main()
{//为指定文件创建或打开命名或未命名的文件映射对象HANDLE hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE,      //要从中创建文件映射对象的句柄NULL,                      //安全属性PAGE_READWRITE,    //指定文件映射对象的页面保护0,                      //文件映射对象的最大大小的高阶 DWORD BUF_SIZE,                  //文件映射对象最大大小的低序 DWORDszName                     //文件映射对象的名称);//将文件映射的驶入映射到调用进程的地址空间LPVOID lpMapAddr = MapViewOfFile(hFileMap,        //文件映射对象的句柄FILE_MAP_ALL_ACCESS,   //对文件映射对象的访问类型,用于确定页面的页面保护0,               //视图开始位置的文件偏移量的高顺序 DWORD 0,        //要开始视图的文件偏移量低序 DWORDBUF_SIZE             //要映射到视图的文件映射的字节数);CopyMemory(lpMapAddr, L"Hello FileMap", (wcslen(L"Hello FileMap")+1)*2);getchar();//从调用进程的地址空间中取消映射文件的映射视图。UnmapViewOfFile(lpMapAddr);CloseHandle(hFileMap);
}

进程二:

// Process2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include <iostream>
#include <Windows.h>#define BUF_SIZE 256
TCHAR szName[] = L"WdigObject";int main()
{//打开文件映射HANDLE hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, szName);LPVOID lpBuffer = MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, BUF_SIZE);MessageBox(NULL, (LPCWSTR)lpBuffer, L"success", NULL);UnmapViewOfFile(lpBuffer);CloseHandle(hFileMap);
}

通信测试:

先运行进程一,在运行进程二:

通信成功:

通信成功

我们来看看文件映射完成进程通信的原理:

就是生成一个文件映射,两个进程都能访问这些内存,就完成了进程通信

2.命名管道

进程一:

// Process1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include <iostream>
#include <Windows.h>int main()
{//创建命名管道的实例,并返回后续管道操作的句柄HANDLE hPipe = CreateNamedPipe(L"\\\\.\\pipe\\Communication",          //管道的唯一名称PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,//打开模式PIPE_TYPE_BYTE,                        //管道模式1,                                     //可为此管道创建的最大实例数1024,                                  //要为输出缓冲区保留的字节数1024,                                  //要为输入缓冲区保留的字节数0,                                     //NULL                                   //安全属性);//创建一个事件HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);//使命名管道服务进程能够等待客户端进程连接到命名管道的实例OVERLAPPED ovlap;ZeroMemory(&ovlap, sizeof(ovlap));ovlap.hEvent = hEvent;if (!ConnectNamedPipe(hPipe, &ovlap)) {//这里需要注意,如果成功,也会报错if (GetLastError() != ERROR_IO_PENDING) {std::cout << "ConnectNamedPipe Failed Code:" << GetLastError() << std::endl;CloseHandle(hPipe);CloseHandle(hEvent);return -1;}}if (WaitForSingleObject(hEvent, INFINITE) == WAIT_FAILED) {std::cout << "WaitForSingleObject Error" << GetLastError() << std::endl;CloseHandle(hPipe);CloseHandle(hEvent);return -1;}CloseHandle(hEvent);char szBuffer[0x100] = { 0 };DWORD dwReadSize = 0;ReadFile(hPipe, szBuffer, 0x100, &dwReadSize, NULL);std::cout << szBuffer << std::endl;char WriteBuffer[] = "Hello";DWORD dwWriteSize = 0;WriteFile(hPipe, WriteBuffer, strlen(WriteBuffer) + 1, &dwWriteSize, NULL);system("pause");return 0;
}

进程二:

// Process2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include <iostream>
#include <Windows.h>int main()
{//等待命名管道if (!WaitNamedPipe(L"\\\\.\\pipe\\Communication",    //命名管道的名称NMPWAIT_WAIT_FOREVER             //函数等待命名管道实例可用的毫秒数)) {std::cout << "WaitNamedPipe Failed Code:" << GetLastError() << std::endl;return -1;}//创建或打开文件或 I/O 设备HANDLE hFile = CreateFile(L"\\\\.\\pipe\\Communication",     //要创建或打开的文件或设备的名称GENERIC_READ | GENERIC_WRITE,     //请求对文件或设备的访问权限NULL,                             //请求的文件或设备的共享模式NULL,                             //安全属性OPEN_EXISTING,                    //要对存在或不存在的文件或设备执行的操作FILE_ATTRIBUTE_NORMAL,            //文件或设备属性和标志NULL);char WriteBuffer[] = "Hello";DWORD dwWrittenByte = 0;WriteFile(hFile, WriteBuffer, strlen(WriteBuffer) + 1, &dwWrittenByte, NULL);char lpBuffer[0x100] = { 0 };DWORD dwReadBytes = 0;ReadFile(hFile, lpBuffer, 0x100, &dwReadBytes, NULL);std::cout << lpBuffer << std::endl;system("pause");return 0;}

通信测试:

先运行进程一,在运行进程二

通信成功:

通信成功

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

公众号11周年,终于向公域流量打开了大门

是的&#xff0c;在这篇文章要发布之前&#xff0c;看了下日期&#xff0c;才惊觉明天就是公众号11周年了。 时间真的过得飞快&#xff0c;总觉得10周年刚过不久。 已经11年的公众号&#xff0c;或许少了很多关注&#xff0c;或许很多目光也被视频号夺去了。 但让人欣喜的是…

前馈神经网络dropout实例

直接看代码。 &#xff08;一&#xff09;手动实现 import torch import torch.nn as nn import numpy as np import torchvision import torchvision.transforms as transforms import matplotlib.pyplot as plt#下载MNIST手写数据集 mnist_train torchvision.datasets.MN…

基于开源模型搭建实时人脸识别系统(四):人脸质量

续人脸识别实战之基于开源模型搭建实时人脸识别系统&#xff08;三&#xff09;&#xff1a;人脸关键点、对齐模型概览与模型选型_CodingInCV的博客-CSDN博客 不论对于静态的人脸识别还是动态的人脸识别&#xff0c;我们都会面临一个问题&#xff0c;就是输入的人脸图像的质量可…

使用 OpenTelemetry 构建可观测性 04 - 收集器

在之前的博文中&#xff0c;我们讨论了如何使用 SDK 和链路追踪生产者来导出进程中的遥测数据。尽管有多种类型的导出器可供选择&#xff0c;但其中一个常见的目标是将数据导出到 OpenTelemetry Collector。本篇文章将深入探讨收集器以及如何使用它。 选 OTel Collector 还是…

Eduma主题 - 线上教育WordPress主题/网站

Eduma主题 – 线上教育WordPress主题是为教育网站、LMS、培训中心、课程中心、学院、大学、学校、幼儿园而制作的。基于我们使用以前的主题eLearning WP构建WordPress LMS的经验&#xff0c;Education WP是下一代&#xff0c;也是围绕WordPress最好的教育主题之一&#xff0c;它…

一个炫酷的头像悬停效果 2

基于上次翻译的 &#x1f525;&#x1f525;一个炫酷的头像悬停效果 收获了不少同学的喜欢&#xff0c;原作者近期进行了优化升级。本文将升级后的核心实现过程进行梳理讲解&#xff0c;如果没看过第一期的推荐先看看第一期的实现过程。升级后的效果如下图所示。 gif动画效果如…

Baklib是比语雀、Notion、石墨文档更好用的在线知识库管理工具

在当今信息爆炸的时代&#xff0c;如何高效地管理和利用知识成为了每个人都面临的问题。在线知识库管理工具应运而生&#xff0c;帮助用户整理、存储和共享知识。在这篇文章中&#xff0c;我将介绍一个更好用的在线知识库管理工具——Baklib&#xff0c;并探讨它相对于其他知识…

Spring系列篇--关于IOC【控制反转】的详解

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于Spring的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.什么是Spring 二.Spring的特点 三.什…

【O2O领域】Axure外卖订餐骑手端APP原型图,外卖众包配送原型设计图

作品概况 页面数量&#xff1a;共 110 页 兼容软件&#xff1a;Axure RP 9/10&#xff0c;不支持低版本 应用领域&#xff1a;外卖配送、生鲜配送 作品申明&#xff1a;页面内容仅用于功能演示&#xff0c;无实际功能 作品特色 本品为外卖订餐骑手端APP原型设计图&#x…

Datawhale Django 后端开发入门 Task05 DefaultRouter、自定义函数

一、DefaultRouter是Django REST framework中提供的一个路由器类&#xff0c;用于自动生成URL路由。路由器是将URL与视图函数或视图集关联起来的一种机制。Django REST framework的路由器通过简单的配置可以自动生成标准的URL路由&#xff0c;从而减少了手动编写URL路由的工作量…

Redis Lua脚本执行原理和语法示例

Redis Lua脚本语法示例 文章目录 Redis Lua脚本语法示例0. 前言参考资料 1. Redis 执行Lua脚本原理1.1. 对Redis源码中嵌入Lua解释器的简要解析&#xff1a;1.2. Redis Lua 脚本缓存机制 2. Redis Lua脚本示例1.1. 场景示例1. 请求限流2. 原子性地从一个list移动元素到另一个li…

基于郊狼算法优化的BP神经网络(预测应用) - 附代码

基于郊狼算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于郊狼算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.郊狼优化BP神经网络2.1 BP神经网络参数设置2.2 郊狼算法应用 4.测试结果&#xff1a;5.Matlab代码 摘要…

【深入解析:数据结构栈的魅力与应用】

本章重点 栈的概念及结构 栈的实现方式 数组实现栈接口 栈面试题目 概念选择题 一、栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶&#xff0c;另一端称为栈底。栈中的数…

指针(一)【C语言进阶版】

大家好&#xff0c;我是深鱼~ 【前言】&#xff1a; 指针的主题&#xff0c;在初阶指针章节已经接触过了&#xff0c;我们知道了指针的概念&#xff1a; 1.指针就是个变量&#xff0c;用来存放地址&#xff0c;地址的唯一标识一块内存空间&#xff08;指针变量&#xff09;&a…

【云原生|Docker系列第3篇】Docker镜像的入门实践

欢迎来到Docker入门系列的第三篇博客&#xff01;在前两篇博客中&#xff0c;我们已经了解了什么是Docker以及如何安装和配置它。本篇博客将重点介绍Docker镜像的概念&#xff0c;以及它们之间的关系。我们还将学习如何拉取、创建、管理和分享Docker镜像&#xff0c;这是使用Do…

jenkins同一jar包部署到多台服务器

文章目录 安装插件配置ssh服务构建完成后执行 没有部署过可以跟这个下面的步骤先部署一遍&#xff0c;我这篇主要讲jenkins同一jar包部署到多台服务器 【Jenkins】部署Springboot项目https://blog.csdn.net/qq_39017153/article/details/131901613 安装插件 Publish Over SSH 这…

stm32g070的PD0/PD2 PA8和PB15

目前在用STM32G070做项目&#xff0c;其中PD2TIMER3去模拟PWM&#xff0c;PD0用作按键检测&#xff0c;测试发现PD0低电平检测没有问题&#xff0c;高电平检测不到&#xff0c;电路图如下图所示&#xff1a; 用万用表测试电平&#xff0c;高电平1.0V左右&#xff0c;首先怀疑硬…

rust踩雷笔记(4)——刷点Vec相关的题(持续更新)

俗话说&#xff0c;孰能生巧&#xff0c;今天是第六天接触Rust&#xff0c;感觉基础语法和特性没什么问题了&#xff08;当然如果你整天都学这个可能2天半就够了&#xff09;&#xff0c;但是想达到熟练使用&#xff0c;还需要刷点题。算法我相信能来看rust博客的人都是大牛&am…

【项目实践】基于LSTM的一维数据扩展与预测

基于LSTM的一维数据拟合扩展 一、引(fei)言(hua) 我在做Sri Lanka生态系统服务价值计算时&#xff0c;中间遇到了一点小问题。从世界粮农组织(FAO)上获得Sri Lanka主要农作物产量和价格数据时&#xff0c;其中的主要作物Sorghum仅有2001-2006年的数据&#xff0c;而Millet只有…

算法通关村第4关【黄金】| 表达式问题

1. 计算器问题 思路&#xff1a;此题不考虑括号和负数情况&#xff0c;单纯使用栈即可解决。注意的是数字可能是多位数需要保留完整的num&#xff0c; 保留数字的前缀符号&#xff0c;当碰到加号&#xff0c;存进去&#xff1b;当碰到减号&#xff0c;存相反数进去&#xff1b;…