查看原文
其他

逆向 | 类的继承关系

计算机与网络安全 计算机与网络安全 2022-06-01

一次性进群,长期免费索取教程,没有付费教程。

教程列表见微信公众号底部菜单

进微信群回复公众号:微信群;QQ群:460500587



微信公众号:计算机与网络安全

ID:Computer-network

可以说类的继承关系是C++逆向中最为重要的一部分,因为它不但关乎逆向成败、可以透析作者的思想,还可以辅助我们理清程序的整体结构。


C++逆向是一个比较特殊的东西,往往复杂些的例子反而能把问题阐述得更加清晰。图1所示是对我们将要分析的菱形继承关系的结构描述。

图1  菱形继承关系示意图

我们可以看到,由CPeople、CBoy、CGirl与CBaby共同组建成了一个菱形继承的关系。下面我们再看代码清单1。

代码清单1  有虚函数的菱形继承示例

以下为这个类的内存结构(此例子的内存结构与《逆向 | 虚函数与纯虚函数中的例子的结构基本一致,仅多出了一个CBaby。为了节省篇幅,这里仅给出CBaby的内存结构):

下面我们仍然从main()函数开始,如代码清单2所示。

代码清单2  有虚函数的菱形继承示例的Debug版反汇编代码

通过以上的分析我们已经得到了比较丰富的信息,但是我们仍然需要深入求证一下。首先从构造看起,我们直接分析第四个,其反汇编信息如代码清单3所示。

代码清单3  位于0x004144EA处的Call(即Cbaby的构造函数)

由以上代码不难发现,在这个构造中构造函数不但调用了第二、第三个类的构造,而且还拥有两张虚表,这是典型的多重继承,由此可以判断第四个类是由第二、第三个类继承而来的。我们此时应该在笔记本上画出图2所示的关系图。

图2  类的关系图(一)

在确认了这种继承关系后,由于这个类存在两个虚表,因此我们现在至少有51%的把握认为第四个类的3个成员函数是虚函数了。为了进一步确认,我们跟进虚表中观察。

由此我们已经基本可以确定第四个类中的3个函数就是虚函数。因此现在我们的笔记本中的图应该如图3所示。

图3  类的关系图(二)

确定了虚表后,我们再跟进代码清单3里的那两个构造函数中看看。

通过IDA的注释我们可以知道,这里调用了第五个类CPeople的构造函数,在实际逆向情况中,我们需通过比对这几个构造函数的地址,从而判断出这是一个我们还未“收录”的类。我们在此跟进这个类后发现,这个类是由第一个类派生出来的,因此我们此时的笔记本信息应该如图4所示。

图4  类的关系图(三)

现在我们在跟进第五个类的虚表中一探究竟。

在上述代码中,我们发现了两个熟悉的__purecall函数,由此可以判断第二个类与第三个类中可能会有两个函数是从此纯虚函数中继承下来的。再由其虚表的第一项我们可判定函数一不属于此范畴,因此可以判定第五个类中剩下的那两个就是纯虚函数了。此时我们笔记本上的内容应该如图5所示。

图5  类的关系图(四)

到目前为止我们已经将这个类的大体结构分析出来了,剩下的工作就是完善它了,例如确定调用方式与一些细节问题。除此之外,我们可以用分析得出的真实类名等信息重新画一下图5,如图6所示。

图6  完善后的类的关系图

分析完Debug版,按照惯例我们就应该分析Release版了,但由于Release版有很多优化,因此如果代码过于简单,我们几乎不可能有效地还原出所有结构。但是如果为了讲解Release版的菱形继承而专门使用一个数百行的复杂例子会显得过于臃肿,其实只要您能完全理解Debug版的例子就足够了。


综上所述,我们得出以下经验:


根据构造函数内的构造顺序分辨此函数所属类的继承情况。

及时总结并记录分析结果。

Visual Studio的Release版中存在同时使用ecx、esi寄存器传递this指针的情况。

微信公众号:计算机与网络安全

ID:Computer-network

【推荐书籍】

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存