Skip to content
This repository has been archived by the owner on Apr 22, 2024. It is now read-only.

Getting SDL offsets

8dcc  edited this page Apr 20, 2024 · 4 revisions

Introduction

For rendering the menu, the game needs to hook SDL_GL_SwapWindow and SDL_PollEvent. In cs:go, we can get each function with dlsym() and replace a pointer inside it (osiris, thread). We would get those functions from GAMEDIR/bin/linux64/libSDL2-2.0.so.0. On tf2, however, we only have GAMEDIR/bin/libSDL2-2.0.so.0, and there is no jmp instruction to patch there.

Finding the offset

Finding the offsets is not hard, we just have to open the library in IDA and look for the function. The symbols are exported so we can just Ctrl + L.

Once we find one of the functions, it should look something like this:

The offset we want will be the second off_*, the one in the call instruction.

Getting to the offset from C

Now we have to get the location from C. First of all, what kind of pointer is this? This is a pointer to a function pointer. To make it more visual, I typedef’d it:

/* In the cheat this is declared with the DECL_SDL_FUNC macro.
 * Note that it's a pointer to the function pointer we just typedef'd */
typedef int (*PollEvent_t)(SDL_Event* event);
PollEvent_t* PollEventPtr;

Now that we have the offset, we can get it with my GET_OFFSET macro. The macro simply casts the void* handler we pass it to a link_map struct, and then adds the offset to the l_addr member.

#define GET_OFFSET(HANDLER, OFFSET) \
    ((void*)(((struct link_map*)HANDLER)->l_addr) + OFFSET)

void* h_sdl2 = dlopen("./bin/libSDL2-2.0.so.0", RTLD_LAZY | RTLD_NOLOAD);
PollEventPtr = (PollEvent_t*)GET_OFFSET(h_sdl2, 0xFCF64);

The link_map structure is defined in <link.h>, and it’s the following:

struct link_map {
    ElfW(Addr) l_addr; /* Difference between the address in the ELF file and the address in memory */
    char* l_name;      /* Absolute pathname where object was found */
    ElfW(Dyn) * l_ld;  /* Dynamic section of the shared object */
    struct link_map *l_next, *l_prev; /* Chain of loaded objects */

    /* Plus additional fields private to the implementation */
};

Hooking the functions

For hooking we can just store and replace the pointer. I made a HOOK_SDL macro for this:

#define HOOK_SDL(name)       \
    ho_##name  = *name##Ptr; \
    *name##Ptr = h_##name;

/* For storing the original function ptr. Note that its not a PollEvent_t* since
 * this is a direct pointer to the function */
PollEvent_t ho_PollEvent = NULL;

/* Our hook */
int h_PollEvent(SDL_Event* event);

/* Hook it */
HOOK_SDL(SwapWindow);

We can call the original with the ORIGINAL macro just like any other VMT hook.

Update: 64-bits

With the new 64-bit update, this is no longer needed, since you can just patch the jmp instruction inside SDL_PollEvent and SDL_GL_SwapWindow.