首页
社区
课程
招聘
[原创]从逆向还原的角度探讨x86与x64的异常 一.32位3代异常处理还原(VS2019)
发表于: 2025-3-1 21:52 7476

[原创]从逆向还原的角度探讨x86与x64的异常 一.32位3代异常处理还原(VS2019)

2025-3-1 21:52
7476

大家好,我是科锐逆向49期学员,此系列文章旨在探讨在VS2019环境下,代码还原中遇到x86的异常和x64的异常时如何准确还原try-catch的嵌套关系,如何定位核心的catch代码块,准确还原try的包含范围,以及获取thorw参数的值及其类型。如果表述或者内容有误恳请各位前辈斧正。

编译器: Microsoft Visual Studio Community 2019 16.11.34
系统:Microsoft Windows 11 专业工作站版 10.0.22631 版本 22631
分析工具: IDA 7.5

此系列文章我们将从如下5个方面入手讨论有关异常还原的奥妙。

我们分为3个章节,本文为第一章,章节目录如下

首先对于异常还原我们需要解决的问题有如下几点

当我们能够准确做到以上四点之后就能够准确还原源程序中的try-catch结构,在微软的异常体系架构中并未使用C++标准的异常体系,反而发展出了自己的异常体系微软的异常架构按照还原难度从易到难如下排列

本篇文章将会介绍x86中的三代异常还原相关的还原技巧,首先我们从32位程序的3代异常说起。

首先要识别x86的3代异常是比较简单的,在拥有异常处理的函数当中其开头一般都会有如下两点行为,请谨记这两点识别技巧

形如下图
图片描述
在我们点击进入到SEH回调函数指针之后会见到__CxxFrameHandler3这个调用,一般来讲IDA都会将其进行标记,此标志就代表着3代异常处理框架。如下图所示
图片描述
此调用主要依托于RTTI机制进行异常的派发

接下来我们先回到throw的识别,对于throw来讲我们一般是通过_CxxThrowException来抛出异常的,一般我们在IDA中看到有形如_CxxThrowException时就可以判定为抛出点。形如下图所示
图片描述
当我们找到此调用的时候还原throw参数的值及其类型就比较简单了,此调用传入两个参数

想要确定值就查看跟踪pExceptionObject的赋值流程。想要确定抛出参数类型就分析_ThrowInfo结构体。接下来我们对_ThrowInfo结构体进行解析,在微软的头文件eh.h里面有包含异常管理的结构体定义。值得注意的是VC6和VS2019的结构体是具有差异的。此_ThrowInfo结构体的定义如下

字段解释

CatchableTypeArray 定义

字段解析

nCatchableTypes类型

arrayOfCatchableTypes CatchableType*结构体

CatchableType*结构体定义如下

我们给出TypeDescriptor定义

至此我们可以知道抛出异常的类型了,我们再用图像梳理一遍参数的指向流程。
图片描述
这就是throw的识别方式,根据传参分析到thorw的类型然后跟踪第一个参数pExceptionObject拿到参数的值。接下来我们回到catch的还原方法。

对于catch的还原我们需要对传入的SEH回调函数指针进行分析,此函数的调用会传入一个参数,此参数IDA在DEBUG版是不会解析的,我们需要搞清楚这个表的结构,此结构体叫FuncInfo此结构体定义如下

重要字段解析

其中固定第一个magicNumber19930522是固定值据传是发明这个结构体的日期。TryBlockMapEntry结构体记录了Try的各种信息定义如下

重要参数解析

HandlerType结构体解析,此结构体主要用来描述Catch的信息。

重要参数解析

我们至此可以通过分析FuncInfo来获取到catch代码块的位置,我贴出图来辅助大家理解此结构
图片描述

接下来我们需要确定try的范围,在Debug版中可以通过确定try等级下标来确定进入的是第几个try,但是在Release版中等级下标的语句是有可能被编译器优化掉的,我们先给出Debug版的图例稍后在编译器优化后的异常代码结构还原技巧此章节讨论Release版本的判断技巧。
图片描述

接下来我们需要看的就是,try的嵌套范围。Try的嵌套不能简单通过stat_tryLevel看出,在Release版中因为没有退出赋值-1的情况所以就无法看出,所以通过tryLow tryHigh catchHigh 这三个字段来描述是否有嵌套。
图片描述
以如上的TryBlockMap举例很明显能看出包含关系,其中下面的参数为0,2,3 也就是说从0到2都会进入,然后上面那个只是1,1,3也就是说下面的try包含上面的try,这样就可以明确的还原出嵌套关系。

我们给出复杂点的示例用于判断练习
图片描述
解析:从上到下我们将其标号为0~5,其嵌套关系如下表示

如果在Catch中再写try只是单独在Catch块中具有try-catch结构不会嵌入当前层次的分析。

对于异常处理的还原,在还原代码结构的时候是十分重要的。我们需要准确地还原出异常结构,并且识别出编译器对其的优化处理,这样才能更好地还原软件的行为,做到二进制上的相同,同时使得还原后的代码更具有健壮性,维护性,最后谢谢大家观看。
PS:本来我是想将此系列文章作为一篇文章发出,由于论坛编辑器过于卡顿所以我分为了三个文章一个系列,如果我的内容有误恳请各位前辈在评论区斧正。

CxxThrowException(pExceptionObj, __ThrowInfo)
CxxThrowException(pExceptionObj, __ThrowInfo)
typedef const struct _s_ThrowInfo {
 unsigned int   
attributes;                        
(Bit field)
 PMFN           
pmfnUnwind;                        
when exception has been handled or aborted
 int (__cdecl * pForwardCompat)(...);               
frame handler
 CatchableTypeArray* pCatchableTypeArray;           
pointers to types
 #endif
 } ThrowInfo;
typedef const struct _s_ThrowInfo {
 unsigned int   
attributes;                        
(Bit field)
 PMFN           
pmfnUnwind;                        
when exception has been handled or aborted
 int (__cdecl * pForwardCompat)(...);               
frame handler
 CatchableTypeArray* pCatchableTypeArray;           
pointers to types
 #endif
 } ThrowInfo;
typedef const struct _s_CatchableTypeArray {
 int nCatchableTypes;
 CatchableType*  arrayOfCatchableTypes[];
 } CatchableTypeArray;
typedef const struct _s_CatchableTypeArray {
 int nCatchableTypes;
 CatchableType*  arrayOfCatchableTypes[];
 } CatchableTypeArray;
typedef const struct _s_CatchableType {
    unsigned int        properties;         // Catchable Type properties (Bit field)
#if _EH_RELATIVE_TYPEINFO
    int                 pType;              // Image relative offset of TypeDescriptor
#else
    TypeDescriptor *    pType;              // 这里记录类型
#endif
    PMD                 thisDisplacement;   // Pointer to instance of catch type within thrown object.
    int                 sizeOrOffset;       // Size of simple-type object or offset into
                                            //  buffer of 'this' pointer for catch object
    PMFN                copyFunction;       // 这里会记录拷贝构造函数的地址
} CatchableType;
typedef const struct _s_CatchableType {
    unsigned int        properties;         // Catchable Type properties (Bit field)
#if _EH_RELATIVE_TYPEINFO
    int                 pType;              // Image relative offset of TypeDescriptor
#else
    TypeDescriptor *    pType;              // 这里记录类型
#endif
    PMD                 thisDisplacement;   // Pointer to instance of catch type within thrown object.
    int                 sizeOrOffset;       // Size of simple-type object or offset into
                                            //  buffer of 'this' pointer for catch object
    PMFN                copyFunction;       // 这里会记录拷贝构造函数的地址
} CatchableType;
typedef struct TypeDescriptor
{
#if defined(_WIN64) || defined(_RTTI) || defined(BUILDING_C1XX_FORCEINCLUDE)
    const void * pVFTable;  // Field overloaded by RTTI
#else
    unsigned long   hash;           // Hash value computed from type's decorated name
#endif
    void *  spare;          // reserved, possible for RTTI
    char            name[];         // 这里的name就是名称在IDA中会标注出来
    } TypeDescriptor;
typedef struct TypeDescriptor
{
#if defined(_WIN64) || defined(_RTTI) || defined(BUILDING_C1XX_FORCEINCLUDE)
    const void * pVFTable;  // Field overloaded by RTTI
#else
    unsigned long   hash;           // Hash value computed from type's decorated name
#endif
    void *  spare;          // reserved, possible for RTTI
    char            name[];         // 这里的name就是名称在IDA中会标注出来
    } TypeDescriptor;
typedef const struct _s_FuncInfo
{
    unsigned int        magicNumber:29;     // Identifies version of compiler
    unsigned int        bbtFlags:3;         // flags that may be set by BBT processing
    __ehstate_t         maxState;           // Highest state number plus one (thus
                                            // number of entries in unwind map)
    UnwindMapEntry*     pUnwindMap;         // Where the unwind map is
    unsigned int        nTryBlocks;         // Number of 'try' blocks in this function *****
    TryBlockMapEntry*   pTryBlockMap;       // Where the handler map is *****
    unsigned int        nIPMapEntries;      // # entries in the IP-to-state map. NYI (reserved)
    void*               pIPtoStateMap;      // An IP to state map.  NYI (reserved).
    ESTypeList*         pESTypeList;        // List of types for exception specifications
    int                 EHFlags;            // Flags for some features.
} FuncInfo;
typedef const struct _s_FuncInfo
{
    unsigned int        magicNumber:29;     // Identifies version of compiler
    unsigned int        bbtFlags:3;         // flags that may be set by BBT processing
    __ehstate_t         maxState;           // Highest state number plus one (thus
                                            // number of entries in unwind map)
    UnwindMapEntry*     pUnwindMap;         // Where the unwind map is
    unsigned int        nTryBlocks;         // Number of 'try' blocks in this function *****
    TryBlockMapEntry*   pTryBlockMap;       // Where the handler map is *****
    unsigned int        nIPMapEntries;      // # entries in the IP-to-state map. NYI (reserved)
    void*               pIPtoStateMap;      // An IP to state map.  NYI (reserved).

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2025-3-1 21:59 被TeddyBe4r编辑 ,原因: 优化文章结构
收藏
免费 11
支持
分享
最新回复 (4)
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
大佬!
2025-3-1 22:09
0
雪    币: 415
活跃值: (350)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
牛逼我的李哥!
2025-3-2 21:10
0
雪    币: 3203
活跃值: (3937)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
4
学习了!!!!
2025-3-3 15:21
0
雪    币: 245
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
大佬!
2025-3-3 15:45
0
游客
登录 | 注册 方可回帖
返回