首页 > 系统相关 >windows C++ 异常调用栈简析

windows C++ 异常调用栈简析

时间:2022-10-11 15:02:29浏览次数:57  
标签:__ 函数 windows ntdll C++ dll 简析 vcruntime140 1d

楔子

以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里面调用了函数RtlGuardRestoreContext
image
RtlGuardRestoreContext里面有个跳转指令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

标签:__,函数,windows,ntdll,C++,dll,简析,vcruntime140,1d
From: https://www.cnblogs.com/tangyanzhi1111/p/16779140.html

相关文章

  • Windows / Mac 安装Typora(1.2.4版)
    TyporaTypora是一款支持实时预览的Markdown文本编辑器。附件下载:Typora附件Windows版本1.解压Typora_1.3.8_windows.rar文件 2.双击Setup.exe直接选择推荐的......
  • C++STL
    STL1.说说STL的基本组成部分STL由6部分组成:容器(Container)、算法(Algorithm)、迭代器(Iterator)、仿函数(Functionobject)、适配器(Adaptor)、空间配制器(Allocator)。容器......
  • windows下关于Objective-C开发环境的配置
    最近IOS一直很火,也想学习以以下OC开发,一般装个虚拟机,然后装个mac,我相信大多数人的机子跑不起来或者很卡,所以之前借鉴了一个文章,就是关于在windows下配置OC开发环境,这里我把......
  • windows下的gvim配置
    首要任务是下载安装Gvim7.3。安装完后,gvim菜单中文出现乱码,在_vimrcset文件中增加:"配置多语言环境,解决中文乱码问题ifhas("multi_byte")"UTF-8编码seten......
  • Linux下的lua和boost c++的搭建和安装
    先下载lua,boostc++​​http://www.lua.org/versions.html#5.2​​​​http://www.boost.org/​​ ​​http://sourceforge.net/projects/luabind/​​1.安装lua[root@l......
  • Spread for Windows Forms高级主题(8)---通过暂停布局提高性能
    一种改善控件性能的方法是,当需要对许多单元格进行变动时,可以先保持或挂起重画,直到所有的变动都完成时再进行。通过在对单元格修改和重算时保持重画(挂起布......
  • Windows 7样式地址栏(Address Bar)控件实现
    介绍从Vista开始,地址栏就有了很大的改变,不知道大家有什么感觉,笔者觉得很方便,同时又兼容之前的功能,是个很不错的创新。不过,微软并不打算把这一很酷的功能......
  • windows系统下安装gym运行atari游戏报错:ale_interface/ale_c.dll OSError
    安装gym的atari支持:pip install gym[atari]  为gym下的atari环境下载游戏镜像ROMs文件:​需要注意的是由于gym的版本更新现在的gym依赖的atari库已经不叫做atari-py了,......
  • 开源状态机代码生成 StateSmith 支持C/C++
     StateSmithStateSmithisacrossplatform,free/opensourcetoolforgeneratingstatemachines.Thegeneratedcodeishumanreadable,haszerodependencies......
  • Java Style的C++容器流式处理类
    很久没有上博客园了,最近一段时间,因为工作的关系时间上比较闲,利用闲暇时间重新翻了一下丢弃很久的C++语言。C++从98、11、14、17目前已经也走到了20版本,发生了很多变化,也引......