vtable
ISO 표준
Itanium ABI
MSVC ABI
테스트 환경
ubuntu 22.04 WSL
class Base {
public:
Base(){}
~Base(){}
int b0 = 10;
int b1 = 20;
int* b2 = &b0;
int* b3 = &b1;
void print() { std::cout << "test" << std::endl; }
};
class Derive : public Base {
public:
int d0 = 11;
int d1 = 21;
int* d2 = &d0;
int* d3 = &d1;
void print2() { std::cout << "test" << std::endl; }
};
class Base2 {
public:
Base2(){}
~Base2(){}
int b0 = 30;
int b1 = 40;
int* b2 = &b0;
int* b3 = &b1;
virtual void print() { std::cout << "test" << std::endl; }
};
class Derive2 : public Base2 {
public:
int d0 = 31;
int d1 = 41;
int* d2 = &d0;
int* d3 = &d1;
void print() override { std::cout << "test" << std::endl; }
};
pwndbg> x/8gx *(void**)($rbp-0x20)
0x55555556b300: 0x0000555555557d38 0x000000280000001e
0x55555556b310: 0x000055555556b308 0x000055555556b30c
0x55555556b320: 0x000000290000001f 0x000055555556b320
0x55555556b330: 0x000055555556b324 0x000000000000ecd1
pwndbg> x/8gx *(void**)($rbp-0x28)
0x55555556aeb0: 0x000000140000000a 0x000055555556aeb0
0x55555556aec0: 0x000055555556aeb4 0x000000150000000b
0x55555556aed0: 0x000055555556aec8 0x000055555556aecc
0x55555556aee0: 0x0000000000000000 0x0000000000000411
virtual 함수가 있어야만 vtable 포인터가 생성된다.
+0x00: int b0
+0x04: int b1
+0x08: int* b2 (&b0)
+0x10: int* b3 (&b1)
+0x18: int d0
+0x1C: int d1
+0x20: int* d2 (&d0)
+0x28: int* d3 (&d1)
+0x00: void* vptr (vtable 엔트리들을 가리킴)
+0x08: int b0
+0x0C: int b1
+0x10: int* b2 (&b0)
+0x18: int* b3 (&b1)
+0x20: int d0
+0x24: int d1
+0x28: int* d2 (&d0)
+0x30: int* d3 (&d1)
Derive2 클래스 vtable의 offset-to-top 부터 읽어보면, 이런 형식으로 되어있다.
vtable[0]은 override된 print함수이다.
pwndbg> x/4gx (*((void**)*(void**)($rbp-0x20)) - 0x10)
0x555555557d28 <_ZTV7Derive2>: 0x0000000000000000 0x0000555555557d58
0x555555557d38 <_ZTV7Derive2+16>: 0x00005555555553f8 0x0000000000000000
pwndbg> x/10i **(void***)*(void**)($rbp-0x20)
0x5555555553f8 <_ZN7Derive25printEv>: endbr64
0x5555555553fc <_ZN7Derive25printEv+4>: push rbp
0x5555555553fd <_ZN7Derive25printEv+5>: mov rbp,rsp
0x555555555400 <_ZN7Derive25printEv+8>: sub rsp,0x10
pwndbg> x/6gx *(void**)(*((void**)*(void**)($rbp-0x20)) - 0x8)
0x555555557d58 <_ZTI7Derive2>: 0x00007ffff7fa3c30 0x0000555555556010
0x555555557d68 <_ZTI7Derive2+16>: 0x0000555555557d70 0x00007ffff7fa2fa0
0x555555557d78 <_ZTI5Base2+8>: 0x0000555555556019 0x0000000000000001
RTTI 객체는 7d58이고, 구조는 아래와 같다.
[0] vptr to vtable of __si_class_type_info
[1] const char* name
[2] const __class_type_info* base_type
그래서 실제로 읽어보면 [1]6010은 Derive 문자열이고, [2]7d70은 Base 클래스의 RTTI라서 [2][1]은 Base 문자열인 것을 확인할 수 있다.
pwndbg> x/1s 0x555555556010
0x555555556010 <_ZTS7Derive2>: "7Derive2"
pwndbg> x/2gx 0x0000555555557d70
0x555555557d70 <_ZTI5Base2>: 0x00007ffff7fa2fa0 0x0000555555556019
pwndbg> x/1s 0x0000555555556019
0x555555556019 <_ZTS5Base2>: "5Base2"
Comments