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

admin 2025年3月5日19:49:41评论10 views字数 5090阅读16分58秒阅读模式

此系列文章旨在探讨在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回调函数

形如下图:

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

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

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

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

throw类型及其值确定

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

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

当我们找到此调用的时候还原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.arrayOfCatchableTypesCatchableType*结构体

CatchableType*结构体定义如下:

typedef const struct _s_CatchableType {
unsigned intproperties;// Catchable Type properties (Bit field)
#if _EH_RELATIVE_TYPEINFO
intpType;// Image relative offset of TypeDescriptor
#else
TypeDescriptor *pType;// 这里记录类型
#endif
PMD thisDisplacement;// Pointer to instance of catch type within thrown object.
intsizeOrOffset;// Size of simple-type object or offset into
// buffer of 'this' pointer for catch object
PMFNcopyFunction;// 这里会记录拷贝构造函数的地址
} CatchableType;

我们给出TypeDescriptor定义

typedef struct TypeDescriptor
{
#if defined(_WIN64) || defined(_RTTI) || defined(BUILDING_C1XX_FORCEINCLUDE)
const void * pVFTable;// Field overloaded by RTTI
#else
unsigned longhash;// Hash value computed from type's decorated name
#endif
void *spare;// reserved, possible for RTTI
charname[];// 这里的name就是名称在IDA中会标注出来
} TypeDescriptor;

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

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

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

catch块的定位与还原

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

typedef const struct _s_FuncInfo
{
unsigned intmagicNumber:29;// Identifies version of compiler
unsigned intbbtFlags:3;// flags that may be set by BBT processing
__ehstate_tmaxState;// Highest state number plus one (thus
// number of entries in unwind map)
UnwindMapEntry*pUnwindMap;// Where the unwind map is
unsigned intnTryBlocks;// Number of 'try' blocks in this function *****
TryBlockMapEntry*pTryBlockMap;// Where the handler map is *****
unsigned intnIPMapEntries;// # 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
intEHFlags;// Flags for some features.
} FuncInfo;

重要字段解析

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

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

3.maxState最大状态

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

typedef const struct _s_TryBlockMapEntry {
__ehstate_ttryLow;// Lowest state index of try
__ehstate_ttryHigh;// Highest state index of try
__ehstate_tcatchHigh;// Highest state index of any associated catch
intnCatches;// 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.pHandlerArrayCatch的类型

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

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

重要参数解析

1.adjectivestry_level

2.pTypeRTTI 指针指向接收异常类型

3.addressOfHandlercatch代码块指针

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

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

try范围还原技巧

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

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

try嵌套识别技巧

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

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

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

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

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

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

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

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

结语

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

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

看雪ID:TeddyBe4r

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

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

原文始发于微信公众号(看雪学苑):从逆向还原的角度探讨x86与x64的异常:32位3代异常处理还原(VS2019)

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年3月5日19:49:41
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   从逆向还原的角度探讨x86与x64的异常:32位3代异常处理还原(VS2019)http://cn-sec.com/archives/3801010.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息