初探反调试程序
本帖最后由 镇北看雪 于 2020-7-24 19:37 编辑反调试:顾名思义就是程序编写者采用一些手段防止程序被调试,从而达到防破解的目的。
———————————————————————————————————————————————————
在破解程序之前我们先来了解一下 线程环境块TEB 和 进程环境块PEB
[*]线程环境块:在线程创建初始化时系统用一块内存来保存线程的一些重要信息,这块内存就是线程环境块TEB(FS : 0指向这块内存)
[*]进程环境块:保存进程的一些重要信息的内存块,这块内存就是进程环境块PEB
[*]我们下面分析一下有关线程环境块所包含的一些重要信息(只是开头一部分其余部分我们不关心)
TEB:
0x00 ExceptionList:
0x04 StackBase:
0x08 StackLimit:
0x0c SubSystemTib:
0x10 FiberData:
0x14 ArbitraryUserPointer:
0x18 Self: ;其指向TEB结构,其指向其自身0x00处
0x1c EnvironmentPointer:
0x20 ClientId:
0x28 RpcHandle:
0x2c Tls Storage:
0x30 PEB Address: ;其保存进程信息块PEB的地址
[*]我们再来分析一下有关进程环境块所包含的一些重要信息(同样只是开头一部分)
PEB:
0x00 InheritedAddressSpace;
0x01 ReadImageFileExecOptions;
0x02 BeingDebugged; ;其标志程序是否被调试
注意PEB的BeingDebugged字段的值如果为0则标志程序没有被调试,所以可以利用检测PEB的BeingDebugged字段来检测程序是否被调试!
—————————————————————————————————————————————————————
下面我们来分析此程序:
我们先运行一下程序,输入伪码发现没反应。
接着我们打开od载入程序。
在程序入口点其调用了一个SetUnhandledExceptionFilter()函数,这个函数时设置一个顶级异常处理回调函数(我们不管它)
我们运行程序输入伪码后程序停了下来。观察堆栈原来是调用了DebugBreak()此函数功能相当于int3断点
接下来我们在反汇编窗口观察此函数调用处(在堆栈处右击反汇编跟随)。
我们看到DebugBreak()函数返回后会跳向执行结束进程函数ExitProcess( ),我们往上看如何能让其跳过此处代码
我们发现其在调用了IsDebuggerPresent( )函数后如果返回值为0则会跳过此处代码,不会结束进程。
我们跟进IsDebuggerPresent( )发现其代码就三行:
[*]其先读取fs : 处的值给eax(fs : 处存放的是TEB线程环境块,fs : 为PEB进程环境块的地址所以eax此时为PEB进程环境块的地址)
[*]接着其又把的值给eax后返回(eax为PEB的地址,[ eax + 2]就是PEB结构中用来检测是否在被调试的字段BeingDebugged的值)
mov eax,dword ptr fs:
movzx eax,byte ptr ds:retn
所以实际上IsDebuggerPresent()函数就是通过检测PEB的BeingDebugged字段来判断程序是否正在被调试。
我们在IsDebugPresent( )下断点,重新运行程序输入伪码后程序停在此处。
我们可以通过直接修改此函数返回值,或者通过改变进程环境块PEB的BeingDebugged字段的值来绕过IsDebuggerPresent()函数的检测,从而使其不调用ExitProcess函数结束进程。(实际上很多插件打补丁的原理就是第二种方法)
[*]下面我们采用第二种方法:
[*]通过fs:0指向TEB
[*]而TEB偏移0x30处为PEB的地址
[*]然后在数据窗口我们搜索此地址,此地址向后偏移2个字节就为PEB的BeingDebugged字段。我们发现此时其值为1,说明此时程序正在被调试直接修改其值为0即可
因为我们修改了其值为0,此时IsDebuggerPresent函数就会认为程序没有被调试,返回值就为0
——————————————————————————————————————————————————————————————
我们F9执行发现其发生异常程序停了下来,观察发现原来是程序试图向地址0处写数据,从而发生了内存读写异常
此时可以按shift + f7忽略此异常,而我们发现忽略此异常后程序会终止运行(这应该是程序设计者故意为之,不可能是程序的bug)
这时我们想起来了在程序入口处其调用了SetUnhandledExceptionFilter( )函数,
_________________________________________________________________________________________________________________________________________________________
下面的分析涉及到windows的异常处理(如果不了解windows的异常处理就很难理解)。
因为当程序产生一个异常时,首先会被系统内核捕捉然后系统内核会判断当前是否有调试器调试,有的话把异常交给调试器。如果没有调试器或者调试器处理不了此异常的话,异常会被分发给SEH链上的各个异常处理回调函数依次处理,这些回调函数的地址是通过一个链表存储。通过遍历这个链表从而调用各个异常处理回调函数。如果异常被某个回调函数正常处理了就继续从产生异常的代码处继续往下执行。如果一直遍历到链表的最后一个异常回调函数之前都没能够处理此异常的话,此异常就会交给最后一个默认的异常处理程序处理。
[*]而最后一个异常处理回调函数在调用前会先调用UnhandledExceptionFilter( )过滤函数,
[*]此函数又会调用ZwQueryInformationProcess( )函数先判断是否有调试器存在,有的话会直接返回进行异常的二次分发(一般就是会结束进程)。
[*]如果ZwQueryInformationProcess()函数没有检测到调试器的存在的话其将会调用默认异常处理回调函数。而一般默认异常处理回调函数是默认的终止程序的函数
但是windows提供一个函数来对UnhandledExceptionFilter( )过滤函数进行干预从而修改其返回值让其正常返回,而不执行默认的异常回调函数终止程序。此函数就是SetUnhandledExceptionFilter( ),此函数具有唯一的参数就是设置用来干预UnhandledExceptionFilter( )过滤函数的回调函数的地址,UnhandledExceptionFilter( )会在内部调用这个函数。(一般称这个函数为顶级异常处理回调函数,我认为这么称是不准确的。因为真正的顶级异常处理函数是默认的异常回调函数,此函数应该称为顶级过滤干预函数)
[*]所以我们刚刚之所以按了shift + F7后程序退出了就是因为UnhandledExceptionFilter( )调用了ZwQueryInformationProcess( )检测到了程序正在被调试,所以我们在程序入口通过SetUnhandledExceptionFilter( )设置的顶级过滤干预函数没有被执行,进行异常的二次分发,最后结束程序。
[*]而我们在一开始直接运行程序因为没有检测到调试器,所以异常会被顶级异常处理回调函数处理,程序不会崩溃结束。
所以我们要想让其调用顶级过滤干预函数对UnhandledExceptionFilter( )的返回值进行干预,从而继续让程序往下执行的话我们应该改变ZwQueryInformationProcess( )函数的Buffer参数,因为函数返回后其为0值表示没有检测到调试器,这样其就可以调用顶级过滤干预函数了
—————————————————————————————————————————————————————
由程序开头处的SetUnhandledExceptionFilter( )我们知道其设置的顶级异常处理回调函数地址为00401108,
我们在00401108地址处下断点,然后我们再在ZwQueryInformationProcess( )处下断点。运行程序重新运行程序,然后按前面的方法过掉IsDebuggerPresent函数的检测后,程序会停在发生异常处。然后我们shift + f7忽略异常后,程序会停在ZwQueryInformationProcess( )函数处。
在数据窗口搜索Buffer参数,当函数返回后其值为0表示无调试器,返回值为FFFFFFFF则表示有调试器。我们ctry + F9执行到返回后再改变其值为0。
此时认为其没有检测到调试器,所以会调用我们SetUnhandledExceptionFilter( )设置的那个顶级过滤干预函数。
其地址为00401108,运行程序后,程序果然停在了此地址处。
接着往下分析发现其会弹出一个在执行一个跳转后弹出一个消息框
是不是注册正确的消息框呢?我们更改跳转后,继续执行
弹出了注册成功消息框!
所以上方跳转为关键跳转,和关键算法
注册算法我就不分析了,主要是了解他的反调试和异常处理部分!
最后补充一下有关StrongOD插件的使用。
[*]选择Normal可以实现绕过IsDebuggerProcess函数的检测
如果发现内存访问异常等一些异常无法使程序停止运行,有可能是你的OD在调试设置中忽略了该异常
在破解本程序时为了让其在内存写入异常时断下,应该把忽略非法访问内存去除。
这样在向0地址写入数据时就会断下,需要按shift+F7才能忽略
如果设置了之后异常还不能断下,可能是你的某个插件设置里忽略某些异常。
[*]如StrongOD的此选项就可以忽略某些异常
程序链接:
链接:https://pan.baidu.com/s/1gUEeudQ3H2hEbVhLTIWNGw
提取码:xurl
最近在学win32汇编,所以我用MASM32写了一个保护方法类似的程序,有兴趣的可以玩玩
链接:https://www.52pojie.cn/thread-1130363-1-1.html
夕雪 发表于 2020-3-13 10:43
很厉害 我也遇到了利用异常来反调试的 找了很多异常资料来分析 都还是看不懂看了你这篇之后 豁然开朗了 ...
我也是看了好多资料才搞懂 很厉害 我也遇到了利用异常来反调试的 找了很多异常资料来分析 都还是看不懂看了你这篇之后 豁然开朗了 后面没怎么看明白,是利用异常处理捕获调试器吗,想看看样本 能否把程序发上来让我们呢依葫芦画瓢操练一遍 多点这类帖子就好了 谢谢分享,受益匪浅!!!
太专业了,看不懂。 学习了,正在找这反面的资料。 先收藏了,虽然还没有学到但是以后肯定会用到的
页:
[1]
2