C++虚表引出的问题。黑客指日可待。
2019-08-07 本文已影响0人
晴空一垩
笔者环境:
$ gcc --version
gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516
$ uname -a
Linux debian9-64-Desktop 4.9.0-8-amd64 #1 SMP Debian 4.9.110-3+deb9u6 (2018-10-08) x86_64 GNU/Linux
虚表:
#include<iostream>
typedef void (*fun)(void) ;
class A{
private:
virtual void printA(){
std::cout<<"hello A"<<std::endl;
}
virtual void printB(){
std::cout<<"hello B"<<std::endl;
}
void printC(){
std::cout<<"hello C"<<std::endl;
}
};
int main(int argc,char * argv[]){
A a;
long* pa =(long*)&a;
long* ppa = (long*)(*pa);
void (*funA)(void) =(void (*)(void))(*ppa);
funA();
fun funB =(fun)(*(++ppa));
funB();
return 0;
}
问题一:
请问上例中的,sizeof(A)
有多少个字节?
在64位机,是8个字节
在32位机,是4个字节,
问题二:
请问上例中,为什么要使用 long *
呢?
因为笔者的环境是64位机,
void*
是占用8个字节的。所以这里使用long
方便我调用下一个虚方法,只需要通过long* +1
即可
问题三:
请问上例中,为啥会取了那么多次的指针,分别是干什么的。
long* pa =(long*)&a;
表示取得类的地址
long* ppa = (long*)(*pa);
表示取得虚表的地址
void (*funA)(void) =(void (*)(void))(*ppa);
表示取得在虚表中首个虚函数的地址
++ppa
表示地址往后偏移8个字节,就是第二个虚函数的地址
问题四:
请问上例中,如果编译出可执行文件,可以看得到 printC
的函数符号吗?为什么?
让我们执行编译然后验证一下:
$ g++ -I. main.cpp $ objdump -Tt a.out |grep print 0000000000000b0a w F .text 0000000000000037 _ZN1A6printBEv 0000000000000ad2 w F .text 0000000000000037 _ZN1A6printAEv
经过 粉碎命名 ,但是也依旧可以看到是没有
printC
的签名的。那我们换种方式
$ strings a.out |grep print vfwprintf printB vwprintf vswprintf printA _ZN1A6printAEv printC _ZN1A6printBEv _ZN1A6printCEv _ZN1A6printBEv _ZN1A6printAEv
可以看到也是有的,证明这个符号在,并且存在在代码区。
问题五:
请问上例中,如果我要按照你这样调用 printC
可不可以呢?
经过一通询问,与查找,发现,可以使用这里的文章所列举的方法进行调用