---------[ Reversing Virtual Functions ]

> 01 Dec 2024
> Author: heap

Virtual function is used to help code polymorphism and it is a key feature of inheritance, allowing developers to write flexible code. However, this feature can make the code more challenging to comprehend and analyze, especially for reverse engineers. Complicating the flow of execution while determining which function to invoke at runtime based on the object's type.

To see how it works, I have coded a simple application in C++:

#include <iostream> class Animal { public: virtual void sound(){ std::cout << "Default Sound\n"; }; virtual void kind() { std::cout << "Default Kind\n"; } }; class Dog : public Animal { public: void sound() override { std::cout << "Woof\n"; }; void kind() override { std::cout << "Bulldog\n"; } }; void vcall (Animal *a){ a->sound(); a->kind(); } int main () { Dog* dawg = new Dog(); Animal* anim = new Animal(); vcall(dawg); vcall(anim); delete dawg; delete anim; return 0; }

Our Animal class is the base class. And with Dog sub-class, sound() and kind() functions are overridden. With vcall(Animal *a) function, we can use both of the base and sub-class. Let's have a look what's going on under the hood.

Running it with radare2 (UNIX-like Disassembler/Debugger. You can use your favorite):

Firstly, it allocates 8 bytes for the pointer using the new operator and calls the Dog::Dog() constructor, then stores the pointer address in the stack (var_20h).

It does the same process for the base class. 2 different areas in the memory have been allocated for 2 different classes. Each being passed into the vcall() function.

Here, we see an unusual way of calling a function. That, is the indirect function call. It uses a register to make a function call unlike the direct function calls. So when statically analyzed, we can't know the address of the rdx register because it is dynamically allocated in the memory and determined at runtime. This is done with the Virtual Function Table.

What is a Virtual Function Table?

VFTable is simply an array that stores pointers to virtual functions. That being said, each class with virtual functions has its own VFTable. And the compiler adds an additional vpointer (VFTable pointer) member to the class.

As the diagram shows, 2 different VFTable can have an entry that points to the same function address.

If we get to our application, first argument is the entry address of the VFTable (vpointer).

After getting the entry address, it dereferences the pointer and goes to that VFTable.

First call is going to be our Dog::sound() function that we have overridden from the Animal class. As the offset is 0 (no operation has been made to the pointer yet), it is our first entry in the VFTable. But afterwards, we can see that the code adds 8 to the rax register that stores the pointer of our VFTable entry. As you know (hopefully), each pointer is 8 bytes due to the x64_86 architecture. Now the offset is 8, rax points to our second function which is Dog::kind(). The application prints "Woof" and "Bulldog" respectively. Since it is the same with the base Animal class, i am not going to provide screenshots of that.

Even though it is pain in the arse to reverse engineer, it is fun to mess with virtual functions and polymorphic code.

References



==[EOF]==


revdiaries.com © 2025