一
前言
最近玩JD上头但是被各路挂壁打的头大,偶然看到隔壁群友发的村瓦挂壁赛直播,起了逆下看看的念头。
遂顺着网线一路找到了发卡网,拿到了本次逆向的主角。
貌似不简单阿,不过“易语言程序”这几个字了怎么没了?
二
分析阶段
网络验证及外壳程序
经典E语言程序配VMP,想必代码肯定是运行时解密的,直接运行dump拖IDA一条龙。
这里推荐一下fjqisba老哥的E-Decompiler(https://github.com/fjqisba/E-Decompiler),运行插件之后看到了熟悉的易语言函数。
直接搜下4D 5A 90能看到有两个结果:
据我所知易语言开发通常会把PE文件当作图片资源存放在易语言资源里面,而资源的数据区上面的DWORD就是数据大小。
按这个规律可以直接dump出两个PE文件:
可以看到存在易语言资源中的两个PE文件都是驱动程序,想必这就是包含作弊功能的驱动了,按顺序先分析第一个位于0x004C09A8位置的程序。
Mapper
标准的WDF驱动,直接跟进DriverInit里面:
感谢皮总和养猪哥提供的KDDEBUGGER_DATA_OFFSET。
很经典的自删除、清空PiDDBlock、PiDDBCacheTable以及KernelHashBucketList用来隐藏驱动加载过的痕迹,不敢说跟kdmapper(https://github.com/TheCruZ/kdmapper)一模一样,只能说有个八分像。
还有一些像系统版本、PteBase等等的初始化操作,同时可以看出驱动是走的IoControl与用户层通讯,符号链接是TpSafe。
下面进IoControl的派遣函数看看:
用户层DeviceIoControl的Buffer中传递了一个用来被Map的驱动文件,通过用户层程序也可以看出这一结果。
MapDriverThreadProc里面无非一些拉伸PE、修复导入重定位的操作,内存是通过MmAllocateIndependentPages申请的;这里不再赘述,想看的朋友可以自己逆向看。
功能驱动(VT加持)
上来就是vmcall、vmresume,虚拟化技术驱动的歪瓜。。。太卷了。
DriverEntry直接进了VM,看来常规方法逆不动了。
字符串里有脏东西嗷,直接xref追一下。
他Hook了AXE-BASE.sys的某个函数(就是AXE自己的CreateHook),特征搜一下看看。
好家伙,它直接Hook了它的Hook;幸好之前分析过一部分BASE,今天正好派上用场。
再xref看下CreateEptHook的调用:
大致Hook了MmIsAddressValid、MouseClassServiceCallback、KiPreprocessFault、NtCreateSection、PspExitProcess、MmCopyMemory、RtlWalkFrameChain这几个内核函数。
拦截MmIsAddressValid和MmCopyMemory实现隐藏内存,参数中的虚拟地址/物理地址包含驱动模块所在内存区域时返回失败。
拦截RtlWalkFrameChain对抗栈回溯,当调用者回溯的堆栈中包含驱动模块所在内存区域时抹除堆栈信息。
拦截NtCreateSection并过滤特定文件,当文件句柄对应的文件名称为“APEX_Clothing_x64.dll”时调用者即为游戏进程,随即进行初始化。
初始化操作(ShadowBreakPoint即“无痕”软件断点):
拦截PspExitProcess以便于在进程退出时卸载EPT Hook并关闭外挂功能等待下一次游戏启动。
拦截MouseClassServiceCallback获取鼠标输入信息以及向系统注入鼠标输入:
拦截KiPreprocessFault接管操作系统中的所有异常,当异常代码为0x80000003 (EXCEPTION_BREAKPOINT) 并且异常地址为预设位置时执行对应操作。
对照上面的ShadowBreakPoint可以断定两个用户模式的Hook点分别位于DWM和游戏进程,外挂驱动在用户层通过EPT技术绕过游戏的内存完整性校验写入了“无痕”的0xCC(int3)然后在内核层的KiPreprocessFault接管掉自身设置的软件断点并分别进行辅助绘制和操作游戏数据的功能。
绘制部分
内核DWM+ImGui
部分函数识别的不是很准确,大概过程即通过特征搜索dwmcore.dll!PostSubgraph、dxgi.dll!CDXGISwapChainDWMLegacy::PresentDWM、 dxgi.dll!CDXGISwapChain::PresentDWM并设置隐形软件断点进行挂钩,初始化必要数据。
随后在Detour函数中初始化ImGui,其中的用户模式调用类似DoubleCallBack(https://github.com/wbaby/DoubleCallBack/blob/master/DoubleCallback/callBack.h):
内核DWM的具体分析参考[原创]记一次仿写一个内核DWM绘制(https://bbs.kanxue.com/thread-279167.htm)。
功能部分
第一次调用时会初始化游戏窗口句柄,窗口位置/大小、客户区位置等信息。
话说为啥不直接调用NtUserFindWindowEx。
通过GetForegroundWindow判断游戏窗口置顶时启动作弊功能,绘制信息(画框、画线、画字)通过ImDrawList传递给dwm的渲染部分:
其中获取Actor位置以及屏幕坐标转换即GetActorLocation和ProjectWorldLocationToScreen是通过ProcessEvent调用游戏内的功能函数实现的,非常经典虚幻引擎操作。
StaticFindObject
经典tvm,将混淆还原过后可以看到确实是FindObject,参数也对的上。
ProcessEvent
不知道原本就长这样还是被混淆了。函数逻辑乱七八糟的。
自瞄实现:
关于虚拟化技术
懂得都懂不多说,直接看代码吧:
最后感谢DJ哥百忙之中抽出时间帮忙分析VT功能。
看雪ID:R0g
https://bbs.kanxue.com/user-home-910874.htm
# 往期推荐
1、阿里云CTF2024-暴力ENOTYOURWORLD题解
2、Hypervisor From Scratch – 基本概念和配置测试环境、进入 VMX 操作
4、套娃之arm架构下的MacBook通过parallels+rosetta安装Linux amd64版本的IDA Pro
球分享
球点赞
球在看
点击阅读原文查看更多
原文始发于微信公众号(看雪学苑):逆向分析VT加持的无畏契约纯内核挂