首页
社区
课程
招聘
[原创]异常处理流程(一) 异常处理三剑客
2018-5-9 17:35 5865

[原创]异常处理流程(一) 异常处理三剑客

2018-5-9 17:35
5865

异常处理流程是学习逆向中至关重要的一环,我们试图揭开异常处理流程的神秘面纱。

调试器:OllyDbg

环境:WIN XP SP3 虚拟机


异常处理的本质是一个外部中断。中断是计算机硬件的一个重要概念,这里分享一个百度百科的解释给不清楚的朋友参考。

https://baike.baidu.com/item/%E4%B8%AD%E6%96%AD/3933007?fr=aladdin

具体讲,就是在遇到异常时,程序会跳出正常的程序执行流程,执行一段远在程序代码之外的代码,这段代码叫SEH( Structured Exception Handling )结构化异常处理函数。顾名思义是处理异常的专用函数。很多壳会利用系统的这一特性实现反调试,可谓是别出心裁了。只要有想法,任何一个不起眼的函数或者机制都可以摇身一变成为反调试的利器。本例就是如此

初次配置反调试插件时,首先忽略所有异常,用插件把FindWindow和IsDebuggerPresent过掉,保存,重启,让插件生效。

载入OD,发现运行以后停下来,显示无法处理的异常,很奇怪,明明忽略所有的异常,为什么还会因为异常被断下?

遇到这情况,需要明白异常区前都执行了什么函数

载入程序,首先来了解一些,SetUnhandleExeptionFilter函数的作用


翻译第二段:当这个函数调用完成后,如果一个未调试器调试的进程有异常发生后,异常会让这个进程运行结构化异常处理函数(SEH),这个函数(原文是filter,不是function。所以这里是意译)会调用由 lpTopLevelExceptionFilter所指定的异常处理函数。

所以这个函数很关键很关键很关键。

这个函数怎么用呢?我们看一下。

 #include   <windows.h>   
    
  long   __stdcall   callback(_EXCEPTION_POINTERS*   excp)   
  {   
  MessageBox(0,"Error","error",MB_OK);   
  printf("Error   address   %x/n",excp->ExceptionRecord->ExceptionAddress);   
  printf("CPU   register:/n");   
  printf("eax   %x   ebx   %x   ecx   %x   edx   %x/n",excp->ContextRecord->Eax,   
  excp->ContextRecord->Ebx,excp->ContextRecord->Ecx,   
  excp->ContextRecord->Edx);   
  return   EXCEPTION_EXECUTE_HANDLER;   
  }
    
  int   main(int   argc,char*   argv[])   
  {   
  SetUnhandledExceptionFilter(callback);   
  _asm   int   3   //只是为了让程序崩溃
  return   0;   
  }
由此可见,只要调用过一次这个函数,以后再出现异常时,就调用这个函数设置好的异常处理函数。

查看函数列表,有这个函数,下断(请思考,如果没有该怎么办呢?)

该函数的参数很重要,它要做的事很重要

断下,看堆栈:

Cal到SetUnhandledExceptionFilter来自00401005

Ptoplevelfilter=00401108

返回到7c81776f

给00401108设置断点bp 401108,这个参数就是 lpTopLevelExceptionFilter 。它是一个专门处理异常的函数的首地址,有异常就执行这个函数。

然而这里401108不是处理异常的,一点异常处理的功能都没有,由于无法处理,系统就自动退出了,实现了反调试。作者很有想法,他的思路值得我们借鉴。

SetUnhandledExceptionFilter 这个函数的返回值有三种情况:


EXCEPTION_EXECUTE_HANDLER equ 0x1,翻译:“ UnhandledExceptionFilter 函数执行完毕后得到这一返回值,并且执行事先关联好的异常句柄。这通常(usually)会导致程序终止”  
EXCEPTION_CONTINUE_EXECUTION  equ -1 “ UnhandledExceptionFilter 函数执行完毕后得到这一返回值 ,并且从异常的代码那里继续执行。”  
EXCEPTION_CONTINUE_SEARCH  equ  0   执行 UnhandledExceptionFilter 规定的一般异常处理流程。也就是执行默认异常流程。

这个call不轻易调用,必须得有异常,而且没人管,而且没有进行调试,就会调用SetUnhandledExceptionFilter,没调试时出现异常,没人管时,调用异常处理函数。

接下来,我们给UnhandledExceptionFilter设置断点。每次设置API断点都记住要注释,然后再去查询一下这个函数啥作用

给zwqueryinformationprocess 设置断点 。这三个函数就是函数处理的流程。

SetUnhandledExceptionFilter会调用UnhandledExceptionFilter,UnhandledExceptionFilter调用zwqueryinformationprocess。zwqueryinformationprocess用来检测程序是否被调试,重点是第三个,决定了程序运行还是不运行 。通过故意制造异常,可以调用SetUnhandledExceptionFilter。

首先,我们来看看 zwqueryinformationprocess函数是什么


其中 Infoclass参数的作用如图


具体到本例中

Hprocess=FFFFFFFF           可无视

Infoclass=7                            表示函数的功能是:检测程序是否在被调试。关于这个参数上图的说明是: “抽取一个双字长度的数值。这个数值是调试该进程的端口数。一个非零值表明这个进程正在被一个三环的调试器调试中。 想实现这一功能最好使用 CheckRemoteDebuggerPresent或IsDebuggerPresentfunction.”

Buffer=0012f6a0                    这里存放了结果,在数据窗口跟随,取一个dword,一开始里面的数是0,当执行到段位后变为FFFFFFFF,只要不是0就表示在被调试。

Bufsize=4                               取四个字节

Preqsize=null                         可无视

这里我们补充一点。第三函数可以不依赖前两函数时被单独调用,可以检测是否在调试程序哦


如何过检测呢?我们需要借助插件。网络上有很多其他的优秀插件,所以其他的插件也可以。

怎么配置呢?先来到hidedebugger,给unhandle exception tricks打上勾(屏蔽zwqueryinformationprocess)给terminate process也打上勾

再打开hideod-option,在这里执行两个操作

第一除了unhandleexceptonfilter不勾其他的都勾上,第二zwqueryinformationprocess要选择method1它可让那个buffer里的数永远保持00 00 00 00.也就是说,本来有调试器那里是ff ff 但是这次永远是0。但注意 zwqueryinformationprocess还有很多其他作用,不要因为使用插件而导致其他功能无法使用。


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

最后于 2018-5-11 12:29 被r0Cat编辑 ,原因:
收藏
点赞2
打赏
分享
最新回复 (2)
雪    币: 4195
活跃值: (5634)
能力值: ( LV7,RANK:116 )
在线值:
发帖
回帖
粉丝
VirtualCC 2018-6-5 11:18
2
0
文章写得很清楚,小白学习了一波,谢谢
雪    币: 2694
活跃值: (80)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
BlackJZero 2018-6-18 08:41
3
0
游客
登录 | 注册 方可回帖
返回