对于破解来说,总是希望能以最快的速度来搞定目标程序,破解的方法有很多,当然最通用的方法就是爆破了,而针对爆破最简单的防范方法就是anti debug,因为不管是刚入门的新人还是有一定经验的破解者,必须通过使用调试器才能进行程序分析,因此禁止程序调试就成了防止程序被破解的第一步。好友冷阳前几天传给了我一个Cracker_Me让我玩一下,因为学破解已经有段时间了,也想练一下手,于是就试着破解了一下,最后得出结论,此CrackerMe绝对是Anti debug中的极品。
一、突破计时器
我对于Cracker_Me的处理方法一般是先查看它的16进制代码,一方面可以加深对PE结构的理解和记忆,另一方面也可以更方便的找到一些敏感信息,比如和注册有关的提示信息、程序调用的APl函数等等。很多次破解失败后,我都是通过分析程序调用的API函数才想出解决方法的,这样不但可以学到破解的方法,还可以提高编程能力,可谓是一箭双雕。
程序运行大约10秒钟后就会自动退出,这很让人郁闷。从Windows API函数的实现角度来分析,一般是通过SetTimer()添加计时器来实现的,我们先使用OD载人程序,从程序人口特征可以看出,程序并没有加壳,而编写的语言应该是Borland C++,Borland C++编写的程序有个特点,就是调用的API是密集分布的(一串jmp),拖动鼠标到00401 5de处就可以看到这个密集区了。
在004015FC处的SetTimer函数上下断点,按F9运行程序,取消断点,然后按F8单步,进人User32.dll的领空,按Alt+F9返回程序领空,看到如下的代码:
因为SetTimer函数发送的是WM_TIMER消息,这个消息可以在winuser.h中找到它的宏对应16进制值为Oxl13,所以我们可以判断出a04014100 3D 13010000 CMP EAX.113上面离它最近的SetTimer()才是最有意义的,仔细看它的参数,不难看出其计时器标识为15,每一个计数器的添加都必须调用SetTimer函数,而多个计时器的区别就在于计时器的标识不同。Windows系统会根据计时器标识调用onTime()中的switch结构通过case语句进行处理,所以“cmp eax,5”下面就对应了case 5语句,如果等于5也就是计时器标识为5时就会执行下面的语句,从上面的代码中可以看出,程序调用的是SendMessage函数,而push 10对应的是SendMessage函数的第二个参数,也就是要发送的消息,在Winuser.h中可以轻松的知道它对应的消息为WM_CLOSE(关闭窗口的消息),再看一下SetTimer函数上面的push 2710,2710是16进制,对应10进制的10000,所以程序是通过添加一个计数器,当10秒后,就会通过计时器标识调用case 5条件下的语句,通过SendMessage()发送WM_CLOSE消息关闭掉程序,所以我们只要将0040140D处的JNZ SHORT 00401422修改为jmp就可以解决这个问题了。
二、第二次郁闷的退出
在使用C32ASM查看程序信息的时候,就已经发现了一些敏感的注册提示信息,使用OD插件搜索ASCII码,找到了这些信息,双击进入相应代码后,按F2下断后执行程序,无奈的事情再次发生——程序又无情的退出了,看来还有问题。再次分析程序调用的API函数,看到了那几个熟悉的进程枚举函数——CreateToolhlep32Snalshot、Process32First、Process32Next,这应该是另一个Anti Deloug的方法,通过枚举其父进程确定程序是不是在调试器中,学过操作系统原理的朋友都明白,一股的操作系统提供了三个交互接口,一个是GUI图形界面,一个是命令行界面,还有一个就是API接口,对于Windows来说它们分别对应于exlporer.exe、cmd.exe和Windows Api,而要实现这种判断,程序显然是通过比较父进程是否为exploer.exe或者是cmd.cxe,我们用一中的方法,在CreateToolhelp32Snapshot函数上按F2下断,然后按Alt+F9返同程序领空,关键的代码如下所示:
程序先通过GetWindowsDirectory函数得到Windows目录的具体路径,然后通过调用lstrcat函数将exploer.exe加到Windows目录之后,最后与枚举出来的父进程进行比较,如果不相同再查看其父进程是否为cmd.exe,如果不是就执行0040114A下面的语句,相反则不执行,因此搞定JE SHORT 0040117E这句关键跳就可以了。
三、意外的Anti-debug
本来以为程序到这里爆破后就算搞定了,但没想到再来到注册信息的地方,下断后程序还是失败了,后来冷阳给了我一点提示——SetUnhandledExceptionFilter函数,通过查找MSDN,我理解了这个函数的用处,原来当程序出现错误弹出来的对话框是由UnhandledExceptionFilter显示的,这是在没有安装异常捕获函数的时候出现的调用,如果安装了捕获函数,Windows则会通过回调函数将异常信息通知应用程序,而安装捕获函数调用的API就是SetnhandledExceptionFilter。
还是返回到原点,将OD中“Options”菜单下的所有异常前面的勾都去掉,然后重新加载程序,会发现程序出现了未知错误,停在了如下的代码处:
分析反汇编代码不难看出,内存访问违规了,“mov dword ptr ds:[eax],]”而Windows的4G平坦内存中0x0000 0000——0x0000 FFFF这段地址为NULL空指针区,ring3下是不能访问的(i386加入的权限限制),这就使程序产生了异常,对于这个异常由SetUnhandledExceptionFilter函数的参数所指的函数来处理,如果程序在调试器中则无法处理该异常,从而到了Anti Debug的效果,解决的方法就是可以将其nop掉。
到这里三层Anti Debug的处理才算搞定,本文中使用的这个Cracker_Me可谓Anti Deoug的精品,从中我们可以学到至少三种Anti Debug的方法,破解与编程本就是一对矛盾体,编程学的好破解自然是小case,反过来破解或者逆向一些程序对编程又具有重大的意义。
本程序其实还有许多值得学习的地方,比如在代码修复上(当我们修改关键跳的时候,程序会自动检测并将其进行修改,类似于文件自检验功能),因为本文的主要目的是介绍几种Anti debug的方法,所以就不再进行说明了,有兴趣的朋友请Go on!
本文内容所提及均为本地测试或经过目标授权同意,旨在提供教育和研究信息,内容已去除关键敏感信息和代码,以防止被恶意利用。文章内提及的漏洞均已修复,作者不鼓励或支持任何形式的非法破解行为。