- [原创]关于X64程序中RUNTIME_FUNCTION,UNWIND_INFO,UNWIND_CODE结构理解 2021-2-3 19:27 4012
X64程序会生成一个Pdata段,用于记录每个函数的栈帧和异常信息,结构如下:
struct RUNTIME_FUNCTION
{
void __ptr32 FunctionStart;
void ptr32 FunctionEnd;
void *ptr32 UnwindInfo;
};
FunctionStart起始位置(RVA)
FunctionEnd结束位置(RVA)
UnwindInfo信息描述(RVA)
本人ida反汇编中RUNTIME_FUNCTION的一条信息(以下就用这条信息做介绍)
RUNTIME_FUNCTION <rva sub_180006840, rva loc_180006874, rva stru_1801FADC8>
起始位置
text:0000000180006840 sub_180006840 proc near
.text:0000000180006840 4C 89 44 24 18 mov [rsp+arg_10], r8
.text:0000000180006845 4C 89 4C 24 20 mov [rsp+arg_18], r9
.text:000000018000684A 53 push rbx
.text:000000018000684B 55 push rbp
.text:000000018000684C 56 push rsi
.text:000000018000684D 48 83 EC 40 sub rsp, 40h
.text:0000000180006851 33 DB xor ebx, ebx
结束位置(这里结束并不是函数结束)
loc_180006874: ; DATA XREF:
.text:0000000180006874 ; .rdata:00000001801FADF0↓o ...
.text:0000000180006874 48 89 7C 24 38 mov [rsp+58h+var_20], rdi
信息描述数据
stru_1801FADC8 UNWIND_INFO <1, 11h, 4, 0>
1801FADCC 11 72 UNWIND_CODE <11h, 72h> ; UWOP_ALLOC_SMALL
1801FADCE 0D 60 UNWIND_CODE <0Dh, 60h> ; UWOP_PUSH_NONVOL
1801FADD0 0C 50 UNWIND_CODE <0Ch, 50h> ; UWOP_PUSH_NONVOL
1801FADD2 0B 30 UNWIND_CODE <0Bh, 30h> ; UWOP_PUSH_NONVOL
信息描述是一个UNWIND_INFO结构体,其中第三个参数4代表有四个UNWIND_CODE 数据,UNWIND_CODE可以理解成伪指令,用于描述这段代码对栈的操作,第一个UNWIND_CODE是在栈上分配内存(sub rsp,*),11代表起始位置偏移,也就是(180006840+11),地址0x180006851这条地址的上一条指令就是sub rsp,40h,第二个UNWIND_CODE的0D是可以理解成伪指令,对应的操作是push rsi,第三条OC代表push rbp,第四条 0B代表push rbx,所以X86没有像X86一样通过ebp来进行栈回溯,而是通过.pdata段中的RUNNING_FUNCTION 直接找到栈中的返回地址,从而实现栈回溯,编译器已经把所有函数的结构生成在PE文件中
以上如有错误希望大佬指出
[招生]科锐逆向工程师培训46期预科班将于 2023年02月09日 正式开班
收藏 ・3 点赞・1 打赏 分享
| |
---|---|
MarkHui 2022-7-2 15:58
|
X64 SEH的展开
iihacker_cat于 2017-12-08 01:08:05 发布1603 收藏 分类专栏: 软件调试 版权 软件调试专栏收录该内容 12 篇文章1 订阅 订阅专栏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
标签:ConsoleApplication3,00,UNWIND,00000000,00000001,APP,x64,回溯,rsp From: https://www.cnblogs.com/ioriwellings/p/17091762.html