专栏名称: 看雪学苑
致力于移动与安全研究的开发者社区,看雪学院(kanxue.com)官方微信公众帐号。
目录
相关文章推荐
FreeBuf  ·  2025年2月网络安全产品精选 ·  3 天前  
小互AI  ·  Manus:一个真正能完成任务的AI代理 ·  17 小时前  
小互AI  ·  Manus:一个真正能完成任务的AI代理 ·  17 小时前  
深圳市网络与信息安全行业协会  ·  CCRC-DSO数据安全官认证课程招生中! ·  22 小时前  
深圳市网络与信息安全行业协会  ·  CCRC-DSO数据安全官认证课程招生中! ·  22 小时前  
网信上海  ·  网警:警惕网络“毒流量” ·  2 天前  
51好读  ›  专栏  ›  看雪学苑

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

看雪学苑  · 公众号  · 互联网安全  · 2025-03-05 17:59

正文

此系列文章旨在探讨在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 个方面入手讨论有关异常还原的奥妙。


1.异常还原中的核心问题

2.x86中的三代异常还原

3.x64中三代异常还原

4.x64中四代异常还原

5.编译器优化后的异常代码结构还原技巧


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


1.x86中的三代异常还原(本文)

2.x64中三代异常与四代还原

3.编译器优化后的异常代码结构还原技巧


异常还原中的核心问题

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


1.try范围确定

2.try嵌套关系确定

3.throw类型及其值确定

4.catch代码块的定位


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


1.x86中的三代异常还原

2.x64中三代异常还原

3.x64中四代异常还原


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


x86中的三代异常还原

x86中的三代异常识别


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


1.进入-1的try_level

2.放入SEH回调函数


形如下图:


图片描述


在我们点击进入到SEH回调函数指针之后会见到 __CxxFrameHandler3 这个调用,一般来讲IDA都会将其进行标记,此标志就代表着3代异常处理框架。如下图所示。


图片描述

此调用 主要依托于RTTI机制进行异常的派发


throw类型及其值确定


接下来我们先回到 throw 的识别,对于throw来讲我们一般是通过 _CxxThrowException 来抛出异常的,一般我们在IDA中看到有形如 _CxxThrowException 时就可以判定为抛出点。形如下图所示:


图片描述

当我们找到此调用的时候还原throw参数的值及其类型就比较简单了,此调用传入两个参数:


1.pExceptionObject 抛出参数值的指针

2.pThrowInfo _ThrowInfo结构体指针


CxxThrowException(pExceptionObj, __ThrowInfo)


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


字段解释


1. attributes throw 的信息

2. pmfnUnwind 异常的展开

3. pForwardCompat 为了兼容的框架

4. pCatchableTypeArray Catch 类型数组指向一个数组


CatchableTypeArray 定义:


typedef const struct _s_CatchableTypeArray {
int nCatchableTypes;
CatchableType* arrayOfCatchableTypes[];
} CatchableTypeArray;


字段解析


1. nCatchableTypes 类型

2. arrayOfCatchableTypes CatchableType* 结构体


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;


我们给出 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;


至此我们可以知道抛出异常的类型了,我们再用图像梳理一遍参数的指向流程。


图片描述


这就是throw的识别方式,根据传参分析到thorw的类型然后跟踪第一个参数 pExceptionObject 拿到参数的值。接下来我们回到catch的还原方法。


catch块的定位与还原


对于catch的还原我们需要对传入的SEH回调函数指针进行分析,此函数的调用会传入一个参数,此参数IDA在DEBUG版是不会解析的,我们需要搞清楚这个表的结构,此结构体叫 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).
ESTypeList* pESTypeList; // List of types for exception specifications
int EHFlags; // Flags for some features.
} FuncInfo;


重要字段解析


1. nTryBlocks 这个函数写了几个try

2. pTryBlockMap 每个try的信息由此结构体指明

3. maxState 最大状态


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


typedef const struct _s_TryBlockMapEntry {
__ehstate_t tryLow; // Lowest state index of try
__ehstate_t tryHigh; // Highest state index of try
__ehstate_t catchHigh; // Highest state index of any associated catch
int nCatches; // Number of entries in array
HandlerType* pHandlerArray; // List of handlers for this try
} TryBlockMapEntry;


重要参数解析


1. tryLow

2. tryHigh 如果前两个字段相同则证明无嵌套

3. catchHigh 这三个字段用于匹配第几个try

4. nCatches 此try有几个catch

5. pHandlerArray Catch的类型



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


typedef const struct _s_HandlerType {
unsigned int adjectives; // Handler Type adjectives (bitfield)
TypeDescriptor* pType; // RTTI
ptrdiff_t dispCatchObj; // Displacement of catch object from base
void * addressOfHandler; // Catch的代码位置
} HandlerType;


重要参数解析


1. adjectives try_level

2. pType RTTI 指针指向接收异常类型

3. addressOfHandler catch代码块指针


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


图片描述


try范围还原技巧


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


图片描述


try嵌套识别技巧


接下来我们需要看的就是,try的嵌套范围。Try的嵌套不能简单通过 stat_tryLevel 看出,在Release版中因为没有退出赋值 -1 的情况所以就无法看出,所以通过 tryLow tryHigh catchHigh 这三个字段来描述是否有嵌套。


图片描述

以如上的 TryBlockMap 举例很明显能看出包含关系,其中下面的参数为 0,2,3 也就是说从0到2都会进入,然后上面那个只是 1,1,3 也就是说下面的 try 包含上面的 try ,这样就可以明确的还原出嵌套关系。


我们给出复杂点的示例用于判断练习


图片描述

解析 :从上到下我们将其标号为0~5,其嵌套关系如下表示


1
0
0
1
5
2
2
4
3
3
4
5


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

结语

对于异常处理的还原,在还原代码结构的时候是十分重要的。我们需要准确地还原出异常结构,并且识别出编译器对其的优化处理,这样才能更好地还原软件的行为,做到二进制上的相同,同时使得还原后的代码更具有健壮性,维护性。





图片


看雪ID:TeddyBe4r

https://bbs.kanxue.com/user-home-983513.htm

*本文为看雪论坛精华文章,由 TeddyBe4r 原创,转载请注明来自看雪社区


# 往期推荐







请到「今天看啥」查看全文