SEH 结构化异常处理(1)

  [工具]flyod1.10

  [目]学习SEH手法,另书中是用SoftICE调试,看起来不习惯.根据原文内容重新整理下,便于和我菜鸟们起学习.

  今天下决心,好好学习,这是就算是个开始吧!感觉学明白确很不容易!

  [注释]?--为不能理解地方,请大侠们指点下.学习过程中,有理解地方,肯请大侠们多多指教.

  [练习对象]加密和加密 2版第10章,光盘配套练习软件Software:seh.exeseh2.exe

  [writer] ytcswb 2005.2.1 感谢看学及论坛大侠们为我们提供这么好学习资料

  1.例子seh.exe学习:

00401000>$ 8D4424F8  leaeax,dwordptrss:[esp-8]//入口!根据下面代码分析,这里显然可以
                            //理解为开辟8字节空间,并把栈顶指针保存到eax
                            //相当于subesp,8;leaeax,dwordptrss:[esp]
00401004 . 64:870500000>xchgdwordptrfs:[0],eax  //记住fs[0]永远是指向当前err结构指针,
                            //执行完成后,fs[0]指向栈顶,准备在堆栈中构造1个err结构
                            //eax等于原fs[0],即指向原来err结构指针,即那个err结构地址
0040100B . BB2E104000 movebx,Seh.0040102E    //地址40102e-->ebx,建议在此地址上设断点,才能正常跟踪入seh代码中
00401010 . 53      pushebx          //压入堆栈,即当前err结构handler成员,当前异常处理代码入口地址
00401011 . 50      pusheax          //压入原fs[0],即当前err结构prev成员,即下个err结构地址
此时堆栈:
0012FFBC 0012FFE0 指针到下个SEH记录//0012FFE0是个指针,看看就知道指向下个err结构,数值上等于下个err结构地址
0012FFC0 0040102E SE句柄       //建立了1个当前err结构
0012FFE0 FFFFFFFF SEH链尾部
0012FFE4 77E74809 SE句柄
err结构定义[在Essup.INC源文件中定义---VCCRT(CRT含义:CRunTimelibrary)]:
_EXCEPTION_REGISTERATIONstru
 prev  dd?//指向下个err结构指针,数值上等于下个err结构首地址(在堆栈中)
 handlerdd?//指向异常处理代码指针,数值上等于异常处理代码入口地址即首地址
_EXCEPTION_REGISTERATIONends
00401012 . BE00000000 movesi,0          //简单赋值语句
00401017 . 8B06     moveax,dwordptrds:[esi] //读取线性地址0,产生异常
                            //执行后,windows检查到异常,执行线程马上被中段,从用户模式转到内核模式
                            //控制权交到操作系统异常调试(exceptiondispatcher),由它负责找到
                            //处理这个异常思路方法,即所有应用异常最终都是由windwos来处理,
                            //同个版本windows有固定异常处理代码.
//如果你把这句nop掉了,也就等于去除了异常.会接着执行到下面代码,并显示"SEHFail"!
      
00401019 . 6A00    push0                 ;/Style=MB_OK|MB_APPLMODAL
0040101B . 6800304000 pushSeh.00403000            ;|Title="OK"
00401020 . 6810304000 pushSeh.00403010            ;|Text="SEHFail"
00401025 . 6A00    push0                 ;|hOwner=NULL
00401027 . E81C000000 call<jmp.&USER32.MessageBoxA>     ;MessageBoxA
0040102C . EB13    jmpSeh.00401041
0040102E . 6A00    push0                 ;/Style=MB_OK|MB_APPLMODAL
00401030 . 6800304000 pushSeh.00403000            ;|Title="OK"
00401035 . 6803304000 pushSeh.00403003            ;|Text="SEHSucceed"
0040103A . 6A00    push0                 ;|hOwner=NULL
0040103C . E807000000 call<jmp.&USER32.MessageBoxA>     ;MessageBoxA
00401041 > 6A00    push0                 ;/ExitCode=0
00401043 . E806000000 call<jmp.&KERNEL32.ExitProcess>    ;ExitProcess
00401048 $-FF2508204000jmpdwordptrds:[<&USER32.MessageBoxA>]; USER32.MessageBoxA
0040104E .-FF2500204000jmpdwordptrds:[<&KERNEL32.ExitProcess>; kernel32.ExitProcess
00401054   00      db00
00401055   00      db00
---------------------------------------------------------------------------------------------------
00401017 . 8B06     moveax,dwordptrds:[esi] 
//读取线性地址0,产生异常
//执行完这1条指令,od状态行可以看到,产生了什么异常.状态行内容如下:
//访问违反:读取[00000000],使用sht+F7/F8/F9键跳过异常以继续执行.
//windows检测到了这个异常,就会向堆栈压入3个结构.压入顺序为EXCEPTION_RECORD,EXCEPTION_CONTEXT,EXCEPTION_POINTERS
//EXCEPTION_POINTERS结构就在栈顶,其定义如下:
typedefstrut_EXCEPTION_POINTERS{
+0pEXCEPTION_RECORDExceptionRecordDWORD?//指针,指向EXCEPTION_RECORD结构,即EXCEPTION_RECORD首地址
+4pCONTEXTContextRecord      DWORD?//指针,指向EXCEPTION_CONTEXT结构,即EXCEPTION_CONTEXT首地址
}_EXCEPTION_POINTERSends
  在看看EXCEPTION_RECORD结构:

EXCEPTION_RECORDstruct{   //共6个成员
+0 DWORDExceptionCode   //异常代码,定义了产生异常原因
+4 DWORDExceptionFlags   //异常标志?
+8 structEXCEPTION_RECORD //指针,指向另个EXCEPTION_RECORD结构
+C DVOIDExceptionAddress  //异常发生地址
+10DWORDNumberParameters  //和异常联系参数个数(0~15)般=0?
+14ULONG_PTRExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS] //异常信息?
}EXCEPTION_RECORDends
//执行完401017指令后,我们在od代码窗口看到代码如下:
77FB4DAF> 8B4C2404   movecx,dwordptrss:[esp+4]
77FB4DB3  8B1C24     movebx,dwordptrss:[esp]//来到了ntdll领空,即系统领空
 {
 //马上看看堆栈:
 0012FCCC 0012FCD4-|//指针,指向EXCEPTION_RECORD结构,即EXCEPTION_RECORD首地址-----这就是EXCEPTION_POINTERS
 0012FCD0 0012FCF0-|//指针,指向EXCEPTION_CONTEXT结构,即EXCEPTION_CONTEXT首地址---
 0012FCD4 C0000005---------------1--异常代码.这里开始就是EXCEPTION_RECORD结构
 0012FCD8 00000000       2--异常标志=0
 0012FCDC 00000000       3--指针,指向另个EXCEPTION_RECORD结构,这里=0
                       没有另个EXCEPTION_RECORD结构,为NULL指针.
 0012FCE0 00401017 Seh.004010174--异常发生地址,这就是发生异常那条指令地址.
 0012FCE4 00000002       5--和异常联系参数个数=2?
 0012FCE8 00000000       6--异常信息?
 0012FCEC 00000000---------------
 0012FCF0 0001003F---------------这里开始就是EXCEPTION_CONTEXT结构,ContextFlags
 0012FCF4 00000000       Dr0 
 0012FCF8 00000000       Dr1
 0012FCFC 00000000       Dr2
 0012FD00 00000000       Dr3
 0012FD04 0000A000       Dr6
 0012FD08 00000000       Dr7
  我们重点看看0012FCF0+B8=12FDA8

 0012FDA4 0012FFF0
 0012FDA8 00401017 Seh.00401017异常发生地址,这就是发生异常那条指令地址.
 0012FDAC 0000001B
 }
  继续跟踪:

77FB4DB6  51       pushecx//指针,指向EXCEPTION_CONTEXT结构
77FB4DB7  53       pushebx//指针,指向EXCEPTION_RECORD结构
77FB4DB8  E8ACBDFAFF  callntdll.77F60B69//如果f8过,会出现SEHsucceed提示窗口,即执行了自己异常代码,
                        //为了看系统是如何处理,我们f7进入
77FB4DBD  0AC0      oral,al
77FB4DBF  740C     jentdll.77FB4DCD
77FB4DC1  5B       popebx
77FB4DC2  59       popecx
77FB4DC3  6A00     push0
77FB4DC5  51       pushecx
77FB4DC6  E8480BFCFF  callntdll.ZwContinue
77FB4DCB  EB0B     jmpntdll.77FB4DD8
77FB4DCD  5B       popebx
77FB4DCE  59       popecx
77FB4DCF  6A00     push0
77FB4DD1  51       pushecx
77FB4DD2  53       pushebx
77FB4DD3  E8F213FCFF  callntdll.ZwRaiseException
77FB4DD8  83C4EC    addesp,-14
77FB4DDB  890424     movdwordptrss:[esp],eax
77FB4DDE  C744240401000>movdwordptrss:[esp+4],1
77FB4DE6  895C2408   movdwordptrss:[esp+8],ebx
77FB4DEA  C744241000000>movdwordptrss:[esp+10],0
77FB4DF2  54       pushesp
77FB4DF3  E8AFC2F9FF  callntdll.RtlRaiseException
77FB4DF8  C20800    retn8
  继续跟到这段代码里:

77F79B7E  55       pushebp
77F79B7F  8BEC      movebp,esp
77F79B81  FF750C    pushdwordptrss:[ebp+C]
77F79B84  52       pushedx
77F79B85  64:FF350000000>pushdwordptrfs:[0]
77F79B8C  64:89250000000>movdwordptrfs:[0],esp
 {
 //堆栈建立了1个err结构
 0012FC04 0012FFBC 指针到下个SEH记录//enter键看看
 0012FC08 77F79BB8 SE句柄
 0012FFBC 0012FFE0 指针到下个SEH记录
 0012FFC0 0040102E SE句柄//熟悉这个地址吧
 0012FFE0 FFFFFFFF SEH链尾部
 0012FFE4 77E74809 SE句柄
 }
77F79B93  FF7514    pushdwordptrss:[ebp+14]
77F79B96  FF7510    pushdwordptrss:[ebp+10]
77F79B99  FF750C    pushdwordptrss:[ebp+C]
77F79B9C  FF7508    pushdwordptrss:[ebp+8]
77F79B9F  8B4D18    movecx,dwordptrss:[ebp+18]
 {
 //此时我们马上看看堆栈:
 0012FBF4 0012FCD4//指针,指向EXCEPTION_RECORD结构,即EXCEPTION_RECORD首地址 -----回调参数1
 0012FBF8 0012FFBC//指向err结构.可以看看上面我们截取SEH链表        -----回调参数2
 0012FBFC 0012FCF0//指针,指向EXCEPTION_CONTEXT结构,即EXCEPTION_CONTEXT首地址-----回调参数3
 0012FC00 0012FCAC//参数4 _lpDispatchrContext?最先被压入堆栈.
 0012FC04 0012FFBC 指针到下个SEH记录
 0012FC08 77F79BB8 SE句柄
 0012FC0C 0012FFBC
 }
77F79BA2  FFD1      callecx           //Seh.0040102E到这里执行,就是自己异常处理代码,f7
                             //这就是异常处理回调,其参数含义请往下看.
 {
 0040102C ./EB13    jmpSeh.00401041
 0040102E .|6A00    push0                 ;/Style=MB_OK|MB_APPLMODAL
 00401030 .|6800304000 pushSeh.00403000            ;|Title="OK"
 00401035 .|6803304000 pushSeh.00403003            ;|Text="SEHSucceed"
 0040103A .|6A00    push0                 ;|hOwner=NULL
 0040103C .|E807000000 call<jmp.&USER32.MessageBoxA>     ;MessageBoxA
 00401041 >6A00    push0                 ;/ExitCode=0
 00401043 . E806000000 call<jmp.&KERNEL32.ExitProcess>    ;ExitProcess
 00401048 $-FF2508204000jmpdwordptrds:[<&USER32.MessageBoxA>]; USER32.MessageBoxA
 0040104E .-FF2500204000jmpdwordptrds:[<&KERNEL32.ExitProcess>; kernel32.ExitProcess
 }
77F79BA4  64:8B250000000>movesp,dwordptrfs:[0]
77F79BAB  64:8F050000000>popdwordptrfs:[0]
77F79BB2  8BE5      movesp,ebp
77F79BB4  5D       popebp
77F79BB5  C21400    retn14
  [整理总结]

  //读取线性地址0,产生异常

  //执行后,windows检查到异常,执行线程马上被中段,从用户模式转到内核模式

  //控制权交到操作系统异常调试(exceptiondispatcher),由它负责找到

  //处理这个异常思路方法,即所有应用异常最终都是由windwos来处理,

  //那么同个版本windows就有固定异常处理代码.跟踪seh保护时,以此为切入点,可以轻而举地找到关键!

  2.例子seh2.exe学习:

00401000>/$ 6851104000 pushseh2.00401051           ; SEhandleration发生异常后到这里执行
                                  //看学强调:提前在这个handler设个断点,否则容易跑飞!
                                  //只有这样才能正常跟进seh处理代码!
00401005 |. 64:FF3500000>pushdwordptrfs:[0]
0040100C |. 64:892500000>movdwordptrfs:[0],esp        //构造1个err结构
0012FFBC 0012FFE0 指针到下个SEH记录//fs:[0]=esp=0x0012FFBC
0012FFC0 00401051 SE句柄
0012FFE0 FFFFFFFF SEH链尾部
0012FFE4 77E74809 SE句柄
00401013 |. BE00000000 movesi,0
00401018 |. 8B06     moveax,dwordptrds:[esi]//产生异常
//这里实际是故意引发个异常,为就是通过修改CONTEXT,来实现反跟踪及改变流程(设置暗桩吗?)
0040101A |. 6A00    push0                 ;/Style=MB_OK|MB_APPLMODAL
0040101C |. 6800304000 pushseh2.00403000           ;|Title="SEH"
00401021 |. 680F304000 pushseh2.0040300F           ;|Text="SEH没有运行"
00401026 |. 6A00    push0                 ;|hOwner=NULL
00401028 |. E857000000 call<jmp.&USER32.MessageBoxA>     ;MessageBoxA
0040102D |. 6A00    push0                 ;/Style=MB_OK|MB_APPLMODAL
0040102F |. 6800304000 pushseh2.00403000           ;|Title="SEH"
00401034 |. 6804304000 pushseh2.00403004           ;|Text="Hello,SEH!"
00401039 |. 6A00    push0                 ;|hOwner=NULL
0040103B |. E844000000 call<jmp.&USER32.MessageBoxA>     ;MessageBoxA
00401040 |. 64:8F0500000>popdwordptrfs:[0]
00401047 |. 83C404   addesp,4
0040104A |. 6A00    push0                 ;/ExitCode=0
0040104C . E839000000 call<jmp.&KERNEL32.ExitProcess>    ;ExitProcess
00401051 /$ 55      pushebp                ; Structuredexceptionhandler
00401052 |. 8BEC     movebp,esp
00401054 |. 53      pushebx
00401055 |. 8B4510   moveax,dwordptrss:[ebp+10]
00401058 |. 8D1D2D104000leaebx,dwordptrds:[40102D]
0040105E |. 8998B8000000movdwordptrds:[eax+B8],ebx
00401064 |. 33DB     xorebx,ebx
00401066 |. 895804   movdwordptrds:[eax+4],ebx
00401069 |. 895808   movdwordptrds:[eax+8],ebx
0040106C |. 89580C   movdwordptrds:[eax+C],ebx
0040106F |. 895810   movdwordptrds:[eax+10],ebx
00401072 |. C7401855010>movdwordptrds:[eax+18],155
00401079 |. B800000000 moveax,0
0040107E |. 5B      popebx
0040107F |. C9      leave
00401080 . C21000   retn10
00401083   CC      3
  发生异常,就来到这里:

Tags:  半结构化面试 结构化 结构化面试

延伸阅读

最新评论

发表评论