Reverse engineering fake xlive.dll for Fallout 3
While modding Fallout 3 and installing GFWL remover I’ve become curious on how GFWL remover actually works so naturally I’ve decided to fire up Ghidra and try to reverse engineer it and in this article I’m going to walk you through the process. I would also like credit the author of this DLL timeslip, who has many other projects related to Creation Engine games as well as other games.
Tools used:
- Ghidra
- Dependency Walker
- Visual Studio 2022
Examining the file
From error messages like “Ordinal 53 not found” when GFWL is missing or simply by using Dependency Walker we know that Fallout 3 imports functions by ordinal instead of using function names. Using Dependency Walker we can also find out what exactly does Fallout 3 import from this library.
After importing the DLL in Ghidra and examining the code we find out that the DLL contains basically empty functions with constant return values. In the assembly window Ghidra tells us that functions use __stdcall calling convention. In the Microsoft Developer Documentation we can see that it means that parameters passed by pushing them onto the stack and the callee function cleans up the stack after execution.
How __stdcall works on a lower level
So we figured out that the program uses __stdcall calling
convention which means that the caller function passes the arguments by
pushing n number of bytes to the stack and the callee function cleans up
the stack by calling the ret
instruction. This instruction
does two things. Firstly if function has arguments passed to it, the
instruction pops n number of bytes from the stack, then it returns from
subroutine and the program continues execution.
Also this might be considered a corner case, but even when normally reverse engineering programs Ghidra oftentimes doesn’t show function arguments correctly. At the first examination I missed this and spent way too much time debugging why the game was crashing with my version of GFWL remover.
**************************************************************
* *
* FUNCTION *
**************************************************************
undefined4 __stdcall f5256(void)
undefined4 EAX:4 <RETURN>
0x1060 5256 f5256
Ordinal_5256 XREF[2]: Entry Point(*), 1000681c(*)
f5256
10001060 83 c8 ff OR EAX,0xffffffff
10001063 c2 14 00 RET 0x14
In this example, where the arguments would normally be shown, Ghidra
only shows that the function has a 4-byte return value, but if we
examine the function more closely, we can see that the ret
instruction has an argument, a hex value of 0x14, which means that it
pops 20 bytes off the stack when returning, which means that function
has arguments passed to it and we need to take this into a consideration
when writing the functions.
Writing the code
The first thing we need is an entry point. We can look up a standard entry function for a DLL in Microsoft Developer Documentation.
So here’s our entry function copied straight from Microsoft documentation:
BOOL WINAPI DllMain(
HINSTANCE hinstDLL,
DWORD fdwReason,
LPVOID lpReserved )
{
switch( fdwReason )
{
case DLL_PROCESS_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Because we’re going to use Windows specific types we need to include
windows.h
in the beginning of the file.
Next we’ll need to write the functions.
Because we don’t use any of the arguments that are passed to the functions, we don’t care about names and types they have as long as arguments occupy exactly the same amount of memory on stack. Also because we don’t know how return values affect execution of the program, we make sure that the functions return exactly the same value as in the original and return type has the same size as the original function.
Here’s how aforementioned function looks like when written in C:
DWORD __stdcall f5256(DWORDLONG arg, DWORDLONG arg1, DWORD arg2) {
return -1;
}
DWORDLONG
has a size of 8 bytes and DWORD
a
size of 4 bytes, which means that our function has a return size of 4
and when summed together the arguments take 20 bytes of space which is
0x14 in hex, which is the argument of the ret
instruction
that we’ve seen previously. So our function should be nearly identical
to the function we’ve seen in the decompilation. The only thing that
should be different is you might see some new intructions added:
10001020 55 PUSH EBP
10001021 8b ec MOV EBP,ESP
10001023 83 c8 ff OR EAX,0xffffffff
10001026 5d POP EBP
10001027 c2 14 00 RET 0x14
If you’re intrested in what those instructions do, you can read more in this article.
After declaring all the functions we need to create a definitions file where we define ordinal for each function. Syntax for the file can also be found in Microsoft Developer Documentation.
Here’s an example of a definitions file:
LIBRARY XLIVE
EXPORTS
f651 @651 NONAME
f652 @652 NONAME
f5361 @5361 NONAME
f5360 @5360 NONAME
f5356 @5356 NONAME
f5355 @5355 NONAME
f5310 @5310 NONAME
f5297 @5297 NONAME
f5292 @5292 NONAME
f5278 @5278 NONAME
Here f652 is the function name, @652 is the ordinal and NONAME means that you export functions using only ordinal and not function name. This helps reduce the size of the export table.
Conclusion
After writing all the functions and writing the definitions file, or just using a Python script to do both, we can finally build the project, move the file to the game folder and voilà! The game starts! Of course for some people this might be useless now that Bethesda has officially removed GFWL from the game, but it still was a fun learning experience! Also because loaded DLL:s have access to the process memory, using this DLL you can modify any part of the game’s memory, but that’s beyond the scope of this article.