Exploration : Anti-cheating in mhyprot2

一直以来,反作弊程序是维护游戏公平最重要的工具。不过我早在7月才知道我玩的某个游戏里面发反作弊程序驱动有很大的权限漏洞。到现在这么久应该被许多大佬玩坏了。不过就这样的好奇我打开了久违的IDA,去看看里面有什么。

开搞

一上来直接就是有被吓到,这么多的Instruction,我一时间不知道是什么。不过我大概看了一下,这大部分Instruction的代码在一个名叫.upx0的字段。不过很明显,现在是可以修改字段名称去忽悠很多的人。而且,基于占了这么多,很大怀疑是VMP的虚拟保护壳,对几十个重要的函数进行了虚拟保护。我们可以去查看入口代码去验证。

我们直接去看驱动的入口函数DriverEntry,可以看见很明显的WDF的加载过程,所以这个函数应该是 FxDriverEntryWorker ,WDF加载先会判断DriverObject是否为空,然后再去直接或间接去执行入口函数。所以说我们干脆直接进去。

我们可以看到他跳转到了.upx0下的代码。

好家伙,还真是VMP的虚拟壳,你可以看见很明显的push 563A851Ecall sub_FFFFF80351349ECD的虚拟入口特征。

好了,到这里几乎就是玩个锤子了。

可以看到,连最基本的驱动加载的过程全部被保护掉,而且会复制整个IAT加载进虚拟壳,避免被跟踪。不然也不至于连个 IoCreateDevice 都无法追踪调用。

本来想使用大佬们写的unicorn_pe去dump整个驱动,结果无论如何都出现了堆栈爆炸的错误。

不过没关系,我们可以分析其他的一些函数。最重要的我注意到了导出表有几个熟悉的函数,比如说 ZwTerminateProcess。很明显,这一般用来杀掉作弊程序,所以说我们跟踪他。

经典的结束进程过程。被两个函数所调用,我们选第一个继续跟踪。

草,搁这套娃呢?

跳转到了一个很大的地方。

而且一上来就可以看见。

很明显的虚拟入口调用的特征,跟我们刚刚讲的一样。

到这里,基本IDA的F5功能基本失效,因为IDA无法分析VMP虚拟入口传递的参数个数,破坏了IDA的栈线分析。

没事不用慌,我们看看是谁调用了TerminateProcessByProcessId

我们可以看见这个部分,先进行了判断某个变量是0x81034000,再执行TerminateProcessByProcessId,然后再进行下个判断,也是比较这个函数是否为0x81044000,如果是执行对应的函数。

估计熟悉驱动开发的估计很明显就知道这个变量和函数是干嘛了。很明显,这是使用 switch 循环在比较 IoControlCode ,即控制代码。这也说明了一点,这就是 DispatchDeviceControl ,即驱动控制的入口之一,为什么要说之一,因为在这函数前还有个函数调用它。

很好,有了个很重要的突破——我们可以从中挖掘用户层会跟驱动层进行了什么通信,做了什么。

不过讲道理,里面很多都重要的函数被保护了,不愧是你。

DispatchDeviceControl的内容

我能在DispatchDeviceControl 里面看到的内容。

  1. 结束进程
  2. 检查调试
  3. 获取进程路径
  4. 获取线程退出时间
  5. 读取虚拟内存
  6. 获取进程创建时间
  7. 枚举进程模块列表
  8. 获取进程EROCESS
  9. 获取进程Wow64
  10. 剥离句柄(剥离父进程创建进程的句柄)
  11. 检查回调是否注册、是否被调试等
  12. 设置保护进程
  13. 初始化保护
  14. 至少还有5个函数已经被虚拟,无法理解.jpg

一个比一个高级,一个比一个猛.cpp

我看不懂,但我大受震撼.jpg

那些函数有本事别虚拟给我康康啊。

听话!让我康康!

听话!让我康康!

接下来,我们先回分析最简单的实现,至于那些复杂的实现我回继续单独开一个文章去讲。

获取进程路径

通过进程ID获取进程路径

调用 PsLookupProcessByProcessId 获取EPROCESS。

调用PsReferenceProcessFilePointer获取文件指针传入IoQueryFileDosDeviceName获取Dos路径。

最后还要记得使用 ObDereferenceObject 函数来释放对象。

获取线程退出时间

通过线程ID获取退出时间

调用 PsLookupThreadByThreadId 获取ETHREAD。

因为ETHREAD结构未公开,随意访问不正确的地址会导致报错,所以访问前使用 MmIsAddressValid 验证地址是否能正常访问。

其中 qword_FFFFF803512AA728 记录偏移。经过跟踪,该全局变量在一个函数上统一赋值(这个函数以后再说),而且该全局变量仅对Version为61的版本系统有效(即Windows7系统)。

经过查询,ETHREAD在Version为61的0x0368偏移的结构如下。

union {
    LARGE_INTEGER ExitTime;
    LIST_ENTRY KeyedWaitChain;
};

所以我直接盲猜是获取ExitTime。

其实也好猜,就直接判断那个使用的多,一般来说 KeyedWaitChain 使用范围几乎没有。而且这个函数仅在Windows7上获取这个变量,确实让我这个小白百思不得其解。

施工中

最近太忙了,真的尤其要咕咕咕了
最后编写日期为 2021-12-03 09:06:23
如有错误欢迎大佬指正
点赞

发表回复

电子邮件地址不会被公开。必填项已用 * 标注