Windows系统编程(六)内存操作与InlineHook

内存操作

我们以代码进行讲解有关系统内存的操作

cpp 复制代码
#include <iostream>
#include <Windows.h>
#include <Psapi.h>//进程相关操作需要包括的头文件

int main()
{
    //获取系统当前物理内存和虚拟内存使用情况的信息
	MEMORYSTATUSEX lpMemStatus = { sizeof(MEMORYSTATUSEX) };//该结构用于保存系统物理内存和虚拟内存(包括扩展内存)的当前状态的信息。
	GlobalMemoryStatusEx(&lpMemStatus);//获取

	//获取系统内存信息
	SYSTEM_INFO lpSysInfo = { 0 };//该结构用于保存当前计算机系统的相关信息
	GetSystemInfo(&lpSysInfo);//获取

	//获取进程内存信息
	//GetCurrentProcess();获取当前进程句柄
	//GetCurrentProcessId();获取当前进程ID
	PROCESS_MEMORY_COUNTERS pmcInfo;//该结构接收有关进程的内存使用情况的信息。
	GetProcessMemoryInfo(GetCurrentProcess(), &pmcInfo, sizeof(PROCESS_MEMORY_COUNTERS));//获取

    //获取指定进程的虚拟地址空间中的页面范围的信息
	LPVOID lpAddr = VirtualAlloc(NULL, 0x100, MEM_COMMIT, PAGE_READONLY);//在调用进程的虚拟地址空间中分配或保留一块内存区域
    MEMORY_BASIC_INFORMATION mbiInfo;//该结构用于进程虚拟地址空间中的页面范围的信息
	VirtualQueryEx(GetCurrentProcess(), lpAddr, &mbiInfo, sizeof(mbiInfo));//查询指定进程的目标虚拟地址空间中内存区域的相关信息
	VirtualFree(lpAddr, 0, MEM_RELEASE);//释放已申请的虚拟内存

    //修改指定进程的虚拟地址空间中的页面范围属性
    LPVOID lpAddr = VirtualAlloc(NULL, 0x100, MEM_COMMIT, PAGE_READONLY);//此处申请的虚拟内存只能读
	DWORD lpflOldProtect = 0;//用于接收原有的内存访问属性
	VirtualProtectEx(GetCurrentProcess(), lpAddr, 0x100, PAGE_READWRITE, &lpflOldProtect);//修改虚拟内存属性为可读可写
	memcpy(lpAddr, "123456", 7);//此时可在指定虚拟内存写信息
	VirtualProtectEx(GetCurrentProcess(), lpAddr, 0x100, lpflOldProtect, &lpflOldProtect);//修改为原来的内存访问属性
	VirtualFree(lpAddr, 0, MEM_RELEASE);//释放已申请的虚拟内存

    //创建堆
	//堆:由一种数据结构进行管理的内存,一个进程可以有多个堆,但通常使用默认堆,默认堆具有默认大小,可随认为申请而变大
    //内存是在进程的默认堆中进行分配
	HANDLE hHeap = HeapCreate(HEAP_NO_SERIALIZE, 0, 1024);//创建一个堆对象,此时操作系统为该堆保留一段连续虚拟内存,此时并未分配物理内存
	LPVOID lpAddr = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, MAX_PATH);//在目标堆分配物理内存,此时虚拟内存映射物理内存
	memcpy(lpAddr, "123456", 7);//在目标堆内存写数据
	HeapFree(hHeap, HEAP_NO_SERIALIZE, lpAddr);//释放堆内存
	HeapDestroy(hHeap);//销毁堆对象
	system("pause");
	return 0;
}

如上代码所提及的API,Ex版本通常可以跨进程,不带Ex版本只可以自己的进程

Inline HOOK

Inline hook(内联钩子)是一种在程序运行时修改函数执行流程的技术。它通过修改函数的原始代码,将目标函数的执行路径重定向到自定义的代码段,从而实现对目标函数的拦截和修改。

当我们需要修改某API的参数时,有两种方法:

1.API传参时修改内存

2.Inline HOOK:API传参以后,在函数内部执行之前,拦截函数进行参数修改

接下来我们将通过一个实例讲解Inline 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;
}

接下来我们将通过第二种方法Inline HOOK来实现MessageBox参数的修改

1.找到API汇编实现处

将该exe文件拖入x32dbg

如图是上文程序调用MessageBoxA的汇编代码:首先压入了四个参数,然后调用MessageBoxA

如图是MessageBoxA的实现代码

根据上图内存地址可知,前三行代码正好五个字节

2.Inline HOOK

原理:当我们需要通过HOOK修改API的传参时,便要进入API内部,在功能实现前做拦截:修改前五个字节为jmp到目标内存处,使得当程序进入API内部时,在功能实现前便会跳转到目标内存处而不会执行原先的实现代码

注意:32位程序内存地址四个字节,jmp指令一个字节,一共五个字节

实现流程:

1.定位目标函数的地址:通过函数名或者导入表等方式找到目标函数在内存中的地址。

2.修改目标函数的内存权限:将目标函数的内存权限修改为可写可执行,以便后续修改函数的指令。

3.备份目标函数的原始指令:将目标函数的原始指令备份到自定义的缓冲区中。

4.修改目标函数的指令:将目标函数的指令修改为跳转到自定义代码的指令,以实现拦截和修改

5.编写自定义代码:编写自定义的代码,实现对目标函数的拦截和修改逻辑。

6.执行自定义代码:将自定义的代码插入到目标函数的执行流程中,使其被调用时执行自定义逻辑。

7.恢复目标函数的原始指令:在自定义代码执行完毕后,恢复目标函数的原始指令,以确保目标函数的正常执行。

cpp 复制代码
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <Windows.h>

//保存目标函数的地址(被HOOK的函数)
PROC m_FunAddress = NULL;
//保存MessageBoxA老的五个字节
BYTE m_OldBytes[5] = { 0 };
//保存MessageBoxA新的五个字节
BYTE m_NewBytes[5] = { 0 };

//Hook
//pszModuleName:目标函数所处模块的名称
//pszFuncName:目标函数名
//pfnHookFunc:劫持流程函数的地址
BOOL Hook(const char * pszModuleName, const char * pszFuncName, PROC pfnHookFunc)
{
	HMODULE hModule = GetModuleHandleA(pszModuleName);//获取目标函数所处模块的句柄
	m_FunAddress = GetProcAddress(hModule, pszFuncName);//获取目标函数地址
	if (m_FunAddress == NULL)
	{
		return FALSE;
	}
	DWORD dwRet = 0;
	BOOL bRet = ReadProcessMemory(GetCurrentProcess(), m_FunAddress, m_OldBytes, 5, &dwRet);//读取MessageBoxA老的五个字节
	if (!bRet)
	{
		return FALSE;
	}
	m_NewBytes[0] = '\xE9';//jmp的硬编码
    //jmp跳转的地址为相对地址
	//相对地址 = 劫持函数地址 - 目标函数地址 - 指令长度
	*(DWORD *)(m_NewBytes + 1) = (DWORD)pfnHookFunc - (DWORD)m_FunAddress - 5;
	bRet = WriteProcessMemory(GetCurrentProcess(), m_FunAddress, m_NewBytes, 5, &dwRet);//在目标函数处写入新的五个字节
	if (!bRet)
	{
		return FALSE;
	}
	else
	{
		return TRUE;
	}

}

VOID UnHook()//卸载hook
{
	if (m_FunAddress!= NULL)
	{
		DWORD dwRet = 0;
		WriteProcessMemory(GetCurrentProcess(), m_FunAddress, m_OldBytes, 5, &dwRet);
	}
}

VOID ReHook()//恢复hook
{
	if (m_FunAddress != NULL)
	{
		DWORD dwRet = 0;
		WriteProcessMemory(GetCurrentProcess(), m_FunAddress, m_NewBytes, 5, &dwRet);
	}
}

int
WINAPI
MyMessageBoxA//HOOK的函数
(
	_In_opt_ HWND hWnd,
	_In_opt_ LPCSTR lpText,
	_In_opt_ LPCSTR lpCaption,
	_In_ UINT uType
)
{
	//卸载HOOK
	UnHook();
	int bRet = MessageBoxA(hWnd, "hook", "hook", 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://dll加载时
		Hook("user32.dll", "MessageBoxA", (PROC)MyMessageBoxA);
		break;
    case DLL_THREAD_ATTACH:
		break;
    case DLL_THREAD_DETACH:
		break;
    case DLL_PROCESS_DETACH://dll卸载时
		UnHook();
        break;
    }
    return TRUE;
}
相关推荐
SummerGao.2 分钟前
Windows 快速搭建C++开发环境,安装C++、CMake、QT、Visual Studio、Setup Factory
c++·windows·qt·cmake·visual studio·setup factory
Igallta_8136222 小时前
【小游戏】C++控制台版本俄罗斯轮盘赌
c语言·开发语言·c++·windows·游戏·游戏程序
wave_sky3 小时前
Visual Studio中打开多个项目
ide·windows·visual studio
donglxd4 小时前
防御黑客系列-第一集-电脑登录记录提示和登录远程推送
windows·网络安全·电脑·系统安全
iOS技术狂热者5 小时前
如何通过Windows环境远程控制MusicGPT在线生成高质量AI音乐
人工智能·windows
Zucker n8 小时前
deepseek本地调用
java·服务器·windows
我真不会起名字啊9 小时前
“深入浅出”系列之C++:(8)libevent 库
开发语言·c++·windows
a41324471 天前
亲测Windows部署Ollama+WebUI可视化
windows·ollama·open-webui·deepseek本地化部署
※※冰馨※※1 天前
【C#】无法安装程序包“DotSpatial.Symbology 4.0.656”
windows·microsoft·c#
电手1 天前
微软宣布 Windows 11 将不再免费升级:升级需趁早
windows·microsoft