“anti-”一词在E文中是表示“反对, 抵抗”之义的前缀,而“debugger”是泛指各种调试(出错)器,包括大家熟悉的softice、TRW2000及DOS下的debug、turbodebug, 还包括一些大家不是很熟悉的监测分析工具等等,凡是能够对应用程序进行动态跟踪分析、调试、监测的软件都可以称作“debugger”。所谓“anti-debuggers”技术, 顾名思义就是防止、防范这类工具起作用的技术,简而言之就是反调试、反跟踪技术。 <BR> 为什么会有“anti-debuggers”技术的存在?它有什么作用呢?我们知道,世界上的事物总是对立与统一的结合,就像矛与盾的关系,有矛就有盾,失去了矛,盾也就没有存在的必要了。 有人开发各种软件,为了防止别人未经授权擅自使用,于是在程序中使用了加密防范技术,使得任何其它人想要使用它都必须得到作者的许可,可是偏偏就有那么一些人(cracker)秉承“share”的理念, 非要靠自己的技术在特殊工具的帮助下亲自动手来揭开“潘多拉盒子”之谜,于是加密者发现自己的东西被别人“窃取”,所以就要想出对付那些特殊工具、使它们失去作用的技术, 这就是“anti-debuggers”技术产生的根源。现在破解早已不是什么隐晦的事情了,各种debuggers为这种技术的实现奠定了基础,特别是像softice这种大哥级的人物,只要想起它, 加密者心里都会抖一抖,但是任何debugger本身也只不过是一个程序而已,它有自己的运行方式,有自己的弱点,有被别人当作漏洞的地方,这就是“anti-debuggers”技术产生的基础。 <BR> 讲了这么多,是不是觉得有点形而上学呢?也许你会问,我怎么知道什么样的程序使用了“anti-debuggers”技术呢?其实也没有什么确定的方法来判断应用程序是否使用了“anti-debuggers”技术, 不过我们始终要注意一点,那就是“anti-debuggers”技术的目的主要是防止debugger的跟踪、分析及调试,所以首先是要想办法发现系统中存在的debuggers,然后采取行动阻止debuggers起作用, 其表现无非就是出现异常死机、系统异常重启、异常错误、错误警告、软件自毁、更严重的还会破坏文件甚至大肆破坏系统。。。等等非常可怕的行为。当然,不是说凡是破解中遇到这些情况就一定说明程序使用了“anti-debuggers”技术, 首先你要排除异常的情况是否是由系统不稳定或是被跟踪的程序“正常”抛出的,如果你的调试器本身不稳定而导致系统异常,那就没办法了。不过值得庆幸的是如果你使用softice,通常不会有什么问题的, 因为softice真的是稳定无比,就像泰山一样,而其它的调试器就不敢恭维了^_^!不过正是由于softice实在太强大,因而现在的“anti-debuggers”技术主要是冲着它来的。在排除“正常”的错误之后,如果有条件的话,我们还可以通过改变跟踪系统、环境及电脑的方法来重演错误事件,如果问题依旧, 应该可以肯定是中了“anti-debuggers”技术的陷井。 <BR> 下面给大家介绍一些常用的“anti-debuggers”技术(主要是debugger的检测技术): <BR>1. 利用softice的后门指令(Back Door commands): <BR> 后门指令通过中断INT 03来进行,通过softice的后门指令可以获得softice版本信息、获得和设置断点、执行softice中的指令等。。。 <BR>----------------------------------------------------------------------------------------------- <BR>INT 03 子功能:获取softice版本信息 <BR>入口参数: <BR>-AX = 0000h <BR>-SI = 4647h ('FG') <BR>-DI = 4A4Dh ('JM') <BR>返回值:BCD版本号(如0280h = v2.80) <BR>----------------------------------------------------------------------------------------------- <BR>INT 03 子功能:弹出softice窗口并开始调试程序 <BR>入口参数: <BR>-AX = 0902h <BR>-SI = 4647h ('FG') <BR>-DI = 4A4Dh ('JM') <BR>-DS:BX -> 寄存器初始化值 <BR>返回值:未知 <BR>寄存器初始化值的格式: <BR>偏移 大小 内容 <BR>00h WORD SP初始化值 <BR>02h WORD SS初始化值 <BR>04h WORD IP初始化值 <BR>06h WORD CS初始化值 <BR>08h WORD DS和ES初始化值 <BR>0Ah WORD ??? <BR>0Ch WORD ??? <BR>0Eh WORD ??? <BR>10h WORD AX初始化值 <BR>12h WORD ??? (默认为000AH ???) <BR>14h WORD ??? (默认为0001H ???) <BR>16h WORD ??? (默认为0100H ???) <BR>----------------------------------------------------------------------------------------------- <BR>INT 03 子功能:在softice窗口中显示字符串 <BR>入口参数: <BR>-AX = 0910h <BR>-SI = 4647h ('FG') <BR>-DI = 4A4Dh ('JM') <BR>-DS:DX -> 需要显示的ASCIZ字符串(最多100个字节, 0Dh表示OK) <BR>返回值:无 (在softice中显示的字符串为“Soft-ICE is a debugger by Nu-Mega Technologies, Inc.”) <BR>----------------------------------------------------------------------------------------------- <BR>INT 03 子功能:执行softice中的命令 <BR>入口参数: <BR>-AX = 0911h <BR>-SI = 4647h ('FG') <BR>-DI = 4A4Dh ('JM') <BR>-DS:DX -> ASCIZ命令字符串(最多100个字节, 0Dh表示OK,例如:“HBOOT”,ODH,0) <BR>返回值:无 <BR>说明:这里的所说的命令可以是我们在softice中使用的任何一个命令,例如:LDT, IDT, GDT, TSS, RS ... <BR>甚至是HBOOT,总之,通过这个功能调用,使用“anti-debuggers”技术的程序能够完全控制softice的操作, <BR>比如它如果执行HBOOT,那么结果就是重新启动系统了!!! <BR>----------------------------------------------------------------------------------------------- <BR>INT 03 子功能:获取softice断点信息 <BR>入口参数: <BR>-AX = 0912h <BR>-SI = 4647h ('FG') <BR>-DI = 4A4Dh ('JM') <BR>返回值: <BR>-BH = 上一次断点设置的入口序号 <BR>-BL = 上一次断点设置的类型 <BR>-DH = 上一次被触发的断点的入口序号 <BR>-DL = 上一次被触发的断点的类型 <BR>断点类型值: <BR>00h BPM (断点寄存器类型) <BR>01h I/O <BR>02h INTerrupt <BR>03h BPX (INT 03形式的断点) <BR>04h reserved <BR>05h range <BR>----------------------------------------------------------------------------------------------- <BR>INT 03 子功能:设置softice断点 <BR>入口参数: <BR>-AX = 0913h <BR>-SI = 4647h ('FG') <BR>-DI = 4A4Dh ('JM') <BR>-DS:DX -> 断点结构(breakpoint structure) <BR>返回值: <BR>-AX = 状态 <BR>00h 设置成功,此时BX = 断点序号 <BR>03h 断点表已经满 <BR>06h 内存限制错误 <BR>07h I/O限制错误 <BR>09h 范围限制错误 <BR>16h 重复断点 <BR>----------------------------------------------------------------------------------------------- <BR>INT 03 子功能:清除softice断点 <BR>入口参数: <BR>-AX = 0914h <BR>-SI = 4647h ('FG') <BR>-DI = 4A4Dh ('JM') <BR>-BX = 断点序号(由子功能0913h返回的) <BR>返回值:BX(未知信息) <BR>----------------------------------------------------------------------------------------------- <BR> 我们可以看到,每个子功能的入口参数都有相同的部分:即SI = 4647h ('FG');DI = 4A4Dh ('JM'),这两个入口值在softice中叫做“魔法值”(magic values), 凡是softice的后门指令都必须以这两个数为标志。 <BR> 利用softice的后门指令是非常常用的“anti-debuggers”技术,因为所谓后门指令其实也就是softice自己所使用的,它不仅可以获取softice的信息, 甚至可以随意控制softice的操作,威力无比! <BR>2. 搜索由任何系统调试器所返回的魔法数-->0F386h(magic number),通过调用INT 41H的子功能4Fh实现,有下面的一些方式: <BR>----------------------------------------------------------------------------------------------- <BR> mov ax,4fh <BR> int 41h <BR> cmp ax, 0F386 <BR> jz SoftICE_detected <BR>----------------------------------------------------------------------------------------------- <BR> mov bx, cs <BR> lea dx, int41handler2 <BR> xchg dx, es:[41h*4] <BR> xchg bx, es:[41h*4+2] <BR> mov ax,4fh <BR> int 41h <BR> xchg dx, es:[41h*4] <BR> xchg bx, es:[41h*4+2] <BR> cmp ax, 0f386h <BR> jz SoftICE_detected <BR>int41handler2 PROC <BR> iret <BR>int41handler2 ENDP <BR>----------------------------------------------------------------------------------------------- <BR> xor ax,ax <BR> mov es,ax <BR> mov bx, cs <BR> lea dx, int41handler <BR> xchg dx, es:[41h*4] <BR> xchg bx, es:[41h*4+2] <BR> in al, 40h <BR> xor cx,cx <BR> int 41h <BR> xchg dx, es:[41h*4] <BR> xchg bx, es:[41h*4+2] <BR> cmp cl,al <BR> jnz SoftICE_detected <BR>int41handler PROC <BR> mov cl,al <BR> iret <BR>int41handler ENDP <BR>----------------------------------------------------------------------------------------------- <BR> 这个方法不只是针对softice,而是所有任意的系统调试器(debugger),因此效果比较明显,也比较常用。 <BR>3. 通过中断INT 68H检测WinICE句柄: <BR>----------------------------------------------------------------------------------------------- <BR> mov ah,43h <BR> int 68h <BR> cmp ax,0F386h <BR> jz SoftICE_Detected <BR>----------------------------------------------------------------------------------------------- <BR> 注意:在softice中我们是不能通过 BPINT 68 来设置断点的,但是可以通过另外一个命令实现:BPX exec_int if ax==68, 其中被调用的函数位于[ebp+1Dh],客户EIP(client eip)位于[ebp+48h],这个断点设置方法只对32位应用程序有效; <BR>4. 利用WINDOWS的API函数CreateFileA来试图打开调试器的驱动程序句柄,这就是著名的“MeltICE”方法,NuMega公司的人用这个方法来使Symbol Loader检查softice是否已经激活 (这段代码位于nmtrans.dll中),虽然这个方法最初来源于softice,但是它对其它类型的debugger检测依然有效,调试器的驱动程序句柄有如下一些例子: <BR> SICE, SIWVID (对应softice Win9x版) <BR> NTICE (对应softice WinNT版) <BR> TRW、TRW2000、TRDEBUG (对应TRWIN) <BR> REGVXD (对应Registry Monitor) <BR> VKEYPRO (我也不知道这个对应什么程序^_^) <BR> FILEVXD (对应File Monitor) <BR> 。。。 <BR> 具体检测方法可以用下面的程序来说明: <BR>BOOL IsSoftIce95Loaded() <BR>{ <BR> HANDLE hFile; <BR> hFile = CreateFile( "\\\\.\\SICE", GENERIC_READ | GENERIC_WRITE, <BR> FILE_SHARE_READ | FILE_SHARE_WRITE, <BR> NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); <BR> if( hFile != INVALID_HANDLE_VALUE ) <BR> { <BR> CloseHandle(hFile); <BR> return TRUE; <BR> } <BR> return FALSE; <BR>} <BR> 对应于汇编代码的检测程序如下面所示: <BR> push 。。。 ; <BR> push 00402025 ; '\\.\SICE',0 <BR> call CreateFileA <BR> cmp eax,-001 <BR> jnz SoftICE_detected <BR> 对于16位应用程序或是兼容16位的应用程序来说,它们并不支持API函数CreateFileA,而使用的是16位的_lopen函数: <BR> push 00 <BR> mov eax,[00656634] ; '\\.\SICE',0 <BR> push eax <BR> call KERNEL32!_lopen <BR> inc eax <BR> jnz SoftICE_detected <BR>为了检测这些检测程序,我们可以在softice中通过下面的断点来拦截它们: <BR> BPX CreateFileA if *(esp->4+4)=='SICE' || *(esp->4+4)=='SIWV' || *(esp->4+4)=='NTIC' <BR> BPINT 30 if eax==002A001F && (*edi=='SICE' || *edi=='SIWV') ; 将会中断3次 <BR> BPINT 30 if (*edi=='SICE' || *edi=='SIWV') <BR> BPX KERNEL32!ORD_0001 if *edi=='SICE' || *edi=='SIWV' ; 将会中断3次 <BR> BPX VMM_GetDDBList if eax->3=='SICE' || eax->3=='SIWV' <BR>5. 利用softice的边界检查签名(The signature of BoundsChecker): <BR>----------------------------------------------------------------------------------------------- <BR> mov ebp, 4243484Bh ; 'BCHK' <BR> mov ax, 04h <BR> int 3 <BR> cmp al,4 <BR> jnz SoftICE_Detected <BR>----------------------------------------------------------------------------------------------- <BR> 这个方法被大量地使用在网上各种加壳、加密软件中; <BR>6. 搜索softice虚拟设备驱动程序的ID值(ID of SoftICE VxD),通过调用INT 2FH的子功能1684h实现: <BR>----------------------------------------------------------------------------------------------- <BR> xor di,di <BR> mov es,di <BR> mov ax, 1684h <BR> mov bx, 0202h ; 搜索winice的VxD ID <BR> int 2Fh <BR> mov ax, es ; ES:DI 指向 VxD API 入口点 <BR> add ax, di <BR> test ax,ax <BR> jnz SoftICE_Detected <BR>----------------------------------------------------------------------------------------------- <BR> xor di,di <BR> mov es,di <BR> mov ax, 1684h <BR> mov bx, 7a5Fh ; 搜索SIWVID的VxD ID <BR> int 2fh <BR> mov ax, es ; ES:DI 指向 VxD API 入口点 <BR> add ax, di <BR> test ax,ax <BR> jnz SoftICE_Detected <BR>----------------------------------------------------------------------------------------------- <BR> 这种方法用得较少; <BR>7. 下面的方法同样也是检测softice的VxD ID来判断它是否已经装入系统,不过使用的不是中断: <BR>----------------------------------------------------------------------------------------------- <BR> mov eax, Device_ID ; softice的VxD ID,对应SICE等于202h;对应SIWVID,等于07a5Fh; <BR> mov edi, Device_Name ; VxD名字,针对于没有VxD ID的情况,不过对于我们讨论的问题老说是没有意义的; <BR> VMMCall Get_DDB <BR> mov [DDB], ecx ; 如果VXD没有安装,ECX返回0,否则返回DDB <BR>----------------------------------------------------------------------------------------------- <BR> VMMCall Test_Debug_Installed <BR> je not_installed <BR>----------------------------------------------------------------------------------------------- <BR> 这种方法只适用于系统0级(ring0),即虚拟设备驱动程序VxD或者是使用VxdCall的ring3应用程序;Get_DDB服务功能用来检查指定的设备是否安装,如果安装,则将设备描述块地址返回到ECX中;我们可以很容易的在softice中通过断点:bpx Get_DDB if ax==0202 || ax==7a5fh 来找到这个检测程序处; <BR>8. 下面的方法通过VxDCall的后门(backdoor)来检测系统中是否安装了任何性质的调试器: <BR>----------------------------------------------------------------------------------------------- <BR> push 0000004fh ; 功能号为4fh <BR> push 002a002ah ; 高位字指定是哪一个VxD (VWIN32) <BR> ; 低位字指定使用什么服务(VWIN32_Int41Dispatch) <BR> call Kernel32!ORD_001 ; VxdCall <BR> cmp ax, 0f386h ; 系统调试器返回的魔法数(magic number) <BR> jz SoftICE_detected <BR>----------------------------------------------------------------------------------------------- <BR> 这种方法因为使用了VxdCall,所以只能在Windows 95/98中使用(NT不支持VxD),为了检测这种方法,我们可以在softice中通过下面的断点来拦截它: <BR>BPINT 41 if ax==4f <BR>BPINT 30 if ax==0xF386 ; <BR>BPX Exec_PM_Int if eax==41 && edx->1c==4f && edx->10==002A002A <BR>BPX Kernel32!ord_0001 if esp->4==002A002A && esp->8==4f ; 非常非常慢!!! <BR>9. 通过注册表中的键值来直接检测是否安装了softice,不过并不能知道softice是否是激活的(即是否在内存中): <BR> -#1: HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall\SoftICE <BR> -#2: HKEY_LOCAL_MACHINE\Software\NuMega\SoftICE <BR> -#3: HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\App Paths\Loader32.Exe <BR> -#4: HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\NTice <BR> 某些应用程序发现了注册表中的softice键值后会直接将其删掉,从而破还已经安装好的softice, 在softice中可以用断点:BPX _regopenkey if *(esp->8+0x13)=='tICE' || *(esp->8+0x37)=='tICE'来拦截这种检测; <BR>10. 通过对整个内存范围对softice特征字符串的搜查来判断softice是否已经安装并激活,下面是个例子: <BR>----------------------------------------------------------------------------------------------- <BR>DATA_SICE DB 67h,66h,8bh,06h,0fh,22h,0d8h,26h,67h,66h <BR> DB 8bh,46h,04h,66h,26h,67h,0fh,01h,10h <BR>DATA_SICE_END <BR>assume cs:code,ds:code <BR> mov si,offset DATA_SICE <BR> mov dx,cs <BR> mov al,26h <BR> xor bp,bp <BR> cld <BR>loop1: <BR> mov es,bp <BR> mov cx,100h <BR> xor di,di <BR>loop2: <BR> repnz scasb <BR> jnz loop3 <BR> push cx <BR> push si <BR> push di <BR> mov cx,offset DATA_SICE_END-offset DATA_SICE <BR> repz cmpsb <BR> jz SoftICE_detected <BR> pop di <BR> pop si <BR> pop cx <BR> jmp short loop2 <BR>loop3: <BR> add bp,10h <BR> cmp bp,dx <BR> jb loop1 <BR>not_found: <BR> 。。。 <BR>----------------------------------------------------------------------------------------------- <BR>11. 通过改变DOS中断调用的25H/35H来设置改变原有的中断服务程序(主要是INT 01H和INT 03H),使系统崩溃,这个方法只是搞破坏,防止任何形式的对程序的非正常中断, 因为程序已经改变了INT 01或INT 03中断服务程序,使得我们使用调试器设置断点或使用其它任何需要改变这两个中断的应用程序时系统直接崩溃或死机, 而不管是否这个断点是如何产生的,甚至是正常程序出于正常需要的使用也不允许: <BR>----------------------------------------------------------------------------------------------- <BR> mov ah, 25h <BR> mov al, Int_Number (主要是INT 01H和INT 03H) <BR> mov dx, offset New_Int_Routine <BR> int 21h <BR>----------------------------------------------------------------------------------------------- <BR> 这种方法本人曾经领教过,确实是很厉害,只要一设置断点,马上死机,不过多少显得有点不道德,为什么这样说呢? 因为假如中断并不是由于debugger产生,而是某些应用程序出于自身需要而产生的,这也要死机,大家有没有感觉有些强盗逻辑的味道呢! <BR> 上面介绍了这么多的“anti-debuggers”技术,我想以后再碰到这样的情况就不会那么头晕了吧!不过你肯定会问既然有如此多的“anti-debuggers”技术, 那么我们如何去防范它们呢?一般说来有三种方法可以试一下: <BR> 第一、也是最简单易行的办法--采用专门的防止“anti-debuggers”技术的工具,比较有名的就是FrogsICE, 用它对付大部分含有“anti-debuggers”技术的应用程序是没有什么问题的,其具体的使用方法请参阅主页上“破解教学”中的“FrogsICE使用指南”; <BR> 第二、通过对程序的静态分析(如用反编译工具W32DASM分析程序代码),找到其中“anti-debuggers”代码部分,然后用编辑工具(如HIEW)修改程序, 使其“anti-debuggers”代码失效;不过这种方法对于加壳的程序来说没有用处,因为加壳的程序是不能进行静态分析的,我们可以先脱壳,然后再用反编译工具进行分析; <BR> 第三、在对程序的动态跟踪过程中和程序斗法,动态发现、找到“anti-debuggers”代码的位置,然后将其歼灭,对于断点的设置前面针对不同的“anti-debuggers”技术已经分别讨论过了。 这种方法难度较大,可有些时候却是唯一的办法,需要有很大的耐心(面对无数次的死机、蓝屏。。。)!<BR> 最后,有关于上面所提到的中断的更详细信息,请参阅主页上“汇编语言”中的“中断大全”,里面有更多更详细的相关中断功能介绍。 <BR> |