SEH异常处理学习总结

原创 2005年03月02日 20:48:00
前一段时间,在看异常处理一章内容的时候,发现这一部分还真的挺有尿水:)所以上网搜了一下有关内容,呦嗬,还挺丰富的。当然有些自己还是看不懂,现在就将这些宝贝拿出来跟大家共享一下。
首先我们看一下使用异常处理的几种情况:
A. 用来处理非致命的错误
B. 对API函数的参数合法性的检验(假设参数都是合法的,只有遇到异常的时候进行合法性检验)
C. 处理致命错误(退出时最好的选择,但是有的时候可以用异常处理函数在程序退出前释放资源,删除临时文件等,甚至可以详细记录产生异常的指令位置和环境)
D. 处理“计划内”的异常(我们可能更关心这种情况,因为可以做很多的手脚,哈哈)
接着我们看看Windows下异常处理的两种方式:1使用筛选器2 SEH异常处理
一、 使用筛选器
因为这里我要重点关注的是SEH的处理方式,所以还是简单的提一下筛选器处理方式。筛选器异常处理是通过异常回调函数来指定程序处理异常。这种方式的回调函数必须是唯一的,设置新的回调函数后以前的将失效。适用于进程范围。看一下这个函数的定义
Invoke SetUnhandledExecpionFilter,offset_Handler
Mov lpPrevHandler,eax
(先到这里吧有些难受,明天接着来)
######题外话:想起“司令”的一句话,觉得挺有道理:明天不一定美好,但是更美好的明天一定会到来!祝福所有的朋友。######

上午有会,什么也没有做,下午?还有会,我tm晕了,中午不睡觉了,不把事情做不完心里不踏实。
回调函数的格式:
_Handlerproc pExecptionInfo
看看pExecptionInfo这个指针参数指向的一个数据结构
EXCEPTION_POINTERS STRUCT 
              pExceptionRecord  DWORD      ?              
              ContextRecord    DWORD      ? 
            EXCEPTION_POINTERS ENDS
下面介绍 EXCEPTION_RECORD和CONTEXT结构的定义: 
  
;//===================== 以下是两个成员的详细结构=========================

        EXCEPTION_RECORD STRUCT 
          ExceptionCode        DWORD      ?      ;//异常码 
          ExceptionFlags        DWORD      ?      ;//异常标志 
          pExceptionRecord      DWORD      ?      ;//指向另外一个EXCEPTION_RECORD的指针 
          ExceptionAddress      DWORD      ?      ;//异常发生的地址 
          NumberParameters      DWORD      ?      ;//下面ExceptionInformation所含有的dword数目 
          ExceptionInformation  DWORD EXCEPTION_MAXIMUM_PARAMETERS dup(?) 
   EXCEPTION_RECORDENDS            ;//EXCEPTION_MAXIMUM_PARAMETERS ==15 

;//=============================具体解释================================

ExceptionCode 异常类型,SDK里面有很多类型,你可以在windows.inc里查找STATUS_来找到更多的异常类型,下面只给出hex值,具体标识定义请查阅windows.inc,你最可能遇到的几种类型如下: 

              C0000005h----读写内存冲突 
              C0000094h----非法除0 
              C00000FDh----堆栈溢出或者说越界 
              80000001h----由Virtual Alloc建立起来的属性页冲突 
              C0000025h----不可持续异常,程序无法恢复执行,异常处理例程不应处理这个异     常 
              C0000026h----在异常处理过程中系统使用的代码,如果系统从某个例程莫名奇妙的返回,则出现此代码, 如果RtlUnwind时没有Exception Record参数也同样会填入这个代码 
              80000003h----调试时因代码中int3中断 
              80000004h----处于被单步调试状态 

              注:也可以自己定义异常代码,遵循如下规则: 
              ____________________________________________________________________

              位:      31~30            29~28          27~16          15~0 
              ____________________________________________________________________
              含义:    严重程度          29位            功能代码        异常代码 
                        0==成功        0==Mcrosoft    MICROSOFT定义  用户定义 
                        1==通知        1==客户 
                        2==警告          28位 
                        3==错误        被保留必须为0 
ExceptionFlags 异常标志 
              0----可修复异常 
              1----不可修复异常 
              2----正在展开,不要试图修复什么,需要的话,释放必要的资源 
pExceptionRecord 如果程序本身导致异常,指向那个异常结构 
ExceptionAddress 发生异常的eip地址 
ExceptionInformation 附加消息,在调用RaiseException可指定或者在异常号为C0000005h即内存异常时含义如下 
              第一个dword 0==读冲突 1==写冲突 
              第二个dword 读写冲突地址 
;//================================解释结束============================
                                                          off. 
        CONTEXT STRUCT                   ; _ 
          ContextFlags  DWORD      ?     ;  |            +0 
          iDr0          DWORD      ?       ;  |            +4 
          iDr1          DWORD      ?      ;  |            +8 
          iDr2          DWORD      ?      ;  >调试寄存器  +C 
          iDr3          DWORD      ?      ;  |            +10 
          iDr6          DWORD      ?      ;  |            +14 
          iDr7          DWORD      ?      ; _|            +18 
          FloatSave    FLOATING_SAVE_AREA <>  ;浮点寄存器区 +1C~~~88h 
          regGs        DWORD      ?      ;--|            +8C 
          regFs        DWORD      ?      ;  |/段寄存器    +90  
          regEs        DWORD      ?      ;  |/            +94            
          regDs        DWORD      ?      ;--|            +98 
          regEdi        DWORD      ?      ;____________    +9C 
          regEsi        DWORD      ?      ;      |  通用  +A0 
          regEbx        DWORD      ?      ;      |  寄    +A4 
          regEdx        DWORD      ?      ;      |  存    +A8 
          regEcx        DWORD      ?      ;      |  器    +AC 
          regEax        DWORD      ?      ;_______|___组_  +B0      
          regEbp        DWORD      ?      ;++++++++++++++++ +B4 
          regEip        DWORD      ?      ;    |控制        +B8 
          regCs        DWORD      ?      ;    |寄存        +BC 
          regFlag      DWORD      ?      ;    |器组        +C0 
          regEsp        DWORD      ?      ;    |            +C4 
          regSs        DWORD      ?      ;++++++++++++++++ +C8 
          ExtendedRegisters db MAXIMUM_SUPPORTED_EXTENSION dup(?) 
        CONTEXT ENDS 
;//============================以上是两个成员的详细结构============        
程序使用筛选器异常处理时可以通过查看上面结构中的regEip来找到产生异常的地址!调试的时候可以改变EIP的值以达到越过异常程序,转到“安全”的地方。
最后看一下筛选器异常处理回调函数的返回值
EXECPTION_EXECUTE_HANDLER           1;进程被终止,终止前不会出现提示错误的对话框
EXECPTION_CONTINUE_SEARCH 0;同样终止程序,显示错误对话框
EXECPTION_CONTINUE_EXECUTION -1;系统将CONTECT设置回去,继续执行程序

使用筛选器程序是最简单的处理异常方法,不足:1 不便于封装。2 处理是全局性的也就是无法对每个线程或子程序设置一个私有的异常处理程序进行异常处理。
进入正题:SEH异常处理
首先解释一下什么是SEH异常处理:SEH("Structured Exception Handling",即结构化异常处理.是操作系统提供给程序设计者的强有力的处理程序错误或异常的武器。
下面结合冷雨飘心的一个SEH异常处理程序来说明具体的用法:
;//====================================================================
;// ex. 2,by Hume,2001  线程相关的异常处理 
;//====================================================================
.386 
.model flat, stdcall 
option casemap :none  ; case sensitive 
include hd.h          ;//相关的头文件,你自己维护一个吧 
;//============================ 
.data 
szCap    db "By Hume[AfO],2001...",0 
szMsgOK db "It's now in the Per_Thread handler!",0 
szMsgERR1 db "It would never Get here!",0 
buff    db 200 dup(0) 

.code 
_start: 
;//========prog begin==================== 
  ASSUME FS:NOTHING 
        push    offset perThread_Handler 
       push    fs:[0]      
        mov    fs:[0],esp          ;//建立SEH的基本ERR结构,如果不明白,就仔细研究一下吧 
        xor    ecx,ecx                          
        mov    eax,200     
        cdq                ;//双字扩展到四个字节,因为是除法
       div    ecx 
                                                  ;//以下永远不会被执行 
 invoke  MessageBox,NULL,addr szMsgERR1,addr szCap,MB_OK+MB_ICONINFORMATION 
        pop    fs:[0] 
        add    esp,4 
        invoke    ExitProcess,NULL        

;//============================ 
perThread_Handler: 
 invoke    MessageBox,NULL,addr szMsgOK,addr szCap,MB_OK+MB_ICONINFORMATION 
        mov    eax,1          ;//ExceptionContinueSearch,不处理,由其他例程或系统处理 
        ;mov    eax,0          ;//ExceptionContinueExecution,表示已经修复CONTEXT,可从异常发生处继续执行 
    ret                        ;//这里如果返回0,你会陷入死循环,不断跳出对话框.... 

;//=============================Prog Ends============== 
end _start
程序本身很简单,注释也很详细。我们来看看是如何注册回调函数的
push    offset perThread_Handler 
       push    fs:[0]      
        mov    fs:[0],esp 
仅仅三个语句就解决了~那么为什么要用fs这个段寄存器呢?这里又涉及一个重要的内容:TIB(Thread Information Block线程信息块)。我们来看看这个重要的数据结构(引用了《罗聪浅谈利用SEB实现反跟踪》的部分内容)
TEB(Thread Environment Block) 在 Windows 9x 系列中被称为 TIB(Thread Information Block),它记录了线程的重要信息,而且每一个线程都会对应一个 TEB 结构。 Matt Pietrek 大牛已经给我们列出了它的结构,我就不多说啦,见下:(摘自 Matt Pietrek 的 Under The Hood - MSJ 1996) 

//=========================================================== 
// file: TIB.H 
// Author: Matt Pietrek 
// From: Microsoft Systems Journal "Under the Hood", May 1996 
//=========================================================== 
#pragma pack(1) 

typedef struct _EXCEPTION_REGISTRATION_RECORD 

  struct _EXCEPTION_REGISTRATION_RECORD * pNext; 
  FARPROC                                pfnHandler; 
} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD; 

typedef struct _TIB 

PEXCEPTION_REGISTRATION_RECORD pvExcept; // 00h Head of exception record list 
PVOID  pvStackUserTop;        // 04h Top of user stack 
PVOID  pvStackUserBase;        // 08h Base of user stack 

union                          // 0Ch (NT/Win95 differences) 

  struct  // Win95 fields 
  { 
      WORD    pvTDB;          // 0Ch TDB 
      WORD    pvThunkSS;      // 0Eh SS selector used for thunking to 16 bits 
      DWORD  unknown1;      // 10h 
  } WIN95; 

  struct  // WinNT fields 
  { 
      PVOID SubSystemTib;    // 0Ch 
      ULONG FiberData;        // 10h 
  } WINNT; 
} TIB_UNION1; 

PVOID  pvArbitrary;            // 14h Available for application use 
struct _tib *ptibSelf;          // 18h Linear address of TIB structure 

union                          // 1Ch (NT/Win95 differences) 

  struct  // Win95 fields 
  { 
      WORD    TIBFlags;          // 1Ch 
      WORD    Win16MutexCount;    // 1Eh 
      DWORD  DebugContext;      // 20h 
      DWORD  pCurrentPriority;  // 24h 
      DWORD  pvQueue;            // 28h Message Queue selector 
  } WIN95; 

  struct  // WinNT fields 
  { 
      DWORD unknown1;            // 1Ch 
      DWORD processID;            // 20h 
      DWORD threadID;            // 24h 
      DWORD unknown2;            // 28h 
  } WINNT; 
} TIB_UNION2; 

PVOID*  pvTLSArray;            // 2Ch Thread Local Storage array 

union                          // 30h (NT/Win95 differences) 

  struct  // Win95 fields 
  { 
      PVOID*  pProcess;      // 30h Pointer to owning process database 
  } WIN95; 
} TIB_UNION3; 

} TIB, *PTIB; 
#pragma pack()
让我们抬头看看上面的 Matt Pietrek 的代码,其中有这么一行: 

PEXCEPTION_REGISTRATION_RECORD pvExcept; // 00h Head of exception record list 

注意到 PEXCEPTION_REGISTRATION_RECORD 这个定义,它表示 pvExcept 这个变量正是 exception record list 的入口,这个入口位于整个结构的 0 偏移处。同时,在 M 的 Intel i386 Windows NT/2K/XP 内核中,每当创建一个线程,OS 均会为每个线程分配 TEB ,而且 TEB 永远放在 fs 段选择器指定的数据段的 0 偏移处。 
这样一来,你就明白了 SEH 注册的偏移为什么是在 fs:[0] 了吧? 
事实上 Windows 系统都是通过这种方法来为应用程序提供信息的,比如有这样的例子: 
struct _tib *ptibSelf;          // 18h Linear address of TIB structure 
DWORD threadID;                // 24h 

Windows 提供了一个 API :GetCurrentThreadID(),它的内部工作原理其实是这样的:(利用了上面的这两个地址) 

mov eax, fs:[18h]    ;因为 18h 偏移处是 TIB 结构的线性偏移地址 
mov eax, [eax + 24h] ;因为 24h 偏移处是 threadID 的地址 
ret                  ;把 eax 中储存的 threadID 地址返回
注:为什么要声明assume fs:nothing?因为masm编译器默认将fs段寄存器定义为error,所以程序在使用fs前必须将它启动!
接下来看看SEH的回调函数
_Handler proc _lpExecptionRecord, _lpSEH,lp_context,lp_DispatcherContext
 
_lpExecptionRecord指向一个EXECPTION_RECORD结构。
lp_context 指向一个CONTEXT结构。
_lpSEH  指向注册回调函数时使用的EXXCEPTION_REGISTRATION结构的地址。
返回值有四种取值:
ExecptionContinueExecution ( 0 :系统将线程环境设置为_lpContext指向的CONTEXT结构并继续执行。
ExceptionContinueSearch(1):回调函数拒绝处理这个异常,系统通过EXECPTION_REGISTRATION结构的prev字段得到前一个回调函数的地址并调用它。
ExecptionNestedExecption (2):发生异常嵌套。
ExecptionCollidedUnwind  (3):异常展开操作。这一个部分不做多讲,有兴趣的可以看看罗云彬的书,其实是很重要的一部分。
如果一个程序既有筛选器异常处理又有SEH异常处理,而且系统还有默认的异常处理机制,那么他们被调用的先后次序是怎么样的呢?
发生异常时系统的处理顺序(by Jeremy Gordon): 
    1.系统首先判断异常是否应发送给目标程序的异常处理例程,如果决定应该发送,并且目标程序正在被调试,则系统挂起程序并向调试器发送EXCEPTION_DEBUG_EVENT消息.呵呵,这不是正好可以用来探测调试器的存在吗? 
    2.如果你的程序没有被调试或者调试器未能处理异常,系统就会继续查找你是否安装了线程相关的异常处理例程,如果你安装了线程相关的异常处理例程,系统就把异常发送给你的程序seh处理例程,交由其处理.
    3.每个线程相关的异常处理例程可以处理或者不处理这个异常,如果他不处理并且安装了多个线程相关的异常处理例程,可交由链起来的其他例程处理. 
    4.如果这些例程均选择不处理异常,如果程序处于被调试状态,操作系统仍会再次挂起程序通知debugger. 
    5.如果程序未处于被调试状态或者debugger没有能够处理,并且你调用SetUnhandledExceptionFilter安装了最后异 常处理例程的话,系统转向对它的调用. 
6.如果你没有安装最后异常处理例程或者他没有处理这个异常,系统会调用默认的系统处理程序,通常显示一个对话框, 你可以选择关闭或者最后将其附加到调试器上的调试按钮.如果没有调试器能被附加于其上或者调试器也处理不了,系统就调用ExitProcess终结程序. 
    7.不过在终结之前,系统仍然对发生异常的线程异常处理句柄来一次展开,这是线程异常处理例程最后清理的机会.

说了这么多你也许会问SEH异常处理到底有什么用处呢?呵呵,且听小生慢慢道来~~~
第一道菜:病毒程序巧用SEH
这里简单的说一下如何利用SEH异常处理程序来躲避下毒软件的反病毒引擎。一个反病毒引擎在一个程序运行的时候会模拟程序的代码,当发现程序代码的疑点比较多的时候会报告成病毒。看看下面这段程序:
start:call Set_SEH;这句其实就是 push offset CONTINUE
;      JMP Set_SEH
CONTINUE:mov esp, [esp+8]; [ESP+8]存储的是旧的堆栈地址。
push offset Start_Virus ;----_ 把Start_Virus 的地址压栈,当作返回地址
ret;----跳到Start_Virus去,是不是很magic? 

Set_SEH:sub edx, edx            ;Edx =0 
Assume fs:nothing 
push dword ptr fs:[edx];把指去 _EXCEPTIONAL_REGISTRATION_RECORD 结构的指针入栈
mov fs:[edx], esp;安装一个seh
mov [edx],edx;引起一个内存读写冲突,发生异常因为edx=0 
;如果反病毒引擎不处理异常,不进入seh 处理程序(即 CONTINUE: ,继续模 
;拟下个指令,也就是jmp start,那么就进入一个死循环,可能会引起死机。               
jmp start       
Start_Virus:    .....
是不是很简单呢?就是让反病毒引擎不处理这个人为的异常时进入死循环~!!
第二道菜:TEB反跟踪初探
如果你的记性够好的话一定记得上面介绍过的TEB(TIB)线程信息块结构中有这么一句:
PVOID*  pProcess;      // 30h Pointer to owning process database 
这个偏移地址处的内容非常有用,它指向本线程的拥有者的 PDB(Process Database) 的线性地址。当你用动态调试器,例如 OllyDbg 的时候,调试器是把调试的对象作为一个子线程进行跟踪的,在这种情况下,被调试的对象的“拥有者”就是调试器本身,也就是说,它的 TEB 的 30h 处的偏移指向的内容肯定不为 0 ,这样,我们就可以利用这一点,判断 30h 偏移指向的内容,来判断是否有调试器跟踪。 
最后给出一个 Anti-Debug 的例子程序,用 MASM 编译完成后,请用 OllyDbg 来加载调试一下,看看与正常的运行结果有什么不同。 
;********************************************************* 
;程序名称:演示利用 TEB 结构进行 Anti-Debug 
;          请用 OllyDbg 进行调试 
;适用OS:Windows NT/2K/XP 
;作者:罗聪 
;日期:2003-2-9 
;出处:::URL::http://www.LuoCong.com(老罗的缤纷天地)  
;注意事项:如欲转载,请保持本程序的完整,并注明: 
;转载自“老罗的缤纷天地”(::URL::http://www.LuoCong.com)  
;********************************************************* 

.386 
.model flat, stdcall 
option casemap:none 

include /masm32/include/windows.inc 
include /masm32/include/kernel32.inc 
include /masm32/include/user32.inc 
includelib /masm32/lib/kernel32.lib 
includelib /masm32/lib/user32.lib 

.data 
szCaption  db  "Anti-Debug Demo by LC, 2003-2-9", 0 
szDebugged  db  "Hah, let me guess... U r dEBUGGINg me! ", 0 
szFine      db  "Good boy, no dEBUGGEr detected!", 0 

.code 
main: 
  assume  fs:nothing
  mov    eax, fs:[30h]              ;指向 PDB(Process Database)
  movzx  eax, byte ptr [eax + 2h];无符号数带零扩展
  or      al, al
  jz      _Fine
_Debugged:
  push    MB_OK or MB_ICONHAND
  push    offset szCaption
  push    offset szDebugged
  jmp    _Output
_Fine:
  push    MB_OK or MB_ICONINformATION
  push    offset szCaption
  push    offset szFine
_Output: 
  push    NULL
  call    MessageBoxA
  invoke  ExitProcess, 0
 end main
第三道菜:利用SEH执行shellcode
假设异常处理例程入口00401053,程序刚开始执行时esp是0012ffc4,以前的fs:[0]是0012ffe0
建立了TIB结构的第一个成员后堆栈的情况如下:

  内存低地址
  
| E0 |12ffbc(esp)
| FF |
| 12 |  --ERR结构的第一个成员
|_00_|
| 53 |12ffc0
| 10 |
| 40 |  --ERR结构的第二个成员
| 00 |

  内存高地址

  好了然后程序CALL一个函数,函数里面有一个局部变量并且在往其分配的空间中写入的数据时产生溢出.这时堆栈如下

____
|    |12f000 局部变量分配的空间,并且向12ffc0方向溢出了.
|    |
....
....
|_EBP|12ffb4 函数中保存老的EBP
| xx |
| xx |
| xx |
|_EIP|12ffb8 call函数时EIP进栈
| xx |
| xx |
|_xx_|
| E0 |12ffbc(esp)   {当SEH起作用的时候EBX刚好指向这个地址(也可说总是指向当前ERR结构)}
| FF |
| 12 |  --ERR结构的第一个成员
|_00_|
| 53 |12ffc0
| 10 |
| 40 |  --ERR结构的第二个成员
|_00_|
|    |12ffc4
   继续看,假设溢出代码一直到了12ffc4,然后call的函数该返回了,因为保存的EIP被溢出代码代替所以程序出错(不会不出错吧?),这样ESH开始起作用了(注:在这期间系统要执行一些操作,所以EBX才会指向当前ERR).这样一来程序就会跳到12ffc0里的地址去执行!而12ffc0里的东东早已不是原来的00401053了.这样我们不就改变了程序的流向了么.12ffc0中该写入什么内容呢?应是内存中JMP EBX的代码的地址.这样跳了3下后最终就会跳到12ffbc去执行.这个四字节可是宝贵的啊现在假设JMP EBX这个指令在内存中的地址是0x77e33f4d
那下具体看一下现在堆栈的情况:

| EB |12ffbc(esp)   {当ESH起作用的时候EBX刚好指向这个地址(也可说总是指向当前ERR结构)}
| 06 |
| 90 |  --ERR结构的第一个成员,执行JMP EBX后就到这儿来执行了(EB 06是短跳转JMP 12FFC4的机器码)
|_90_|  后面的90是nop空指令的机器码.
| 4D |12ffc0
| 3F |
| E3 |  --ERR结构的第二个成员,出错处理函数的入口地址(现在成了JMP EBX的地址)
|_77_|
|    |12ffc4
....

  好现在来看看12ffc4里面有些什么代码.(简单的说这段代码的作用是计算真正的shellcode的起始地址,然后跳过去执行.

低地址

|    |12f000(shellcode开始地址)
....
....
| 81 |12ffc4
| C3 |  add ebx,FFFFF03Ch(ebx=12ffc4,指令长度6,作用计算计算shellcode地址)
| 3C |
| F0 |
| FF |
| FF |
| FF |12ffca jmp ebx
| D3 |  

高地址

 
测试程序

-------------------------SEH.ASM------------------
.386
.model flat,stdcall
option casemap:none

include        ../include/user32.inc
includelib    ../lib/user32.lib
include        ../include/kernel32.inc
includelib    ../lib/kernel32.lib



.data
hello        db '利用一个读INI文件的API来演示WIN2000本地溢出',0
lpFileName    db './seh.ini',0            
lpAppName    db 'iam',0
lpKeyName    db 'czy',0            
lpDefault    db 'ddd',0
szCap     db "SEH TEST",0
szMsgOK db "OK,the exceptoin was handled by final handler!",0
szMsgERR1 db "It would never Get here!",0

.code

testov    proc
    local   lpReturnedString[2224] : byte    ;返回的字串搞成本地变量这样就和C语言一样了,它是在栈中    
    invoke    GetPrivateProfileString,offset    lpAppName,offset,lpKeyName,offset lpDefault,ADDR lpReturnedString,2249,offset lpFileName    
    invoke    MessageBox,0,addr lpReturnedString,addr lpReturnedString,1
    ret 
testov    endp
    
start:
    ASSUME fs:NOTHING
    invoke  MessageBox,0,addr szMsgERR1,addr szCap,30h+1000h ;下断点    
    push    offset Final_Handler    ;压入正常的出错处理程序入口地址
    push    FS:[0]                  ;把前一个TIB的地址压入
    mov    fs:[0],esp
    call    testov    
    pop     fs:[0]                     ;还原FS:[0]     
    
Final_Handler:   ;由于溢出了下面的代码不会被执行.
       invoke       MessageBox,0,addr szMsgOK,addr szCap,30h
       invoke       ExitProcess,0
       mov       eax,1
       ret
end start

-----------------end-------------

1如何更好的在内存中找JMP EBX的代码:
     在softice中执行S 10:0 L FFFFFFFF FF D3就可以了,但实际上这样找到的
地址可能不能执行代码.所以用下面的方法:
   map32 kernel32(在当前进程中查找映射的kernel32 DLL的信息)   
一般有如下显示:
  Owner        Obj Name    Obj#    Address        Size     TYPE
kernel32    .text        0001    001b:77b61000    0005d1ae code RO
......
  然后
S 77b61000 L 5d1ae FF D3
如果显示如下说明找到了:
  Pattern Found at 0023:77e61674 ....

2)关于缓冲区的大小的问题:
  利用SEH的办法就起码要设成1000个字节多,你的shellcode才不会被不知哪来的数据覆盖!
这道菜czy做的不好吃:(我感觉理解起来有些困难~!因为关于缓冲区溢出自己接触的太少,不过好东西要保留的,以后回过头看!
第四道菜:用 SEH 技术实现 API Hook
这一部分不想展开了,给大家一个链接吧。

::URL::http://www.luocong.com/articles/show_article.asp?Article_ID=25

最后作为结束语说说的缺点吧:)一个人只有正视自己的缺点才能不断地进步!呵呵
在SEH异常处理链中最后一个被装载的SEH异常处理程序总是被第一个调用,想想如果自己花了一个星期才写出来一个异常处理程序,能够完美处理所有异常,并希望异常全部由你来处理,但很不幸,比如你调用了一个外部模块,而这个模块自己安装了一个ugly的seh处理例程,他的动作是只要有异常发生就简单地终止程序,哈哈,那就死悄悄了。又比如你想在你的加壳程序里面加密目标程序代码段,然后发生无效指令异常的时候用你自己安装的处理句柄来解密代码段继续执行,听起来这的确是一个好主意,但遗憾的是大多数C/C++代码都用_try{}_except{}块来保证其正确运行,而这些异常处理例程是在你壳注册的例程之后安装的,因而也就在链的前面,无效指令一执行,首先是C/C++编译器本身提供的处理例程或者程序其他的异常处理例程来处理,可能简单结束程序或者....
好累!~~~~~~
写了两天,错了,应该是剪接+消化了两天,有很多的程序和文字是从hume,老罗,还有czy那里“剽窃”的:)希望高手们不要生气~~天下书籍一大抄。你们的必将是我的,当然我的也会共享给你们的。呵呵,现在还不行,级别不够啊。




版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

加密与解密二版菜鸟学习笔记(2) - SEH 结构化异常处理

标 题: 【原创】加密与解密二版菜鸟学习笔记(2) - SEH 结构化异常处理 作 者: ytcswb 时 间: 2005-02-01,16:40:24 链 接: http://bbs.pedi...

对于结构化异常处理(SEH)的进一步探索

原文来自:http://vicchina.51.net/show_article.php?id=64 本文关键字:SEH, _except_handler3, Windows, VisualC写本文的...

windows异常处理 - SEH简析

SEH - 结构化异常处理,是windows中处理程序异常的机制,是系统机制,与某种特定的编程语言无关。从本质上讲,SEH就是windows系统在终结应用程序之前,给程序的一个执行其预设定的回调函数的...

Win32 结构化异常处理(SEH)探秘

Win32 结构化异常处理其核心是操作系统提供的服务,你能找到的关于 SEH 的所有文档都是描述一个特定的编译器运行时库,这个运行库包装着操作系统实现。在本文中,我将一层一层对 SEH 进行剥离,以便...

SEH——Structured Exception Handling(结构化异常处理)

SEH是windows操作系统处理程序错误或异常的技术。SEH是一种系统体制,与具体的程序设计语言无关,但是windows下的编译器多使用SEH实现异常处理。     系统级别的SEH比较好理解,利...

Win32 结构化异常处理(SEH)探秘(续)

未处理异常 在文章的前面,我并没有全面描述 UnhandledExceptionFilter 这个 API。通常情况下你并不直接调用它(尽管你可以这么做)。大多数情况下它都是由 KERNEL32 ...

Win32 结构化异常处理(SEH)探秘

Win32 结构化异常处理其核心是操作系统提供的服务,你能找到的关于 SEH 的所有文档都是描述一个特定的编译器运行时库,这个运行库包装着操作系统实现。在本文中,我将一层一层对 SEH 进行剥离,以便...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)