Warsow, first assault (bonus)

Warsow, first assault (bonus)

Introduction

This article is the continuation of the last post about Warsow. In this one, we will see how to achieve the same result than the useless tool describes previously but, this time, with a shared library.

The theory

If you read the article series about shared library injection, the method exposed here will not be hard to understand. This one relies on the same few steps already described:

  1. Get the base address of libcgame_x86_64.so.
  2. Compute the opponent position address.
  3. Read the memory.
  4. Use a thread

The practice

Step 1: get the base address of libcgame_x86_64.so

Like the previous Python tool, our shared library can parse the maps file but that’s not a very clever solution. The library has all the accesses to the process memory in which it is loaded so it is possible to use dlopen (man page). If the library is already loaded, it just returns a handle on it. This handle contains some information including the lib base address. Here is the code:

#include <dlfcn.h>
#include <link.h>

static char* libcgame_path = "libcgame_x86_64.so";

static void* get_libcgame_addr()
{
    void* handler = dlopen(libcgame_path, RTLD_NOW);
    if (handler == NULL)
        return NULL;
    return (void*)((struct link_map*)handler)->l_addr;
}

Step 2/3: compute the opponent position address and read its values

We just have to use the previously found offsets:

static off_t base_ptr_off  = 0x4670C0;
static off_t pos_off       = 0x1C;

/* the static pointer into libcgame */
void*  opp_ptr  = (char*)libcgame + base_ptr_off;
/* the value of opp_ptr is the address of the opp struct */
void*  opp_addr = *(char**)opp_ptr;
/* use the pos_off to get the position field (3 consecutive floats) */
float* opp_pos  = (float*)((char*)opp_addr + pos_off);

To read the position:

opp_pos[0] /* x */
opp_pos[1] /* y */
opp_pos[2] /* z */

Step 4: use a thread

If you remember, when we inject a library, its constructors are run by one of the victim process thread. So we can not make a simple infinite loop in the constructor of our library. However, the constructor can just start a thread which does the infinite reading loop:

#include <pthread.h>

static pthread_t thread;
static bool      is_running;

static void* pos_reader(void* _)
{
    /* [...] */
    while (1)
        read_the_memory();
}

/* call when the library is loaded */
__attribute__((constructor))
static void run()
{
    is_running = true;
    pthread_create(&thread, NULL, pos_reader, NULL);
}

/* call when the library is unloaded */
__attribute__((destructor))
static void stop()
{
    is_running = false;
    pthread_join(thread, NULL);
}

Final result

Here is the final code:

#include <stdio.h>
#include <stdbool.h>
#include <pthread.h>
#include <dlfcn.h>
#include <link.h>
#include <unistd.h>


/*
 * Some useful constants.
 */
static char* libcgame_path = "libcgame_x86_64.so";
static off_t base_ptr_off  = 0x4670C0;
static off_t pos_off       = 0x1C;

/*
 * Thread related variables.
 */
static pthread_t thread;
static bool      is_running;


static void* get_libcgame_addr()
{
    void* handler = dlopen(libcgame_path, RTLD_NOW);
    if (handler == NULL)
        return NULL;
    return (void*)((struct link_map*)handler)->l_addr;
}

static void* pos_reader(void* _)
{
    void* libcgame = get_libcgame_addr();
    if (libcgame == NULL)
        return NULL;

    void*  opp_ptr  = (char*)libcgame + base_ptr_off;
    void*  opp_addr = *(char**)opp_ptr;
    float* opp_pos  = (float*)((char*)opp_addr + pos_off);

    while (is_running)
    {
        printf("%f, %f, %f\n", opp_pos[0], opp_pos[1], opp_pos[2]);
        usleep(10000);
    }

    return NULL;
}

__attribute__((constructor))
static void run()
{
    is_running = true;
    pthread_create(&thread, NULL, pos_reader, NULL);
}

__attribute__((destructor))
static void stop()
{
    is_running = false;
    pthread_join(thread, NULL);
}

Warning: the path of the libcgame changes between two executions. Thus it is necessary to run the game, get the libcgame path, modify the libcgame_path variable, compile the lib and inject it.

Demonstration

In this demonstration, I used my dummy injector to inject the library:

Improvements

The thread method is not necessarily the best one. For example, it may be smarter to hook a function into the Warsow process to call our code. A good candidate is a function called at each frame.

Related articles

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.