TheMaxus

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:

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.