SEH解释

来源:互联网 发布:谢顿计划 知乎 编辑:程序博客网 时间:2024/04/29 10:06

跟踪程序:SEH_1源码编译连接后的程序:
调试工具:Ollydbg v1.1(异常设置为忽略所有异常,即所有的复选框都打上勾)
调试平台:XP SP2

1.操作系统为每个线程分配一个TEB结构的数据块,并用FS指向它:
NT_TIB STRUCT  
    +0 ExceptionList   dd     ?
     StackBase   dd    ?
     StackLimit   dd     ?
     SubSystemTib   dd     ?
     union
         FiberData  dd     ?
         Version  dd      ?
     ends
     ArbitraryUserPointer dd ?
     Self    dd  ?
NT_TIB ENDS

EXCEPTION_REGISTRATION_RECORD struct
  +0 prev  dd ?
  +4 handler  dd ?
EXCEPTION_REGISTRATION_RECORD ends

FS指向NT_TIB结构,FS:[0]就指向了EXCEPTION_REGISTRATION_RECORD结构
*************************************************************************************
执行到异常发生指令上是看堆栈,里面就是_EXCEPTION_REGISTRATION结构的prev和handler:
异常指令:
00401018  |.  8B06          mov     eax, [esi]
此时堆栈:
0012FFBC   0012FFE0  指向下一个 SEH 记录的指针
0012FFC0   0040102F  SE处理程序
在 地址0040102F上下断bp 0040102F,然后F9运行即可来到异常处理代码处
*************************************************************************************

2.异常发生时,操作系统向引起异常的线程的堆栈压入3个结构:
EXCEPTION_RECORD,CONTEXT,EXCEPTION_POINTERS

EXCEPTION_POINTERS STRUCT
  +0 pExceptionRecord  DWORD      ?
  +4 ContextRecord     DWORD      ?
EXCEPTION_POINTERS ENDS

即是说:EXCEPTION_POINTERS包含指向EXCEPTION_RECORD,CONTEXT这两个结构的指针
*************************************************************************************
执行到异常发生指令上继续单步即可进入系统异常处理代码,查看堆栈,里面就是EXCEPTION_POINTERS结构
的ExceptionRecord和ContextRecord,分别指向EXCEPTION_RECORD,CONTEXT这两个结构
系统异常处理程序入口:
7C92EAF0    8B1C24          mov     ebx, [esp]
7C92EAF3    51              push    ecx
7C92EAF4    53              push    ebx
7C92EAF5    E8 C78C0200     call    7C9577C1
此时堆栈:
0012FCCC   0012FCD4 ;指向EXCEPTION_RECORD结构
0012FCD0   0012FCF0 ;指向CONTEXT结构
*************************************************************************************

3.EXCEPTION_RECORD结构包含了发生异常的详细信息,这些信息独立于CPU
EXCEPTION_RECORD STRUCT
  +0 ExceptionCode         DWORD      ? ;异常发生的原因代码
  +4 ExceptionFlags        DWORD      ? ;异常处理操作标志
  +8 pExceptionRecord      DWORD      ?
  +0Ch ExceptionAddress      DWORD      ? ;异常发生的地址
  +10h NumberParameters      DWORD      ?
  +14h ExceptionInformation  DWORD EXCEPTION_MAXIMUM_PARAMETERS dup(?)
EXCEPTION_RECORD ENDS
*************************************************************************************
执行到异常发生指令上继续单步即可进入系统异常处理代码,查看堆栈,里面就是EXCEPTION_POINTERS结构
的ExceptionRecord和ContextRecord,分别指向EXCEPTION_RECORD,CONTEXT这两个结构
系统异常处理程序入口处堆栈:
0012FCCC   0012FCD4 ;指向EXCEPTION_RECORD结构
0012FCD0   0012FCF0 ;指向CONTEXT结构
此时用命令 d 0012FCD4即可查看EXCEPTION_RECORD结构的内容:
0012FCD4  05 00 00 C0 00 00 00 00 00 00 00 00 18 10 40 00  ..?.......@.
0012FCE4  02 00 00 00 00 00 00 00                          .......
或直接在堆栈中找 0012FCD4,其实就在下面不远的地方:
0012FCD4   C0000005   ;除零异常
0012FCD8   00000000
0012FCDC   00000000
0012FCE0   00401018  SEH_1.00401018  ;异常发生地址是 00401018
0012FCE4   00000002
0012FCE8   00000000
*************************************************************************************

4.CONTEXT结构包含了特定处理器的寄存器数据
CONTEXT STRUCT
   ContextFlags  DWORD      ?
 //调试寄存器
  +4 iDr0          DWORD      ?
  +8 iDr1          DWORD      ?
  +0C iDr2          DWORD      ?
  +10 iDr3          DWORD      ?
  +14 iDr6          DWORD      ?
  +18 iDr7          DWORD      ?
 //浮点寄存器
   FloatSave     FLOATING_SAVE_AREA <>
 //段寄存器
  +8C regGs         DWORD      ?
  +90 regFs         DWORD      ?
  +94 regEs         DWORD      ?
  +98 regDs         DWORD      ?
 //通用寄存器
  +9C regEdi        DWORD      ?
  +A0 regEsi        DWORD      ?
  +A4 regEbx        DWORD      ?
  +A8 regEdx        DWORD      ?
  +AC regEcx        DWORD      ?
  +B0 regEax        DWORD      ?
 //控制寄存器
  +B4 regEbp        DWORD      ?
  +B8 regEip        DWORD      ?
  +BC regCs         DWORD      ?
  +C0 regFlag       DWORD      ?
  +C4 regEsp        DWORD      ?
  +C8 regSs         DWORD      ?
   ExtendedRegisters db MAXIMUM_SUPPORTED_EXTENSION dup(?)
CONTEXT ENDS
*************************************************************************************
执行到异常发生指令上继续单步即可进入系统异常处理代码,查看堆栈,里面就是EXCEPTION_POINTERS结构
的ExceptionRecord和ContextRecord,分别指向EXCEPTION_RECORD,CONTEXT这两个结构
系统异常处理程序入口处堆栈:
0012FCCC   0012FCD4 ;指向EXCEPTION_RECORD结构
0012FCD0   0012FCF0 ;指向CONTEXT结构
此时用命令 d 0012FCF0即可查看CONTEXT结构的内容:
0012FCF0  3F 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00  ?..............
0012FD00  00 00 00 00 00 00 00 00 00 00 00 00 7F 02 FF FF  ............
0012FD10  00 00 FF FF FF FF FF FF 5E E4 2D F7 08 00 C4 05  ..^??.?
0012FD20  28 6E B3 F7 10 00 FF FF 6C 00 6C 00 04 01 05 01  (n橱.l.l.
0012FD30  A8 D0 6E 00 69 00 00 00 00 00 00 00 00 00 00 00  ㄐn.i...........
0012FD40  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0012FD50  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0012FD60  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0012FD70  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0012FD80  3B 00 00 00 23 00 00 00 23 00 00 00 38 07 93 7C  ;...#...#...8搢
0012FD90  00 00 00 00 00 60 FD 7F 94 EB 92 7C B0 FF 12 00  .....`?旊抾?.
0012FDA0  00 00 00 00 F0 FF 12 00 18 10 40 00 1B 00 00 00  ....?.@....
0012FDB0  46 03 01 00 BC FF 12 00                          F.?.#
或直接在堆栈中找 0012FCF0,就在下面不远的地方:
0012FCF0   0001003F ;
0012FCF4   00000000 ;iDr0
0012FCF8   00000000 ;iDr1
0012FCFC   00000000 ;iDr2
0012FD00   00000000 ;iDr3
0012FD04   00000000 ;iDr6
0012FD08   00000000 ;iDr7
0012FD0C   FFFF027F
0012FD10   FFFF0000
0012FD14   FFFFFFFF
0012FD18   F72DE45E
0012FD1C   05C40008
0012FD20   F7B36E28
0012FD24   FFFF0010
0012FD28   006C006C
0012FD2C   01050104
0012FD30   006ED0A8
0012FD34   00000069
0012FD38   00000000
0012FD3C   00000000
0012FD40   00000000
0012FD44   00000000
0012FD48   00000000
0012FD4C   00000000
0012FD50   00000000
0012FD54   00000000
0012FD58   00000000
0012FD5C   00000000
0012FD60   00000000
0012FD64   00000000
0012FD68   00000000
0012FD6C   00000000
0012FD70   00000000
0012FD74   00000000
0012FD78   00000000
0012FD7C   00000000
0012FD80   0000003B
0012FD84   00000023
0012FD88   00000023
0012FD8C   7C930738  ntdll.7C930738
0012FD90   00000000
0012FD94   7FFD6000
0012FD98   7C92EB94  ntdll.KiFastSystemCallRet
0012FD9C   0012FFB0 ;regEcx
0012FDA0   00000000 ;regEax
0012FDA4   0012FFF0 ;regEbp
0012FDA8   00401018  SEH_1.00401018 ;regEip 异常发生的地址
0012FDAC   0000001B ;regCs
0012FDB0   00010346  UNICODE "C:/Program Files/ATI Technologies/ATI Control Panel;F:/"
0012FDB4   0012FFBC
0012FDB8   00000023
*************************************************************************************

5.当系统检测到异常时,正在执行的线程立即被中断,处理由用户模式转向内核模式,控制权就交给了异常
调试程序,NT系统的异常处理程序是NTDLL.DLL中的KiUserExceptionDispatcher函数来实现的。
加载程序后下断点:bp KiUserExceptionDispatcher,F9运行被断下来到:
7C92EAEC >  8B4C24 04       mov     ecx, [esp+4]
7C92EAF0    8B1C24          mov     ebx, [esp]
7C92EAF3    51              push    ecx
7C92EAF4    53              push    ebx
7C92EAF5    E8 C78C0200     call    7C9577C1
这就是上面2.中执行异常代码后进入的空间,所以有两种方法来到此位置:
一是直接单步异常代码直接来到,二是下断点bp KiUserExceptionDispatcher,F9(Shift + F9)运行来到
*************************************************************************************
当系统对异常处理完毕后就会跳到我们的异常处理代码处,我们来找该跳转代码:(对XP SP2)
KiUserExceptionDipatcher函数入口:
7C92EAEC >  8B4C24 04       mov     ecx, [esp+4]
7C92EAF0    8B1C24          mov     ebx, [esp]
7C92EAF3    51              push    ecx
7C92EAF4    53              push    ebx
7C92EAF5    E8 C78C0200     call    7C9577C1 ;F7跟进
来到:
7C9577C1    8BFF            mov     edi, edi                         ; ntdll.7C930738
7C9577C3    55              push    ebp
7C9577C4    8BEC            mov     ebp, esp
7C9577C6    83EC 64         sub     esp, 64
........
7C957852    8D45 EC         lea     eax, [ebp-14]
7C957855    50              push    eax
7C957856    FF75 0C         push    dword ptr [ebp+C]
7C957859    53              push    ebx
7C95785A    56              push    esi
7C95785B    E8 F3BEFCFF     call    7C923753 ;F7跟进
来到:
7C923753    BA D837927C     mov     edx, 7C9237D8
7C923758    EB 0D           jmp     short 7C923767
7C92375A    90              nop
.......
7C923767    53              push    ebx
7C923768    56              push    esi
7C923769    57              push    edi
7C92376A    33C0            xor     eax, eax
7C92376C    33DB            xor     ebx, ebx
7C92376E    33F6            xor     esi, esi
7C923770    33FF            xor     edi, edi
7C923772    FF7424 20       push    dword ptr [esp+20]
7C923776    FF7424 20       push    dword ptr [esp+20]
7C92377A    FF7424 20       push    dword ptr [esp+20]
7C92377E    FF7424 20       push    dword ptr [esp+20]
7C923782    FF7424 20       push    dword ptr [esp+20]
7C923786    E8 0E000000     call    7C923799 ;F7跟进
来到:
7C923799    55              push    ebp
7C92379A    8BEC            mov     ebp, esp
7C92379C    FF75 0C         push    dword ptr [ebp+C]
7C92379F    52              push    edx
7C9237A0    64:FF35 0000000>push    dword ptr fs:[0]
7C9237A7    64:8925 0000000>mov     fs:[0], esp
7C9237AE    FF75 14         push    dword ptr [ebp+14] ;
7C9237B1    FF75 10         push    dword ptr [ebp+10] ;压入CONTEXT
7C9237B4    FF75 0C         push    dword ptr [ebp+C] ;压入EXCEPTION_REGISTERATION_RECORD
7C9237B7    FF75 08         push    dword ptr [ebp+8] ;压入EXCEPTION_RECORD
7C9237BA    8B4D 18         mov     ecx, [ebp+18] ;取异常处理句柄
7C9237BD    FFD1            call    ecx  ;指向SEH Handler;由此处返回到我们的程序,F7进
7C9237BF    64:8B25 0000000>mov     esp, fs:[0]
7C9237C6    64:8F05 0000000>pop     dword ptr fs:[0]
7C9237CD    8BE5            mov     esp, ebp
7C9237CF    5D              pop     ebp
7C9237D0    C2 1400         retn    14
直接来到call ecx的办法:bp 7C9237BD,但这有个局限性,就是不同系统的DLL版本不同。
单步执行 call    ecx 时的堆栈:
0012FBF0   0012FCD4 ;指向EXCEPTION_RECORD结构
0012FBF4   0012FFBC ;指向EXCEPTION_REGISTERATION_RECORD结构
0012FBF8   0012FCF0 ;指向CONTEXT结构
0012FBFC   0012FCA8 ;指向DispatchContext

ecx中即为我们的异常处理代码地址,或来到0012FFBC处:
0012FFBC   0012FFE0  指向下一个 SEH 记录的指针
0012FFC0   0040102F  SE处理程序
这个0040102F即为我们的异常处理代码地址,就是我们在初始化ERR结构时压入的。
*************************************************************************************

6.异常处理回调函数
_Handler proc _lpExceptionRecord:DWORD,_lpSEH:DWORD,_lpContext:DWORD,_lpDispatcherContext:DWORD
参数:
_lpExceptionRecord: 指向EXCEPTION_RECORD结构
_lpSEH:   指向EXCEPTION_REGISTERATION结构
_lpContext:  指向CONTEXT结构
_lpDispatcherContext: ;
返回值:
ExceptionContinueExecution  equ 0  返回异常处或修改后的EIP处继续执行
ExceptionContinueSearch     equ 1  不处理异常,系统通知下个异常处理例程
ExceptionNestedException    equ 2
ExceptionCollidedUnwind     equ 3
*************************************************************************************
F7进call    ecx时即调用了异常处理回调函数,来到我们的异常处理代码:
0040103E  /$  6A 00         push    0                                ; /Style = MB_OK|MB_APPLMODAL; 结构异常处理程序
00401040  |.  68 10204000   push    00402010                         ; |Title = "ok"
00401045  |.  68 20204000   push    00402020                         ; |Text = "SEH succeed"
0040104A  |.  6A 00         push    0                                ; |hOwner = NULL
0040104C  |.  E8 1F000000   call    <jmp.&user32.MessageBoxA>        ; /MessageBoxA
00401051  |.  55            push    ebp
00401052  |.  8BEC          mov     ebp, esp
00401054  |.  8B5D 10       mov     ebx, [ebp+10]
00401057  |.  C783 A0000000>mov     dword ptr [ebx+A0], <模块入口点>
00401061  |.  B8 00000000   mov     eax, 0
00401066  |.  8BE5          mov     esp, ebp
00401068  |.  5D            pop     ebp
00401069  /.  C3            retn

看堆栈:
0012FBEC   7C9237BF  返回到 ntdll.7C9237BF
0012FBF0   0012FCD4 ;指向CONTEXT结构
0012FBF4   0012FFBC ;指向EXCEPTION_REGISTERATION结构
0012FBF8   0012FCF0 ;指向EXCEPTION_RECORD结构
以上4个就是压入的异常处理回调函数的参数,CONTEXT结构也在,这样我们就可以在这里改变CONTEXT的值
以达到我们的目的,如清除断点,修改程序流向等。
异常处理回调函数是系统异常处理程序的子过程,所以直接用ret返回就会回到系统领空
*************************************************************************************

7.从异常处理代码可知程序返回后会从异常发生处继续执行,下断,bp 00401018
单步从
00401069  /.  C3            retn
返回系统来到:
7C9237BF    64:8B25 0000000>mov     esp, fs:[0]
7C9237C6    64:8F05 0000000>pop     dword ptr fs:[0]
7C9237CD    8BE5            mov     esp, ebp
7C9237CF    5D              pop     ebp
7C9237D0    C2 1400         retn    14  ;F7返回

来到:
7C92378B    5F              pop     edi                              ; ntdll.7C930738
7C92378C    5E              pop     esi
7C92378D    5B              pop     ebx
7C92378E    C2 1400         retn    14  ;F7返回

来到:
7C957860    F605 5AC3997C 8>test    byte ptr [7C99C35A], 80
7C957867    8BF8            mov     edi, eax
7C957869    0F85 16720100   jnz     7C96EA85
7C95786F    395D 08         cmp     [ebp+8], ebx
7C957872    0F84 1B720100   je      7C96EA93
7C957878    8BC7            mov     eax, edi
7C95787A    33C9            xor     ecx, ecx
7C95787C    2BC1            sub     eax, ecx
7C95787E  ^ 0F85 8631FFFF   jnz     7C94AA0A
7C957884    F646 04 01      test    byte ptr [esi+4], 1
7C957888    0F85 4F720100   jnz     7C96EADD
7C95788E    C645 FF 01      mov     byte ptr [ebp-1], 1
7C957892    5F              pop     edi
7C957893    5B              pop     ebx
7C957894    8A45 FF         mov     al, [ebp-1]
7C957897    5E              pop     esi
7C957898    C9              leave
7C957899    C2 0800         retn    8  ;F7返回

来到:
7C92EAFA    0AC0            or      al, al
7C92EAFC    74 0C           je      short 7C92EB0A
7C92EAFE    5B              pop     ebx
7C92EAFF    59              pop     ecx
7C92EB00    6A 00           push    0
7C92EB02    51              push    ecx
7C92EB03    E8 11EBFFFF     call    ZwContinue ;F7进

来到:
7C92D619 >  B8 20000000     mov     eax, 20
7C92D61E    BA 0003FE7F     mov     edx, 7FFE0300
7C92D623    FF12            call    [edx] ;F7进

来到:
7C92EB8B >  8BD4            mov     edx, esp
7C92EB8D    0F34            sysenter  ;F7,就会来到我们的断点地址00401018
7C92EB8F    90              nop
7C92EB90    90              nop
7C92EB91    90              nop
7C92EB92    90              nop
7C92EB93    90              nop
7C92EB94 >  C3              retn

来到这里:
00401018  |.  8B06          mov     eax, [esi]
0040101A  |.  6A 00         push    0                                ; /Style = MB_OK|MB_APPLMODAL
0040101C  |.  68 10204000   push    00402010                         ; |Title = "ok"
00401021  |.  68 13204000   push    00402013                         ; |Text = "修",B8,"",B4,"了异常?,A1,""
00401026  |.  6A 00         push    0                                ; |hOwner = NULL
00401028  |.  E8 43000000   call    <jmp.&user32.MessageBoxA>        ; /MessageBoxA
0040102D  |.  64:8F05 00000>pop     dword ptr fs:[0]
00401034  |.  83C4 04       add     esp, 4
00401037  |.  6A 00         push    0                                ; /ExitCode = 0
00401039  /.  E8 2C000000   call    <jmp.&kernel32.ExitProcess>      ; /ExitProcess


OVER ^_^

 

 

原创粉丝点击