[原创]windows-SEH详解

SEH是window操作系统默认的异常处理机制,逆向分析中,SEH除了基本的异常处理功能外,还大量用于反调试程序(这里SEH时保存在栈中的,漏洞利用的时候会用到)

1.SEH

    SEH是windows操作系统异常处理机制,在程序源代码中使用__try,__except,__finally关键字来具体实现。

2.OS异常处理的办法

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

     进程运行过程中若发生异常,OS会委托进程进行处理。若进程代码中存在具体的异常处理(如SEH异常处理器)代码,则能够顺利处理相关异常,程序继续运行,但如果进程内部没有具体实现SEH,那么相关异常就无法处理,OS就会启动默认的异常处理机制,终止进程运行

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

     被调试的进程内部发生异常,OS会首先把异常抛给调试进程处理。调器拥有被调试者的所有权限。被调试者内部发生的异常都由调试器处理。调试过程中的所有异常都先由调试器管理。被调试者发生异常时,调试器会停止运行,必须采取相应的措施来处理异常,完成后续的调试。遇到异常的时候的处理方法如下。

      1)直接修改异常:代码、寄存器、内存

      2)将异常泡杯被调试程序,使用od的shift+f7/f8/f9直接将异常抛还给被调试者

      3)OS默认异常处理机制

3.异常

     操作系统中常见的异常

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

EXCEPTION_ACCESS_VIOLATION     0xC0000005     程序企图读写一个不可访问的地址时引发的异常。例如企图读取0地址处的内存。

EXCEPTION_ARRAY_BOUNDS_EXCEEDED    0xC000008C     数组访问越界时引发的异常。

EXCEPTION_BREAKPOINT                           0x80000003     触发断点时引发的异常。

EXCEPTION_DATATYPE_MISALIGNMENT    0x80000002     程序读取一个未经对齐的数据时引发的异常。

EXCEPTION_FLT_DENORMAL_OPERAND     0xC000008D     如果浮点数操作的操作数是非正常的,则引发该异常。所谓非正常,即它的值太小以至于不能用标准格式表示出来。

EXCEPTION_FLT_DIVIDE_BY_ZERO                   0xC000008E     浮点数除法的除数是0时引发该异常。

EXCEPTION_FLT_INEXACT_RESULT           0xC000008F     浮点数操作的结果不能精确表示成小数时引发该异常。

EXCEPTION_FLT_INVALID_OPERATION            0xC0000090     该异常表示不包括在这个表内的其它浮点数异常。

EXCEPTION_FLT_OVERFLOW                             0xC0000091     浮点数的指数超过所能表示的最大值时引发该异常。

EXCEPTION_FLT_STACK_CHECK                  0xC0000092     进行浮点数运算时栈发生溢出或下溢时引发该异常。

EXCEPTION_FLT_UNDERFLOW                    0xC0000093     浮点数的指数小于所能表示的最小值时引发该异常。

EXCEPTION_ILLEGAL_INSTRUCTION          0xC000001D     程序企图执行一个无效的指令时引发该异常。

EXCEPTION_IN_PAGE_ERROR                        0xC0000006     程序要访问的内存页不在物理内存中时引发的异常。

EXCEPTION_INT_DIVIDE_BY_ZERO                   0xC0000094     整数除法的除数是0时引发该异常。

EXCEPTION_INT_OVERFLOW                             0xC0000095     整数操作的结果溢出时引发该异常。

EXCEPTION_INVALID_DISPOSITION                  0xC0000026     异常处理器返回一个无效的处理的时引发该异常。

EXCEPTION_NONCONTINUABLE_EXCEPTION     0xC0000025     发生一个不可继续执行的异常时,如果程序继续执行,则会引发该异常。

EXCEPTION_PRIV_INSTRUCTION                     0xC0000096     程序企图执行一条当前CPU模式不允许的指令时引发该异常。

EXCEPTION_SINGLE_STEP                          0x80000004     标志寄存器的TF位为1时,每执行一条指令就会引发该异常。主要用于单步调试。

EXCEPTION_STACK_OVERFLOW                   0xC00000FD     栈溢出时引发该异常。

       调试的时候经常触发5中最具代表性的异常

       3.1  EXCEPTION_ACCESS_VIOLATION     0xC0000005    

       程序企图读写一个不可访问的地址时引发的异常(不存在,或者不具有访问权限)。例如企图读取0地址处的内存。

       3.2EXCEPTION_BREAKPOINT  0x80000003  

       触发断点时引发的异常。在运行的代码中设置断点以后,cpu尝试执行该处的指令时将触发队形的EXCEPTION_BREAKPOINT异常

      INT3,设置断点对应的汇编指令为INT3,对应的机器指令为0xCC.灵活运用这个原理可以为程序运行带来很大的便利。比如使用hex editor打开PE文件,修改EP地址对应的文件偏移处的第一个字节为0xCC,然后运行该PE文件就会发生EXCEPTION_BREAKPOINT异常,经过OS的默认异常处理后会终止程序运行,若在操作系统的注册表中将默认调试器设置为OllyDbg,那么发生以上异常时OS会自动运行ollydbg调试器,附加发生异常的进程。

      3.3EXCEPTION_ILLEGAL_INSTRUCTION  0xC000001D    

      程序企图执行一个无效的指令时引发该异常。

      3.4EXCEPTION_INT_DIVIDE_BY_ZERO   0xC0000094   

      整数除法的除数是0时引发该异常。

      3.5EXCEPTION_SINGLE_STEP   0x80000004    

      标志寄存器的TF位为1时,每执行一条指令就会引发该异常。主要用于单步调试。

4.SEH详细说明

      4.1SEH链

      SEH以链的形式存在。第一个异常处理中未处理相关异常,它就会被传递到下一个异常处理器,直到得到处理。SEH是由_EXCEPTION_REGISTRATION_RECORD结构体组成的链表

1

2

3

4

ntdll!_EXCEPTION_REGISTRATION_RECORD

   +0x000 Next             : Ptr32 _EXCEPTION_REGISTRATION_RECORD

   +0x004 Handler          : Ptr32 _EXCEPTION_DISPOSITION 

}

      Next成员指向下一个_EXCEPTION_REGISTRATION_RECORD结构体指针,handler成员是异常处理函数(异常处理器)。若Next成员的值为FFFFFFFF,则表示它是链表最后一个结点

                

        发生异常的时候会按照(A)->(B)->(C)的顺序依次传递,直到由异常处理器处理

     4.2异常处理函数定义

1

2

3

4

5

6

EXCEPTION_DISPOSITION __cdecl _except_handler (

  EXCEPTION_RECORD      *pRecord,

  EXCEPTION_REGISTRATION_RECORD *pFrame,

  CONTEXT        *pContext,

  PVOID          pValue

);

       由系统调用,是一个回调函数,第一个参数是一个指向EXCEPTION_RECORD结构体的指针

1

2

3

4

5

6

7

8

typedef struct _EXCEPTION_RECORD {

    DWORD ExceptionCode;   //异常代码

    DWORD ExceptionFlags;

    struct _EXCEPTION_RECORD *ExceptionRecord;

    PVOID ExceptionAddress;   //异常发生地址

    DWORD NumberParameters;

    ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];

} EXCEPTION_RECORD;

      异常处理函数的第三个参数是指向CONTEXT结构体的指针,CONTEXT结构体的定义如下,   CONTEXT结构体用来备份CPU的值。 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

typedef struct _CONTEXT {

    DWORD ContextFlags;

    DWORD   Dr0;

    DWORD   Dr1;

    DWORD   Dr2;

    DWORD   Dr3;

    DWORD   Dr6;

    DWORD   Dr7;

    FLOATING_SAVE_AREA FloatSave;

    DWORD   SegGs;

    DWORD   SegFs;

    DWORD   SegEs;

    DWORD   SegDs;

    DWORD   Edi;

    DWORD   Esi;

    DWORD   Ebx;

    DWORD   Edx;

    DWORD   Ecx;

    DWORD   Eax;

    DWORD   Ebp;

    DWORD   Eip;

    DWORD   SegCs;              // MUST BE SANITIZED

    DWORD   EFlags;             // MUST BE SANITIZED

    DWORD   Esp;

    DWORD   SegSs;

    BYTE    ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];

} CONTEXT;

       异常发生的时候,执行异常代码的线程就会发生中断,转而运行SEH,此时OS会把线程 CONTEXT结构体的指针传递给异常处理函数的相应参数。里面有个eip成员,在异常处理函数中将参数传递过来的CONTEXT.eip设置为其他地址,然后返回处理函数。这样之前暂停的线程会执行新的EIP地址处的代码(反调试中经常使用这个技术)  

        4.3  TEB.NtTib.ExceptionList

        通过TEB结构体的NtTib成员可以很容易的访问进程的SEH链,TEB。

                          NtTib.ExceptionList=FS:[0]

       4.4 SEH安装/删除方法

       汇编中安装使用

1

2

3

push @MyHandler  ;异常处理程序

push FS:[0]       ;SEH Linked List头

mov dword ptr fs:[0],esp  ;添加链表

      汇编中的删除SEH代码   

1

2

POP DWORD PRT FS:[0] ;读取栈值并将其放入FS:[0],这里的栈值存放的下一个SEH的起始地址,执行该命令之后,就可以从栈中删除对应的SEH。

ADD ESP,4

     od中有查看SEH链的功能

5  od中的SEH

      程序在正常运行与调试运行的时候有不同的分支代码,借助SEH实现的反调试及时很多,这为代码的调试带来了很多不便,使调试更加困难。OD提供了很多调试选项,调试中发生异常的时候,调试器不会暂停,会自动将异常派送给被调试者。od中选择options-debugging options-.exception选项卡:灵活使用od的excettion选项,可以在不暂停调试器的前提下自动规避使用SEH的反调试“花招”,从而继续调试。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值