逆向分析某VT加持的无畏契约纯内核挂

31 篇文章 24 订阅
23 篇文章 1 订阅

前言

最近玩JD上头但是被各路挂壁打的头大,偶然看到隔壁群友发的村瓦挂壁赛直播,起了逆下看看的念头。
 

0dde5ba6e2f259f21f2857d49fed7e08.png


 

f7fb8e1673523456d3d9718ef7d88aaa.png


 

24e096b4765ddd087df0581a600992fb.png


遂顺着网线一路找到了发卡网,拿到了本次逆向的主角。
 

c6abca2666fe947476de219972dae3fc.png


 

587238faa7fe53e88d88a9e749fdd291.png


貌似不简单阿,不过“易语言程序”这几个字了怎么没了???

3328973437f2ae292c6c33c034dcc9b6.png

分析阶段

网络验证及外壳程序

38d5ac9d0d301bbac50729fd63629d8a.png


经典E语言程序配VMP,想必代码肯定是运行时解密的,直接运行dump拖IDA一条龙。
 

dca9400ac19800c8fb8aee429833671b.png


这里推荐一下fjqisba老哥的E-Decompiler,运行插件之后看到了熟悉的易语言函数。

直接搜下4D 5A 90能看到有两个结果:

b6177338c8d371aa697db0308d18636a.png

据我所知易语言开发通常会把PE文件当作图片资源存放在易语言资源里面,而资源的数据区上面的DWORD就是数据大小。

51dc46521eb46eba8deb71ac4a98cec6.png

按这个规律可以直接dump出两个PE文件:
 

28814f661dead32a9d9aabb257bb6329.png


 

d613101df47c76da7a45ea0baedf1d5e.png


可以看到存在易语言资源中的两个PE文件都是驱动程序,想必这就是包含作弊功能的驱动了,按顺序先分析第一个位于0x004C09A8位置的程序。

5b2b207d721b586ea56e927b9c993747.png

Mapper

标准的WDF驱动,直接跟进DriverInit里面:
 

a4859db128c22f0eff168dd31ae41f89.png


 

c30ac50d1ec205acce79aa0069916106.png


 

74820aa8dd3ec679c9afeac14ce34182.png


感谢皮总和养猪哥提供的KDDEBUGGER_DATA_OFFSET。
很经典的自删除、清空PiDDBlock、PiDDBCacheTable以及KernelHashBucketList用来隐藏驱动加载过的痕迹,不敢说跟kdmapper一模一样,只能说有个八分像。
还有一些像系统版本、PteBase等等的初始化操作,同时可以看出驱动是走的IoControl与用户层通讯,符号链接是TpSafe。
下面进IoControl的派遣函数看看:
 

69e383c3ad386c5baf678ed55c553637.png


 

0285305108acd823222c399d270e7b17.png


用户层DeviceIoControl的Buffer中传递了一个用来被Map的驱动文件,通过用户层程序也可以看出这一结果。
 

786fd6f0298d46eb6ead7fec1b1492c3.png


MapDriverThreadProc里面无非一些拉伸PE、修复导入重定位的操作,内存是通过MmAllocateIndependentPages申请的;这里不再赘述,想看的朋友可以自己逆向看。

28caa05ab0266ab1d2135683bbea9a8c.png

功能驱动(VT加持)

76c6ae19f0189cefc972eb889ffa7f00.png


 

e176a1498e138e09ab7b22ab8e9706da.png


上来就是vmcall、vmresume,虚拟化技术驱动的歪瓜。。。太卷了。
 

705ff54f2890787211c19ffb1e01019f.png


DriverEntry直接进了VM,看来常规方法逆不动了。
 

357fbb3b7fe2798924953921362b9c63.png


字符串里有脏东西嗷,直接xref追一下
 

0679aee41dc3b1bb1d1d9c77347f89e6.png


他Hook了AXE-BASE.sys的某个函数(就是AXE自己的CreateHook),特征搜一下看看
 

0d85f941d000d947f3f32c211f79b0e4.png


 

07c8a12db14b7049fff37ac67de9472c.png


好家伙,它直接Hook了它的Hook;幸好之前分析过一部分BASE,今天正好派上用场。

再xref看下CreateEptHook的调用:

af6d717025e203b5bb2f02e35c7512d0.png

大致Hook了MmIsAddressValid、MouseClassServiceCallback、KiPreprocessFault、NtCreateSection、PspExitProcess、MmCopyMemory、RtlWalkFrameChain这几个内核函数。

bf4f224c6329617aaf24535d94ddba41.png

拦截MmIsAddressValid和MmCopyMemory实现隐藏内存,参数中的虚拟地址/物理地址包含驱动模块所在内存区域时返回失败。

a4dd650fa83af047e26812e10f447b9c.png

拦截RtlWalkFrameChain对抗栈回溯,当调用者回溯的堆栈中包含驱动模块所在内存区域时抹除堆栈信息。

0d76e4f58dc33f93e8b1437fe986f90b.png

拦截NtCreateSection并过滤特定文件,当文件句柄对应的文件名称为“APEX_Clothing_x64.dll”时调用者即为游戏进程,随即进行初始化。
 

7779ede014de6b89139b51577de83b16.png


初始化操作(ShadowBreakPoint即“无痕”软件断点):

2a4d31d1a206df9fa414aa0961b01bf5.png

拦截PspExitProcess以便于在进程退出时卸载EPT Hook并关闭外挂功能等待下一次游戏启动。

e1fa4306bc9c9bcb1325d01d0a13c500.png

拦截MouseClassServiceCallback获取鼠标输入信息以及向系统注入鼠标输入:

2bbe53bb4ed571e1764fe8eb1f2c798f.png

拦截KiPreprocessFault接管操作系统中的所有异常,当异常代码为0x80000003 (EXCEPTION_BREAKPOINT) 并且异常地址为预设位置时执行对应操作。
 

39177765a9c129900c31ac318c3edc27.png


 

ae7b806d49cc7c3adc6ee9df91733a22.png


 

b27b223b55888bb79d34c1e2f2566c71.png


对照上面的ShadowBreakPoint可以断定两个用户模式的Hook点分别位于DWM和游戏进程,外挂驱动在用户层通过EPT技术绕过游戏的内存完整性校验写入了“无痕”的0xCC(int3)然后在内核层的KiPreprocessFault接管掉自身设置的软件断点并分别进行辅助绘制和操作游戏数据的功能。

绘制部分

内核DWM+ImGui

3174910c8c60156b48bd03a4b86d3efe.png


 

3fa78cb8376746dd5da6d2b648aee186.png


 

22b6102eed6951cdd06552a2b54946cb.png


部分函数识别的不是很准确,大概过程即通过特征搜索dwmcore.dll!PostSubgraph、dxgi.dll!CDXGISwapChainDWMLegacy::PresentDWM、 dxgi.dll!CDXGISwapChain::PresentDWM并设置隐形软件断点进行挂钩,初始化必要数据。
随后在Detour函数中初始化ImGui,其中的用户模式调用类似DoubleCallBack
 

dabfc226b65f5f7de454c9fb33598fae.png


 

552915c02c2ce7fefaec891098a3bc88.png


内核DWM的具体分析参考 [原创]记一次仿写一个内核DWM绘制

功能部分

第一次调用时会初始化游戏窗口句柄,窗口位置/大小、客户区位置等信息。
 

fd193a53d06c88d403db012d07ad0403.png


话说为啥不直接调用NtUserFindWindowEx。。。

通过GetForegroundWindow判断游戏窗口置顶时启动作弊功能,绘制信息(画框、画线、画字)通过ImDrawList传递给dwm的渲染部分:
 

2ffffcac3d674ae81c7d6c67c0060ec3.png


 

22bfc8781bd8ef2a1fe4028162a77d62.png


 

7de14777a1ae0d8e2cefd7c741977c5a.png


 

e0a52c164d96922ddff58847f808b8c1.png

d81f4a711c0737668a0a4d6ef07c0786.png

其中获取Actor位置以及屏幕坐标转换即GetActorLocation和ProjectWorldLocationToScreen是通过ProcessEvent调用游戏内的功能函数实现的,非常经典虚幻引擎操作。
 

f10615e44d0b1ab6ac5289687b336a97.png

a6da87080e13e7cc9e815597b77840c6.png

StaticFindObject

2781d6150227b075061240d2f08e4234.png


 

8df944822546a2ada2ca725c1ed37481.png


 

df111fe5fe3df5fc894abd1a88fc0153.png


经典tvm,将混淆还原过后可以看到确实是FindObject,参数也对的上。

101b9810d9c8b9cfc67cfc65f9c02a11.png

ProcessEvent

2c286e9f1c9b975cffd9c7fdc900375f.png


 

a8f4350356e027decb4d6371e5799366.png


 

5fa764f38682142fe9cd21b6b557cafe.png


 

a1457a1ad2310c59e9e7be4684daa5fd.png


不知道原本就长这样还是被混淆了。。。函数逻辑乱七八糟的。

自瞄实现:

1be54f22368294c696e5bdb0adb2d9a3.png


 

5daae402b770c324a78f4c297b5a7d90.png

1c2f21302565bac6d51fab74ab72fb5d.png

关于虚拟化技术

懂得都懂不多说,直接看代码吧
 

02e927bb1a2f92b403ea8956ca484303.png


 

bad6965b4542a3d9ff762d6a8012c14a.png


 

0c70350cb33b55e0afa7783ad2fb1705.png


 

944fa1cfc6a88a53b7f11b32fff569e2.png


 

a672ee99384b96614fde860d88010657.png


 

3a751b1ab345821c216a23dd021e2a18.png

  • 28
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值