5

windows C++ 异常调用栈简析 - 江湖评谈

 1 year ago
source link: https://www.cnblogs.com/tangyanzhi1111/p/16779140.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

windows C++ 异常调用栈简析

以win11 + vs2022运行VC++ 编译观察的结果。
如果安装了Visual Studio 2022,比如安装在D盘,则路径:

D:\Visual Studio\IDE\VC\Tools\MSVC\14.33.31629

下面包含了vcruntime.dll的源码,主要VC编译器和ntdll.dll 以及KernelBase.dll交互。
注:本篇不叙述正常的windows用户态和内核态异常处理,仅看用户态下偏角的运作方式。

void main()
{
	char* pStr = NULL;
	try
	{
		throw pStr;
	}
	catch (char* s)
	{
		printf("Hello S");
	}
	getchar();
}

try里面抛出一个异常,异常调用堆栈如下

红色箭头,throw抛出异常之后,调用了_CxxThrowException函数,这个函数刚好在vcruntime.dll里面。

image

_CxxThrowException函数源码在VS路径:

D:\Visual Studio\IDE\VC\Tools\MSVC\14.33.31629\crt\src\vcruntime\throw.cpp
extern "C" __declspec(noreturn) void __stdcall _CxxThrowException(
    void *pExceptionObject, // The object thrown
    _ThrowInfo *pThrowInfo  // Everything we need to know about it
) {
    //为了方便观看,此处省略一万字
    RaiseException(EH_EXCEPTION_NUMBER, EXCEPTION_NONCONTINUABLE, _countof(parameters), parameters);
}

_CxxThrowException又调用了RaiseException函数。RaiseException函数会进入到内核里面分别调用如下:

ntdll.dll!KiUserExceptionDispatch-》
ntdll.dll!RtlDispatchException-》
ntdll.dll!RtlpExecuteHandlerForException-》

windows异常分为内核态和用户态处理过程,RtlpExecuteHandlerForException则刚好是用户态处理过程。这些过程过于复杂,此处为了避免无端枝节,不赘述。

RtlpExecuteHandlerForException是调用异常处理的函数,通俗点就是跳转到catch地址,然后执行catch后面的代码。

在VS2022里面,异常处理函数是__CxxFrameHandler4(此函数在vcruntime.dll里面)
源码在路径:

D:\Visual Studio\IDE\VC\Tools\MSVC\14.33.31629\crt\src\vcruntime\risctrnsctrl.cpp

__CxxFrameHandler4后面的调用函数是:

__CxxFrameHandler4-》
vcruntime140_1d.dll!__InternalCxxFrameHandler-》
vcruntime140_1d.dll!FindHandler-》
vcruntime140_1d.dll!CatchIt-》
vcruntime140_1d.dll!__FrameHandler4::UnwindNestedFrames-》
ntdll.dll!RtlUnwindEx-》
ntdll.dll!RtlGuardRestoreContext-》
ntdll.dll!RtlRestoreContext-》
ntdll.dll!RtlpExecuteHandlerForUnwind-》
vcruntime140_1d.dll!__CxxFrameHandler4-》

到了这里看似已经接近完成了,但是实际上还远不止如此。如果再继续调用,会直接跳到函数

ntdll.dll!RcConsolidateFrames -》
vcruntime140_1d.dll!__FrameHandler4::CxxCallCatchBlock

从__CxxFrameHandler4到RcConsolidateFrames经历什么?会发现跟上面的对不上。堆栈也没有显示。
为此,还需要继续跟踪

为了能看到从__CxxFrameHandler4到RcConsolidateFrames经历什么,我们跟踪下汇编

image

__CxxFrameHandler4调用了RtlGuardRestoreContext,继续单步F11,RtlGuardRestoreContext里面调用了函数RtlRestoreContext

image

RtlRestoreContext里面有个跳转指令jmp rdx。看下图:

image

jmp指令调到了如下

image

而call rax的rax就是CxxCallCatchBlock函数的指针。
因为RcConsolidateFrames函数是在ntdll.dll里面没有被开源,所以两次跳转(jmp 和 call 应该是这个函数里面所做的动作)
如此一来就对上上面的那个函数调用顺序(从上到下),但是还有一个问题,这个try里面抛出了异常,那么catch是何时被执行的呢?

Catch

理顺了RcConsolidateFrames函数调用顺序,RcConsolidateFrames自己则调用了函数CxxCallCatchBlock。这个函数里面调用了catch处理异常。
CxxCallCatchBlock函数源码地址:

D:\Visual Studio\IDE\VC\Tools\MSVC\14.33.31629\crt\src\vcruntime\frame.cpp(1344行)
void * RENAME_EH_EXTERN(__FrameHandler4)::CxxCallCatchBlock(
    EXCEPTION_RECORD *pExcept
    )
{
          //为了方便观看,此处省略一万行
            continuationAddress = RENAME_EH_EXTERN(_CallSettingFrame_LookupContinuationIndex)
}
RENAME_EH_EXTERN(_CallSettingFrame_LookupContinuationIndex)
这段的原型是:

image

image

注意的点:

CxxCallCatchBlock函数不会返回,直接跳转到catch大括号下面的代码里面继续执行后面的代码段。

void * RENAME_EH_EXTERN(__FrameHandler4)::CxxCallCatchBlock(
    EXCEPTION_RECORD *pExcept
    )
{
   //为了便于观察, 此处省略一万字,
    return continuationAddress;
}
image

堆栈的调用如下:

	vcruntime140_1d.dll!__FrameHandler4::CxxCallCatchBlock
	(jmp rdx)ntdll.dll!RcConsolidateFrames
	ntdll.dll!RtlRestoreContext
	ntdll.dll!RtlGuardRestoreContext	
 	ntdll.dll!RtlUnwindEx	
 	vcruntime140_1d.dll!__FrameHandler4::UnwindNestedFrames
 	vcruntime140_1d.dll!CatchIt
 	vcruntime140_1d.dll!FindHandler
 	vcruntime140_1d.dll!__InternalCxxFrameHandler
 	vcruntime140_1d.dll!__CxxFrameHandler4	
 	ntdll.dll!RtlpExecuteHandlerForException()	
 	ntdll.dll!RtlDispatchException
 	ntdll.dll!KiUserExceptionDispatch()
 	KernelBase.dll!RaiseException()
 	vcruntime140d.dll!_CxxThrowException
 	ConsoleApplication2.exe!main	

作者:江湖评谈
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

image

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK