0xC0000094: Integer division by zero

C++ 代码:

#include "stdafx.h"
#include <windows.h>


ULONG WINAPI FilterFunc(DWORD dwExceptionCode)
{
	return (dwExceptionCode == STATUS_INTEGER_DIVIDE_BY_ZERO) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
}

int add1(int a,int b)
{
	int c = 0,d=0;
	__try
	{
		printf("a address=%p\n",&a);
		__try
		{
			printf("b address=%p\n", &b);
			c = a / b;
		}
		__except (FilterFunc(GetExceptionCode()))
		{
			printf("c address=%p\n", &c);
			c = a / (a + b);
		}

	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		printf("d address=%p\n", &d);
		c = c + 1;
	}

	return c;
}

#include <stdio.h> 
#include <Windows.h> 
int main()
{
	DebugBreak();
	add1(0,0);
	return 0;
}

百度云代码:SEH_Sample.zip



1 X64 的基本概念和结构体

相比于X86 在程序运行中动态构建SEH结构,X64-SEH 是静态的,其信息包含在PE文件中。下面我们首先看一下对应的结构,然后看看其提供的信息是否能够满足异常捕获以及异常处理的功能。

为异常处理和调试器支持展开数据


执行异常处理和调试支持所需的数据结构

RUNTIME_FUNCTION
typedef struct _RUNTIME_FUNCTION_ { 
    DWORD BeginAddress;                             // 函数起始地址 
    DWORD EndAddress;                                // 函数结束地址 
    DWORD UnwindInfoAddress;               // 展开信息地址看下面的_UNWIND_INFO 结构体    
}RUNTIME_FUNCTION , *_RUNTIME_FUNCTION ;
该结构在内存中必须为DWORD 对齐。所有的地址都是ImageBase 的 RVA 值。这些项已经经过排序了(按照BeginAddress升序排列),放置在PE32+ 的 .pdata节中。对于动态生成的函数,我们暂时不介绍。
UNWIND_INFO
// 
// Define unwind information flags. 
//
#define UNW_FLAG_NHANDLER 0x0 
#define UNW_FLAG_EHANDLER 0x1 
#define UNW_FLAG_UHANDLER 0x2 
#define UNW_FLAG_CHAININFO 0x4
上面四个标志依次代表:
既没有EXCEPT_FILTER也没有EXCEPT_HANDLER
有EXCEPT_FILTER 和 EXCEPT_HANDLER
有 FINALLY_HANDLER
有多个UNWIND_INFO 串联在一起,
typedef struct _UNWIND_INFO { 
    UCHAR Version : 3;                     // 版本信息,当前为1 
    UCHAR Flags : 5;                          // 对应上面的四个标志 
    UCHAR SizeOfProlog;                 // Prolog 的大小,单位是字节 
    UCHAR CountOfCodes;              // UNWIND_INFO 包含多少UNWIND_CODE结构 
    UCHAR FrameRegister : 4;   
    UCHAR FrameOffset : 4;  
    UNWIND_CODE UnwindCode[1];
// 
// unwind codes 数组后面是一个可选的DWROD 对齐的成员。此成员有两种可能,异常处理函数地址或者function table entry(flags中指定了UNW_FALGS_CHAININFO),如果是异常处理函数地址的话,它将为一个语言相关的异常处理数据 
// 
  union { 
      struct { //下面两个组成一个结构体,不是联合体,看清楚
          ULONG ExceptionHandler; 
          ULONG ExceptionData[]; 
      }; 
 
      RUNTIME_FUNCTION FunctionEntry; //如果上面的Flags指定的是 UNW_FLAG_CHAININFO,该联合体为 RUNTIME_FUNCTION
  }; 

} UNWIND_INFO, *PUNWIND_INFO;
UNWIND_INFO 结构体必须是DWORD 对齐的。
FrameRegister
如果不是0 的话,这个函数使用了帧指针,该值表示帧指针使用的非易失性寄存器的数目。与UNWIND_CODE中的成员使用相同的编码。
FrameOffset
如果FrameRegister 不为0,表示在刚建立栈帧时,应用于FP 寄存器的RSP 的缩放偏移量。实际的FP 为RSP+16*当前值,范围是0~240。这样允许将FP 寄存器指向本地动态栈帧的中间位置,然后通过更短的指令来提高代码密度(更多的 指令将可以使用8位带符号偏移形式)。
Unwind code 数组
这一系列的code 代表了prolog 中对于非易失性寄存器和RSP 寄存器的影响。由于对齐, 这个数组始终有偶数个,最后的一个成员可能未被使用。
Exception Handler
ExceptionHandler 该域为一个RVA,指向exception handler的地址,ExceptionData 指向一个类似于scopetable 的地址
如果Flags指定的是 UNW_FLAG_CHAININFO,该域为 RUNTIME_FUNCTION
UNWIND_CODE
typedef enum _UNWIND_OP_CODES {  
    UWOP_PUSH_NONVOL = 0, /* info == register number */  
    UWOP_ALLOC_LARGE,     /* no info, alloc size in next 2 slots */  
    UWOP_ALLOC_SMALL,     /* info == size of allocation / 8 - 1 */  
    UWOP_SET_FPREG,       /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */  
    UWOP_SAVE_NONVOL,     /* info == register number, offset in next slot */  
    UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */  
    UWOP_SAVE_XMM128,     /* info == XMM reg number, offset in next slot */  
    UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */  
    UWOP_PUSH_MACHFRAME   /* info == 0: no error-code, 1: error-code */  
} UNWIND_CODE_OPS;  
  
typedef union _UNWIND_CODE {  
    struct {  
        UBYTE CodeOffset;  
        UBYTE UnwindOp : 4;  
        UBYTE OpInfo   : 4;  
    };  
    USHORT FrameOffset;  
} UNWIND_CODE, *PUNWIND_CODE;  
UNWIND_CODE结构体用来记录在proglog 中影响非易失性寄存器和RSP寄存器的序列。每个UNWIND_CODE有上述结构。其中,分别表示本操作在prolog中的offset,Unwind操作码,操作信息。该数组的排列按照prolog中的offset的降序排列。
 
 有些展开操作代码需要本地栈帧的一个无符号偏移。这个偏移是相对于固定栈申请而言的。如果UNWIND_INFO的Frame Register 成员为0,offset 是对RSP而言的,否则,offset 是相对于在建立栈帧的时候RSP 被存储的的地方。此时需要栈帧-栈帧寄存器的偏移(16*缩放帧寄存器在UNWIND_INFO中的偏移)。如果FP 寄存器被使用,所有使用offset的unwind code必须在prolog建立栈帧之后才可以使用。


2  查找add1函数的RUNTIME_FUNCTION

 使用windbg 

0:000> .fnent ConsoleApplication3!add1
Debugger function entry 00000000`003a0fc0 for:
d:\work\temp\consoleapplication3\consoleapplication3.cpp(14)
(00000001`3f501030)   ConsoleApplication3!add1   |  (00000001`3f5010e0)   ConsoleApplication3!main
Exact matches:
    ConsoleApplication3!add1 (int, int)


BeginAddress      = 00000000`00001030
EndAddress        = 00000000`000010d4
UnwindInfoAddress = 00000000`00002670


Unwind info at 00000001`3f502670, 10 bytes
  version 1, flags 1, prolog c, codes 1
  handler routine: ConsoleApplication3!_C_specific_handler (00000001`3f501e10), data 2
  00: offs c, unwind op 2, op info 8	UWOP_ALLOC_SMALL.

根据上面的信息,结合Study_PE+,从.pdata section中找到对应的信息




2 .pdada Section中寻找add1 UNW_INFO的信息

我们知道add1的UNW_INFO 的RVA是2670,所以我们使用PE工具得到FOA




3 add1的 UNW_INFO 解析

000000001A70: 09 0C 01 00

09的二进制00001 001 ,Flags:5  = 00001 ,Version:3 = 001 也就是说版本是1 。Flags是1,也就是#define UNW_FLAG_EHANDLER 0x1 。

0C 也就是prelog 占用的大小为0c , 可以用windbg看下,

     

00000001`3f501030 89542410        mov     dword ptr [rsp+10h],edx
00000001`3f501034 894c2408        mov     dword ptr [rsp+8],ecx
00000001`3f501038 4883ec48        sub     rsp,48h
prelog占用C大小

01  仅仅包含一个UNWIND_INFO 结构。需要注意的是unwind_info数组通常都是偶数出现,为了对齐,另外unwind_info是2字节的union联合体,所以这个例子应该占用4个字节,后两个字节用00 00 对齐


00  

    UCHAR FrameRegister : 4;   
    UCHAR FrameOffset : 4;  


000000001A74: 0C 82 00 00 

对应的是UNWIND_CODE结构,可以参考https://msdn.microsoft.com/zh-cn/library/ck9asaa9.aspx

CodeOffset为0C,即偏移为0C

UnwinOp为2 ,即UWOP_ALLOC_SMALL

OpInfo为8,即申请大小为(08+1)*8 = 72 =0h48


可以反汇编验证一下

0:000> u ConsoleApplication3!add1
ConsoleApplication3!add1 [d:\work\temp\consoleapplication3\consoleapplication3.cpp @ 14]:
00000001`3fc71030 89542410        mov     dword ptr [rsp+10h],edx
00000001`3fc71034 894c2408        mov     dword ptr [rsp+8],ecx
00000001`3fc71038 4883ec48        sub     rsp,48h

 此外我们也验证一下main函数的

:000> .fnent ConsoleApplication3!main
Debugger function entry 00000000`003a5140 for:
d:\work\temp\consoleapplication3\consoleapplication3.cpp(43)
(00000001`3fc710e0)   ConsoleApplication3!main   |  (00000001`3fc71100)   ConsoleApplication3!__vcrt_va_start_verify_argument_type<char const * __ptr64 const>
Exact matches:
    ConsoleApplication3!main (void)

BeginAddress      = 00000000`000010e0
EndAddress        = 00000000`000010fa
UnwindInfoAddress = 00000000`000026a8

Unwind info at 00000001`3fc726a8, 6 bytes
  version 1, flags 0, prolog 4, codes 1
  00: offs 4, unwind op 2, op info 4	UWOP_ALLOC_SMALL.//栈空间大小为(4+1)*8 =40 =0h28

0:000> u ConsoleApplication3!main
ConsoleApplication3!main [d:\work\temp\consoleapplication3\consoleapplication3.cpp @ 43]:
00000001`3fc710e0 4883ec28        sub     rsp,28h//栈空间大小为0h28
00000001`3fc710e4 ff15160f0000    call    qword ptr [ConsoleApplication3!_imp_DebugBreak (00000001`3fc72000)]
00000001`3fc710ea 33d2            xor     edx,edx
00000001`3fc710ec 33c9            xor     ecx,ecx
00000001`3fc710ee e83dffffff      call    ConsoleApplication3!add1 (00000001`3fc71030)
00000001`3fc710f3 33c0            xor     eax,eax
00000001`3fc710f5 4883c428        add     rsp,28h 
00000001`3fc710f9 c3              ret


剩下的00 00是为了对齐


unwind_info 和unwind_code主要为了发生异常的时候,SEH可以根据这个信息进行栈回滚。

   这样在x64 中,MSC 为几乎所有的函数都登记了完备的信息,用来在展开过程中完整的回滚函数所做的栈、寄存器操作。登记的信息包括:
       函数是否使用了 SEH、
       函数使用的是什么组合的 SEH(__try/__except?__try/__finally?)、
       函数申请了多少栈空间、
       函数保存了哪些寄存器、
       函数是否建立了栈帧,
       等等,
       同时也记录了这些操作的顺序(以保证回滚的时候不会乱套)。



000000001A78: 10 1E 00 00

这里是一个异常处理函数的RVA,我们可用windbg断下看下。

步骤 

  

0:000> lmvm ConsoleApplication3
Browse full module list
start             end                 module name
00000001`3fc70000 00000001`3fc78000   ConsoleApplication3 C (private pdb symbols)  D:\work\temp\ConsoleApplication3\x64\Release\ConsoleApplication3.pdb
    Loaded symbol image file: D:\work\temp\ConsoleApplication3\x64\Release\ConsoleApplication3.exe
    Image path: D:\work\temp\ConsoleApplication3\x64\Release\ConsoleApplication3.exe
    Image name: ConsoleApplication3.exe
    Browse all global symbols  functions  data
    Timestamp:        Thu Dec 07 23:40:44 2017 (5A2960FC)
    CheckSum:         00000000
    ImageSize:        00008000
    Translations:     0000.04b0 0000.04e4 0409.04b0 0409.04e4

 对RVA异常函数加断点

0:000> bp 00000001`3fc70000 + 1e10
当发生异常的时候就会断下

0:000> g
(1458.210c): Integer divide-by-zero - code c0000094 (first chance)//这是DIV 0的第一次机会
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
ConsoleApplication3!add1+0x44:
00000001`3fc71074 f77c2458        idiv    eax,dword ptr [rsp+58h] ss:00000000`002dfa18=00000000
0:000> g
Breakpoint 0 hit //断点断下
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows\SYSTEM32\ntdll.dll - 
ConsoleApplication3!_C_specific_handler:
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows\system32\VCRUNTIME140.dll - 
00000001`3fc71e10 ff257a020000    jmp     qword ptr [ConsoleApplication3!_imp___C_specific_handler (00000001`3fc72090)] ds:00000001`3fc72090={VCRUNTIME140!_C_specific_handler (000007fe`f076bff0)}

0:000> kn
 # Child-SP          RetAddr           Call Site
00 00000000`002deba8 00000000`77c9812d ConsoleApplication3!_C_specific_handler
01 00000000`002debb0 00000000`77c8855f ntdll!RtlDecodePointer+0xad
02 00000000`002debe0 00000000`77cbbcb8 ntdll!RtlUnwindEx+0xbbf
03 00000000`002df2c0 00000001`3fc71074 ntdll!KiUserExceptionDispatcher+0x2e
04 00000000`002df9c0 00000001`3fc710f3 ConsoleApplication3!add1+0x44 [d:\work\temp\consoleapplication3\consoleapplication3.cpp @ 22]
05 00000000`002dfa10 00000001`3fc71409 ConsoleApplication3!main+0x13 [d:\work\temp\consoleapplication3\consoleapplication3.cpp @ 46]
06 (Inline Function) --------`-------- ConsoleApplication3!invoke_main+0x22 [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 64]
07 00000000`002dfa40 00000000`77a659cd ConsoleApplication3!__scrt_common_main_seh+0x11d [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 253]
08 00000000`002dfa80 00000000`77c9a561 kernel32!BaseThreadInitThunk+0xd
09 00000000`002dfab0 00000000`00000000 ntdll!RtlUserThreadStart+0x21

__C_specific_handler function

Called by the compiler to implement structured exception handling extensions.

The relative address of the language specific handler is present in the UNWIND_INFO whenever flags UNW_FLAG_EHANDLER or UNW_FLAG_UHANDLER are set. The language specific handler is called as part of the search for an exception handler or as part of an unwind. For more information see Language Specific Handler.

Syntax

C++
_CRTIMP  __C_specific_handler(
  _In_    struct _EXCEPTION_RECORD   *ExceptionRecord,
  _In_    void                       *EstablisherFrame,
  _Inout_ struct _CONTEXT            *ContextRecord,
  _Inout_ struct _DISPATCHER_CONTEXT *DispatcherContext
);

0:000> r
rax=0000000000000000 rbx=00000000002e0000 rcx=00000000002df7b0
rdx=00000000002df9c0 rsi=000000013fc7400c rdi=00000000002dd000
rip=000000013fc71e10 rsp=00000000002deba8 rbp=00000000002df9c0
 r8=00000000002df2c0  r9=00000000002dec70 r10=000000013fc71e10
r11=000000013fc74000 r12=000000013fc71074 r13=0000000000000000
r14=00000000002df7b0 r15=000000013fc70000
iopl=0         nv up ei pl nz na pe nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
ConsoleApplication3!_C_specific_handler:
00000001`3fc71e10 ff257a020000    jmp     qword ptr [ConsoleApplication3!_imp___C_specific_handler (00000001`3fc72090)] ds:00000001`3fc72090={VCRUNTIME140!_C_specific_handler (000007fe`f076bff0)}
0:000> dt 00000000002df7b0 _EXCEPTION_RECORD
ConsoleApplication3!_EXCEPTION_RECORD
   +0x000 ExceptionCode    : 0xc0000094
   +0x004 ExceptionFlags   : 0
   +0x008 ExceptionRecord  : (null) 
   +0x010 ExceptionAddress : 0x00000001`3fc71074 Void //异常地址
   +0x018 NumberParameters : 0
   +0x020 ExceptionInformation : [15] 0x3fb95b
0:000> !error 0xc0000094  //好像没有显示成功,查找网络 

0xC0000094: Integer division by zero

Error code: (NTSTATUS) 0xc0000094 (3221225620) - {
0:000> u 0x00000001`3fc71074
ConsoleApplication3!add1+0x44 [d:\work\temp\consoleapplication3\consoleapplication3.cpp @ 22]:
00000001`3fc71074 f77c2458        idiv    eax,dword ptr [rsp+58h] //这里处理问题
00000001`3fc71078 89442420        mov     dword ptr [rsp+20h],eax
00000001`3fc7107c eb30            jmp     ConsoleApplication3!add1+0x7e (00000001`3fc710ae)
00000001`3fc7107e 488d542420      lea     rdx,[rsp+20h]
00000001`3fc71083 488d0da6110000  lea     rcx,[ConsoleApplication3!GS_ExceptionPointers+0x30 (00000001`3fc72230)]
00000001`3fc7108a e8e1000000      call    ConsoleApplication3!printf (00000001`3fc71170)
00000001`3fc7108f 8b442458        mov     eax,dword ptr [rsp+58h]
00000001`3fc71093 8b4c2450        mov     ecx,dword ptr [rsp+50h]


此时我们需要知道此时的寄存器等信息,这个保存在了第三个参数(X64 参数顺序 rcx rdx r8 r9),

0:000> dt _CONTEXT @r8
ConsoleApplication3!_CONTEXT
   +0x000 P1Home           : 0x2df7b0
   +0x008 P2Home           : 0x2df2c0
   +0x010 P3Home           : 0
   +0x018 P4Home           : 0
   +0x020 P5Home           : 0x000007fe`f3ac55f0
   +0x028 P6Home           : 0x3fb940
   +0x030 ContextFlags     : 0x10005f
   +0x034 MxCsr            : 0x1f80
   +0x038 SegCs            : 0x33
   +0x03a SegDs            : 0x2b
   +0x03c SegEs            : 0x2b
   +0x03e SegFs            : 0x53
   +0x040 SegGs            : 0x2b
   +0x042 SegSs            : 0x2b
   +0x044 EFlags           : 0x10206
   +0x048 Dr0              : 0
   +0x050 Dr1              : 0
   +0x058 Dr2              : 0
   +0x060 Dr3              : 0
   +0x068 Dr6              : 0
   +0x070 Dr7              : 0
   +0x078 Rax              : 0
   +0x080 Rcx              : 0x000007fe`f3ac4198
   +0x088 Rdx              : 0
   +0x090 Rbx              : 0x000007fe`f3ac59f4
   +0x098 Rsp              : 0x2df9c0
   +0x0a0 Rbp              : 0
   +0x0a8 Rsi              : 0
   +0x0b0 Rdi              : 0x000007fe`f3ac5a10
   +0x0b8 R8               : 0x2ddc98
   +0x0c0 R9               : 0x3fb95b
   +0x0c8 R10              : 0
   +0x0d0 R11              : 0x2df890
   +0x0d8 R12              : 0
   +0x0e0 R13              : 0
   +0x0e8 R14              : 0
   +0x0f0 R15              : 0
   +0x0f8 Rip              : 0x00000001`3fc71074
   +0x100 FltSave          : _XSAVE_FORMAT
   +0x100 Header           : [2] _M128A
   +0x120 Legacy           : [8] _M128A
   +0x1a0 Xmm0             : _M128A
   +0x1b0 Xmm1             : _M128A
   +0x1c0 Xmm2             : _M128A
   +0x1d0 Xmm3             : _M128A
   +0x1e0 Xmm4             : _M128A
   +0x1f0 Xmm5             : _M128A
   +0x200 Xmm6             : _M128A
   +0x210 Xmm7             : _M128A
   +0x220 Xmm8             : _M128A
   +0x230 Xmm9             : _M128A
   +0x240 Xmm10            : _M128A
   +0x250 Xmm11            : _M128A
   +0x260 Xmm12            : _M128A
   +0x270 Xmm13            : _M128A
   +0x280 Xmm14            : _M128A
   +0x290 Xmm15            : _M128A
   +0x300 VectorRegister   : [26] _M128A
   +0x4a0 VectorControl    : 0x00000020`00001000
   +0x4a8 DebugControl     : 0x1000000
   +0x4b0 LastBranchToRip  : 0
   +0x4b8 LastBranchFromRip : 0
   +0x4c0 LastExceptionToRip : 0
   +0x4c8 LastExceptionFromRip : 0

+0x078 Rax              : 0
   +0x098 Rsp              : 0x2df9c0

0:000> dq 0x2df9c0+58 L1
00000000`002dfa18  00000000`00000000


EAX/0 导致异常。


剩下的是SCOPE_TABLE结构

000000001A7C: 02 00 00 00

表示有两个ScopeRecord,剩下的数据是这两个ScopeRecord数据


000000001A80: 5E 10 00 00 7E 10 00 00 D0 1E 00 00 7E 10 00 00

第一个ScopeRecord 的对应关系






我们对Handler下断点

0:000> bp 00000001`3fc70000+ 1ed0
0:000> g
Breakpoint 1 hit
ConsoleApplication3!`add1'::`1'::filt$0:
00000001`3fc71ed0 4055            push    rbp
0:000> k
 # Child-SP          RetAddr           Call Site
00 00000000`002deb38 000007fe`f076c090 ConsoleApplication3!`add1'::`1'::filt$0 [d:\work\temp\consoleapplication3\consoleapplication3.cpp @ 24]
01 00000000`002deb40 00000000`77c9812d VCRUNTIME140!_C_specific_handler+0xa0
02 00000000`002debb0 00000000`77c8855f ntdll!RtlDecodePointer+0xad
03 00000000`002debe0 00000000`77cbbcb8 ntdll!RtlUnwindEx+0xbbf
04 00000000`002df2c0 00000001`3fc71074 ntdll!KiUserExceptionDispatcher+0x2e
05 00000000`002df9c0 00000001`3fc710f3 ConsoleApplication3!add1+0x44 [d:\work\temp\consoleapplication3\consoleapplication3.cpp @ 22]
06 00000000`002dfa10 00000001`3fc71409 ConsoleApplication3!main+0x13 [d:\work\temp\consoleapplication3\consoleapplication3.cpp @ 46]
07 (Inline Function) --------`-------- ConsoleApplication3!invoke_main+0x22 [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 64]
08 00000000`002dfa40 00000000`77a659cd ConsoleApplication3!__scrt_common_main_seh+0x11d [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 253]
09 00000000`002dfa80 00000000`77c9a561 kernel32!BaseThreadInitThunk+0xd
0a 00000000`002dfab0 00000000`00000000 ntdll!RtlUserThreadStart+0x21

发现会跳转到我们的处理函数中




000000001A90: 4C 10 00 00 B0 10 00 00 FB 1E 00 00 B0 10 00 00

第二个ScopeRecord可以同样分析。



C++对应的汇编

ConsoleApplication3!add1:
00000001`3fc71030 89542410        mov     dword ptr [rsp+10h],edx
00000001`3fc71034 894c2408        mov     dword ptr [rsp+8],ecx
00000001`3fc71038 4883ec48        sub     rsp,48h
00000001`3fc7103c c744242000000000 mov     dword ptr [rsp+20h],0
00000001`3fc71044 c744242c00000000 mov     dword ptr [rsp+2Ch],0
00000001`3fc7104c 488d542450      lea     rdx,[rsp+50h]
00000001`3fc71051 488d0db8110000  lea     rcx,[ConsoleApplication3!GS_ExceptionPointers+0x10 (00000001`3fc72210)]
00000001`3fc71058 e813010000      call    ConsoleApplication3!printf (00000001`3fc71170)
00000001`3fc7105d 90              nop
00000001`3fc7105e 488d542458      lea     rdx,[rsp+58h]
00000001`3fc71063 488d0db6110000  lea     rcx,[ConsoleApplication3!GS_ExceptionPointers+0x20 (00000001`3fc72220)]
00000001`3fc7106a e801010000      call    ConsoleApplication3!printf (00000001`3fc71170)
00000001`3fc7106f 8b442450        mov     eax,dword ptr [rsp+50h]
00000001`3fc71073 99              cdq
00000001`3fc71074 f77c2458        idiv    eax,dword ptr [rsp+58h]
00000001`3fc71078 89442420        mov     dword ptr [rsp+20h],eax
00000001`3fc7107c eb30            jmp     ConsoleApplication3!add1+0x7e (00000001`3fc710ae)
00000001`3fc7107e 488d542420      lea     rdx,[rsp+20h]
00000001`3fc71083 488d0da6110000  lea     rcx,[ConsoleApplication3!GS_ExceptionPointers+0x30 (00000001`3fc72230)]
00000001`3fc7108a e8e1000000      call    ConsoleApplication3!printf (00000001`3fc71170)
00000001`3fc7108f 8b442458        mov     eax,dword ptr [rsp+58h]
00000001`3fc71093 8b4c2450        mov     ecx,dword ptr [rsp+50h]
00000001`3fc71097 03c8            add     ecx,eax
00000001`3fc71099 8bc1            mov     eax,ecx
00000001`3fc7109b 89442428        mov     dword ptr [rsp+28h],eax
00000001`3fc7109f 8b442450        mov     eax,dword ptr [rsp+50h]
00000001`3fc710a3 99              cdq
00000001`3fc710a4 8b4c2428        mov     ecx,dword ptr [rsp+28h]
00000001`3fc710a8 f7f9            idiv    eax,ecx
00000001`3fc710aa 89442420        mov     dword ptr [rsp+20h],eax
00000001`3fc710ae eb1b            jmp     ConsoleApplication3!add1+0x9b (00000001`3fc710cb)
00000001`3fc710b0 488d54242c      lea     rdx,[rsp+2Ch]
00000001`3fc710b5 488d0d84110000  lea     rcx,[ConsoleApplication3!GS_ExceptionPointers+0x40 (00000001`3fc72240)]
00000001`3fc710bc e8af000000      call    ConsoleApplication3!printf (00000001`3fc71170)
00000001`3fc710c5 ffc0            inc     eax
00000001`3fc710c7 89442420        mov     dword ptr [rsp+20h],eax
00000001`3fc710cb 8b442420        mov     eax,dword ptr [rsp+20h]
00000001`3fc710cf 4883c448        add     rsp,48h
00000001`3fc710d3 c3              ret
00000001`3fc710d4 cc              int     3
00000001`3fc710d5 cc              int     3
00000001`3fc710d6 cc              int     3
00000001`3fc710d7 cc              int     3
00000001`3fc710d8 cc              int     3
00000001`3fc710d9 cc              int     3
00000001`3fc710da cc              int     3
00000001`3fc710db cc              int     3
00000001`3fc710dc cc              int     3
00000001`3fc710dd cc              int     3
00000001`3fc710de cc              int     3


扩展话题:

windbg 在X64的栈回溯,也是利用了RUNTIME_FUNCTION的信息

1 得到函数的任意RIP - EXE的基地址的地址差值RVA,然后遍历.pdata找到这个差值所在的RUNTIME_FUNCTION信息,得到本函数的栈空间大小,可以直接使用.fnent @rip

2 根据上面函数的返回地址替代RIP,重复上面的第一个步骤

3 重复上面所有步骤就可以得到X64的栈.








参考:

https://www.cnblogs.com/lanrenxinxin/p/4762858.html

http://blog.csdn.net/qq_18218335/article/details/72722320


后补:

C++代码解析SEH

http://blog.csdn.net/Shevacoming/article/details/7826527

猜你喜欢

转载自blog.csdn.net/xiaohua_de/article/details/78747449