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

news2024/11/24 11:36:12

上一讲讲到的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,                  //文件映射对象最大大小的低序 DWORD
		szName                     //文件映射对象的名称
	);

	//将文件映射的驶入映射到调用进程的地址空间
	LPVOID lpMapAddr = MapViewOfFile(
		hFileMap,        //文件映射对象的句柄
		FILE_MAP_ALL_ACCESS,   //对文件映射对象的访问类型,用于确定页面的页面保护
		0,               //视图开始位置的文件偏移量的高顺序 DWORD 
		0,        //要开始视图的文件偏移量低序 DWORD
		BUF_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.coloradmin.cn/o/893148.html

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

相关文章

Android性能优化----执行时间优化

作者&#xff1a;lu人皆知 在APP做启动优化时&#xff0c;Application会做一些初始化的工作&#xff0c;但不要在Application中做耗时操作&#xff0c;然而有些初始化工作可能是很耗时的&#xff0c;那怎么办&#xff1f;初始化操作可以开启子线程来完成。 计算执行时间 常规…

Android控件【ProgressBar】

文章目录 常用属性项目结构主要代码 常用属性 max&#xff1a;进度条的最大值progress&#xff1a;进度条已完成进度值indeterminate&#xff1a;如果设置成true&#xff0c;则进度条不精确显示进度style"?android:attr/progressBarStyleHorizontal"&#xff1a;水…

ubuntu 22.04 LTS 在 llvm release/17.x 分支上编译 cookbook llvm example Chapter 02

下载 llvm-cookbook example: $ git clone https://github.com/elongbug/llvm-cookbook.git 开发环境&#xff1a; 从apt source 安装llvm、clang和lldb&#xff1a; $ apt install llvm $ apt install llvm-dev $ apt install clang $ apt install lldb $ cd llvm-cookboo…

c语言生成.exe文件的方法

一、在Windows shell下使用gcc命令 1、需要下载获取GCC安装包 官网:MinGW-w64 - for 32 and 64 bit Windows - Browse Files at SourceForge.net 其他下载: 文件文件大小:48.0 M|https://wwnb.lanzoul.com/iIsq815lktfchttps://download.c

归并排序算法细节演示

【样例输入】 5 11 8 34 2 9 【样例输出】 2 8 9 11 34 #include<iostream> using namespace std; int n;//n个数 int data[100]; int tmp[100]; int nums 1; void ss(){cout<<endl<<"-----------------"<<endl; for(int i1;i<n;i){cou…

创建一个简单的HTML Viewer应用程序

使用wxPython和内嵌浏览器来创建一个简单的HTML Viewer应用程序。 在本篇文章中&#xff0c;我们将使用Python和wxPython模块来创建一个简单的HTML Viewer应用程序。这个应用程序可以让用户输入HTML内容&#xff0c;并在内嵌浏览器中显示该内容的效果。 准备工作 在开始之前…

Shell学习笔记之基础部分

Shell基础&#xff1a; 查看操作系统支持的shell&#xff1a; [rootrhel9 ansible]# cat /etc/shells /bin/sh /bin/bash /usr/bin/sh /usr/bin/bashShell的基本元素&#xff1a; 声明&#xff1a;声明用哪个命令解释器来解释并执行当前脚本文件中的语句&#xff0c;一般写的…

Cross-Site Scripting

文章目录 反射型xss(get)反射型xss(post)存储型xssDOM型xssDOM型xss-xxss-盲打xss-过滤xss之htmlspecialcharsxss之href输出xss之js输出 反射型xss(get) <script>alert("123")</script>修改maxlength的值 反射型xss(post) 账号admin密码123456直接登录 …

tauri-react:快速开发跨平台软件的架子,支持自定义头部UI拖拽移动和窗口阴影效果

tauri-react 一个使用 taurireacttsantd 开发跨平台软件的模板&#xff0c;支持窗口头部自定义和窗口阴影&#xff0c;不用再自己做适配了&#xff0c;拿来即用&#xff0c;非常 nice。而且已经封装好了 tauri 的 http 请求工具&#xff0c;省去很多弯路。 开原地址&#xff…

抖音短视频SEO矩阵系统源码开发

一、概述 抖音短视频SEO矩阵系统源码是一项综合技术&#xff0c;旨在帮助用户在抖音平台上创建并优化短视频内容。本文将详细介绍该系统的技术架构、核心代码、实现过程以及优化建议&#xff0c;以便读者更好地理解并应用这项技术。 二、技术架构 抖音短视频SEO矩阵系统采用前…

ai之美:探索写真照片软件的创造力

小青&#xff1a;嘿&#xff0c;小华&#xff0c;你知道最近ai艺术写真非常流行吗&#xff1f; 小华&#xff1a;真的吗&#xff1f;我还不知道呢。告诉我更多细节吧&#xff01; 小青&#xff1a;好的&#xff0c;ai艺术写真是指使用人工智能技术将照片转化为艺术作品的过程…

飞天使-k8s简单搭建(编写中)

文章目录 k8s概念安装部署无密钥配置与hosts与关闭swap开启ipv4转发安装前启用脚本开启ip_vs安装指定版本docker 安装kubeadm kubectl kubelet k8s单节点部署参考链接地址 k8s概念 K8sMaster : 管理K8sNode的。 K8sNode:具有docker环境 和k8s组件&#xff08;kubelet、k-prox…

C#小轮子:MiniExcel,快速操作Excel

文章目录 前言环境安装功能测试普通读写读新建Excel表格完全一致测试&#xff1a;成功大小写测试&#xff1a;严格大小写别名读测试&#xff1a;成功 写普通写别名写内容追加更新模板写 其它功能xlsx和CSV互转 前言 Excel的操作是我们最常用的操作&#xff0c;Excel相当于一个…

【markdown】使用 Mermaid 制作的图表和图形的一些示例

Mermaid是一种用于绘制流程图,状态图,序列图和甘特图的文本标记语言。 Mermaid的主要特点包括: 基于文本 - 使用类似Markdown的语法来描述图表,之后可以转换为SVG图表。支持流程图 - 使用图形节点和箭头来表示流程步骤和流转方向。支持状态图 - 可以绘制状态之间的转换条件。支…

【使用Zookeeper当作注册中心】自己定制负载均衡常见策略

自己定制负载均衡常见策略 一、前言随机&#xff08;Random&#xff09;策略的实现轮询&#xff08;Round Robin&#xff09;策略的实现哈希&#xff08;Hash&#xff09;策略 一、前言 大伙肯定知道&#xff0c;在分布式开发中&#xff0c;目前使用较多的注册中心有以下几个&…

Centos 8 网卡connect: Network is unreachable错误解决办法

现象1、ifconfig没有ens160配置 [testlocalhost ~]$ ifconfig lo: flags73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10<host> loop txqueuelen 1000 (Local Loopba…

css学习4(背景)

1、CSS中&#xff0c;颜色值通常以以下方式定义: 十六进制 - 如&#xff1a;"#ff0000"RGB - 如&#xff1a;"rgb(255,0,0)"颜色名称 - 如&#xff1a;"red" 2、background-image 属性描述了元素的背景图像. 默认情况下&#xff0c;背景图像进…

SaaS当然是一门好生意了啊

&#xff08;1&#xff09;SaaS关键特征 1、应用架构&#xff1a;多租户 2、部署&#xff1a;公有IaaS部署 3、商业模式&#xff1a;年度订阅续费 &#xff08;2&#xff09;用户云注册、用户在线付费 用户云注册、用户在线付费&#xff0c;站在中国乙方利益视角&#xff0c;多…

【Linux命令详解 | ssh命令】 ssh命令用于远程登录到其他计算机,实现安全的远程管理

文章标题 简介一&#xff0c;参数列表二&#xff0c;使用介绍1. 连接远程服务器2. 使用SSH密钥登录2.1 生成密钥对2.2 将公钥复制到远程服务器 3. 端口转发3.1 本地端口转发3.2 远程端口转发 4. X11转发5. 文件传输与远程命令执行5.1 文件传输5.1.1 从本地向远程传输文件5.1.2 …

WPF国际化的实现方法(WpfExtensions.Xaml)

https://blog.csdn.net/eyupaopao/article/details/120090431 resx资源文件实现 resx资源文件&#xff0c;实现的过程比第一种复杂&#xff0c;但resx文件本身编辑比较简单&#xff0c;维护起来比较方便。需要用到的框架&#xff1a;WpfExtensions.Xaml 为每种语言添加.resx资…