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;
}
相关推荐
这儿有一堆花1 天前
用原生脚本编写无害恶作剧
windows
因我你好久不见1 天前
Windows部署springboot jar支持开机自启动
windows·spring boot·jar
夜流冰1 天前
Excel - MS Support for Excel: 2 Collaborate
数据库·windows·excel
林瞅瞅1 天前
PowerShell 启动卡顿?内存飙升?原来是 800MB 的历史记录在作祟!
windows
Shepherd06191 天前
【Windows Server 实战】WAC 反向代理配置
windows
云小逸1 天前
【windows系统编程】第一章 Windows 系统核心架构与基础概念
windows·架构
怣疯knight1 天前
Docker Desktop 4.55.0版本安装成功教程
windows·docker
liulilittle1 天前
VEthernet 框架实现 tun2socks 的技术原理
网络·windows·c#·信息与通信·通信
独钓寒江雨1 天前
win11在安全模式下删除360tray.exe
windows·电脑
PieroPc1 天前
Windows 远程到 PVE 9.X Mac os (像window远程桌面)
windows·mac·远程桌面