将类的 vtable 的静态地址与对象持有的指向它的指针进行比较

逆向工程 linux 海湾合作委员会 指针 虚表
2021-06-25 20:18:22

我知道这是编译器/ABI 相关的,不一定是标准化的,等等。我一直假设,从我在几个地方读到的内容(例如这里的答案维基百科中的示例),编译器所做的典型事情是Class在类型为 的对象的开头有一个指向 vtable 的指针Class

我现在正在调试和检测一个接收Class*参数的函数Class有虚函数。我想确定它是否属于我关心的类我可能还想用它做其他事情,所以我试图得到一个很好的理解。

我查看了 Ghidra 中的反汇编,我发现有关该类的 vtable 的内容(粘贴来自我编写的示例,但它等效于我想要 RE 的真正二进制文件):

                             **************************************************************
                             * vtable for DerivedClass                                    *
                             **************************************************************
                             _ZTV12DerivedClass                              XREF[3]:     Entry Point(*), 
                             DerivedClass::vtable                                         operator():001039d1(*), 
                                                                                          _elfSectionHeaders::00000650(*)  
        00107c70 00 00 00        ptrdiff_
                 00 00 00 
                 00 00
           00107c70 [0]                                  0h
        00107c78 88 7c 10        addr       DerivedClass::typeinfo                           = 
                 00 00 00 
                 00 00
                             PTR_ARRAY_00107c80                              XREF[2]:     operator():001039d8(*), 
                                                                                          operator():001039dc(*)  
        00107c80 94 42 10        addr[1]
                 00 00 00 
                 00 00
           00107c80 94 42 10 00 00  addr      DerivedClass::someVirt  [0]                               XREF[2]:     operator():001039d8(*), 
                    00 00 00                                                                                         operator():001039dc(*)  

这就是我的惊喜所在:

  1. 我得到了_ZTV12DerivedClass(例如通过 Frida,Module.findExportByName(null, "_ZTV12DerivedClass")或在 GDB 中info address _ZTV12DerivedClass)的地址。
  2. 我再次在调试器中或使用 Frida 获取对象的 vtable 的地址(它是 64 位,所以是对象的第一个字节 8 个)。
  3. 我比较了两者,我发现它们相距 16 个字节,即 2 个指针的大小:对象实例中的指针不是指向vtable 的开头,而是在指向虚函数的指针数组的开头!

这当然不是为了比较指针而惊天动地。只需添加 16。我已经看到前两个指针应该是什么的解释。但我有一些疑问:

  1. 假设 16 的差异在同一个二进制文件中的所有类中保持一致是否可靠?
  2. 这在更多的编译器中很常见吗?
  3. 表中的两个位置(它的最开始和虚函数指针数组的开始)是否有任何命名约定?我发现令人惊讶的是,有一个指向某物的指针,而不是在反汇编中发现的 vtable 的开始。
  4. 可能是一些历史遗产?如果 C++ 首先有虚函数但没有虚继承或 RTTI,这可能解释了这两个额外的指针被添加到顶部以不改变其他一些假设。
1个回答

安腾C ++ ABI有时不是关于究竟是“虚函数表”非常清楚。

实际上,符号例如_ZTV12DerivedClass指向完整的 vtable 结构,其中包括它之前的两个指针大小的槽(RTTI 指针和到顶部的偏移量)。

因此,要获得虚函数指针表(通常理解为“虚表”)的开头,您必须将 16(或在 32-but 平台上为 8)添加到符号的地址。