【软件破解】使用OllyDbg从零开始Cracking 第二十二章-OllyDbg反调试之UnhandledExceptionFilter,ZwQueryInformationProcess
第二十二章-OllyDbg反调试之UnhandledExceptionFilter,ZwQueryInformationProcess
本章我们继续讨论反调试技术,我们将介绍反调试的另外两个小技巧,由于其中一个可以配合另一个来使用,所以我们两个一起介绍。本章我们使用上一章打过补丁的OllyDbg,也就是Nvp11,其中HideDebugger插件的配置如下:
这里我们可以看到其中有一个Unhandled exception tricks的选项。接下来我们将学习Unhandled exception和另一个API
函
关于工期滞后的函关于工程严重滞后的函关于工程进度滞后的回复函关于征求同志党风廉政意见的函关于征求廉洁自律情况的复函
数ZwQueryInformationProcess检测调试器的工作原理。
这里我们实验的对象叫做sphynx。如果你勾选上了HideDebugger插件的Unhandled exception tricks就可以正常运行起来了,但是使用插件之前我们还是来介绍一下它的实现原理。
另外说一点,我们现在暂时不解决这个CrackMe,我们只是来看看该CrackMe是如何检测OD的。
我们使用Nvp11(打过补丁的OllyDbg)加载该CrackMe,并且确保HideDebugger1.23插件的配置如第一幅图所示,接着将Debugging options-Exceptions选项中忽略的异常选项全部勾选上。
运行起来。
出现了CrackMe的主窗口,那么反调试体现在哪里呢?我们随便输入一个错误的序列号然后点击Check按钮。
OD的左下方提示存在不可处理的异常,程序将关闭,我们继续运行。
好了,我们不用OLLYDBG加载该CrackMe,它是不会关闭的,我们可以尝试输入不同的序列号,同样也不会关闭。 好了,现在我们重新启动该CrackMe,看看其使用了哪些API函数。
我们记得HideDebugger插件中有绕过该反调试的选项。
我们设置该选项前先来学习一下如何手工绕过该反调试以及其原理。
我们先来看看MSDN中关于SetUnhandledExceptionFilter的说明。
该函数的唯一一个参数为异常处理函数指针。当程序发生异常是,且程序不处于调试模式(在VS或者其他调试器里运行)则首先调用该异常处理函数。因此,程序可以主动抛出一个异常来判断当前程序是否正在被调试,嘿嘿,这里我们并不需要使用ZwQueryInformationProcess。
我们回到OD中,看到SetUnhandledExceptionFilter的调用处。
正如你所看到的,只有一个参数,在程序执行过程中会抛出一个异常,如果当前程序没有被调试,那么就会调用该参数指定的异常处理函数,嘿嘿。这里该异常处理函数入口地址为401108,如果当前程序正在被调试的话,程序最终将终止运行。 这是我们看到的该程序安装的其中一个异常处理函数,当有异常发生并且当前程序没有被调试的情况下,该异常处理函数将得以执行。
好了,我们现在给SetUnhandledExceptionFilter,UnhandledExceptionFilter这两个函数设置断点。
我们运行起来。
断在了SetUnhandledExceptionFilter的入口处。我们看下堆栈的情况。
正如你所看到的异常处理函数入口地址为401108,我们在命令栏中输入BP 401108给该函数设置断点。
我们运行起来,可以看到又断在SetUnhandledExceptionFilter的入口处,这个调用来至一个shellext.dll。
我们对这处调用不感兴趣,异常处理函数前面已经设置过了,所以我们将对SetUnhandledExceptionFilter设置的断点删除掉。
现在,我们随便输入一个错误的序列号,然后单击Check按钮,将会断在系统默认的异常处理函数入口处,因为程序有异常发生,并且当前程序正在被调试,所以,并不会首先调用程序之前设置的入口为401108的异常处理函数,而异常转交给调试器处理了,而调试器也无法处理该异常,所以最终调用系统默认的异常处理函数UnhandledExceptionFilter来处理,嘿嘿。
这个API函数用来检测当前程序是否正在被调试,我们F8键单步看看该函数是如何实现检测调试器的。
这里是我们今天要介绍的第二个反调试知识点,这个函数也可以单独用来检测调试器只需要把InfoClass设置为7。
该函数通过将InfoClass参数设置为7,将可以获取到当前进程是否被调试的信息,该信息将保存在Buffer参数指向的缓冲区中。 我们在数据窗口中定位到给缓冲区。
我们可以看到该缓冲区的大小为4个字节,如果该缓冲区返回的是FFFFFFFF的话表示当前程序正在被调试,如果返回的是0的话,
表示当前程序没有被调试,我们按F8键单步执行该函数,看看缓冲区中返回的是什么。
我们可以看到返回值为FFFFFFFF,表示当前程序正在被调试,嘿嘿。
可以看到现在判断该值是否为零,当前EDI为零。
这里,JE条件跳转将不会发生。
所以说该缓冲区中的值不为零的话,JE条件跳转将不会执行,程序最终将终止执行。
我们可以看到OD下方的提示:调试器遇到不可处理的异常,程序将终止。
现在重启OD,重复上面的步骤,直到调用完ZwQueryInformationProcess,然后我们来修改其返回的结果。
我们来到这里
我们在数据窗口中定位缓冲区。
我们按F8键单步执行该API函数。
缓冲区中返回的值跟上次一样,也是FFFFFFFF,我们将其修改为零。
将其修改为零。
接着还是进行比较。
现在两者都为零。
好了,现在JE条件跳转将实现。
我们运行起来。
断在了401108地址处,我们看到下面的关键部分。
这里验证序列号是否正确以决定是否弹出正确的消息框。
如果我们修改跳转,让其跳转不实现,将会弹出序列号正确的消息框。
运行起来。
好了,我们前面已经提到过,通过HideDebugger插件就可以绕过UnhandledExceptionTricks的反调试。我们勾选上UnhandledExceptionTricks选项以后,重启OD,可以看到运行的很正常。
好了,那么不考虑UnhandledExceptionFilter,如何单独绕过ZwQueryInformationProcess这个函数的检测呢。 当然,我们可以手工将其返回值修改为零,那么有自动绕过的插件吗?当然有,那就是HideOD这款插件。
我们可以看到很多HideDebugger插件中没有的选项,我们和HideDebugger配合起来用,有一点很重要,就是别忘了勾选上Auto Run HideOD这个选项,这样我们就不必每次启动OllyDbg的时候配置该插件了。
这里我们不勾选上UnhandledExceptionFilter,因为该选项最后也会绕过ZwQueryInformationProcess的反调试。我们现在只使用右边的单独绕过ZwQueryInformationProcess检测调试器的选项。
好了,本章我们学会了如何绕过UnhandledExceptionFilter以及ZwQueryInformationProcess的反调试原理,我们配合使用HideOD和HideDebugger两款插件让OD更加健壮了,嘿嘿。