Hello all. For today’s topic, we will continue our chapter of function hooks and explore another avenue of hooking. This one involves virtual tables, and if you haven’t been exposed to these yet, you are surely in for it today. These are a personal favorite of mine, so I get to tell you all about them. This covers a lot of topics in a single post, so once again, might be a bit intimidating.
What
First of all, you need to know what a virtual table is. If you already do, you can skip ahead to “How”.
Virtual tables (often called vtables) are an assorted list of functions that are unique to a class object. Think of them as “dynamic functions”. They are declared with the keyword virtual
and are commonly used when you want to override the logic of a function within a class’ superclass. Say for example you have a class function called CanBePickedUp
. In its current implementation, anyone can pick up this item. However, if you wanted to create an item that only players on the red team can pick up, you would create a class that inherits this current class, and override the function to return true only if the player’s team is red. Here is an example of what the code would look like:
#define TEAM_RED 0
#define TEAM_BLUE 1
class Item
{
public:
virtual bool CanBePickedUp(Player *player)
{
return true;
}
};
class RocketLauncher : public Item
{
public:
virtual bool CanBePickedUp(Player *player)
{
return player->m_Team == TEAM_RED;
}
};
Invoking these functions is the same as any other function. The handy thing is that your code will magically know how to behave depending on which Item is being picked up. Here’s some more implementation code:
class Player
{
public:
void PickupItem(Item *item)
{
if (!item->CanBePickedUp(this))
return;
//...
}
int m_Team;
};
Since the RocketLauncher inherits from Item, it can be passed into the function as a parameter implicitly. Therefore, any classes that inherit from Item and implement their own CanBePickedUp
function will have their function dispatched properly. This is called dynamic dispatch. You can also invoke virtual functions manually or statically. This is useful if you want to run some code before or after executing a baseclass’ virtual function.
How
The address that holds these functions are handily stored at the “zeroth” index of a class object’s memory block. This address lists all of the virtual functions that the class has. This is what one looks like:
const Gridder::`vftable'
0000 sub_404210
0004 sub_404230
0008 sub_404250
A function is listed in an array, each of which are 4 bytes (or 8 bytes if you’re working in x64) wide.
Implementation in code looks almost exactly the same as a regular function, but under the hood, things are a bit different.
When told to invoke a virtual function, the compiler, instead of just calling the memory address that holds the function location, will instead dereference the pointer at the zeroth index of the class object, move to the desired location of the function within the dereferenced table, then call the address that is stored at the index. So, without the compiler being fancy, this is what the above PickupItem
would look like:
void PickupItem(Item *item)
{
if (!(**(bool (__thiscall ***)(Item *, Player *))item)(item, this))
return;
}
That looks terrible. Fortunately, the function is at the zeroth index so that saves a big headache from being even larger. The assembly looks pretty funky too:
push ebp
mov ebp, esp
push ecx
mov [ebp-4], ecx
mov eax, [ebp-4]
push eax
mov ecx, [ebp+8] ; Item *
mov edx, [ecx] ; Item vtable
mov ecx, [ebp+8]
mov eax, [edx] ; vtable[0]
call eax
Contrary to the classic call func_loc
manner that you may be used to, the compiler calls a register instead. The register holds the address of the function to be called, which is held in the virtual table of the object passed as a parameter.
Why
Virtual functions are a bit sneaky since they won’t show up under pseudocode or under any Xrefs-to. Sometimes malware authors use these to thwart analysts. In other cases, malware (or even video game plugins) can insert their own functions within a virtual table so that those are called instead of the ones expected by the target program, which is what you’re going to do!
The only other reason you would use a vtable hook is if you wanted to see if it gets called within the constrains of a given subroutine. If you have a massive or obfuscated function X and want to see if Foo::Bar is called within it, you would want to use a vtable hook.
Now that that mouthful is out of the way, now we can actually perform a vtable hook.
Methodology
Acquire and reverse engineer a basic program;
Find the virtual functions used and prepare to hook one of them;
Write a .DLL that implements a vtable hook;
Run the program and inject your .DLL;
Requirements
C++ knowledge;
A disassembler that can parse RTTI preferably (IDA Free does not);
A .DLL injecting program;
I’ve got a program for you to reverse engineer. It’s called gridder.exe and it’s available on my Tor site as per usual. I want you to reverse engineer it and hook one of the virtual functions used in the program.
Running the program prompts you for an X and Y value, then shows a grid of what you just entered. It also constrains your entries for X and Y to 0 <= X <= 20 and 0 <= Y <= 10.
PS C:\dev\gridder> .\gridder.exe
Enter x: 4
Enter y: 5
********************
********************
********************
********************
********************
****X***************
********************
********************
********************
********************
Now, to reverse engineer the program.
The main
function has all of the goodies that we’re really looking for, mainly the bottom (I renamed a few variables and undid casts with backslash \).
This looks much different than anything else I’ve shown. As mentioned much earlier in this post, these are functions within dword_435F48
’s virtual table. Functions in x86 are 4 bytes wide (duh), so the table shifts 4 bytes with each succinct call. The problem, though, is that you need to find out what virtual table is being used. There are no Xrefs to help you, so you need to either dynamically discover where the function is or just plain guess which type it is. Good luck on the latter, although it might be a bit obvious for this basic program.
Just set a breakpoint at one of the functions and step into the next instruction. Then, you’ll be at the function you’re looking for.
At this point, you can click on the function and press X to view where the function is located in its vtable.
You can go there and/or copy that address for later. You can stop debugging now.
Now, you have the location of the vtable you want to hook. You can inspect each of the functions listed for their functionality. There are 3 in this case.
The first 2 just set a class member field to whatever is passed as a parameter, and the 3rd draws the grid.
If you recall the pseudocode of the main function from earlier, the first 2 functions set the graph’s X and Y. Let’s hook one of those.
Grab the memory address of the first function (005F73D0
) and subtract that by the base of the program (005D0000
). It may be different for you. This gets you the offset of 0273D0
. Save this for later. Obviously, if you wanted to hook the other functions, just grab their memory offset.
Now, to write the program.
It’s a DLL, so it should be a basic DllMain entrypoint.
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
if (fdwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hinstDLL);
CreateThread(0, 0, [](LPVOID param) -> DWORD
{
//...
return 0;
}, 0, 0, 0);
}
return TRUE;
}
I went ahead and turned the thread creation into a lambda to keep this simple.
The methodology is: find the address held at the program base + offset you saved earlier and replace the function held at that address with the function you want called instead. Note that the hook function signature type should be the exact same as the target function type. And there are a few discrepancies.
Virtual functions are declared as what’s called a __thiscall
which is Windows’ manner of handling class functions, dubbed a calling convention. The first parameter (this
) is passed in the register ECX
, then the remaining parameters in the function are passed on the stack.
Pulling up the function we want to hook in IDA, it shows the function like this:
_DWORD *__thiscall sub_5D4230(_DWORD *this, int a2)
Also hovering over the parameter shows that this
is in ECX
.
Now, unfortunately, you cannot statically declare a function as a __thiscall
. The compiler will yell at you. However, there is another calling convention that can be used instead. __fastcall
is similar to __thiscall
because it also passes parameters in registers. The first parameter is passed in ECX
, but the second parameter is passed in EDX
. The remaining parameters are passed on the stack. This callconv can be used with EDX ignored.
Now that that’s out of the way, you should now be able to declare the function declaration of the hook function you want.
void __fastcall Grid_SetX(DWORD ecx, DWORD edx, int x)
Another thing to make your life easier is to replicate the structure in this
. If you successfully reverse engineered the function, you will see that X is stored in this + 4
, just beyond the vtable. Y is also stored in this + 8
. You can recreate this struct and use it as a parameter.
struct Grid
{
DWORD vtable;
int x;
int y;
};
void __fastcall Grid_SetX(Grid *grid, DWORD edx, int x)
Now, let’s just set x to a random number.
void __fastcall Grid_SetX(Grid *grid, DWORD edx, int x)
{
grid->x = 44;
}
Our hook function is ready, all that is left is to replace the vtable function with this function.
Within the lambda in DllMain:
CreateThread(0, 0, [](LPVOID param) -> DWORD
{
// Get base address of module
DWORD base = (DWORD)GetModuleHandle(0);
// Get address of vtable
LPVOID vtable = (LPVOID)(base + VTABLE_OFFS);
//g_orgfunc = *(DWORD *)vtable;
DWORD oldprotect;
VirtualProtect(vtable, 4, PAGE_EXECUTE_READWRITE, &oldprotect);
*(DWORD *)vtable = (DWORD)&Grid_SetX;
VirtualProtect(vtable, 4, oldprotect, &oldprotect);
return 0;
}, 0, 0, 0);
Done!
All that is left is to inject your newly built DLL into the program. You can use the injector from the DLL Injection post or just Google one. Make sure it’s x86.
Run gridder.exe and inject it before entering anything. Then, you will have a very different grid output.
PS C:\dev\gridder> .\gridder.exe
Enter x: 4
Enter y: 5
********************
********************
********************
********************
********************
********************
********************
********************
********************
********************
That’s all for this post. It’s a lot to cover in a single post so take it slow.
Have a great weekend, as always.
Go!
-BowTiedCrawfish