Windows异常世界历险记(二)——Win32用户层下SEH机制之对RtlUnwind的逆向分析

用沁恒微CH32X035的IIC驱动OLED屏:一个完整的嵌入式显示项目实践 本文详细介绍了如何使用沁恒微CH32X035的IIC主机功能驱动OLED显示屏,从硬件连接到软件配置,再到图形化界面开发。通过寄存器操作和代码示例,展示了如何实现SSD1306 OLED屏的初始化、基本显示功能和高级图形绘制,为嵌入式开发者提供了完整的项目实践指南。 阅读详情

这篇文章写于14年10月,彼时正对Windows的SEH机制感兴趣,且国庆假期时间比较充裕,便记了几篇相关的笔记。后来逐渐忙于课业便没继续分析下去,3年来多次想完成这个系列,但每次刚一开头总会被各种琐事打断,一直未能如愿,希望这一次能如愿以偿地完成分析吧。



2017.12.04补充:
当时分析这个函数的动机有以下几个方面:

  1. Matt Pietrek在他的大作《A Crash Course on the Depths of Win32 Structured Exception Handling》中提到了退出展开,其中有这么一段话:

    When an exception occurs, the system walks the list of EXCEPTION_REGISTRATION structures until it finds a handler for the exception. Once a handler is found, the system walks the list again, up to the node that will handle the exception. During this second traversal, the system calls each handler function a second time. The key distinction is that in the second call, the value 2 is set in the exception flags. This value corresponds to EH_UNWINDING.

    我认为这段话是不准确的,事实上这样的操作被称为局部展开,是用户代码自愿进行的(言下之意就是这部分操作不是由Windows的用户层结构化异常处理机制来完成的)。在Matt Pietrek上面列举的例子中,由于在最外层使用了C/C++结构化异常处理中的异常处理块,因此导致内部原生异常处理程序不处理异常、执行流程转到外部的try-except块后,在其中(准确的说是except_handler4函数)执行了局部展开操作。因此,“局部展开会由系统完成”是一个很普遍的误会,网上很多文章都有类似的问题。就连《Windows核心编程》中的最后一部分也直接取名叫“结构化异常处理”,而事实上,其讲解的仅仅只是Visual Studio中C/C++的结构化异常处理机制而已,而局部展开行为尽管合理,但也需要用户手动进行,在系统的用户层结构化处理中是不会进行局部展开的。

  2. 其实局部展开的秘密,就隐藏在RtlUnwind函数的代码中。长时间以来,这个函数都处于undocument状态,虽然现在(2017年12月)可以在MSDN上查到了,但也是寥寥数语,不如自己分析来的痛快。

  3. 在漏洞利用中,SEH一直是一个热点话题。在经历了几轮魔道斗争后,SEH机制越来越成熟。而关于SEH安全机制的文章,网上一抓一大把,大多拿出的是伪代码和讲解,挖出代码进行分析的很少很少。本着“大海捞针,总不死心”和“眼见为实”的精神,咱们自己来分析一下。)

  4. Visual C++中的结构化异常处理是构建在Windows的用户层异常处理机制之上的。因此有必要先了解后者,以便更好地分析前者。

逆向成果

RtlUnwind函数是一个用户层的Windows API函数,其作用是执行SEH的展开操作(正是由于Windows的SEH机制不自动进行展开操作,才提供了此API,供需要执行该操作的用户调用)。下面介绍其参数:

void WINAPI RtlUnwind(
  _In_opt_ PVOID             TargetFrame,
  _In_opt_ PVOID             TargetIp,
  _In_opt_ PEXCEPTION_RECORD ExceptionRecord,
  _In_     PVOID             ReturnValue
);
  1. TargetFrame :

    • 当进行局部展开时,传入目标EXCEPTION_REGISTRATION_RECORD结构地址,当完成展开操作后,FS:[0]将指向这里。而执行该函数前的fs:[0]指向的节点到目标节点之间的前闭后开的所有合法节点中的处理函数都会被调用,被调用时传入的ExcepttionRecord.ExceptionFlags将包含EXCEPTION_UNWINDING操作。
    • 当进行全局展开时,传入NULL即可。此时SEH链上所有节点的SEH处理函数都会被调用,调用时传入的ExcepttionRecord.ExceptionFlags将包含EXCEPTION_EXIT_UNWIND标志。
    • 其余情况下需要谨慎传入,特别是不能传入比FS:[0]更低的地址,否则将引发异常。
  2. TargetIp:虽然很多地方说通过这个参数可以设定函数执行完成后要执行的指令地址,但经过分析,至少在Win8.1和XP下,这个参数并没有什么用。

  3. ExceptionRecord:在展开过程中,传递给被调用的异常处理函数的EXCEPTION_RECORD结构。如果传入NULL,将由本函数自己构造一个,其构造的那个ExceptionCode为STATUS_UNWIND,ExceptionAddress为调用本函数时压入的返回地址。

  4. ReturnValue:作为传给被调用的异常处理函数的CONTEXT结构中的eax值与恢复线程上下文环境时的eax值。

先贴上逆向的结果——C语言版本的RtlUnwind的代码(在IDA里按了F5然后对着3年前自己的逆向成果得来的):

void __stdcall RtlUnwind(PVOID TargetFrame, PVOID TargetIp, PEXCEPTION_RECORD ExceptionRecord, PVOID ReturnValue)
{
  PEXCEPTION_RECORD finalExcepttionRecord; // esi
  unsigned int dwCurrentSEHNode; // ebx
  unsigned int pfnHandler; // ecx
  void *pExceptionRecord; // ecx
  int dwRet; // eax
  int *dwPrevSEHNode; // ST10_4
  unsigned int StackLimit; // [esp+Ch] [ebp-384h]
  unsigned int StackBase; // [esp+10h] [ebp-380h]
  unsigned int DispatcherContext; // [esp+14h] [ebp-37Ch]
  EXCEPTION_RECORD tempExceptionRecord; // [esp+18h] [ebp-378h]
  EXCEPTION_RECORD temp2ExceptionRecord; // [esp+68h] [ebp-328h]
  CONTEXT stContext; // [esp+B8h] [ebp-2D8h]
  int savedregs; // [esp+390h] [ebp+0h]
  void *retaddr; // [esp+394h] [ebp+4h]

  finalExcepttionRecord = ExceptionRecord;
  RtlpGetStackLimits((void ***)&StackLimit, (void ***)&StackBase);// 获取线程的栈底和栈顶
  if ( !ExceptionRecord )                       // 如果没有传入ExceptionRecord,则构造一个,作为出现异常时抛出的那个
                                                // 否则就用传入的那个
  {
    finalExcepttionRecord = &temp2ExceptionRecord;
    temp2ExceptionRecord.ExceptionCode = 0xC0000027;// 0xC0000027 = STATUS_UNWIND
    temp2ExceptionRecord.ExceptionFlags = 0;
    temp2ExceptionRecord.ExceptionRecord = 0;
    temp2ExceptionRecord.ExceptionAddress = retaddr;// 这里取出的是调用本函数之前call压入的返回地址
    temp2ExceptionRecord.NumberParameters = 0;
  }
  if ( TargetFrame )
    finalExcepttionRecord->ExceptionFlags |= 2u;// #define EXCEPTION_UNWINDING 0x2
                                                // 如果传入的TargetFrame非空,则进行局部展开
  else
    finalExcepttionRecord->ExceptionFlags |= 6u;// #define EXCEPTION_EXIT_UNWIND 0x4 
                                                // 如果传入的TargetFrame为NULL,则进行退出展开
  RtlpCaptureContext(&savedregs, (int)&stContext);// 填写stContext结构,通用寄存器组清0,
                                                // EIP设置为call本函数时压入的返回地址
                                                // EBP设置为调用本函数时的EBP值(即前一帧的EBP)
                                                // ESP设置为指向TargetFrame的值(因为从本函数ret后这里就应该是ESP该指向的位置)
  stContext.Esp += 16;                          // 上下文结构中的ESP结构加上16,平衡了压入的4个参数
  stContext.Eax = (unsigned int)ReturnValue;    // 将传入的ReturnValue设置给eax作为返回值
  dwCurrentSEHNode = RtlpGetRegistrationHead(); // 从fs:0处获得异常链的首节点
  while ( dwCurrentSEHNode != 0xFFFFFFFF )      // 当前线程注册了自己的SEH处理函数,而非kernel32默认的那个
  {
    if ( (PVOID)dwCurrentSEHNode == TargetFrame )// 如果已到达指定节点,则按照之前设定的Context恢复线程的环境并运行
    {
      ZwContinue(&stContext, 0);
    }
    else if ( TargetFrame && (unsigned int)TargetFrame < dwCurrentSEHNode )// 传入的TargetFrame不为NULL且SEH链首节点高于TargetFrame,抛异常
                                                // (因为合法的SEH节点肯定在栈中,则从SEH首节点开始,应该依次降低)
                                                // 而这里反到首节点在目标节点下方,肯定传入的参数有问题
    {
      tempExceptionRecord.NumberParameters = 0;
      tempExceptionRecord.ExceptionCode = 0xC0000029;
      tempExceptionRecord.ExceptionFlags = 1;
      tempExceptionRecord.ExceptionRecord = finalExcepttionRecord;
      RtlRaiseException(&tempExceptionRecord);
    }
    if ( dwCurrentSEHNode < StackLimit          // 异常情况:SEH首节点比栈顶极限位置还高(这里的高指的是地址更低)
      || dwCurrentSEHNode + 8 > StackBase       // 异常情况:SEH首节点的最末尾成员比栈底还要低(这里的低指的是地址更高)
      || dwCurrentSEHNode & 3                   // 异常情况:SEH首节点地址不能被4整除(没对齐)
      || (pfnHandler = *(_DWORD *)(dwCurrentSEHNode + 4), pfnHandler < StackBase) && pfnHandler >= StackLimit// 异常情况:异常处理节点的函数地址落在栈空间中(说明多半是被溢出了)
      || !RtlIsValidHandler(pfnHandler, 0, (int)&stContext) )// 异常情况:被SafeSEH机制给判定为非法处理函数地址
    {                                           // 以上异常情况,抛出异常,拒绝执行处理函数
      tempExceptionRecord.NumberParameters = 0;
      tempExceptionRecord.ExceptionCode = 0xC0000028;
      tempExceptionRecord.ExceptionFlags = 1;
      tempExceptionRecord.ExceptionRecord = finalExcepttionRecord;
      RtlRaiseException(&tempExceptionRecord);
    }
    else
    {
      dwRet = RtlpExecuteHandlerForUnwind(      // 调用目标SEH节点的处理函数,执行退出前的清理动作
                pExceptionRecord,
                (int)finalExcepttionRecord,
                dwCurrentSEHNode,
                (int)&stContext,
                (int)&DispatcherContext,
                *(_DWORD *)(dwCurrentSEHNode + 4))
            - 1;                                // 这里很顽皮,居然在原SEH函数的结果基础上减了1
                                                // ExceptionContinueExecution = 0n0 //已处理异常,程序可继续执行
                                                // ExceptionContinueSearch = 0n1 //函数不处理该异常,继续搜寻异常处理函数链
                                                // ExceptionNestedException = 0n2 //在处理异常的过程中产生了新的异常
                                                // ExceptionCollidedUnwind = 0n3 //嵌套展开
                                                // 分别减1
      if ( dwRet )                              // 如果不为ExceptionContinueSearch,则表明异常处理函数没按套路出牌
      {
        if ( dwRet == 2 )                       // 如果返回的是CollidedUnwind,则将DispatcherContext作为下一个SEH节点展开
        {
          dwCurrentSEHNode = DispatcherContext;
        }
        else                                    // 否则如果返回ContinueExecution、NestedException都会抛异常
        {
          tempExceptionRecord.NumberParameters = 0;
          tempExceptionRecord.ExceptionCode = 0xC0000026;
          tempExceptionRecord.ExceptionFlags = 1;
          tempExceptionRecord.ExceptionRecord = finalExcepttionRecord;
          RtlRaiseException(&tempExceptionRecord);
        }
      }
      dwPrevSEHNode = (int *)dwCurrentSEHNode;
      dwCurrentSEHNode = *(_DWORD *)dwCurrentSEHNode;// 工作指针后移
      RtlpUnlinkHandler(dwPrevSEHNode);         // 将fs:0指向当前节点的下一个节点,即取消掉本节点
    }
  }
  if ( TargetFrame == (PVOID)0xFFFFFFFF )       // 如果传入的TargetFrame为0xFFFFFFFF,也不可能把这个节点干掉了,表明也到头了,该返回了
    ZwContinue(&stContext, 0);
  else
    ZwRaiseException(finalExcepttionRecord, &stContext, 0);// 如果传入的TargetFrame为其他值,则抛异常(如果匹配到了的话,早在while中就恢复上下文执行了)
}



以下是2014.10.08的笔记:

逆向过程

不知由于何种原因,在Win 8.1 X86平台下,试图用静态方式找出RtlUnwind函数的宿主还费了一番功夫。首先,在kernel32.dll的导出表中发现了该函数,可是立即又发现其仅是一个傀儡:

.text:6B81C83C     ; void __stdcall RtlUnwindStub(PVOID TargetFrame, PVOID TargetIp, PEXCEPTION_RECORD ExceptionRecord, PVOID ReturnValue)
.text:6B81C83C     public _RtlUnwindStub@16
.text:6B81C83C     _RtlUnwindStub@16 proc near
.text:6B81C83C
.text:6B81C83C     TargetFrame= dword ptr  4
.text:6B81C83C     TargetIp= dword ptr  8
.text:6B81C83C     ExceptionRecord= dword ptr  0Ch
.text:6B81C83C     ReturnValue= dword ptr  10h
.text:6B81C83C
.text:6B81C83C 000 jmp     ds:__imp__RtlUnwind@16 ; RtlUnwind(x,x,x,x)
.text:6B81C83C     _RtlUnwindStub@16 endp

于是跟着JMP跳到了:

.idata:6B880A24                 extrn _api_ms_win_core_registry_l1_1_0_NULL_THUNK_DATA:byte:4
.idata:6B880A28 ;
.idata:6B880A28 ; Imports from api-ms-win-core-rtlsupport-l1-2-0.dll
.idata:6B880A28 ;
.idata:6B880A28 ; SIZE_T __stdcall RtlCompareMemory(const void *Source1, const void *Source2, SIZE_T Length)
.idata:6B880A28                 extrn __imp__RtlCompareMemory@12:dword
.idata:6B880A28                                         ; CODE XREF: FSPErrorMessages::CMessageTagCache::LookupNodeByMessageTag(ulong,FSPErrorMessages::MessageTag *)+29p
.idata:6B880A28                                         ; DATA XREF: FSPErrorMessages::CMessageTagCache::LookupNodeByMessageTag(ulong,FSPErrorMessages::MessageTag *)+29r ...
.idata:6B880A2C ; __declspec(dllimport) __stdcall RtlRaiseException(x)
.idata:6B880A2C                 extrn __imp__RtlRaiseException@4:dword
.idata:6B880A2C                                         ; CODE XREF: PsspValidateWin32WalkMarker+9Ep
.idata:6B880A2C                                         ; DATA XREF: PsspValidateWin32WalkMarker+9Er
.idata:6B880A30 ; WORD __stdcall RtlCaptureStackBackTrace(DWORD FramesToSkip, DWORD FramesToCapture, PVOID *BackTrace, PDWORD BackTraceHash)
.idata:6B880A30                 extrn __imp__RtlCaptureStackBackTrace@16:dword
.idata:6B880A30                                         ; DATA XREF: RtlCaptureStackBackTraceStub(x,x,x,x)+6r
.idata:6B880A34 ; void __stdcall RtlUnwind(PVOID TargetFrame, PVOID TargetIp, PEXCEPTION_RECORD ExceptionRecord, PVOID ReturnValue)
.idata:6B880A34                 extrn __imp__RtlUnwind@16:dword
.idata:6B880A34                                         ; DATA XREF: RtlUnwindStub(x,x,x,x)r
.idata:6B880A34                                         ; RtlUnwind(x,x,x,x)r
.idata:6B880A38 ; void __stdcall RtlCaptureContext(PCONTEXT ContextRecord)
.idata:6B880A38                 extrn __imp__RtlCaptureContext@4:dword
.idata:6B880A38                                         ; CODE XREF: RaiseFailFastException(x,x,x)+86p
.idata:6B880A38                                         ; DATA XREF: RtlCaptureContext(x)r ...
.idata:6B880A3C                 extrn _api_ms_win_core_rtlsupport_l1_2_0_NULL_THUNK_DATA:byte:4
.idata:6B880A40 ;    

立即找到api-ms-win-core-rtlsupport-l1-2-0.dll,结果大吃一惊,连傀儡都不如:

.text:1000109E ; void __stdcall RtlUnwind(PVOID TargetFrame, PVOID TargetIp, PEXCEPTION_RECORD ExceptionRecord, PVOID ReturnValue)
.text:1000109E                 public _RtlUnwind@16
.text:1000109E _RtlUnwind@16   proc near               ; DATA XREF: .text:off_100010D8o
.text:1000109E
.text:1000109E TargetFrame     = dword ptr  4
.text:1000109E TargetIp        = dword ptr  8
.text:1000109E ExceptionRecord = dword ptr  0Ch
.text:1000109E ReturnValue     = dword ptr  10h
.text:1000109E
.text:1000109E                 retn    10h
.text:1000109E _RtlUnwind@16   endp

这里要吐槽一下IDA Pro 5.5,在设置了PDB服务器和路径后竟然没有解析出pdb文件中的变量名。害得我连蒙带猜,还开着VS2013调试,只为获取call后的函数名。

.text:6B2E755F     ; //=============== S U B R O U T I N E =======================================
.text:6B2E755F
.text:6B2E755F     ; //VOID _stdcall
.text:6B2E755F     ; //RtlUnwind(
.text:6B2E755F     ; //  IN  PVOID TargetFrame OPTIONAL //指向当前异常处理函数的EXCEPTION_REGISTRAION结构,
.text:6B2E755F     ; //  会展开SEH链上之前所有的节点,
.text:6B2E755F     ; //  并发送EXCEPTION_UNWINDING;
.text:6B2E755F     ; //  若留空,则为推出展开,发送EXCEPTION_UNWINDING or EXCEPTION_UNWINDING_FOR_EXIT
.text:6B2E755F     ; //  IN  PVOID TargetIp OPTIONAL //指定函数返回后下一条指令地址,若留空,则默认,经过分析,该参数无效!
.text:6B2E755F     ; //  IN  PEXCEPTION_RECORD ExceptionRecord OPTIONAL
.text:6B2E755F     ; //  指向EXCEPTION_RECORD,建议留空由系统填写
.text:6B2E755F     ; //  IN  PVOID ReturnValue
.text:6B2E755F     ; //  ); //
.text:6B2E755F     ;
.text:6B2E755F     ; //Attributes: bp-based frame
.text:6B2E755F
.text:6B2E755F                     public RtlUnwind
.text:6B2E755F     RtlUnwind       proc near               ; //CODE XREF: .text:6B2BC649p
.text:6B2E755F                                             ; //.text:6B2BCB42p ...
.text:6B2E755F
.text:6B2E755F     StackLimit      = dword ptr -384h
.text:6B2E755F     StackBase       = dword ptr -380h
.text:6B2E755F     DispatcherContext= dword ptr -37Ch
.text:6B2E755F     tempExceptionRecord= EXCEPTION_RECORD ptr -378h
.text:6B2E755F     stExceptionRecord= EXCEPTION_RECORD ptr -328h
.text:6B2E755F     stContext       = CONTEXT ptr -2D8h
.text:6B2E755F     CookiePointer   = dword ptr -4
.text:6B2E755F     TargetFrame     = dword ptr  8
.text:6B2E755F     ExceptionRecord = dword ptr  10h
.text:6B2E755F     dwRet           = dword ptr  14h
.text:6B2E755F
.text:6B2E755F     ; //FUNCTION CHUNK AT .text:6B2F597C SIZE 0000000F BYTES
.text:6B2E755F     ; //FUNCTION CHUNK AT .text:6B31CD49 SIZE 000000B8 BYTES
.text:6B2E755F
.text:6B2E755F 000                 mov     edi, edi
.text:6B2E7561 000                 push    ebp
.text:6B2E7562 004                 mov     ebp, esp
.text:6B2E7564 004                 and     esp, 0FFFFFFF8h ; //栈内8Byte对齐
.text:6B2E7567 004                 sub     esp, 384h
.text:6B2E756D 388                 mov     eax, ds:Security_Cookie
.text:6B2E7572 388                 xor     eax, esp
.text:6B2E7574 388                 mov     [esp+384h+CookiePointer], eax
.text:6B2E757B 388                 push    ebx
.text:6B2E757C 38C                 push    esi
.text:6B2E757D 390                 mov     esi, [ebp+ExceptionRecord]
.text:6B2E7580 390                 lea     edx, [esp+38Ch+StackBase]
.text:6B2E7584 390                 push    edi
.text:6B2E7585 394                 lea     ecx, [esp+390h+StackLimit]
.text:6B2E7589 394                 call    _RtlpGetStackLimits@8 ; //获取当前线程栈的栈顶和栈底
.text:6B2E758E 394                 xor     ecx, ecx
.text:6B2E7590 394                 test    esi, esi
.text:6B2E7592 394                 jnz     short ValidExceptionRecord ; //若用户指定了ExceptionRecord,跳走
.text:6B2E7594 394                 mov     eax, [ebp+4]    ; //函数返回地址
.text:6B2E7597 394                 lea     esi, [esp+390h+stExceptionRecord]
.text:6B2E759B 394                 mov     [esp+390h+stExceptionRecord.ExceptionCode], STATUS_UNWIND
.text:6B2E75A3 394                 mov     [esp+390h+stExceptionRecord.ExceptionFlags], ecx
.text:6B2E75A7 394                 mov     [esp+390h+stExceptionRecord.ExceptionRecord], ecx
.text:6B2E75AB 394                 mov     [esp+390h+stExceptionRecord.ExceptionAddress], eax
.text:6B2E75AF 394                 mov     [esp+390h+stExceptionRecord.NumberParameters], ecx
.text:6B2E75B3
.text:6B2E75B3     ValidExceptionRecord:                   ; //CODE XREF: RtlUnwind+33j
.text:6B2E75B3 394                 mov     edi, [ebp+TargetFrame] ; //若为空,则为退出展开,否则为局部展开
.text:6B2E75B6 394                 test    edi, edi
.text:6B2E75B8 394                 jz      NullTargetFrame
.text:6B2E75BE 394                 or      [esi+EXCEPTION_RECORD.ExceptionFlags], 2 ; //#define EXCEPTION_UNWINDING 0x2         // Unwind is in progress
.text:6B2E75C2
.text:6B2E75C2     loc_6B2E75C2:                           ; //CODE XREF: RtlUnwind+126j
.text:6B2E75C2 394                 lea     eax, [esp+390h+stContext]
.text:6B2E75C9 394                 push    eax
.text:6B2E75CA 398                 call    _RtlpCaptureContext@4 ;
.text:6B2E75CA                                             ; //CONTEXT.EBP = 调用RtlUnwind前的ebp
.text:6B2E75CA                                             ; //CONTEXT.ESP = 调用RtlUnwind传参后的ESP
.text:6B2E75CA                                             ; //CONTEXT.EIP = RtlUnwind执行完后下一条语句的地址
.text:6B2E75CF 394                 mov     eax, [ebp+dwRet]
.text:6B2E75D2 394                 add     [esp+390h+stContext._Esp], 10h ; //ESP = 还没压参前的ESP
.text:6B2E75DA 394                 mov     [esp+390h+stContext._Eax], eax ; //EAX 被改为传入的返回值
.text:6B2E75E1 394                 call    _RtlpGetRegistrationHead@0 ; //取得首个SEH链节点地址
.text:6B2E75E6 394                 mov     ebx, eax
.text:6B2E75E8
.text:6B2E75E8     ProcessSEH:                             ; //CODE XREF: RtlUnwind+10Cj
.text:6B2E75E8                                             ; //RtlUnwind+3586Aj
.text:6B2E75E8 394                 cmp     ebx, 0FFFFFFFFh ; //SEH链上没有函数
.text:6B2E75EB 394                 jz      NullSEHChain
.text:6B2E75F1 394                 cmp     ebx, edi        ; //到达局部展开的节点
.text:6B2E75F3 394                 jz      short IsDestSEHNode
.text:6B2E75F5 394                 test    edi, edi        ; //是局部展开还是退出展开
.text:6B2E75F7 394                 jz      short CheckOutOfBound
.text:6B2E75F9 394                 cmp     edi, ebx        ;
.text:6B2E75F9                                             ; //由于SEH链按栈生长,后加入的节点在SEH链的前端,
.text:6B2E75F9                                             ; //为小地址。此时竟然出现比SEH链首地址还低的节点,
.text:6B2E75F9                                             ; //一定出现了错误
.text:6B2E75F9                                             ;
.text:6B2E75FB 394                 jb      NotValidExceptionChain ;
.text:6B2E75FB                                             ; //引发一个异常,异常号为STATUS_INVALID_UNWIND_TARGET
.text:6B2E75FB                                             ; //异常标志为EXCEPTION_NONCONTINUABLE,并将传入RtlUnwind
.text:6B2E75FB                                             ; //的PEXCEPTION_RECORD作为关联EXCEPTION_RECORD填入主
.text:6B2E75FB                                             ; //EXCEPTION_RECORD
.text:6B2E7601
.text:6B2E7601     CheckOutOfBound:                        ; //CODE XREF: RtlUnwind+98j
.text:6B2E7601                                             ; //RtlUnwind+120j ...
.text:6B2E7601 394                 cmp     ebx, [esp+390h+StackLimit]
.text:6B2E7605 394                 jb      ErrorOutOfStack
.text:6B2E760B 394                 lea     eax, [ebx+8]    ;
.text:6B2E760B                                             ; //判断SEH链最底端((ebx+8)刚好覆盖SEH结构)
.text:6B2E760B                                             ; //是否越过线程栈底(高地址)
.text:6B2E760E 394                 cmp     eax, [esp+390h+StackBase]
.text:6B2E7612 394                 ja      ErrorOutOfStack
.text:6B2E7618 394                 test    bl, 3           ; //SEH链地址不能被4整除
.text:6B2E761B 394                 jnz     ErrorOutOfStack
.text:6B2E7621 394                 mov     ecx, [ebx+4]    ; //ECX = SEH_Handler
.text:6B2E7624 394                 cmp     ecx, [esp+390h+StackBase] ;
.text:6B2E7624                                             ; //函数绝不可能在栈上,函数要么位于系统Dll领空,
.text:6B2E7624                                             ; //要么位于程序代码段,二者地址都大于堆栈段
.text:6B2E7628 394                 jb      ErrorSEHFuncAddr
.text:6B2E762E
.text:6B2E762E     Unwinding:                              ; //CODE XREF: RtlUnwind+E427j
.text:6B2E762E 394                 lea     eax, [esp+390h+stContext]
.text:6B2E7635 394                 xor     edx, edx
.text:6B2E7637 394                 push    eax
.text:6B2E7638 398                 call    RtlIsValidHandler
.text:6B2E763D 394                 test    al, al
.text:6B2E763F 394                 jz      ErrorOutOfStack
.text:6B2E7645 394                 push    dword ptr [ebx+4] ; //SEH_Handler入栈
.text:6B2E7648 398                 lea     eax, [esp+394h+DispatcherContext]
.text:6B2E764C 398                 push    eax
.text:6B2E764D 39C                 lea     eax, [esp+398h+stContext]
.text:6B2E7654 39C                 push    eax
.text:6B2E7655 3A0                 push    ebx
.text:6B2E7656 3A4                 push    esi
.text:6B2E7657 3A8                 call    _RtlpExecuteHandlerForUnwind@20 ;
.text:6B2E7657                                             ; //(PEXCEPTION_RECORD,SEH链,PCONTEXT,&dwDispatcherContext,SEH_Handler)
.text:6B2E765C 394                 dec     eax
.text:6B2E765D 394                 jnz     loc_6B31CD71
.text:6B2E7663
.text:6B2E7663     UninstMoveForward:                      ; //CODE XREF: RtlUnwind+35839j
.text:6B2E7663                                             ; //RtlUnwind+35842j
.text:6B2E7663 394                 push    ebx
.text:6B2E7664 398                 mov     ebx, [ebx]      ; //ebx = NextSeh
.text:6B2E7666 398                 call    _RtlpUnlinkHandler@4 ;
.text:6B2E7666                                             ; //设置FS:[0]指向下一SEH节点
.text:6B2E766B 394                 jmp     ProcessSEH
.text:6B2E7670     ; //---------------------------------------------------------------------------
.text:6B2E7670
.text:6B2E7670     IsDestSEHNode:                          ; //CODE XREF: RtlUnwind+94j
.text:6B2E7670 394                 push    0
.text:6B2E7672 398                 lea     eax, [esp+394h+stContext]
.text:6B2E7679 398                 push    eax
.text:6B2E767A 39C                 call    ZwContinue      ; //实际到了这句就不会往下走了吧?
.text:6B2E767F 394                 jmp     short CheckOutOfBound
.text:6B2E7681     ; //---------------------------------------------------------------------------
.text:6B2E7681
.text:6B2E7681     NullTargetFrame:                        ; //CODE XREF: RtlUnwind+59j
.text:6B2E7681 394                 or      [esi+EXCEPTION_RECORD.ExceptionFlags], 6 ; //#define EXCEPTION_EXIT_UNWIND 0x4       // Exit unwind is in progress
.text:6B2E7685 394                 jmp     loc_6B2E75C2
.text:6B2E7685     RtlUnwind       endp ; //sp-analysis failed

.text:6B31CD49     ; //START OF FUNCTION CHUNK FOR RtlUnwind
.text:6B31CD49
.text:6B31CD49     NotValidExceptionChain:                 ; //CODE XREF: RtlUnwind+9Cj
.text:6B31CD49 394                 and     [esp+390h+tempExceptionRecord.NumberParameters], 0
.text:6B31CD4E 394                 lea     eax, [esp+390h+tempExceptionRecord]
.text:6B31CD52 394                 push    eax
.text:6B31CD53 398                 mov     [esp+394h+tempExceptionRecord.ExceptionCode], STATUS_INVALID_UNWIND_TARGET
.text:6B31CD5B 398                 mov     [esp+394h+tempExceptionRecord.ExceptionFlags], 1 ; //EXCEPTION_NONCONTINUABLE
.text:6B31CD63 398                 mov     [esp+394h+tempExceptionRecord.ExceptionRecord], esi ; //传入RtlUnwind的PEXCEPTION_RECORD
.text:6B31CD67 398                 call    RtlRaiseException
.text:6B31CD6C 394                 jmp     CheckOutOfBound
.text:6B31CD71     ; //---------------------------------------------------------------------------
.text:6B31CD71
.text:6B31CD71     loc_6B31CD71:                           ; //CODE XREF: RtlUnwind+FEj
.text:6B31CD71 394                 dec     eax
.text:6B31CD72 394                 dec     eax
.text:6B31CD73 394                 jz      short CollidedUnwind
.text:6B31CD75 394                 and     [esp+390h+tempExceptionRecord.NumberParameters], 0
.text:6B31CD7A 394                 lea     eax, [esp+390h+tempExceptionRecord]
.text:6B31CD7E 394                 push    eax
.text:6B31CD7F 398                 mov     [esp+394h+tempExceptionRecord.ExceptionCode], 0C0000026h
.text:6B31CD87 398                 mov     [esp+394h+tempExceptionRecord.ExceptionFlags], 1
.text:6B31CD8F 398                 mov     [esp+394h+tempExceptionRecord.ExceptionRecord], esi
.text:6B31CD93 398                 call    RtlRaiseException
.text:6B31CD98 394                 jmp     UninstMoveForward
.text:6B31CD9D     ; //---------------------------------------------------------------------------
.text:6B31CD9D
.text:6B31CD9D     CollidedUnwind:                         ; //CODE XREF: RtlUnwind+35814j
.text:6B31CD9D 394                 mov     ebx, [esp+390h+DispatcherContext]
.text:6B31CDA1 394                 jmp     UninstMoveForward
.text:6B31CDA6     ; //---------------------------------------------------------------------------
.text:6B31CDA6
.text:6B31CDA6     ErrorOutOfStack:                        ; //CODE XREF: RtlUnwind+A6j
.text:6B31CDA6                                             ; //RtlUnwind+B3j ...
.text:6B31CDA6 394                 and     [esp+390h+tempExceptionRecord.NumberParameters], 0
.text:6B31CDAB 394                 lea     eax, [esp+390h+tempExceptionRecord]
.text:6B31CDAF 394                 push    eax
.text:6B31CDB0 398                 mov     [esp+394h+tempExceptionRecord.ExceptionCode], STATUS_BAD_STACK
.text:6B31CDB8 398                 mov     [esp+394h+tempExceptionRecord.ExceptionFlags], 1
.text:6B31CDC0 398                 mov     [esp+394h+tempExceptionRecord.ExceptionRecord], esi
.text:6B31CDC4 398                 call    RtlRaiseException
.text:6B31CDC9 394                 jmp     ProcessSEH
.text:6B31CDCE     ; //---------------------------------------------------------------------------
.text:6B31CDCE
.text:6B31CDCE     NullSEHChain:                           ; //CODE XREF: RtlUnwind+8Cj
.text:6B31CDCE 394                 lea     eax, [esp+390h+stContext]
.text:6B31CDD5 394                 push    0
.text:6B31CDD7 398                 push    eax
.text:6B31CDD8 39C                 cmp     edi, 0FFFFFFFFh
.text:6B31CDDB 39C                 jnz     short RaiseException
.text:6B31CDDD 39C                 call    ZwContinue
.text:6B31CDE2 394                 jmp     short ExitFunc
.text:6B31CDE4     ; //---------------------------------------------------------------------------
.text:6B31CDE4
.text:6B31CDE4     RaiseException:                         ; //CODE XREF: RtlUnwind+3587Cj
.text:6B31CDE4 39C                 push    esi
.text:6B31CDE5 3A0                 call    ZwRaiseException
.text:6B31CDEA
.text:6B31CDEA     ExitFunc:                               ; //CODE XREF: RtlUnwind+35883j
.text:6B31CDEA 394                 mov     ecx, [esp+390h+CookiePointer]
.text:6B31CDF1 394                 pop     edi
.text:6B31CDF2 390                 pop     esi
.text:6B31CDF3 38C                 pop     ebx
.text:6B31CDF4 388                 xor     ecx, esp
.text:6B31CDF6 388                 call    CheckCookie
.text:6B31CDFB 388                 mov     esp, ebp
.text:6B31CDFD 004                 pop     ebp
.text:6B31CDFE 000                 retn    10h
.text:6B31CDFE     ; //END OF FUNCTION CHUNK FOR RtlUnwind
.text:6B31CE01     ; //---------------------------------------------------------------------------

调用的子函数分析

下面是其中函数的分析:

_RtlpGetStackLimits@8:

.text:6B2E71C5     ; // =============== S U B R O U T I N E =======================================
.text:6B2E71C5
.text:6B2E71C5     ; // BOOL _fastcall RtlpGetStackLimits(lpdwStackLimit(ecx),lpdwStackBase(edx))
.text:6B2E71C5     ; // 1 = NormalStack
.text:6B2E71C5     ; // 0 = DestroyedStack
.text:6B2E71C5     ; // 将栈底和栈顶地址分别写入edx和ecx指向的变量中,
.text:6B2E71C5     ; // 并判断调用函数之前堆栈是否越界。
.text:6B2E71C5     ; // Attributes: bp-based frame
.text:6B2E71C5
.text:6B2E71C5     _RtlpGetStackLimits@8 proc near         ; // CODE XREF: sub_6B2E724E+64p
.text:6B2E71C5                                             ; // RtlUnwind+2Ap
.text:6B2E71C5 000                 mov     edi, edi
.text:6B2E71C7 000                 push    ebp
.text:6B2E71C8 004                 mov     ebp, esp
.text:6B2E71CA 004                 mov     eax, large fs:18h ; // eax = TEB
.text:6B2E71D0 004                 push    esi
.text:6B2E71D1 008                 push    edi
.text:6B2E71D2 00C                 lea     edi, [ebp+4]
.text:6B2E71D5 00C                 mov     esi, [eax+4]    ; // esi = StackBase
.text:6B2E71D8 00C                 mov     eax, [eax+8]    ; // eax = StackLimit
.text:6B2E71DB 00C                 mov     [edx], esi      ; // [edx] = StackBase
.text:6B2E71DD 00C                 mov     [ecx], eax      ; // [ecx] = StackLimit
.text:6B2E71DF 00C                 cmp     eax, edi        ; // 比较限制栈顶和当前栈顶(指call本函数之前的栈)
.text:6B2E71E1 00C                 ja      short DestoryedStack
.text:6B2E71E3 00C                 cmp     edi, esi        ; // 当前栈顶是否高于限制栈底
.text:6B2E71E5 00C                 jnb     short DestoryedStack
.text:6B2E71E7 00C                 mov     al, 1
.text:6B2E71E9
.text:6B2E71E9     RecvyPointerReg:                        ; // CODE XREF: RtlpGetStackLimits(x,x)+2Aj
.text:6B2E71E9 00C                 pop     edi
.text:6B2E71EA 008                 pop     esi
.text:6B2E71EB 004                 pop     ebp
.text:6B2E71EC 000                 retn
.text:6B2E71ED     ; // ---------------------------------------------------------------------------
.text:6B2E71ED
.text:6B2E71ED     DestoryedStack:                         ; // CODE XREF: RtlpGetStackLimits(x,x)+1Cj
.text:6B2E71ED                                             ; // RtlpGetStackLimits(x,x)+20j
.text:6B2E71ED 00C                 xor     al, al
.text:6B2E71EF 00C                 jmp     short RecvyPointerReg
.text:6B2E71EF     _RtlpGetStackLimits@8 endp
.text:6B2E71EF
.text:6B2E71EF     ; // ---------------------------------------------------------------------------

_RtlpCaptureContext@4

.text:6B2BC923     ; // // __stdcall RtlpCaptureContext(x)
.text:6B2BC923     _RtlpCaptureContext@4:                  ; // // CODE XREF: RtlUnwind+6Bp
.text:6B2BC923                     push    ebx
.text:6B2BC924                     mov     ebx, [esp+8]    ; // // [esp+8] = eax = &stContext
.text:6B2BC928                     mov     [ebx+CONTEXT._Eax], 0
.text:6B2BC932                     mov     [ebx+CONTEXT._Ecx], 0
.text:6B2BC93C                     mov     [ebx+CONTEXT._Edx], 0
.text:6B2BC946                     mov     [ebx+CONTEXT._Ebx], 0
.text:6B2BC950                     mov     [ebx+CONTEXT._Esi], 0
.text:6B2BC95A                     mov     [ebx+CONTEXT._Edi], 0
.text:6B2BC964
.text:6B2BC964     loc_6B2BC964:                           ; // // CODE XREF: RtlCaptureContext+2Cj
.text:6B2BC964                     mov     word ptr [ebx+CONTEXT.SegCs], cs
.text:6B2BC96A                     mov     word ptr [ebx+CONTEXT.SegDs], ds
.text:6B2BC970                     mov     word ptr [ebx+CONTEXT.SegEs], es
.text:6B2BC976                     mov     word ptr [ebx+CONTEXT.SegFs], fs
.text:6B2BC97C                     mov     word ptr [ebx+CONTEXT.SegGs], gs
.text:6B2BC982                     mov     word ptr [ebx+CONTEXT.SegSs], ss
.text:6B2BC988                     pushf
.text:6B2BC989                     pop     [ebx+CONTEXT.EFlags]
.text:6B2BC98F                     mov     eax, [ebp+4]    ; // // stContext.eip = RtlUnwind后的第一条指令地址
.text:6B2BC992                     mov     [ebx+CONTEXT._Eip], eax
.text:6B2BC998                     mov     eax, [ebp+0]    ; // // stContext.ebp = RtlUnwind前的EBP值
.text:6B2BC99B                     mov     [ebx+CONTEXT._Ebp], eax
.text:6B2BC9A1                     lea     eax, [ebp+8]    ; // // stContext.esp = ebp+8 = &TargetFrame(最后一个入栈参数的地址)
.text:6B2BC9A4                     mov     [ebx+CONTEXT._Esp], eax
.text:6B2BC9AA                     mov     [ebx+CONTEXT.ContextFlags], CONTEXT_FULL
.text:6B2BC9B0                     pop     ebx
.text:6B2BC9B1                     retn    4

_RtlpGetRegistrationHead@0:

.text:6B2BC9B7     ; __stdcall RtlpGetRegistrationHead()
.text:6B2BC9B7     _RtlpGetRegistrationHead@0:             ; CODE XREF: sub_6B2E724E+69p
.text:6B2BC9B7                                             ; RtlUnwind+82p ...
.text:6B2BC9B7                     mov     eax, large fs:0
.text:6B2BC9BD                     retn
.text:6B2BC9BE     ; ---------------------------------------------------------------------------

_RtlpExecuteHandlerForUnwind@20

.text:6B2BC828     ; // =============== S U B R O U T I N E =======================================
.text:6B2BC828
.text:6B2BC828
.text:6B2BC828     ; // __stdcall RtlpExecuteHandlerForUnwind(x, x, x, x, x)
.text:6B2BC828     _RtlpExecuteHandlerForUnwind@20 proc near ; // CODE XREF: RtlUnwind+F8p
.text:6B2BC828
.text:6B2BC828     PEXCEPTION_RECORD= dword ptr  4
.text:6B2BC828     SEH             = dword ptr  8
.text:6B2BC828     PCONTEXT        = dword ptr  0Ch
.text:6B2BC828     dwDispatcherContext= dword ptr  10h
.text:6B2BC828     lpSEHHandlerFunc= dword ptr  14h
.text:6B2BC828
.text:6B2BC828 000                 mov     edx, offset TempSEHFunc
.text:6B2BC82D 000                 lea     ecx, [ecx]
.text:6B2BC82F
.text:6B2BC82F     loc_6B2BC82F:                           ; // CODE XREF: sub_6B2BC820+5j
.text:6B2BC82F 000                 push    ebx
.text:6B2BC830 004                 push    esi
.text:6B2BC831 008                 push    edi
.text:6B2BC832 00C                 xor     eax, eax
.text:6B2BC834 00C                 xor     ebx, ebx
.text:6B2BC836 00C                 xor     esi, esi
.text:6B2BC838 00C                 xor     edi, edi
.text:6B2BC83A 00C                 push    [esp+0Ch+lpSEHHandlerFunc]
.text:6B2BC83E 010                 push    [esp+10h+dwDispatcherContext]
.text:6B2BC842 014                 push    [esp+14h+PCONTEXT]
.text:6B2BC846 018                 push    [esp+18h+SEH]
.text:6B2BC84A 01C                 push    [esp+1Ch+PEXCEPTION_RECORD]
.text:6B2BC84E 020                 call    CallNodeSEHFunc
.text:6B2BC853 00C                 pop     edi
.text:6B2BC854 008                 pop     esi
.text:6B2BC855 004                 pop     ebx
.text:6B2BC856 000                 retn    14h
.text:6B2BC856     _RtlpExecuteHandlerForUnwind@20 endp
.text:6B2BC856
.text:6B2BC859     ; // ---------------------------------------------------------------------------
.text:6B2BC85B     ; // =============== S U B R O U T I N E =======================================
.text:6B2BC85B
.text:6B2BC85B     ; // Attributes: bp-based frame
.text:6B2BC85B
.text:6B2BC85B     CallNodeSEHFunc proc near               ; // CODE XREF: RtlpExecuteHandlerForUnwind(x,x,x,x,x)+26p
.text:6B2BC85B
.text:6B2BC85B     PEXCEPTION_RECORD= dword ptr  8
.text:6B2BC85B     SEH             = dword ptr  0Ch
.text:6B2BC85B     PCONTEXT        = dword ptr  10h
.text:6B2BC85B     dwDispatcherContext= dword ptr  14h
.text:6B2BC85B     lpfnSEHHandler  = dword ptr  18h
.text:6B2BC85B
.text:6B2BC85B 000                 push    ebp
.text:6B2BC85C 004                 mov     ebp, esp
.text:6B2BC85E 004                 push    [ebp+SEH]
.text:6B2BC861 008                 push    edx
.text:6B2BC862 00C                 push    large dword ptr fs:0
.text:6B2BC869 010                 mov     large fs:0, esp
.text:6B2BC870 010                 push    [ebp+dwDispatcherContext]
.text:6B2BC873 014                 push    [ebp+PCONTEXT]
.text:6B2BC876 018                 push    [ebp+SEH]
.text:6B2BC879 01C                 push    [ebp+PEXCEPTION_RECORD]
.text:6B2BC87C 020                 mov     ecx, [ebp+lpfnSEHHandler]
.text:6B2BC87F 020                 call    ecx             ; // 调用该节点的SEH处理函数
.text:6B2BC881 008                 mov     esp, large fs:0 ; // 利用FS:[0]维持ESP平衡
.text:6B2BC888 008                 pop     large dword ptr fs:0 ; // 恢复原FS:[0]
.text:6B2BC88F 004                 mov     esp, ebp
.text:6B2BC891 004                 pop     ebp
.text:6B2BC892 000                 retn    14h
.text:6B2BC892     CallNodeSEHFunc endp
.text:6B2BC8BC     ; // =============== S U B R O U T I N E =======================================
.text:6B2BC8BC
.text:6B2BC8BC
.text:6B2BC8BC     TempSEHFunc     proc near               ; // DATA XREF: RtlpExecuteHandlerForUnwind(x,x,x,x,x)o
.text:6B2BC8BC
.text:6B2BC8BC     ExceptionRecord = dword ptr  4
.text:6B2BC8BC     lpSEH           = dword ptr  8
.text:6B2BC8BC     lpDispatcherContext= dword ptr  10h
.text:6B2BC8BC
.text:6B2BC8BC 000                 mov     ecx, [esp+ExceptionRecord]
.text:6B2BC8C0 000                 test    [ecx+EXCEPTION_RECORD.ExceptionFlags], 6 ; // 展开 or 退出展开
.text:6B2BC8C7 000                 mov     eax, ExceptionContinueSearch
.text:6B2BC8CC 000                 jz      short ret
.text:6B2BC8CE 000                 mov     ecx, [esp+lpSEH]
.text:6B2BC8D2 000                 mov     edx, [esp+lpDispatcherContext]
.text:6B2BC8D6 000                 mov     eax, [ecx+EXCEPTION_RECORD.ExceptionRecord]
.text:6B2BC8D9 000                 mov     [edx], eax
.text:6B2BC8DB 000                 mov     eax, ExceptionCollidedUnwind
.text:6B2BC8E0
.text:6B2BC8E0     ret:                                    ; // CODE XREF: TempSEHFunc+10j
.text:6B2BC8E0 000                 retn    10h
.text:6B2BC8E0     TempSEHFunc     endp

_RtlpUnlinkHandler@4:

.text:6B2BC8E3     ; // =============== S U B R O U T I N E =======================================
.text:6B2BC8E3
.text:6B2BC8E3
.text:6B2BC8E3     ; // __stdcall RtlpUnlinkHandler(x)
.text:6B2BC8E3     _RtlpUnlinkHandler@4 proc near          ; // CODE XREF: RtlUnwind+107p
.text:6B2BC8E3                                             ; // sub_6B358216+34p
.text:6B2BC8E3
.text:6B2BC8E3     SEH             = dword ptr  4
.text:6B2BC8E3
.text:6B2BC8E3 000                 mov     ecx, [esp+SEH]
.text:6B2BC8E7 000                 mov     ecx, [ecx]
.text:6B2BC8E9 000                 mov     large fs:0, ecx
.text:6B2BC8F0 000                 retn    4
.text:6B2BC8F0     _RtlpUnlinkHandler@4 endp

ZwContinue:

.text:6B2BA920     ; =============== S U B R O U T I N E =======================================
.text:6B2BA920
.text:6B2BA920
.text:6B2BA920                     public ZwContinue
.text:6B2BA920     ZwContinue      proc near               ; CODE XREF: KiUserApcDispatcher+35p
.text:6B2BA920                                             ; KiUserExceptionDispatcher+18p ...
.text:6B2BA920 000                 mov     eax, 42h        ; NtContinue(模拟中断后返回,可切上下文)
.text:6B2BA925 000                 call    large dword ptr fs:0C0h  ;快速陷入内核的又一种姿势
.text:6B2BA92C 000                 retn    8
.text:6B2BA92C     ZwContinue      endp
.text:6B2BA92C

剩余的函数还有RtlIsValidHandler没有分析,该函数会校验传入的SEH异常处理函数是否有效。由于最初SEH并不进行这种校验,使得SEH容易成为溢出攻击的跳板。因此,从Vista系统开始,微软启用了SafeSEH机制,我们将在后续文章中对该机制加以分析。

逆向分析NtDLL.dll!RtlUnwind函数 引言:RtlUnwind是Kernel32.dll!_except_handler3中的全局展开函数。7C957A40 ; int __stdcall RtlUnwind(PVOID TargetFrame, PVOID TargetIp, 7C957A40 PEXCEPTION_RECORD ExceptionRecord, PVOID ReturnValue)7C957A 阅读详情

相关推荐

易语言-利用 RtlWalkFrameChain 回溯调用堆栈

学习到的知识: 通过RtlWalkFrameChain 回溯调用堆栈 通过NtQueryVirtualMemory 查询模块句柄与模块名

Windows异常世界历险记(一)——Windows系统用户级结构化异常处理机制(SEH)的基础知识和Unwind展开操作

Windows系统的用户级结构化异常处理机制(SEH)是基于线程的,因此可为不同的线程指派不同的异常处理函数。且由于其形成了链结构,还可据此为一个线程指定多个异常处理函数,异常发生后,操作系统会沿此链表调用各异常处理函数,直到某个异常处理函数修复了该异常或已达末尾。通常处理完异常后,需要执行展开(Unwind)操作,该操作先通知目标结点前的各异常处理函数释放资源,然后将之前的SEH链全部删除。

LPWSTR的博客 1936

从代码到实践:手把手教你用EmbodiedOcc++实现高效3D占用预测(含ScanNet配置)

本文提供了EmbodiedOcc++框架在ScanNet数据集上实现高效3D占用预测的完整实践指南。从环境搭建、数据预处理入手,详细解析了其核心创新点——几何引导优化(GRM)与语义感知不确定性采样(SUS)的代码实现与配置,并分享了训练、评估及调参的实战技巧,帮助开发者快速掌握这一先进的3D感知技术。

terraform7cloud的博客 589

获取调用栈信息的函数RtlCaptureStackBackTrace()

这是一个导出函数,使用时必须试探页面产生异常的能力(probe the ability to take page fault)—(翻译 ddk文档)。USHORT //返回捕捉到的结构数 RtlCaptureStackBackTrace( __in ULONG FramesToSkip, __in ULONG FramesToCapture, __out_ecount

pureman_mega的博客 5406

Ubuntu技巧-Ubuntu远程访问之SSH反向代理

💡大家好,我是可夫小子,《小白玩转ChatGPT》专栏作者,关注AIGC、互联网和自媒体。要把内网的一台的Ubuntu服务器让外面也能访问,总共有三种可行的方案,前面已经介绍了两种方式,今天就来介绍更健壮稳定的方式——ssh反向代理。大概的框架图如下,今天就从服务器购买到设置,手把手的教程,让你花费100块以内,就拥有一个强大的「云服务器」。

可夫小子的频道 1153

立创开源4G+GPS定位系统:基于微信小程序的低成本低功耗追踪方案

本文介绍了一个基于立创开源平台的超小型4G+GPS定位系统方案。该方案采用28mm*28mm主板,集成4G Cat.1通信与GPS定位,通过微信小程序“点灯专家”实现远程追踪管理,具备低成本、低功耗、易于部署的特点,适用于电动车、宠物、资产追踪等多种场景。

weixin_28895791的博客 396

Win32 结构化异常处理(SEH)探秘【中篇】

通常,展开操作导致堆栈上处理异常的帧以下的堆栈区域上的所有内容都被移除了,就好像我们从来没有调用过这些函数一样。展开的另外一个效果就是 EXCEPTION_REGISTRATION 结构链表上处理异常的那个结构之前的所有 EXCEPTION_REGISTRATION 结构都被移除了。这很好理解,因为这些 EXCEPTION_REGISTRATION 结构通常都被创建在堆栈上...

weixin_34015860的博客 176

Open3D 基于法线的双边滤波

基于法线的双边滤波是一种用于点云平滑的技术,它结合了点与其邻域点之间的几何距离和法线方向的相似性,从而在去除噪声的同时保留几何特征。这种滤波方法广泛应用于点云处理领域,尤其是在需要保留表面细节和边缘的场景中。

纵马踏花向自由 2500

获取当前函数的调用栈函数RtlCaptureStackBackTrace

平日里用VS开发工具在调时在Debug下有一个选项Call Stack快捷键是Alt+7可以看出函数调用的来龙去脉,原来有一个这样原函数存在。 RtlCaptureStackBackTrace The RtlCaptureStackBackTrace routine captures a stack back trace by walking up the stack and re

多媒体编程、网络编程、系统编程、网络安全编程、驱动编程 5261

基于STM32LXXX的模数转换芯片ADC(CMS24AD2001)驱动C程序设计

/ 电压 = (原始值 / 2^23) * Vref (注意:24位有符号数范围为 -2^23 到 2^23-1)//---------------- 硬件端口映射 (请根据实际电路修改) ----------------ADC_SCLK_HIGH();- PGA 放大倍数可选:1、2、4、8、16、32、64、128、256;//---------------- 宏定义操作 -------------------//---------------- 函数声明 -------------------

myron6688的博客 161

利用大模型和聚类算法找出 Excel 文件中重复或相似度高的数据,并使用 FastAPI 进行封装的详细方案

tokenizer将输入的文本列表texts转换为 PyTorch 张量,同时进行填充和截断操作,以确保所有输入具有相同的长度。对输入进行推理,得到模型的输出。对模型的最后一层隐藏状态求平均,得到每个文本的向量表示。.numpy()将 PyTorch 张量转换为 NumPy 数组。使用DBSCAN算法对文本向量进行聚类。eps是邻域半径,是形成核心点所需的最小样本数。fit方法对输入的向量进行聚类,并返回每个样本所属的簇标签。try:定义了一个 POST 请求的 API 路由,路径为。

AI的内部世界的博客 1069

0029__获取当前函数的调用栈函数RtlCaptureStackBackTrace

获取当前函数的调用栈函数RtlCaptureStackBackTrace_cosmoslife的博客-优快云博客RtlCaptureStackBackTrace 函数 (winnt.h) - Win32 apps | Microsoft Learn

行潇 256

【数据分析八步法】将模糊需求转化为可验证的分析问题

国内访问Huggingface官方源常遇网络问题,可通过切换国内镜像提升下载速度。三种配置方式:1)设置HF_ENDPOINT环境变量;2)在from_pretrained中直接指定镜像参数;3)修改本地Huggingface配置文件。常用镜像包括hf-mirror.com、阿里云ModelScope和清华镜像源。注意镜像同步延迟、私有模型限制,建议配合huggingface-cli工具使用,必要时取消代理设置。合理配置镜像源可显著改善Huggingface生态使用体验。

Mr数据杨 858

Win32 SEH异常深度探索_8 异常处理是如何开始的

If youve made it this far, it wouldnt be fair to finish without completing the entire circuit. Ive shown how the operating system calls a user-defined function when an exception occurs. Ive shown

liwei8703的专栏 1064

使用 Python 爬虫抓取 IMDb 电影信息 - 评分、评论与基本信息抓取教程

本文介绍了如何使用 Python 爬虫技术抓取 IMDb 电影评分、评论和基本信息。我们分别展示了通过IMDb API和两种方法抓取数据,并进行了数据存储、分析与可视化。在未来,我们还可以基于这些数据开发电影推荐系统、情感分析工具等,进一步丰富电影数据的应用场景。通过这些实践,您不仅学会了如何抓取和存储数据,还可以通过这些基础来构建自己的数据分析应用,获取更丰富的电影数据。

2201_76125261的博客 601

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

SEH 进阶(2)

上一篇我们自己手动了解了编译器SHE 的机构,下面我们来学习一下其结构体定义以及如何借用这样的结构构建一个完整的SHE 结构。 首先我们来看加入系统的SHE 链表的结构: typedef struct _EXTEND_EXCEPTION_RECORD_ { //Stdandard ESP in frame; //PEXCEPTIO

商少 783

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

深入解析结构化异常处理(SEH) - by Matt Pietrek

尽管以前写过一篇SEH相关的文章《关于SEH的简单总结》, 但那真的只是皮毛,一直对Windows异常处理的原理似懂非懂, 看了下面的文章 ,一切都豁然开朗. 1997年文章,Windows技术的根一直没变:http://www.microsoft.com/msj/0197/exception/exception.aspx MattPietrek著 董岩译 在...

mmmsss987的博客 462

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

深入解析结构化异常处理(SEH)

深入解析结构化异常处理(SEH) 一直对Windows异常处理的原理似懂非懂, 看了下面的文章 ,一切都豁然开朗. 1997年文章,Windows技术的根一直没变: http://www.microsoft.com/msj/0197/exception/exception.aspxMatt Pietrek 著 董岩 译在Win32操作系统提供的所有功能中,使用最广泛而又没有公开的恐怕要数结构

大神之路漫漫,吾将连滚带爬 3660

api-ms-win-core-registry-l1-1-0.dll文件丢失缺少找不到的一些问题

其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个api-ms-win-core-registry-l1-1-0.dll文件(挑选合适的版本文件)把它放入到程序或系统目录中,当我们执行某一个.exe程序时,相应的DLL文件就会被调用,因此将缺失的文件放回到原目录之后就能打开你的软件或游戏了.

2301_76755223的博客 669

【逆向工程核心原理:SEH】

reverse

qq_42363151的博客 1824

[原创]windows-SEH详解

SEH是window操作系统默认的异常处理机制,逆向分析中,SEH除了基本的异常处理功能外,还大量用于反调试程序(这里SEH时保存在栈中的,漏洞利用的时候会用到) 1.SEH SEH是windows操作系统异常处理机制,在程序源代码中使用__try,__except,__finally关键字来具体实现。 2.OS异常处理的办法 2.1正常运行时候的异常处理方法 进程运行过程中若发生异常,OS会委托进程进行处理。若进程代码中存在具体的异常处理(如SEH异常处理器)...

whl0071的专栏 4835

windows核心编程--SEH(结构异常处理)

SEH 的工作原理。         Windows 程序设计中最重要的理念就是消息传递,事件驱动。当GUI应用程序触发一个消息时,系统将把该消息放入消息队列,然后去查找并调用窗体的消息处理函数(CALLBACK),传递的参数当然就是这个消息。我们同样可以把异常也当作是一种消息,应用程序发生异常时就触发了该消息并告知系统。系统接收后同样会找它的“回调函数”,也就是我们的异常处理例程。当然,如果我们

Mobidogs Blog 1920

Windows x64为动态代码添加unwind信息

Windows x64为动态代码添加unwind信息0x000x01 0x00 64位程序的堆栈回溯与32位差别很大,调试器和 RtlCaptureStackBackTrace 无法再使用 ebp 链来遍历整个堆栈,而只能使用 prolog信息来回溯堆栈。正常的C/C++函数的prolog信息在编译时候生成并记录在PE64文件的unwind信息中,另外当前函数对应的异常处理函数也添加在unwind信息的尾部。所以对于不存在unwind信息的函数,异常处理流程无法找到其异常处理函数,也很可能无法正常展开堆栈找

weixin_43787608的博客 1308

vs2010 C++性能调优笔记

工具:very sleep,VS2010 Debug都为程序加了什么额外的东西,通过very sleepke

boyhailong的专栏 1463

逆向基础-Windows异常

Windows异常机制

AppWhite_Star的博客 1251

Windows 系统编程初探 (四)结构化异常处理之一:SEH的基本原理与进程相关异常处理

        上面的内容只是一些基础知识,虽然简单,但有必要了解一下。现在,我将正式开始我的第一个专题:结构化异常处理(SEH)。SEH 是 Windows 系统提供的功能,跟开发工具无关。值得一提的是,VC 将 SEH 进行了封装,也就是我们平常用到的 __try{}__except(){} 和 __try{}__finally{},我没有研究过它的实现方法,这里也不进行讨论,而我将要讲述的是

xlt123的专栏 2239
上一篇: Windows异常世界历险记(一)——Windows系统用户级结构化异常处理机制(SEH)的基础知识和Unwind展开操作
下一篇: Sublime Text 3126 Win32版本暴力破解过程
LPWSTR
博客等级 码龄10年 73粉丝 47原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值