Tomb Raider: Legend SteamStub research


2019-05-11

tl;dr: Set entrypoint to 0x00AE7488, remove the .bind section and rebuild the executable.


The executable is protected by an older version of SteamStub:

Removing SteamStub

There’s an unconditional jmp to 0x00EE7488 towards the end of the .bind section. Convert 0x00EE7488 to an offset, and you’ll get 0x00AE7488, which is the OEP.

.bind:015755DD loc_15755DD:
.bind:015755DD                 popa
.bind:015755DE                 mov     eax, offset sub_EE7488
.bind:015755E3                 jmp     eax
.bind:015755E3 start           endp
.bind:015755E3
.bind:015755E3 ; 

Using CFF Explorer, set the entry point in the header to this and remove the .bind section.

SteamStub configuration

This is an “encrypted” 428-byte blob located at 0x0015755E8. It looks like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
typedef struct SteamStubConfig
{
    using GETMODULEHANDLEA = HMODULE(WINAPI*)(LPCSTR);
    using GETPROCADDRESS   = FARPROC(WINAPI*)(HMODULE, LPCSTR);

    GETMODULEHANDLEA   _GetModuleHandleA; // 32-bit
    GETPROCADDRESS     _GetProcAddress;   // 32-bit
    uint32_t           bind_start;
    uint32_t           bind_size;
    uint32_t           bind_checksum;
    uint32_t           appid;
    char               sz_appid[8];
    char               sz_kernel32[16];
    char               sz_user32[16];
    char               sz_shell32[16];
    char               sz_loadlibraryexa[16];
    char               sz_freelibrary[16];
    char               sz_messageboxa[16];
    char               sz_getmodulefilenamea[24];
    char               sz_lstrlena[16];
    char               sz_lstrcata[16];
    char               sz_exitprocess[16];
    char               sz_shellexecutea[16];
    char               sz_steamerror[16];
    char               sz_steamdll[16];
    char               sz_steamappissubscribed[24];
    char               sz_steamstartup[16];
    char               sz_steamcleanup[16];
    char               sz_failedtofind[32];
    char               sz_failedtoload[32];
    char               sz_steamstore[24];
    char               sz_steamrun[24];
    char               sz_open[8];
} SteamStubConfig;

The decryption algorithm located right at the start of the .bind section. Decompiled, it is as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
void decrypt_config(void *_buf, size_t size)
{
    uint8_t *buf = _buf;

    for(size_t i = 0; i < size; ++i)
    {
        int32_t key = (i * i) & 0x800000FF;

        /* NB: This branch is never taken. */
        if(key < 0)
            key = ((key - 1) | 0x0FFFFFF00) + 1;

        buf[i] = (buf[i] ^ key);
    }
}

Once decrypted, it looks like:

Debug View of the decrypted SteamStub configuration