Windows系统编程(六)内存操作与InlineHook
内存操作
我们以代码进行讲解有关系统内存的操作
#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
如下已知的源代码
#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.恢复目标函数的原始指令:在自定义代码执行完毕后,恢复目标函数的原始指令,以确保目标函数的正常执行。
// 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;
}