【Windows系统编程】05.内存操作与InlineHook(详解InlineHook实现)

news2024/11/16 11:55:13

文章目录

  • 内存相关
  • InlineHook
    • 完整实现代码(dll):
  • InlineHook测试:

内存相关

  • 内存信息

    头文件:#include <Psapi.h>

        //检索有关系统当前使用物理内存和虚拟内存的信息
        MEMORYSTATUSEX mst;
        GlobalMemoryStatusEx(&mst);
    
        //检索有关当前系统的信息
        SYSTEM_INFO SysInfo;
        GetSystemInfo(&SysInfo);
    
        //检索当前进程的伪句柄
        HANDLE hProcess = GetCurrentProcess();
        //检索调用进程的进程标识符。
        DWORD dwProcessId = GetCurrentProcessId();
    
        //检索有关指定进程的内存使用情况的信息
        PROCESS_MEMORY_COUNTERS pmc;
        GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(PROCESS_MEMORY_COUNTERS));
    

    这里需要注意一点,上述API,后面带EX的通常可以跨进程获取信息,不带Ex的版本通常用来查询自身信息

  • 常用:

    查询虚拟内存属性,申请虚拟内存,修改属性

    本地内存读写:

        //保留、提交或更改调用进程的虚拟地址空间中页面区域的状态。 此函数分配的内存会自动初始化为零。
        LPVOID lpAddr = VirtualAlloc(
            NULL,          //要分配的内存的起始地址,如果为NULL,则由系统自动确定地址
            0x100,         //要分配的内存大小
            MEM_COMMIT,    //内存分配的类型,这里MEM_COMMIT基本上是通配了
            PAGE_READONLY  //分配内存的保护属性,这里PAGE_READONLY是只读属性,可以调用API修改的
        );
    
        //检索有关指定进程的虚拟地址空间中的页面范围的信息
        MEMORY_BASIC_INFORMATION mbi;
        VirtualQueryEx(
            GetCurrentProcess(),    //查询内存信息的进程句柄
            lpAddr,                 //指向要查询的页面区域的基址指针
            &mbi,                   //指向接收返回信息的MEMORY_BASIC_INFORMATION指针
            sizeof(MEMORY_BASIC_INFORMATION) //lpBuffer指向的缓冲区大小
        );
    
        //更改指定进程的虚拟地址空间中已提交的页面区域的保护
        DWORD OldProtect = 0;
        VirtualProtectEx(
            GetCurrentProcess(),        //要修改内存保护的进程句柄
            lpAddr,                     //要修改保护属性基址指针
            0x100,                      //要修改保护属性的内存大小
            PAGE_READWRITE,             //修改的新的保护属性
            &OldProtect                 //这里会写入原来的保护属性,为了避免被检测,通常修改之后还会修改回去
        );
    
        //给内存赋值,如果内存是只读属性,那么就会报错0x5(拒绝访问),需要修改内存属性
        memcpy(lpAddr, L"123456789", sizeof(L"123456789"));
    
        //写完内存之后,我们修改回去
        VirtualProtectEx(GetCurrentProcess(), lpAddr, 0x100, OldProtect, &OldProtect);
    
        memcpy(lpAddr, L"123456789", sizeof(L"123456789"));
    
        //释放虚拟内存
        VirtualFreeEx(
            GetCurrentProcess(),     //进程句柄
            lpAddr,                  //指向要释放内存的起始指针
            0,                       //要释放内存的大小,如果ddwFreeType属性为MEM_RELEASE,则必须为0
            MEM_RELEASE              //免费操作的类型
        );
    

    还是一样,这里所有API,Ex版本通常可以跨进程读写虚拟内存,不带Ex版本可以读写自身进程虚拟内存

  • 远程读写内存:

    ReadProcessMemory();
    WriteProcessMemory();
    
  • 堆实际上是由一种数据结构进行管理的内存

    我们用的new,malloc等都是在默认堆上申请,堆会随着我们申请空间而变大

    默认堆,我们也可以自己创建堆结构:

    	//创建可由调用进程使用的专用堆对象
    	HANDLE hHeap = HeapCreate(
    		HEAP_NO_SERIALIZE,       //堆分配选项
    		0,                       //堆的起始大小
    		1024                     //堆的最大大小
    	);
    
    	//从堆中分配内存块。分配的内存不可移动。
    	LPVOID lpHeapAddr = HeapAlloc(
    		hHeap,        //堆的句柄
    		HEAP_ZERO_MEMORY,   //堆分配选项,这里是初始化为0
    		MAX_PATH            //要分配的字节数
    	);
    
    	memcpy(lpHeapAddr, L"123456", sizeof(L"123456"));
    
    	//释放从堆中申请的内存
    	HeapFree(
    		hHeap,            //堆的句柄
    		HEAP_NO_SERIALIZE, //堆免费选项,这里是不会使用序列化访问
    		lpHeapAddr        //指向要释放内存的指针
    	);
    
    	//销毁指定堆对象
    	HeapDestroy(hHeap);
    

InlineHook

我们要HOOK的目标程序源码:

int main()
{
    
    MessageBox(NULL, L"WdIg111", L"提示", NULL);
    system("pause");
    MessageBox(NULL, L"WdIg222", L"提示", NULL);
}

程序拖进x32dbg:

0030181E | 6A 00                    | push 0                                  |
00301820 | 68 307B3000              | push <目标程序.L"\xFFD0:">                  | 307B30:"衏:y"
00301825 | 68 F87B3000              | push <目标程序.L"WdIg222">                  | 307BF8:L"WdIg222"
0030182A | 6A 00                    | push 0                                  |
0030182C | FF15 98B03000            | call dword ptr ds:[<&MessageBoxW>]      |

这是调用MessageBox的部分,有四个push是压入参数,我们来看看MessageBox实现部分:

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的实现了,那么我们要Hook它的实现,那么我们就要在这里做劫持,实际上就是修改前五个字节,做到jmp到我们构造的函数,

也就是:修改指令

770A8E20 | 8BFF                     | mov edi,edi                             |
770A8E22 | 55                       | push ebp                                |
770A8E23 | 8BEC                     | mov ebp,esp                             |

那么为什么是修改5个字节呢?因为我们要做到劫持,jmp到我们的地址上去,jmp指令(E9)占据一个字节,而在32位环境下,地址是四个字节长度,所以我们只需要修改五个字节就可以了

  • 实现:

    主要思想,就是发布一个dll,注入到目标程序,而我们前一讲刚学过,dll里面case DLL_PROCESS_ATTACH,实在进程加载dll的时候,就会触发,我们将Hook在触发事件里完成

完整实现代码(dll):

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"

//全局变量

//目标函数地址(被Hook的函数)
PROC m_FuncAddr = NULL;
//保存被我们修改的5个字节数据
BYTE m_OldByte[5] = { 0 };
//我们修改的5个字节数据
BYTE m_NewByte[5] = { 0 };

//Hook(这里我们需要:目标函数所在模块名称,目标函数名,劫持流程的函数地址)
BOOL InlineHook(const WCHAR* pszModuleName, const char* pszFuncName, PROC pfnHookFunc) {
	//获取模块句柄
	HMODULE hModule = GetModuleHandleW(pszModuleName);
	//从指定的动态链接库 (DLL) 检索导出函数 (也称为过程) 或变量,返回地址
	m_FuncAddr = GetProcAddress(hModule, pszFuncName);
	if (m_FuncAddr == NULL) {
		return FALSE;
	}

	//读取指定进程内存
	SIZE_T dwReadSize = 0;
	BOOL bRet = ReadProcessMemory(
		GetCurrentProcess(),     //由于我们注入到了目标进程,这里直接获取自身的进程句柄就可以了
		m_FuncAddr,              //要读取内存的起始地址
		m_OldByte,                 //接收读取的内容指针
		5,                       //指定读取大小
		&dwReadSize              //接收实际读取大小指针
	);
	//判断是否读取成功
	if (!bRet||dwReadSize != 5) {
		return FALSE;
	}

	//构造新的5字节
	m_NewByte[0] = '\xE9';
	//这里是要跳转的地址,注意计算公式:要跳转的地址 = 要执行的地址 - 原来执行地址 -指令长度
	*(DWORD*)(m_NewByte + 1) = (DWORD)pfnHookFunc - (DWORD)m_FuncAddr - 5;
	SIZE_T dwWrittenByte = 0;
	bRet = WriteProcessMemory(
		GetCurrentProcess(),
		m_FuncAddr,
		m_NewByte,
		5,
		&dwWrittenByte
	);
	if (!bRet || dwWrittenByte != 5) {
		return FALSE;
	}
	else {
		return TRUE;
	}
}

//修改完执行后,我们还需要改回去,不然就进入了死循环
VOID UnHook() {
	if (m_FuncAddr != NULL) {
		DWORD dwWrittenByte = 0;
		WriteProcessMemory(GetCurrentProcess(), m_FuncAddr, m_OldByte, 5, &dwWrittenByte);
	}
}

VOID ReHook() {
	if (m_FuncAddr != NULL) {
		DWORD dwWrittenByte = 0;
		WriteProcessMemory(GetCurrentProcess(), m_FuncAddr, m_NewByte, 5, &dwWrittenByte);
	}
}

//这里构造我们自己的MessageBox(做函数转发)
int
WINAPI
MyMessageBox(
	_In_opt_ HWND hWnd,
	_In_opt_ LPCWSTR lpText,
	_In_opt_ LPCWSTR lpCaption,
	_In_ UINT uType) {
	//恢复
	UnHook();
	int bRet = MessageBox(hWnd, L"Hook", L"InlineHook", uType);
	//重新挂钩
	ReHook();
	return bRet;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
		InlineHook(L"User32.dll", "MessageBoxW", (PROC)MyMessageBox);
		break;
    case DLL_THREAD_ATTACH:
		break;
    case DLL_THREAD_DETACH:
		break;
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

InlineHook测试:

    1. 执行目标程序:
      执行目标程序
    1. 注入dll:
      注入dll
  • Hook成功:
    Hook成功

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

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

相关文章

docker 容器满了常用处理方法

docker 容器满了常用处理方法 1、运行 df -h 查看剩余磁盘占用情况 2、进入到docker目录 cd /var/lib/docker 3、运行du -h --max-depth1 &#xff08;检索文件的最大深度1&#xff0c;即只检索汇总计算当前目录下的文件&#xff09; 4、进入占用最大的 /containers文件夹&am…

pg各种条件判断语句

1.基本查询 搜索语句&#xff1a; select (distinct&#xff08;去重&#xff09;) 内容&#xff08;*代表所有&#xff09; as 别名 from 表 注释&#xff1a; -- 快速查询&#xff1a;select 内容 AS 别名 没有表一般当做计算器来用 2.条件查询 null只能用is 或者is…

7.11 Java方法重写

7.11 Java方法重写 这里首先要确定的是重写跟属性没有关系&#xff0c;重写都是方法的重写&#xff0c;与属性无关 带有关键字Static修饰的方法的重写实例 父类实例 package com.baidu.www.oop.demo05;public class B {public static void test(){System.out.println("这…

CLion 创建Qt工程

环境 CLion &#xff1a;2019.3.6 Qt &#xff1a;5.9.6&#xff08;MinGW&#xff09; Clion 配置 编译环境配置 添加Qt MinGW编译环境 添加Qt工具 创建工程 正常创建C工程 CMakeLists cmake_minimum_required(VERSION 3.8) project(QtApp)set(CMAKE_CXX_STANDARD 11)…

APB register脚本

[github repo]根据Excel表格自动生成寄存器RTL/RALF/C header的脚本 - wudayemen - 博客园 (cnblogs.com) 在芯片设计中&#xff0c;常常会使用APB总线配置每个模块的寄存器。这一部分可以使用脚本生成相应RTL代码和对应的验证所需文件比如RALF&#xff0c;和C语言的头文件&am…

【仿写tomcat】三、通过socket读取http请求信息

仿写tomcat 建立Socket连接获取连接信息查看HTTP信息 建立Socket连接 这里我们也是创建一个专门管理socket的类 package com.tomcatServer.socket;import java.io.*; import java.net.ServerSocket;/*** 套接字存储** author ez4sterben* date 2023/08/15*/ public class Soc…

电影《孤注一掷》观后感

上周看了电影《孤注一掷》&#xff0c;看完后&#xff0c;内心久久无法平静,电影影响还是听深远的的。 &#xff08;1&#xff09;情感与金钱的损失 我们每个人&#xff0c;或多或少&#xff0c;都收到过&#xff0c;诈骗类的短信或者电话&#xff0c;只要你使用的智能手机&am…

【MaxKey对接一】对接gitlab的oauth登录

MaxKey的Oauth过程 引导进入 GET http://{{maxKey_host}}/sign/authz/oauth/v20/authorize?client_idYOUR_CLIENT_ID&response_typecode&redirect_uriYOUR_REGISTERED_REDIRECT_URI 登录后回调地址 YOUR_REGISTERED_REDIRECT_URI/?code{{code}} 换取Access Token GET…

探究主成分分析方法数学原理

目录 1、简介 2、实现原理 3、实现步骤 4、公式分析 5、实例分析 6、⭐协方差矩阵补充说明 7、LaTex文本 ⭐创作不易&#xff0c;您的一键三连&#xff0c;就是支持我写作的最大动力&#xff01;&#x1f979; 关于代码如何实现&#xff0c;请看这篇文章&#xff1a;[机器…

C++ string类相关用法实例

前言&#xff1a; 1 string是表示字符串的字符串类 2 string类是basic_string模板类的一个实例&#xff0c;它使用char来实例化basic_string模板类&#xff0c;并用char_traits和allocator作为basic_string的默认参数&#xff0c;所以string在底层实际是&#xff1a;basic_str…

【漏洞修复】OpenSSH-ssh-agent 越权访问CVE-2023-38408

CVE-2023-38408漏洞升级ssh版本 漏洞说明修复步骤RPM包编译 漏洞说明 漏洞名称&#xff1a;OpenSSH-ssh-agent 存在越权访问漏洞影响范围&#xff1a;ssh-agent(-∞, 9.3-p2) openssh(-∞, 9.3p2-1)漏洞描述&#xff1a;SSH-Agent是SSH的一部分&#xff0c;它是一个用于管理私…

设计模式之享元模式(Flyweight)的C++代码实现

1、享元模式提出 面向对象很好地解决了抽象问题&#xff0c;但是创建对象会带来一定的内存开销。绝大部分情况下&#xff0c;创建对象带来的内存开销是可以忽略不计的&#xff1b;在有些情况下是需要谨慎处理的&#xff0c;如类A的sizeof是50个字节&#xff0c;则创建50*1024*…

AIGC:从入门到精通

一、前言 相信一部分的人在看到本活动的主题是关于AIGC的时候&#xff0c;都会存在疑惑--到底什么是AIGC呢&#xff1f;故本文主要介绍关于AIGC的基本内容&#xff0c;全部内容采用自己搭建的ChatGPT模型生成&#xff0c;具体内容如下&#xff1a; 本文章&#xff0c;参与&…

互联网医院|申办互联网医院这些资质不能少

互联网医院作为一种创新的医疗模式&#xff0c;正在以其便捷、智能、人性化的特点改变着人们的生活方式和医疗体验。它以数字化技术和合作共赢的精神&#xff0c;打开了医疗的新纪元。让我们共同期待&#xff0c;互联网医院将为健康事业带来更多机遇和挑战&#xff0c;引领着健…

Spyglass 脚本

Spyglass小白可以使用Gui界面进行debug Spyglass检查(一)作为IC设计人员&#xff0c;熟练掌握数字前端语法检查工具Spyglass的重要性不言而喻&#xff0c;本文讲解景芯SoChttps://mp.weixin.qq.com/s/fp07o-AvaQvLT79Di0Tb7A 熟悉电路和软件之后可以使用脚本完成整个流程 sp…

【分享】使用FileChannel进行文件拷贝

前言: 项目实际编写中&#xff0c;使用到了多种文件拷贝方式&#xff0c;有包括专门使用c写了拷贝工具&#xff0c;供给Java调用&#xff0c;也有使用标准的输入输出流&#xff0c;这里分享的是借助 FileChannel 来读写,nio中传送数据使用channelbuffer&#xff0c;大的数据可以…

第九章 动态规划part09(代码随想录)

198.打家劫舍 题目&#xff1a; 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。…

图的顺序存储结构

目录 图的顺序存储结构 图的顺序存储结构C语言实现 图的邻接表存储结构详解 邻接表计算顶点的出度和入度 图的十字链表存储结构 图的邻接多重表存储结构 图的顺序存储结构 使用图结构表示的数据元素之间虽然具有“多对多”的关系&#xff0c;但是同样可以采用顺序存储&a…

网络通信原理总结(第五十二课)

网络通信原理 结构目录 1 TCP/IP协议追层分析物理层(第三十九课)_IHOPEDREAM的博客-CSDN博客 2 网络通信TCP/IP协议逐层分析数据链路层(第四十课)_IHOPEDREAM的博客-CSDN博客 3 网络通信原理数据链路层&数制转换(第四十一课)_IHOPEDREAM的博客-CSDN博客 4 网络通信原理…

[LeetCode]矩阵对角线元素的和

解题 思路 1: 循环,找到主对角线的下标和副对角线的下标,如果矩阵长或宽为奇数的时候,需要减去中间公共的那一个值,中间公共的那个数的下标为mat[mat.size()/2][mat.size()/2]副对角线的下标为 mat [i][mat.size()-i-1] class Solution { public:int diagonalSum(vector<ve…