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进行手动忽略

  • 相关阅读:
    Html禁止粘贴 复制 剪切
    表单标签
    自构BeanHandler(用BeansUtils)
    spring配置中引入properties
    How Subcontracting Cockpit ME2ON creates SD delivery?
    cascadia code一款很好看的微软字体
    How condition value calculated in sap
    Code in SAP query
    SO Pricing not updated for partial billing items
    Javascript learning
  • 原文地址:https://www.cnblogs.com/dliv3/p/6492602.html
Copyright © 2011-2022 走看看