实际上,我正在尝试学习一些有关vtable溢出的知识。因此,我的学习文档指出:


要实现的主要要点是,每当我们使用虚拟方法声明C ++类
时,该内存所在的池(堆,
,堆栈等)现在包含指向函数指针表的指针,该表最终将用于调用该函数。如果发生
溢出,我们可以覆盖此指针值,以便下次调用虚拟方法时将调用我们的代码。


因此,我的问题是,如何找到vtable指针的位置?

我的意思是,我必须像尝试从某些模块中查找基址时一样,通过PEB进行搜索。或者,这是针对每种情况的吗?

#1 楼

这取决于编译器-编译器可以始终将vtable放置在所需位置。但是,在大多数情况下,vtable指针是生成的结构的第一个元素(偏移量为0)。 br />
class test {
    int a;
    int b;
    test()          { ...; }
    ~test()         { ...; }
    void somefunc() { ...; }
    int c;
}


so(假设指针和整数均为4个字节),则vtable的偏移量为0,a偏移为4,b偏移为8,c偏移为12.。 >
请注意,并非所有编译器都使用此约定。例如,Watcom C ++ 386编译器根本不使用vtable,而是将函数指针与数据混合在一起。 (我知道这种情况,因为我曾经拆解过20年前用Watcom编译的游戏。并不是说我希望您能在现代编译器中看到这种布局,只是为了提供一个示例,说明它可能会有所不同):偏移量0和4的条目(再次假设4个字节整数/指针)是该类的无参数构造函数和析构函数,其余部分混合使用变量和方法在类定义中出现的顺序。当然,这是极其低效的,因为编译器必须在实例化对象时初始化每个类方法,而不是仅设置一个指向vtable的指针。
vtable指针是类结构的第一个元素,但是您确实需要知道使用了哪个编译器以及该编译器具有哪些约定。

另一件事-您在原始版本中谈到了“ vtable溢出”发布。您的“正常”利用不会使vtable溢出;在您的程序启动时,vtable会被预先初始化,并且(通常)永远不会更改。要编写漏洞利用程序,您可以:


使用缓冲区溢出来修改vtable中的函数指针,因此下次调用类方法时,将改为执行代码
使用缓冲区溢出来修改类实例的vtable指针,因此,下次该类实例执行一个方法时,将使用您的vtable而不是另一个vtable。 ,甚至可能被编译器放置在只读内存段中,您的一般利用会忽略1.并使用2.