对某市场外挂驱动逆向分析
误入了一个外挂驱动交流群,规模挺大;进来就看见难绷的宣传语贴脸

无敌稳定,无特征,无模块,独家核心技术?哈哈
以测试的名义加了驱动作者,SDK眼花缭乱
上手看看是个什么货色
样本提取
如他所说,确实是无模块,但很显然反作弊干的就是无模块。我之前写过一篇文章从内存的角度扫描无模块驱动。 太长不看,具体步骤:
1.扫一遍内核RWX内存
2.加载驱动
3.再扫一遍内核RWX内存
对比一下,较低地址,突然增大的部分大概率就是这期间被映射上去的无模块驱动(不一定,得手动排查一下,另外也有可能是RX内存),如图:

利用这个原理可以把外挂驱动dump下来分析,先说一下文件结构:
- x64.dll 外挂的3环部分,用于通信
- dump.bin dump下来的驱动
- exp_mapper.sys 漏洞驱动,用来映射外挂驱动;看了下是个伪造的网卡驱动,故意写了方便映射的利用部分,疑似是从国外某安全网站买来的,打了WHQL签名,危害有点大就不发了
功能分析
通信
劫持了Null.sys的指针通信
分析x64.dll发现通过通过向Null.sys发送IO control code进行通信,用Ark工具看一下Null.sys的派遣函数
这两个Io地址明显是指向了外挂驱动的地址范围
重定向到了自己的Io分发函数
具体的Hook:

有点混淆,可以给这个dispatch函数打断点追控制流;但混淆不强,直接看ioctl code追对应的swithc-case也行
读写
Drv_ReadMemory
用MmPfnDataBase拿到CR3后将虚拟地址转物理地址,用MmCopyMemory读
处理了跨页、大页等问题

Drv_ReadMemory2
用MmMapIoSpace把物理内存映射到当前地址空间,然后直接读取
比较搞笑的是他这个memcpy写反了

Drv_ReadMemory3
和Drv_ReadMemory一样,不过把TranslateLinearAddress换成了从Pcb->DiretoryTableBase取CR3

写也是一样的,换下参数位置
内存申请
有个Drv_AllocMemory就不放了,很简单的附加+ZwAllocateVirtualMemory申请内存
看看这个Drv_AllocHighMemory
这里的思想就是之前流行的高位注入的内存申请部分;附加到用户进程,在内核申请内存然后通过页表的User位暴露给用户态访问;现在有个问题就是机器普遍开启了KVAS,隔离了内核页表

注入
注入均未处理SEH、TLS;而且代码写的很诡异,每一种shellcode执行方式都写了个函数,看的头痛。导入表处理的不好,动态链接的DLL注入进去,在修复导入表的时候会#PF蓝屏
APC


Hijack
没筛选线程,直接附加+ZwOpenThread拿到任意一个线程的对象然后劫持
还有个remote thread就不看了
Drv_GeneralInject
这玩意我还在想为什么断不下来,看dll原来是纯3环实现,实现方式还很呆;释放并执行了一个名为inject.exe的程序
提取inject.exe,是一个很纯粹的反射注入,WriteProcessMemory + VirtualAllocEx + CreateRemoteThread。好吧,稳定!

进程保护
用ObRegisterCallback注册回调保护进程,注意有两个校验点,先patch掉
注册完之后改回来避免pg
回调函数:
不过测试的时候发现它这个特征码有点问题搜不到,进程直接崩了

进程伪装
伪装了下面内容:
- 镜像名
- 文件指针
- Token
- PEB
- PEB_LDR_DATA
不过疑似进程名伪装写的有问题,没成功;查模块确实重定向到了伪装进程;但是其自身带着这一大堆模块本身也是特征。外挂自己的x64.dll也没做断链处理
这个伪装很不完善,暴露的特征包括:父进程信息、创建时间、VAD、签名、句柄等等。
鉴定为远远不如Process Hollowing
窗口保护
用特征码定位了一个函数

自己搜一下,定位了GreProtectSpriteContent这个函数
参考这篇文章9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6i4q4X3M7X3!0K6N6q4)9J5k6h3y4G2L8g2)9J5c8Y4m8G2M7%4c8K6i4K6u0r3j5%4c8X3i4K6u0r3i4K6t1#2c8e0S2Q4x3U0f1^5y4g2)9J5y4f1u0q4i4K6t1#2c8e0S2Q4x3U0g2m8c8g2)9J5y4f1q4r3i4K6t1#2c8e0k6Q4x3U0g2n7z5q4)9J5y4f1t1^5i4K6t1#2c8e0k6Q4x3U0f1^5z5q4)9J5y4e0S2r3i4K6t1#2c8e0g2Q4x3U0g2m8c8g2)9J5y4e0R3&6i4K6t1#2c8e0g2Q4x3U0f1^5y4g2)9J5y4f1p5^5i4K6t1#2c8e0N6Q4x3U0g2m8b7W2)9J5y4e0W2q4i4K6t1#2c8e0S2Q4x3U0g2n7y4g2)9J5y4e0W2n7i4K6g2X3i4K6t1#2c8e0g2Q4x3U0g2m8c8g2)9J5y4f1p5J5i4K6t1#2c8e0k6Q4x3U0f1^5z5q4)9J5y4f1t1%4i4K6t1#2c8e0N6Q4x3U0g2m8b7W2)9J5y4f1q4r3i4K6t1#2c8e0g2Q4x3U0g2m8c8g2)9J5y4e0R3&6i4K6t1#2c8e0g2Q4x3U0f1^5y4g2)9J5y4f1p5^5i4K6g2X3x3U0l9J5x3W2)9J5c8R3..">这篇文章;可以注入shellcode到dwm中直接调用GetBuffer拿到最终被渲染到桌面上的图像内容
另外,这玩意还不能算是窗口保护,最多是窗口隐藏;常见的窗口保护是hook一些API来实现拒绝访问
键鼠模拟
这地方没怎么细看了,大概是call MouseClassServiceCallback和KeyBoardClassServiceCallback


混淆
最后来看看它的混淆,其实很弱,这里截取几个混淆模式
-
Pattern 1

-
Pattern 2

-
Pattern 3

-
Pattern 4

- Pattern 5

可能还有五六个?混淆模式很固定,都是执行一些dead code,并且完全不影响反汇编器、控制流,只是优化+混淆看的难受。每次运行确实都会重新编译,不过只改变一些寻址方式和混淆的magic number。这种东西还称不上无特征
收手吧阿祖,外面都是ACE(