当前位置: 首页 > news >正文

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

文章目录

  • 内存相关
  • 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,则必须为0MEM_RELEASE              //免费操作的类型);
    

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

  • 远程读写内存:

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

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

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

    	//创建可由调用进程使用的专用堆对象HANDLE hHeap = HeapCreate(HEAP_NO_SERIALIZE,       //堆分配选项0,                       //堆的起始大小1024                     //堆的最大大小);//从堆中分配内存块。分配的内存不可移动。LPVOID lpHeapAddr = HeapAlloc(hHeap,        //堆的句柄HEAP_ZERO_MEMORY,   //堆分配选项,这里是初始化为0MAX_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.xdnf.cn/news/815023.html

相关文章:

  • 色环电阻的识别方法
  • 网管笔记35:不得不看的黑客工具集
  • 天行健咨询 | 谢宁DOE培训的课程内容有哪些?
  • 常量表达式
  • WPF常用的五种绑定方式
  • ERP系统是如何运作的?erp管理系统操作流程
  • QQ.COM二级域名大全 腾讯旗下产品大全
  • Rational rose下载,安装,破解
  • 30个源码网站
  • U8+v13.0-U8+v12.1-U8+v12.5-U8+v12.0注册机-稳定不掉授权-免狗补丁-安装盘下载
  • 关于网狐棋牌6603源码的整理、编译和搭建
  • timequest静态时序分析学习笔记——命令约束
  • 用HTML+CSS做一个漂亮简单的旅游网站——旅游网页设计与实现(6页)HTML+CSS+JavaScript
  • 地统计学的基本概念及公式详解
  • 一篇文章搞懂数据仓库:维度表(设计原则、设计方法
  • ISO27001(BS7799/ISO17799)国标
  • 什么是SCSI和RAID控制器?
  • 什么是UBB代码?
  • 令人震惊的神秘事件:俄罗斯地底钻探到地狱入口
  • 电脑提示msvcr120.dll丢失怎样修复,总结4种有效修复的方法
  • 【WIFI】WiFi的STA和AP、指什么?SSID、BSSID、ESSID、RSSI
  • SqlServer中BULK INSERT用法简介,批量插入数据
  • Java面向对象程序设计
  • 基础教程六(CEGUI和OGRE)
  • 【转】威胁建模的12种方法
  • salt returner mysql_saltstack实战2--远程执行之返回(returner)
  • PyFlipper:一款功能强大的Flipper Zero命令行接口封装器
  • Chrome+ProxySwitchySharp+Putty
  • GSM模块的调试(一)
  • jQueryMobile 秘籍(四)