Warden的机制使得暴雪能够在不需要更新客户端的情况下任意更新检测代码。事实上,自从D2升级到1.11以来,Warden的检测mod和外挂双方一直都在针对对方的改变而进化。这一篇介绍外挂如何躲避前一篇提到的.mod使用的三种检测方法:DLL扫描、进程扫描和窗口扫描。
前面介绍过,外挂一般是做为一个普通的DLL远程注入到游戏进程进行工作的,因此Warden mod最早的检测手段是搜查游戏进程内加载的特定DLL名称(通过Module32First/Module32Next)。那么如何避免这种检测呢,一种简单的方法是截获Module32First/Module32Next,但由于枚举DLL的方法有多种,Windows 9x平台和NT平台也有区别,这种方法比较麻烦。高级一点的做法是利用进程内加载的所有模块由PEB(进程环境块)结构中的一个模块链表维护这一事实,简单的把外挂模块从链表中断开就可达到隐身的目的,当然这种方法只能用在Windows NT based系统中(包括NT/2K/XP/2K3)。更进一步的做法是自己编写DLL装载代码进行手工加载,这样它不会存在PEB的模块链表中,当然也就不会被Module32First/Module32Next找到。手工加载DLL看起来似乎很难,但其实网上就有现成的源代码,我的Remote Run Library就能做到,我自己最早则是从著名的back door软件Back Orifice 2000中学会的。EasyMap早期的一个版本用的断开模块链表法,后来则用了Darawk的ManualMap手工加载。
.mod检查外挂的另外一种方法是进程扫描(Process32First/Process32Next)。外挂一般会有一个loader负责把真正的外挂模块注入到游戏进程,或者有些外挂(基于AutoIt的BOT等)干脆就是一个独立的进程,因此避免这些进程被扫描到也是必要的。避开进程扫描的一种有效的方法是外挂进程的运行时间尽可能短(EasyMap/EasyPlay用的是这种策略),但这只对那些没有界面的loader进程有效。另外一种方法是进程隐藏。进程隐藏的方法有好多种,我的Remote Run Library就能做到(把外挂进程注入到宿主进程),这是d2hackmap的做法。另一种方法跟DLL隐藏类似,通过打开\\Device\\PhysicalMemory内核对象直接操纵物理内存,从系统活动进程链表中把外挂进程去掉-当然这种方法也只能用在Windows NT based系统中。甚至你还可以写个KMD(Kernel Mode Driver)来做,不过这就有点儿小题大做了。
其实躲避进程扫描还有的另外一种简单的方法,即限制游戏进程的权限,直接让Process32First/Process32Next调用失败。这个用一些现成的软件可以做到,我记得在WOW!Sharp刚被BAN的时候老外们讨论的比较多的一个是ProcessGuard。另外一个我觉得更方便的是Sysinternals的PsTools里的一个小工具psexec.exe。psexec.exe一个很棒的功能是可以以尽可能小的权限运行一个程序,命令十分简单:
对于有GUI的外挂进程(如d2hackmap),避开窗口扫描也是必要的。由于EnumWindows方法主要是检测一些固定的window title或window class字符串,因此可行的方法是把这些字符串随机化。
由于.mod随时可变,鉴别哪些.mod是安全的是非常必要的。做法和以前提到的对付version-checking.dll和extrawork.dll的方法一样:分析已截获的.mod,针对这些.mod的检测方法实现相应的反检测,然后计算它的签名,把签名加到安全.mod列表中去。这样外挂就可以在.mod运行前通过计算它的签名和安全列表里的比较,遇到危险或者为止模块就能及时停止工作。
d2maphack用的算法是MD5。EasyMap/EasyPlay系列则在比较长的一段时间里一直用CRC!有点儿密码学常识就能知道,CRC显然不是一个合格的签名算法-让两个不同的.mod具有相同的CRC是很容易的事。在这点上EasyMap/EasyPlay很长一段时间内是有漏洞的,不过暴雪倒是一直也没有利用这一点。再后来,在别人的提醒下,netter终于意识到了这个问题,改用了SHA-256(我和其他黑客都提到过MD5在理论上也是不安全的,因此netter选择了SHA-256)。下面是我曾经给出的一个针对Maphack的MD5签名的可能的攻击序列: