某些类还由多个基类组成,层次结构深达5层以上。 br />
Ida PRO支持制作指针结构以处理vtable,但是由于繁杂的多态性,某些最终类在同一“插槽”中可以具有多个不同的vtable,那么如何组织vtable?您如何告诉IDA,在此方法或该方法中,实际上引用了哪个vtable?
#1 楼
这取决于最初使用的编译器,因为每个编译器都会创建略有不同的布局。您可以在网上找到大多数编译器的教程,我将重点介绍MSVC,因为这是我的经验,并且由于它提供了隐藏的编译器开关,可打印如何在内存中布局类,以供我使用。作为说明。您可能已经猜到了,必须使用C结构重新创建C ++类。这是可能的,但是很麻烦,尤其是如果您有多少门课。减少烦人的一些技巧:
关注您感兴趣的人。
从“最基础”类开始,例如在层次结构树顶部的那些。如果您在那里弄乱了东西,以后修复它可能会很昂贵:想象将一个被遗忘的成员添加到像智能指针基类这样的东西中,该类被数百个类继承,现在您必须调整所有这些。
您可能想在Local中使用类似C的代码来定义
struct
首先键入子视图,而不是在“结构”子视图中。以下示例可以粘贴为本地类型。如果不确定反转结构,则构造函数通常会破坏某些成员的vftable和布局。调用它的函数可以通过为对象保留足够的字节来产生对象的总大小。
IDA 7.0通过自动识别和标记RTTI数据(如果可用)破坏了类层次结构和vftable,但是在多重继承的情况下,它错误地标记了哪个vftable属于哪个基类,这是它基于的IDA6脚本所没有的问题。我在IDAPython for 7.0中重写了它,以解决此问题以及与其他MSVC相关的问题,它还可以自动创建结构,如下所述。
IDA 7.2甚至更好地支持自动检测和重新创建C ++结构,但是以下答案是请注意7.0,并使用7.2命名。
根据您多态性的幻想程度,您的C结构也必须或多或少地幻想。因此,让我们先从简单的案例开始,然后逐步处理更复杂的案例。
无继承(基类)根本没有任何继承,只有一些成员和(纯)虚拟方法以以下内容开头:
class Animal {
int _age;
Animal() { _age = 0; }
int getAge() { return _age; }
virtual void setAge(int value) { _age = value; }
virtual void makeSound() = 0;
};
class Animal size(8):
+---
0 | {vfptr} // pointer to the vftable (s. below)
4 | _age // members of Animal
+---
Animal::$vftable@:
| &Animal_meta // ignore meta for our examples
| 0 // the (pure) virtual method follow
0 | &Animal::setAge
1 | &Animal::makeSound
IDA表示形式:
struct Animal;
struct Animal_vtbl {
void (__thiscall *setAge)(Animal *this, int value);
void (__thiscall *makeSound)(Animal *this);
};
struct Animal_mbrs {
int _age;
};
struct Animal {
Animal_vtbl *__vftable;
Animal_mbrs __members;
};
转发在vftable的
this
参数中声明要使用它的类结构。__thiscall
调用约定是在MSVC中创建类方法所必需的。除了所有其他参数外,它隐式传递一个指针到ecx
寄存器中的类实例。不需要提供参数名称。
makeSound
将是purecall
,而setAge
将成为修改我们成员的典型未知子对象。将成员置于单独的结构中以进行继承(见下文)。 br />
让我们快速繁殖一个继承自
Dog
的Animal
,实现makeSound
方法,并添加新的虚拟方法来设置毛皮颜色:class Dog : public Animal {
int _furColor;
virtual void setAge(int value) { _age = value; }
virtual void makeSound() { cout << "Woof Woof"; }
virtual void setFurColor(int color) { _furColor = color; }
};
MSVC布局:
Animal
基类仅嵌入在Dog
类内。嵌入式Animal
vftable还可以获取所有虚拟Dog
方法并在其末尾添加它们。 Dog
的成员出现在Animal
的成员之后:class Dog size(12):
+---
0 | +--- (base class Animal)
0 | | {vfptr}
4 | | _age
| +---
8 | _furColor
+---
Dog::$vftable@:
| &Dog_meta
| 0
0 | &Dog::setAge
1 | &Dog::makeSound
2 | &Dog::setFurColor // Added behind the Animal methods!
IDA表示形式:保留
Animal
的结构不变,我们添加以下内容:struct Dog;
struct Dog_vtbl : Animal_vtbl {
void (__thiscall *setFurColor)(Dog *this, int color);
};
struct Dog_mbrs : Animal_mbrs {
int _furColor;
};
struct Dog {
Dog_vtbl *__vftable;
Dog_mbrs __members;
};
通过让
Animal
vftable继承Dog
vftable来重用Dog
vftable(在IDA中继承结构只是意味着将其前缀),然后添加Animal
特定的虚拟功能。成员发生相同的事情,这就是为什么我较早分离它们的原因。
多重继承
这需要一点点令人心碎。为此,我们可以杀死我们的狗(对不起,对您来说很残酷,我不好创造快乐的例子):
class Killable {
bool _isDead;
virtual void kill() { makeDeathSound(); _isDead = true; }
virtual void makeDeathSound() = 0;
};
class Dog : public Animal, public Killable {
int _furColor;
virtual void setAge(int value) { _age = value; }
virtual void makeSound() { cout << "Woof Woof"; }
virtual void setFurColor(int color) { _furColor = color; }
virtual void makeDeathSound() { cout << "I'll call WWF, bark-blerg"; }
};
MSVC布局:与
Killable
基类一样,它也将第二个Dog
基类嵌入到Dog
类中,并使Dog
的成员分开。虽然Animal
特定的虚拟方法仍与Killable
vftable(又称第一基类)合并,但与Killable
相关的虚拟方法位于单独的与Dog
相关的vftable中,因此,我们现在具有:class Dog size(20):
+---
0 | +--- (base class Animal)
0 | | {vfptr}
4 | | _age
| +---
8 | +--- (base class Killable)
8 | | {vfptr}
12 | | _isDead
| | <alignment member> (size=3) // since _isDead is a 1-byte bool
| +---
16 | _furColor
+---
Dog::$vftable@Animal@:
| &Dog_meta
| 0
0 | &Dog::setAge
1 | &Dog::makeSound
2 | &Dog::setFurColor // Dog methods still merged with Animal!
Dog::$vftable@Killable@: // All the Killable-related methods in here
| -8 // offset for `this` pointer in Killable methods to get a Dog pointer
0 | &Killable::kill
1 | &Dog::makeDeathSound
IDA表示形式:我们在开始时就保留了一个特定于
Animal
的vftable,它在内部重用了Dog
vftable,因为Animal
虚拟方法仍然附加到Animal
的vftable上。然后Killable
的成员照常跟随。现在,由于没有任何内容合并到它们中,因此未修改的Dog
结构随之出现。最后,我们的Dog::ctor
成员将关注。如果将此与MSVC打印的偏移量进行比较,则很有意义:struct Killable;
struct Killable_vtbl {
void (__thiscall *kill)(Killable *this);
void (__thiscall *makeDeathSound)(Killable *this);
};
struct Killable_mbrs {
bool _isDead;
};
struct Killable {
Killable_vtbl* __vftable;
Killable_mbrs __members;
};
struct Dog;
struct Dog_vtbl : Animal_vtbl {
void (__thiscall *setFurColor)(Dog *this, int color);
};
struct Dog_mbrs { // No more base Animal members as they're split up now!
int _furColor;
};
struct Dog {
Dog_vtbl *__vftable; // Still contains animal methods.
Animal_mbrs __members_Animal; // Animal members come here separately.
Killable_vtbl *__vftable_Killable;
Killable_mbrs __members_Killable;
Dog_mbrs __members;
};
IDA 7.2的命名方式略有不同vftables参与多重继承。我发现手动处理很麻烦,因此我们在这里不再使用它。
让我们看看
Dog
伪代码中的样子: br /> Dog *__thiscall Dog::ctor(Dog *this)
{
j_Animal::ctor((Animal *)this);
j_Killable::ctor((Killable *)&this->__vftable_Killable);
this->__vftable = (Dog_vftable *)&Dog::`vftable';
this->__vftable_Killable = (Killable_vftable *)&Dog::`vftable';
return this;
}
作为构造函数的典型代表,首先调用基类构造函数。然后,设置
Dog::vftable
所需的vftable。但是没有什么意义:为什么将__vftable_Killable
分配给我们的Dog
?好吧,我在这里使用了IDA 7.0的命名方式,我之前提到的是它不再标记哪个vftable映射到哪个基类(与脚本不同)。使用IDA 6.0或我的IDAPython脚本,它会显示:Dog *__thiscall Dog::ctor(Dog *this)
{
j_Animal::ctor((Animal *)this);
j_Killable::ctor((Killable *)&this->__vftable_Killable);
this->__vftable = (Dog_vftable *)&Dog::`vftable for Animal';
this->__vftable_Killable = (Killable_vftable *)&Dog::`vftable for Killable';
return this;
}
现在这些名称不一样,更有意义,所以不要被IDA 7烦恼所困扰,双击这些名称以检查它们的实际位置。
我不建议将vftable的实际类型设置为它们的位置/名称:您一无所获,这只会使反汇编输出混乱。
奖金:从使用多重继承(ww)的类继承的类
这使我有些困惑,这也许是因为我依赖的网络上的大多数教程都没有涵盖它,或者可能是因为我只是傻瓜让我们从
Terrier
继承我们的Animal
类。class Terrier : public Dog {
int _annoyanceLevel;
virtual void setAge(int value) { _age = value; }
virtual void makeSound() { cout << "Bark Bark not Woof Woof"; }
virtual void annoy() { _annoyanceLevel++; }
};
MSVC布局:似乎不太特殊。
Terrier
vftable仍合并了我们Dog
的新虚拟方法,其他所有东西都有其单独的vftable:class Terrier size(24):
+---
0 | +--- (base class Dog)
0 | | +--- (base class Animal)
0 | | | {vfptr}
4 | | | _age
| | +---
8 | | +--- (base class Killable)
8 | | | {vfptr}
12 | | | _isDead
| | | <alignment member> (size=3)
| | +---
16 | | _furColor
| +---
20 | _annoyanceLevel
+---
Terrier::$vftable@Animal@:
| &Terrier_meta
| 0
0 | &Terrier::setAge
1 | &Terrier::makeSound
2 | &Dog::setFurColor
3 | &Terrier::annoy // Animal even takes the Terrier methods (greedy!)
Terrier::$vftable@Killable@:
| -8
0 | &Killable::kill
1 | &Dog::makeDeathSound
IDA表示形式:与我们第一个
Dog
结构创建非常相似,只是我们还需要尊重q4312079q的第二个基类。struct Terrier_vtbl : Dog_vtbl {
void (__thiscall *annoy)(Terrier *this);
};
struct Terrier_mbrs : Dog_mbrs {
int _annoyanceLevel;
};
struct Terrier {
Terrier_vtbl *__vftable;
Animal_mbrs __members_Animal;
Killable_vtbl *__vftable_Killable;
Killable_mbrs __members_Killable;
Terrier_mbrs __members;
};
评论
工作需要“ __thiscall”吗?
– savram
17-10-30在12:01
是的,否则,类方法和反编译器输出的调用约定格式不正确。 (评论已回答)。
–雷
18/12/17在11:16
作为参考,这里是7.2+生成的内容:hex-rays.com/products/ida/support/idadoc/1691.shtml
–Trass3r
10月1日10:23
这是一个传奇的答案,谢谢
–GuidedHacking
11月7日20:21
评论
有趣的是,我通过编写自己的反汇编程序为类繁重的可执行文件解决了一次。在当前状态下,它可以从几个DLL中收集类成员和函数。但不是一个通用的解决方案:该代码非常依赖于编写该代码的特定编译器。为什么我不赞成投票?
尽管这个问题已经很老了,请尝试对Hexrays使用HexRaysPy工具