通过HOOK 常见api监视内存偷检测思路及易语言实现附成品和源码
本帖最后由 cocop 于 2020-4-17 18:52 编辑无聊逛论坛时,偶然发现了关于偷 内存检测的帖子,于是思考了下偷内存的实现。 最先想到的方法肯定是内存对比,但因需要对比游戏全部内存,效率差强人意,而且内存对比需要不断的改变需要获取的内存以过滤无关数据,大部分检测又是只写一次,不会重写多次,根本无法从海量数据中挑出自己需要的。
之后查询相关资料了解到内存监视器的原理是Hook kernel32.dll中的WriteProcessMemory实现监视更改内存的作用。对于我这种菜鸡来说,Hook是仅仅了解过但又从未自己去实现过的新奇事物,自然勾动着我的好奇心。于是我去查阅了相关的资料、代码明白了hook监视内存的实现方法。在这里做下笔记加深理解。
下面是hook的实现过程,大牛就别看了{:cry:},这里拿WriteProcessMemory做示范
1.保存系统函数入口处的代码,此步骤的目的是便于恢复。
WriteProcessMemory_.钩子地址 = GetProcAddress (GetModuleHandleA (“kernel32.dll”), “WriteProcessMemory”)
这里GetModuleHandleA获取了kernel32.dll的模块地址,接着GetProcAddress获取WriteProcessMemory的地址。
VirtualProtect (WriteProcessMemory_.钩子地址, 8, 64, WriteProcessMemory_.OldProtect)
改变要hook地址的虚拟内存页保护属性为64
WriteProcessMemory_.原始数据 = 指针到字节集 (WriteProcessMemory_.钩子地址, 8)
保存原始数据
2.替换掉进程中的系统函数入口指向我们的函数
.版本 2
置汇编代码 ({})
清空汇编代码 ()
Mov_EAX (到整数 (&_WriteProcessMemory))
JMP_EAX ()
WriteProcessMemory_.代码 = 取汇编代码 ()
写到内存 (WriteProcessMemory_.代码, WriteProcessMemory_.钩子地址, 8)
因为我对机器语言不了解,拼接颇为费劲,故利用的超级模块,转换为字节集后写到hook函数的入口。
Mov_EAX (到整数 (&_WriteProcessMemory)),这里_WriteProcessMemory是我们重写的WriteProcessMemory,意思是将这个地址赋值给EAX寄存器
JMP_EAX () 跳转到我们编写的函数地址。
3.当系统函数被调用,立即跳转到我们的函数
.版本 2
.子程序 _WriteProcessMemory, 逻辑型
.参数 进程句柄, 整数型
.参数 内存地址, 整数型
.参数 缓冲指针, 整数型, 参考
.参数 内存长度, 整数型
.参数 实际长度, 整数型, 参考
.局部变量 result, 逻辑型
R_WriteProcessMemory ()
result = WriteProcessMemory (进程句柄, 内存地址, 缓冲指针, 内存长度, 实际长度)
.如果 (PID > 0)
R_ReadProcessMemory ()' 暂时屏蔽读
Write (“WriteProcessMemory”, “进程句柄:” + 到文本 (进程句柄) + tab + “内存地址:” + Get_Base_Address (内存地址) + tab + “内存长度:” + 到文本 (内存长度) + tab + “写入数据:” + _字节集_到十六进制 (读内存字节集 (PID, 内存地址, 内存长度)), 到文本 (result))
写到内存 (ReadProcessMemory_.代码, ReadProcessMemory_.钩子地址, 8)
.否则
Write (“WriteProcessMemory”, “进程句柄:” + 到文本 (进程句柄) + tab + “内存地址:” + Get_Base_Address (内存地址) + tab + “内存长度:” + 到文本 (内存长度) + tab + “写入数据因为未获取到Crossfire进程ID而无法获取”, 到文本 (result))
.如果结束
写到内存 (WriteProcessMemory_.代码, WriteProcessMemory_.钩子地址, 8)
返回 (result)
.版本 2
.子程序 R_WriteProcessMemory
.如果真 (取字节集长度 (WriteProcessMemory_.原始数据) > 0)
写到内存 (WriteProcessMemory_.原始数据, WriteProcessMemory_.钩子地址, )
.如果真结束
首先调用子程序R_WriteProcessMemory,还原入口,毕竟还是要调用WriteProcessMemory实现功能的,我们只是获取数据,不是破坏功能。
之后调用正经的WriteProcessMemory API,得到结果result,将参数带result输出,若要获取写入了什么,调用读内存获取即可。
这里暂时屏蔽ReadProcessMemory是因为同时hook了ReadProcessMemory会干扰结果。
4.再修改系统函数入口指向我们的函数,便于下次调用
写到内存 (WriteProcessMemory_.代码, WriteProcessMemory_.钩子地址, 8)
但是只hook一个API怎么可能满足的了我呢?于是便写了GetModuleHandleA,ReadProcessMemory,VirtualAllocEx,VirtualProtect,VirtualProtectEx。
file:///C:\Users\coco\AppData\Roaming\Tencent\Users\794087777\TIM\WinTemp\RichOle\2NSCS%H4I3I35NA}SPWD))M.png
再一次无聊逛论坛时发现,他们的监视器居然能通过内存找到对应的静态地址!那我肯定坐不住了啊,写起来。
目前只有获取一级基址的思路,多级不能调试游戏估计莫得办法,望各位大哥指点。
.版本 2
.子程序 Get_Base_Address, 文本型, , 输入10进制地址,返回16进制一级基址
.参数 address, 整数型
.局部变量 i, 整数型
.计次循环首 (取数组成员数 (模块信息), i)
.如果真 (address ≥ 模块信息 .基地址 且 address < 模块信息 .基地址 + 模块信息 .大小)
返回 (模块信息 .文件名 + “+” + 十到十六 (address - 模块信息 .基地址))
.如果真结束
.计次循环尾 ()
返回 (到文本 (address))
用timeSetEvent开启了一个时钟监测是否存在游戏,若有游戏则获取PID并用超级模块提供的取进程模块获取模块信息。
XF_Timer = timeSetEvent (500, 0, &Judge_XF_Timer, 1, 1)
.版本 2
.子程序 Judge_XF_Timer
.局部变量 P_ID, 整数型
P_ID = XF_PID ()
.如果 (P_ID > 0)
PID = P_ID
取进程模块 (PID, 模块信息)
timeKillEvent (XF_Timer)
.否则
PID = -1
.如果结束
Get_Base_Address的作用便是输入一个内存,循环比较模块信息,若在模块内,返回对应基址,若找不到则返回原地址。
编译为dll,注入 进程测试,这里就不拿XF测试了,自己随便写了个测试程序把名字改成了crossfire.exe测试。
测试还算挺W美的。至于读写之前为什么要VirtualProtectEx应该是得改内存的属性,避免读写不了。若有错误,希望指正!谢谢各位大佬!{:titter:}
使用方法:dll注入相关 即可,怕成品有毒的就自己编译,模块也只是个超级模块,还是不放心就用自己的超级模块。
理论上注入游戏的 dll也能获取数据,把自己的dll也注入游戏就行。但得过了游戏的保护,我菜,就不注入游戏献丑了{:2_28:}。需要的大佬自己拿着源码改一改过一下保护应该就可以。
还需要hook其他api稍稍改一下就行,不费劲~
最后附上源码模块和成品!**** Hidden Message ***** 学习学习。。。。。。 学习一下看看 学习一下看看 学习一下看看 好东西需要下来看一下,不知道能不能用 谢谢学习谢谢学习 联系我时,请说是在 挂海论坛 上看到的,谢谢! 看看,学学,支持楼主 正好需要 感谢楼主正好需要 感谢楼主 正好需要 感谢楼主