In fact, this article is not really about a killer feature and this is also not very complicated.I decided to write this post to share with you the fun I had playing with the C++ dark internal mechanism. Or maybe this is not even fun anyway …
Some time ago, while I was reading articles about the C++ memory layout of data structures to achieve a reflection system to introspect memory at runtime, I asked me a question. I was wondering if the first member attribute of a class was always placed at the beginning of the memory layout.
1 2 3 4 5 6 7 8 |
class Foo { public: int foo = 0; int bar = 0; }; Foo foo; |
For a given instance of the Foo class, I was wondering if the address of the instance is equivalent to the address of its first member. For this particular case, the answer is yes. We can represent this situation as follow :

I tried many different situations, with inheritance, with complex data structures, with template etc. I encountered one case that breaks my hypothesis, virtual methods. Having only inheritance is not sufficient, we also need at least one virtual method to get a different memory layout. But what happened ? It was a very good question but I knew it has something to do with the well-known virtual table.
Disclaimer
The following code and assumptions are compiler implementation dependent even if it will run with Visual C++ and g++.
After some tests with virtual methods, I figured out that there are always 4 or 8 bytes (depending if the compiler is 32 or 64 bits) before the other members. It was the virtual table pointer called also the vpointer. In fact, the virtual table is one of the possible way to have the virtual method mechanism. In C++, this is most of the time a pointer to an array of function pointers, and this vpointer will be the first member of the class.

Let’s define our scenario.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class Behavior { public: virtual void Awake () { std::cout << "Behavior Awake" << std::endl; } virtual void Start () { std::cout << "Behavior Start" << std::endl; } virtual void Update () { std::cout << "Behavior Update" << std::endl; } virtual void Hack () { std::cout << "Your vtable has been hacked" << std::endl; } }; class GameScript : public Behavior { public: void Awake () final { std::cout << "GameScript Awake" << std::endl; } void Start () final { std::cout << "GameScript Start" << std::endl; } void Update () final { std::cout << "GameScript Update" << std::endl; } }; |
We simply have a class Behavior with some virtual methods and a derived class GameScript that overrides these methods. The method Behavior::Hack() is for further use.
1 2 3 |
GameScript game_script; Behavior* p_behavior = &game_script; p_behavior->Update(); |
Without any surprise, this code displays “GameScript Update”, not really interesting. And our vpointer ? There it is :
1 |
auto** vptr = (uintptr_t**)p_behavior; |
But… wh… ? As we previously said, the vpointer is a pointer to an array of function pointers. A same object can have multiple vpointers in case of multiple inheritance.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class Base1 { public: virtual void FooBase1() {} }; class Base2 { public: virtual void FooBase2() {} }; class Derived : public Base1, public Base2 { public: void FooBase1() final {} void FooBase2() final {} }; Derived derived; auto** vptr_1 = (uintptr_t**)&derived; auto** vptr_2 = (uintptr_t**)(((char*)&derived) + sizeof(uintptr_t*)); |
This code works fine with g++ because the result memory layout of the derived class is the addition of the base classes memory layouts (in the right order). The base classes have no attributes expect their vpointers, this is pretty straight forward to compute the vpointer addresses.The cast to char* is necessary to use the right pointer arithmetic. Moreover static_cast is avoided here to bypass some limitation and for readability too (always prefer using static_cast in C++).
Now we know how to get the vpointer but we don’t know how to use it. Back to our example with the GameScript and the Behavior classes, we will write one method to “hack” the vtable of our class.
1 2 3 4 5 6 |
void HackVTable(uintptr_t** vtable) { (*vtable)[0] = (*vtable)[3]; (*vtable)[1] = (*vtable)[3]; (*vtable)[2] = (*vtable)[3]; } |
If we run this code, it will actually crash because the vtable is located in a read-only memory chunk. We can get rid of this protection with a single call to the windows c++ api :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
void HackVTable(uintptr_t** vtable) { HANDLE process = GetCurrentProcess(); DWORD protection = PAGE_READWRITE; DWORD oldProtection = 0; if(VirtualProtectEx(process, (void *)*vtable, sizeof(uintptr_t), protection, &oldProtection) == 0) { std::cerr << "Unable to changes vtable memory state" << std::endl; return; } else { (*vtable)[0] = (*vtable)[3]; (*vtable)[1] = (*vtable)[3]; (*vtable)[2] = (*vtable)[3]; } } |
And that’s it. You can run this code :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
GameScript game_script; Behavior* p_base = &game_script; auto** vptr = (uintptr_t**)p_base; p_base->Awake(); p_base->Start(); p_base->Update(); HackVTable(vptr); p_base->Awake(); p_base->Start(); p_base->Update(); |
output :
1 2 3 4 5 6 |
GameScript Awake GameScript Start GameScript Update Your vtable has been hacked Your vtable has been hacked Your vtable has been hacked |
All the entries of the vtable are now pointing to the same user defined method.

You should have a better comprehension of how virtual methods work under the hood but the goal of this kind of code is experimentation. This is not intended to be used somewhere else than experimental code and it may result into an unstable application since the implementation of virtual methods is compiler dependent. Keep it in mind.
You can find the complete source code here on my GitHub in the repository Articles.
Thanks for reading !
Leave a Reply