zoukankan      html  css  js  c++  java
  • SEH

    @author: dlive

    SEH是Windows的异常处理机制,在程序源代码中使用__try,__catch,__finally关键字来具体实现。

    但SEH与C++的try, catch异常处理不同,从时间上看,与C++的try, catch相比,微软先创建了SEH机制,然后才将它搭载到VC++中。

    SEH大量应用于压缩器,保护器,恶意程序用来反调试

    0x01 OS的异常处理方法

    正常运行时的异常处理方法

    若进程代码中有具体的异常处理代码,则能顺利处理相关异常,否则OS会启动默认的异常处理机制,终止进程运行

    调试运行时的异常处理方法

    被调试进程内部发生异常,OS会首先把异常交给调试器处理,若调试器不处理则异常交给被调试者处理,若仍不处理,最后由OS默认的异常处理机制处理。

    (OD中使用shift+F7/F8/F9【step into/over/run】可以将当前异常抛还给被调试者)

    0x02 常见异常

    1.EXECPTION_ACCESS_VIOLATION (C0000005)

    试图访问不存在或不具有访问权限的内存区域时,发生非法访异常

    2.EXECPTION_BREAKPOINT(80000003)

    调试器就是利用该异常实现断点功能的

    3.EXCEPTION_ILLEGAL_INSTRUCTION(C000001D)

    CPU遇到无法解析的指令时引发该异常。比如“0FFF“指令在x86 CPU中未定义,CPU遇到该指令将引发此异常。

    4.EXCEPTION_INT_DIVIDE_BY_ZERO(C0000094)

    除法中分母为0引发的异常

    5.EXCEPTION_SINGLE_STEP(80000004)

    CPU进入单步模式后,每执行一条指令就会引发EXCEPTION_SINGLE_STEP异常,暂停运行。

    将EFLAGS寄存器的TF(Trap Flag, 陷阱标志位)位设置为1后,CPU就会进入单步工作模式。

    0x03 SEH

    SEH以链的形式存在,第一个异常处理器若未处理相关异常,它会被传递到下一个异常处理器,直到得到处理。

    从技术层面上看,SEH是由_EXCEPTION_REGISTRATION_RECORD结构体组成的链表

    typedef struct _EXCEPTION_REGISTRATION_RECORD
    {
         PEXCEPTION_REGISTRATION_RECORD Next;
         PEXCEPTION_DISPOSITION Handler;
    } EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;
    

    Next是指向下一个结构体的指针,Handler是异常处理函数的地址。若Next = FFFFFFFF,则表示它是最后一个节点。

    异常处理函数声明如下

    EXCEPTION_DISPOSITION _except_handler
    (
      EXCEPTION_RECORD *pRecord,
      EXCEPTION_REGISTRATION_RECORD *pFrame,
      CONTEXT *pContext,
      PVOID pValue
    );
    

    返回值EXCEPTION_DISPOSITION为枚举类型,ExceptionContinueExecution(0)表示继续执行异常代码,ExceptionContinueSearch(1)表示运行下一个异常处理函数

    函数的第一个参数为EXCEPTION_RECORD结构体指针

    typedef _EXCEPTION_RECORD{
      DWORD ExceptionCode; //异常类型
      ...
      PVOID ExceptionAddress;// 异常发生的地址
    }EXCEPTION_RECORD, *PEXCEPTION_RECORD;
    

    函数的第三个参数为线程的上下文CONTEXT结构体,这个结构体在“调试钩取”部分介绍过。每个线程内部都拥有1个CONTEXT结构体。

    在异常处理函数中将参数传递过来的CONTEXT.Eip设置为其他地址,然后返回异常处理函数。这样,之前暂停的线程会执行新设置的EIP处的代码(反调试中经常采用这一技术)

    TEB.NtTib.ExceptionList(FS:0)存储着SEH链的地址,可以通过FS:[0]获得SEH地址。

    0x04 调试SEH

    使用OD运行seh.exe,找到用户自定义代码

    这里使用一种新的方法定位用户代码,F9运行seh.exe弹出对话框,在OD中点击暂停(F12)

    此时程序运行在MessageBox的代码区域,即在user32.dll的内存空间中运行。

    使用alt+F9(mac为option+F9)快捷键,该快捷键功能为使程序继续运行直到用户代码空间。

    这样EIP便回到用户代码空间中MessageBoxA调用结束的位置,这样就找到了用户代码。

    1.查看SEH链

    在OD数据窗口ctrl+G跟踪dword ptr fs:[0]

    dword ptr fs:[0] == 0012FF78

    将数据窗口调节为如下模式,可以看到OD生成对SEH的注释信息,0012FF78指向SEH链头结点

    查看第一个SEH处理函数

    该函数为VC++生成PE文件时默认添加到其启动函数的

    接着查看SEH链中下一个启动函数

    该SEH结点为SEH链中最后一个节点(Next=FFFFFFFF)SEH处理函数为ntdll.dll中定义的系统函数,这是OS的默认异常处理器,创建进程时OS会自动产生默认的SEH

    2.添加SEH

    图中前三行汇编代码即为SEH的安装代码,具体可参考图中注释,本质就是一个链表插入节点的操作

    可以看到OD在栈上也会生成SEH的注释,ESP指向的为新SEH结构体中的Next成员变量,ESP+4指向的为SEH处理函数的地址

    tips: OD自带查看SEH链功能

    菜单中选择查看->SEH链

    查看选项提供的功能还是很有用的

    3.查看SEH异常处理函数参数

    在所有SEH异常处理函数开头下断点,当发生异常时程序将在0040105A处断下

    ESP+4指向EXCEPTION_RECORD结构体,结构体的第一个成员变量DWORD ExceptionCode的值为c0000005即EXCEPTION_ACCESS_VIOLATION异常

    发生异常的地址PVOID ExceptionAddress为00401019

    ESP+8指向EXCEPTION_REGISTRATION_RECORD结构体,其值为0012FF78,它是SEH的起始地址

    ESP+C为第三个参数,它指向CONTEXT结构体,该结构体中存储着线程上下文信息,CONTEXT.Eip(Address of CONTEXT + 0xB8)为发生异常的代码地址值为00401019

    ESP+10为第四个参数,它供系统使用,可忽略

    4.调试异常处理函数代码

    前面的内容明白了之后看这段代码还是非常简单的,注释部分详细描述了该代码的功能(这是一种常见的反调试方法)

    代码最后return 0表示ExceptionContuineExecution表示异常得到处理,相关线程可以继续执行

    00401023和00401039分别是如下两段代码

    这里有几个需要重视的地址:

    FS:[30] -> PEB的地址

    PEB + 0x02 -> PEB.BeingDebugged的地址

    CONTEXT + 0xB8 -> CONTEXT.Eip的地址

    运行到ret指令后,异常处理函数结束,控制权返回到ntdll.dll模块的代码区域,它属于系统区域,ALT+F9(MAC为OPTION+F9),运行到用户代码区域可以看到运行至00401023处

    好吧,alt+f9不行,好像是因为多线程的原因,ntdll.dll代码空间中调用了ZwContinue恢复线程运行(跟进ZwContinue发现最后使用了系统调用),所以需要手动在00401023处下断点才能断下

    5.删除SEH

    执行完MessageBox之后执行如上0040104D处代码

    执行fs:[0] = Next,之后清理栈空间(add esp,4),最后ret

    tips2: OD调试选项之异常

    勾选上忽略异常之后,被调试者若发生如上异常,调试器会直接将这些异常交给被调试者的异常处理程序处理,无需使用Shift+F7/F8/F9进行手动忽略

  • 相关阅读:
    java中4种修饰符访问权限的区别
    Java中List的排序方法
    Hibernate事务
    @Component、@Service、@Constroller
    MySQL查看一个表的创建文本以及删除表某列的索引
    深入Session2
    Tomcat容器的Session管理
    深入Session
    使用spring mvc或者resteasy构建restful服务
    Spring MVC学习回顾
  • 原文地址:https://www.cnblogs.com/dliv3/p/6492602.html
Copyright © 2011-2022 走看看