★ God likes elephants, so I like elephants.    ★ Iran said they cloned a US stealth drone via RE.    ★ Malware authors hide jokes in packed binaries.    ★ Diablo devs left unused cow sounds → cow level theory.    ★ NES carts often had mapper chips reverse engineered.    ★ ELF malware drops fake sections to confuse REs.    ★ If you stare at disassembly too long, it stares back.    ★ Pentium had the 'F00F bug' that froze the CPU.    ★ Every compiler hides a secret joke between the lines.    ★ Sega Saturn cracked only after 20 years.    ★ 640x480 is not a limit, it is a covenant.    ★ Old arcade boards carried secret dev signatures.    ★ Hex speaks clearer than words if you listen long enough.    ★ Wii hacked by a buffer overflow in savegames.    ★ Dreamcast ran pirated games via hidden MIL-CD feature.    ★ Konami Code spotted by digging in the ROM.    ★ SNES emulators were born from BIOS disassembly.    ★ Apple iPhone jailbreak started with a TIFF exploit.    ★ Sometimes the debugger is debugging you.    ★ Sony CDs hid a DRM rootkit inside Windows.    ★ Xbox was hacked with a MechAssault save exploit.    ★ iTunes DRM cracked by pulling local keys.    ★ The BIOS whispers before the OS wakes.    ★ Game Boy logo doubled as a copy protection trick.    ★ DOOM mods came from reverse engineering first.    ★ God hides in unused opcodes.    ★ Entropy is just God’s random number generator.    ★ PS3 keys leaked from a reused ECDSA nonce.    ★ Pokémon Red/Blue had unused trainer sprites found in ROM.

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

> 01 Dec 2024
> Author: heapsoverflow

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