RTTI结构详细分析(VC++)

2024-01-08 17:36
文章标签 c++ 详细分析 结构 rtti

本文主要是介绍RTTI结构详细分析(VC++),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

对于RTTI结构的资料真的屈指可数,类的逆向也一直是一个不好弄的问题.对此我只想贡献我的一份力量。

文中我不会分析类的内存布局,因为有很多资料已经分析的挺好的了(见参考资料)但是现有我能找到的资料对RTTI结构的表述不完整,或者表述模糊不清,参考Clang的部分源码后,我在他们的基础上,补充,扩展了一下RTTI的结构信息.但是仍然也有一部分我也不甚清楚,见谅。

首先给出RTTI结构的完整结构图(图中->是表示两个结构有关联关系):

@VfTable包含ClassVfTable,类的虚表指针vfptr指向@VfTable+4的位置,即vfptr=@VfTable+4=ClassVfTable
我是为了统一一下才这样表示的.在类虚表位置-4的地方,也就是ClassVfTable-4.指向CompleteObjectLocator结构.
现在对每一个结构进行简单的解释.
CompleteObjectLocator
{
+0 isImageRelative: DWORD
+4 VfOffsetToTop: DWORD
+8 VFPtrOffset: DWORD
+12 pTypeDescriptor: TypeDescriptor*
+16 pClassHierarchyDescriptor: ClassHierarchyDescriptor *
[+20 -pSelf: DWORD *] //只在X64时存在,指向自身
}
isImageRelative: 
RTTI结构中的所有指针是否是与映象基地址相关,这是X64与X32的区别.
当是X32时为0,指针含义没有变.但在X64时为1,此时RTTI结构中所有指针不再是指针,而是相对于基地址的偏移量.

VfOffsetToTop: vftable 在类中的偏移

VFPtrOffset:  
这个含义比较复杂.他是在有虚基类被继承多次,并且虚基类的(纯)虚函数被多次覆写时存在.
是虚表指针相对与最近的一个虚基类的偏移.

TypeDescriptor
{
+0 pVFtable: DWORD* //指向虚表
+4 Spare: DWORD     //存储运行时数据,静态分析时总是0.
+8 pTypeInfoString: char* //指向类名.
}

ClassHierarchyDescriptor
{
+0 Unknown: DWORD = 0  //我没查到它的信息.
+4 ClassHierarchyFlags: DWORD
+8 NumBaseClass: DWORD //基类数量,包括自己,并且对同一基类多次继承(菱形继承)会重复计算的.
+12 pBaseClassDescriptorArray: DWORD** //指向BaseClassDescriptor指针数组
}
说一下ClassHierarchyFlags的含义.
定义枚举如下:
enum ClassHierarchyDescriptorFlags{
00252     HasBranchingHierarchy = 1,
00253     HasVirtualBranchingHierarchy = 2,
00254     HasAmbiguousBases = 4
00255   };
ClassHierarchyFlags是根据类是否是多继承,是否是多虚继承,是否有纯虚基类来对上述
枚举量进行异或组合.例如ClassHierarchyFlags=HasBranchingHierarchy|HasVirtualBranchingHierarchy,
表示的含义已经很清楚了.
还剩最后一个结构了.
BaseClassDescriptor
{
+0 pTypeDescriptor: DWORD TypeDescriptor*
+4 NumContainedBaseClass: DWORD  //基类数量,包括自己,并且对同一基类多次继承(菱形继承)会重复计算的.
+8 OffsetInVBase: DWORD   //vftable 在虚基类中的偏移
+12 VBPtrOffset: DWORD  //vbtable(包含所有虚基类的vftable的偏移的一个表)的偏移.此处参考后面列出的参考资料[1].
+16 OffsetInVBTable: DWORD  //类vftable指针在vbtable中的偏移.
+20 BaseClassHierarchyFlags: DWORD
+24 pClassHierarchyDescriptor: ClassHierarchyDescriptor*
}
对于BaseClassHierarchyFlags定义如下枚举:
enum BaseClassDescriptorFlags
{
IsPrivateOnPath = 1 | 8,
IsAmbiguous = 2,
IsPrivate = 4,
IsVirtual = 16,
HasHierarchyDescriptor = 64
};
BaseClassHierarchyFlags是根据自己是否被私有继承,是否是纯虚类,是否对基类私有继承,是否为虚基类,是否
有HierarchyDescriptor结构对上述枚举量进行异或组合.

我是在ms2013上进行验证的.我不敢保证Microsoft的编译器RTTI结构一直都没有变化.
文中还有一些地方解释并不清楚,见谅.要是Microsoft把RTTI结构公开就没这么多事儿了.

参考资料:
1.Reversing C++ - Black Hat: https://www.blackhat.com/presentations/bh-dc-07/Sabanal_Yason/Paper/bh-dc-07-Sabanal_Yason-WP.pdf
2.openrce  igorsk 写的:http://www.openrce.org/articles/full_view/23
3.<<C++反汇编与逆向分析技术揭秘>>.
4.clang源代码: http://clang.llvm.org/doxygen/MicrosoftRTTI_8cpp_source.html
5.https://msdn.microsoft.com/en-us/library/ms879782.aspx

这篇关于RTTI结构详细分析(VC++)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/584263

相关文章

C++中使用vector存储并遍历数据的基本步骤

《C++中使用vector存储并遍历数据的基本步骤》C++标准模板库(STL)提供了多种容器类型,包括顺序容器、关联容器、无序关联容器和容器适配器,每种容器都有其特定的用途和特性,:本文主要介绍C... 目录(1)容器及简要描述‌php顺序容器‌‌关联容器‌‌无序关联容器‌(基于哈希表):‌容器适配器‌:(

C++中实现调试日志输出

《C++中实现调试日志输出》在C++编程中,调试日志对于定位问题和优化代码至关重要,本文将介绍几种常用的调试日志输出方法,并教你如何在日志中添加时间戳,希望对大家有所帮助... 目录1. 使用 #ifdef _DEBUG 宏2. 加入时间戳:精确到毫秒3.Windows 和 MFC 中的调试日志方法MFC

深入理解C++ 空类大小

《深入理解C++空类大小》本文主要介绍了C++空类大小,规定空类大小为1字节,主要是为了保证对象的唯一性和可区分性,满足数组元素地址连续的要求,下面就来了解一下... 目录1. 保证对象的唯一性和可区分性2. 满足数组元素地址连续的要求3. 与C++的对象模型和内存管理机制相适配查看类对象内存在C++中,规

Java中switch-case结构的使用方法举例详解

《Java中switch-case结构的使用方法举例详解》:本文主要介绍Java中switch-case结构使用的相关资料,switch-case结构是Java中处理多个分支条件的一种有效方式,它... 目录前言一、switch-case结构的基本语法二、使用示例三、注意事项四、总结前言对于Java初学者

结构体和联合体的区别及说明

《结构体和联合体的区别及说明》文章主要介绍了C语言中的结构体和联合体,结构体是一种自定义的复合数据类型,可以包含多个成员,每个成员可以是不同的数据类型,联合体是一种特殊的数据结构,可以在内存中共享同一... 目录结构体和联合体的区别1. 结构体(Struct)2. 联合体(Union)3. 联合体与结构体的

在 VSCode 中配置 C++ 开发环境的详细教程

《在VSCode中配置C++开发环境的详细教程》本文详细介绍了如何在VisualStudioCode(VSCode)中配置C++开发环境,包括安装必要的工具、配置编译器、设置调试环境等步骤,通... 目录如何在 VSCode 中配置 C++ 开发环境:详细教程1. 什么是 VSCode?2. 安装 VSCo

C++11的函数包装器std::function使用示例

《C++11的函数包装器std::function使用示例》C++11引入的std::function是最常用的函数包装器,它可以存储任何可调用对象并提供统一的调用接口,以下是关于函数包装器的详细讲解... 目录一、std::function 的基本用法1. 基本语法二、如何使用 std::function

PostgreSQL如何查询表结构和索引信息

《PostgreSQL如何查询表结构和索引信息》文章介绍了在PostgreSQL中查询表结构和索引信息的几种方法,包括使用`d`元命令、系统数据字典查询以及使用可视化工具DBeaver... 目录前言使用\d元命令查看表字段信息和索引信息通过系统数据字典查询表结构通过系统数据字典查询索引信息查询所有的表名可

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数