12.IDA-虚函数和虚表

vtable

编译器会为每一个包含虚函数的类(或通过继承得到的子类)生成一个表,其中包含指向类中每一个虚函数的指针,这样的表就叫做虚表(vtable)

__vfptr

每个包含虚函数的类对象都获得__vfptr指针,并且是对象的第一个数据成员

编译器必须要保证虚函数表的指针存在于对象实例中最前面的位置

在计算对象的总大小时,也必须考虑到虚表指针。比如new,传递给new的大小值不仅包括类(以及任何超类)中的所有显式声明的字段占用的空间,而且包括虚表指针所需的任何空间
示例代码:

class Base
{
public:
    virtual void vf1() = 0;
    virtual void vf2(){ cout<<"Base::vf2"<<endl; }
    virtual void vf3(){ cout<<"Base::vf3"<<endl; }
    virtual void vf4(){ cout<<"Base::vf4"<<endl; }
private:
    int x,y;
};

class Sub: public Base
{
public:
    virtual void vf1(){cout<<"Sub::vf1"<<endl; }
    virtual void vf3(){cout<<"Sub::vf3"<<endl; }
    virtual void vf5(){cout<<"Sub::vf5"<<endl; }
private:
    int z;
};

void call_vf(Base* b)
{
    b->vf3();
}

int _tmain(int argc, _TCHAR* argv[])
{
    Base *b = new Sub;
    call_vf(b);

IDA分析如下:
这里写图片描述

值得注意的是,虚表索引操作非常类似于结构体引用操作。实际上,它们之间并无区别
。因此,我们可以定义一个结构体来表示一个类的虚表的布局,然后利用这个已定义的结构体来提高反汇编代码清单的可读性
这里写图片描述
这个结构体允许将虚表引用操作重新格式化成以下形式:
这里写图片描述
注意:

C++虚函数绝不会被直接引用,也绝不应成为调用交叉引用的目标。所有C++虚函数应由至少一个虚表条目引用,并且始终是至少一个偏移量交叉引用的目标。

我们随便找到 vf1函数定义,然后点击它的交叉引用:
这里写图片描述
从而跳转到虚表,其显示如下:

.rdata:004031B8 const Base::`vftable' dd offset __purecall ; DATA XREF: Base::Base(void)+A↑o
.rdata:004031BC                 dd offset Base::vf2(void)
.rdata:004031C0                 dd offset Base::vf3(void)
.rdata:004031C4                 dd offset Base::vf4(void)
.rdata:004031C8                 dd offset const Sub::`RTTI Complete Object Locator'
.rdata:004031CC const Sub::`vftable' dd offset Sub::vf1(void)
.rdata:004031CC                                         ; DATA XREF: Sub::Sub(void)+12↑o
.rdata:004031D0                 dd offset Base::vf2(void)
.rdata:004031D4                 dd offset Sub::vf3(void)
.rdata:004031D8                 dd offset Base::vf4(void)
.rdata:004031DC                 dd offset Sub::vf5(void)

可以看到,类构造函数Sub:: Sub(void)使用了虚表的地址Sub::Sub(void)+12↑o, 那是因为new sub 调用了sub的隐式构造函数
这里写图片描述
windbg观察:
这里写图片描述
这里写图片描述
注意,Base的虚表第一个是002816e4 Test!purecall,这时因为在Base中,没有针对纯虚函数vf1的实现,所以在Base的虚表中并没有存储vf1的地址,这时,编译器会插入一个错误处理函数的地址,通常,该函数名为purecall,一旦它被调用,程序就会被终止


可以观察到:

1.new指针不为空,才会调用构造函数,这是由Visual C++的内部机制来保证的
2.函数的虚表位置一般都可以在构造函数中找到(很明显,this指针指向的地址就是虚表的位置)

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
IDA是一款非常强大的反汇编工具,它不仅可以将二进制文件反汇编成汇编代码,还可以对汇编代码进行分析和调试。在IDA中,我们可以进行文本搜索、二进制搜索和替换16进制字节序列等操作。 1. 文本搜索 在IDA中,我们可以使用快捷键Ctrl+F或者点击菜单“Edit”->“Find”来进行文本搜索。在搜索框中输入要查找的文本,点击“Find”按钮即可。如果要查找下一个匹配项,可以使用快捷键F3或者点击菜单“Edit”->“Find Next”。 2. 二进制搜索 在IDA中,我们也可以进行二进制搜索。首先,我们需要打开“Hex View-A”窗口,该窗口可以显示二进制文件的16进制表示。在该窗口中,我们可以使用快捷键Ctrl+F或者点击菜单“Search”->“Find Bytes”来进行二进制搜索。在搜索框中输入要查找的16进制字节序列,点击“OK”按钮即可。如果要查找下一个匹配项,可以使用快捷键F3或者点击菜单“Search”->“Find Next”. 3. 替换16进制字节序列 在IDA中,我们也可以替换16进制字节序列。首先,我们需要打开“Hex View-A”窗口。在该窗口中,我们可以使用快捷键Ctrl+R或者点击菜单“Search”->“Replace Bytes”来进行替换操作。在弹出的对话框中,输入要查找的16进制字节序列和要替换的16进制字节序列,然后点击“OK”按钮即可。如果要替换所有匹配项,可以点击“Replace All”按钮。 以上就是IDA中文本搜索、二进制搜索和替换16进制字节序列的操作方法。在实际使用中,这些操作非常实用,可以帮助我们更快地定位和解决问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值