From 1dcece9c32ea4527cdaed5f28cd24a5868ec0e12 Mon Sep 17 00:00:00 2001 From: chaoskagami Date: Tue, 23 Aug 2016 23:01:51 -0400 Subject: [PATCH] Dump this in a repo --- README.md | 10 +++ b.sh | 3 + config.c | 126 ++++++++++++++++++++++++++++++++++ config.h | 31 +++++++++ gettime_speedhack.c | 160 ++++++++++++++++++++++++++++++++++++++++++++ info_window.c | 83 +++++++++++++++++++++++ 6 files changed, 413 insertions(+) create mode 100644 README.md create mode 100755 b.sh create mode 100644 config.c create mode 100644 config.h create mode 100644 gettime_speedhack.c create mode 100644 info_window.c diff --git a/README.md b/README.md new file mode 100644 index 0000000..70a6879 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +apocalypse +------------ + +This is a very shitty gettimeofday/clock_gettime based speedhack that works on *some* games. + +It's also very old and not maintained, but might serve as a good example of: + + * How to hook functions via LD_PRELOAD + + * How NOT to document code properly diff --git a/b.sh b/b.sh new file mode 100755 index 0000000..1b0a29e --- /dev/null +++ b/b.sh @@ -0,0 +1,3 @@ +gcc -ldl -m64 -fPIC -shared `pkg-config --cflags --libs sdl2` -lGL -o apocalypse64.so gettime_speedhack.c info_window.c config.c +gcc -ldl -m32 -fPIC -shared `pkg-config --cflags --libs sdl2` -lGL -o apocalypse32.so gettime_speedhack.c info_window.c config.c +cp *.so ~/.preloads/ diff --git a/config.c b/config.c new file mode 100644 index 0000000..d742d30 --- /dev/null +++ b/config.c @@ -0,0 +1,126 @@ +#define DEFINED_ENABLES_HERE + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "config.h" + +int ENABLE_TIME_HACKS = 0; +int ENABLE_GETTIMEOFDAY_HOOK = 0; +int ENABLE_CLOCK_GETTIME_HOOK = 0; + +// Is this Isaac: AB or RB? +int IS_TBOI = 0; + +double SLOW_FACTOR = 1.0; + +int HAS_LOADED_CONFIG = 0; + +struct stat info; + +void read_cfg() { + FILE* f = fopen("apocalypse.conf", "r"); + if (!f) { + // Doesn't exist, so write the example. + fprintf(stderr, "[HOOK] No config file found, writing one out to 'apocalypse.conf'\n"); + f = fopen("apocalypse.conf", "w"); + fprintf(f, + "#================================\n" + "# Apocalypse tool config file\n" + "#================================\n\n" + "# Enable time hacks. Required for slowdown\n" + "# and speedup, as well.\n" + "time_hacks=0\n" + "# Enable gettimeofday-based time hack.\n" + "gettimeofday=0\n" + "# Enable clock_gettime-based time hack.\n" + "clock_gettime=0\n" + "# Slow factor with time hack.\n" + "slow_factor=1.0\n\n" + "#================================\n" + "# Game-specific stuff\n" + "#================================\n\n" + "# Binding of isaac hooks\n" + "isaac=0\n" + ); + fclose(f); + f = fopen("apocalypse.conf", "r"); + } + + stat("apocalypse.conf", &info); + + char* line = NULL; + size_t len = 0; + int read = 0; + + while ((read = getline(&line, &len, f)) != -1) { + // Line less than three characters? Next. + if (strlen(line) < 3) + continue; + + // Line is \n? Next. + if (line[0] == '\n') + continue; + + // First character is #? Next. + if (line[0] == '#') + continue; + + char *prop, *value; + + prop = line; + + int i; + for(i=0; i < strlen(line); i++) { + if (line[i] == '=') { + line[i] = '\0'; + value = &line[i+1]; + } + } + + if ( !strcmp(prop, "time_hacks") ) { + sscanf(value, "%d", & ENABLE_TIME_HACKS); + fprintf(stderr, "[HOOK] time_hacks: %d\n", ENABLE_CLOCK_GETTIME_HOOK); + } + else if ( !strcmp(prop, "gettimeofday") ) { + sscanf(value, "%d", & ENABLE_GETTIMEOFDAY_HOOK); + fprintf(stderr, "[HOOK] gettimeofday: %d\n", ENABLE_GETTIMEOFDAY_HOOK); + } + else if ( !strcmp(prop, "clock_gettime") ) { + sscanf(value, "%d", & ENABLE_CLOCK_GETTIME_HOOK); + fprintf(stderr, "[HOOK] clock_gettime: %d\n", ENABLE_CLOCK_GETTIME_HOOK); + } + else if ( !strcmp(prop, "slow_factor") ) { + sscanf(value, "%lf", & SLOW_FACTOR); + fprintf(stderr, "[HOOK] slow factor: %lf\n", SLOW_FACTOR); + } + } + + fprintf(stderr, "[HOOK] Read 'apocalypse.conf'. Settings loaded.\n"); + + fclose(f); + + HAS_LOADED_CONFIG = 1; +} + +int check_cfg_mod() { + struct stat new; + stat("apocalypse.conf", &new); + if (new.st_mtim.tv_sec != info.st_mtim.tv_sec || new.st_mtim.tv_nsec != info.st_mtim.tv_nsec) { + // File was modified. Reload. + read_cfg(); + memcpy(&info, &new, sizeof(struct stat)); + return 1; + } + return 0; +} diff --git a/config.h b/config.h new file mode 100644 index 0000000..44203b5 --- /dev/null +++ b/config.h @@ -0,0 +1,31 @@ +#define _GNU_SOURCE +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#ifndef DEFINED_ENABLES_HERE +extern int ENABLE_TIME_HACKS; +extern int ENABLE_GETTIMEOFDAY_HOOK; +extern int ENABLE_CLOCK_GETTIME_HOOK; + +extern int HAS_LOADED_CONFIG; + +extern int IS_TBOI; + +extern double SLOW_FACTOR; + +#endif + +void read_cfg(); +int check_cfg_mod(); // Returns 1 if reloaded. diff --git a/gettime_speedhack.c b/gettime_speedhack.c new file mode 100644 index 0000000..f155d42 --- /dev/null +++ b/gettime_speedhack.c @@ -0,0 +1,160 @@ +#include "config.h" + +typedef int (*gettimeofday_t)(struct timeval *tv, struct timezone *tz); +typedef int (*clock_gettime_t)(clockid_t clk_id, struct timespec *tp); + +gettimeofday_t gettimeofday_REAL = NULL; +clock_gettime_t clock_gettime_REAL = NULL; + +uint64_t time_div = 1; +uint64_t time_mul = 1; +double time_speedhack = 1.0f; + +int initd_hack = 0; +struct timeval gtod_time; +struct timeval gtod_base; +struct timespec clkgt_m_time; +/* CLOCK_REALTIME + CLOCK_REALTIME_COARSE + CLOCK_MONOTONIC + CLOCK_MONOTONIC_COARSE + CLOCK_BOOTTIME + CLOCK_PROCESS_CPUTIME_ID + CLOCK_THREAD_CPUTIME_ID */ +struct timespec clkgt_m_base; + +int mode_to_int(int mode) { + switch(mode) { + case CLOCK_REALTIME: + return 0; + case CLOCK_REALTIME_COARSE: + return 1; + case CLOCK_MONOTONIC: + return 2; + case CLOCK_MONOTONIC_COARSE: + return 3; + case CLOCK_BOOTTIME: + return 4; + case CLOCK_PROCESS_CPUTIME_ID: + return 5; + case CLOCK_THREAD_CPUTIME_ID: + return 6; + default: + return -1; + } +} + +int int_to_mode(int mode) { + switch(mode) { + case 0: + return CLOCK_REALTIME; + case 1: + return CLOCK_REALTIME_COARSE; + case 2: + return CLOCK_MONOTONIC; + case 3: + return CLOCK_MONOTONIC_COARSE; + case 4: + return CLOCK_BOOTTIME; + case 5: + return CLOCK_PROCESS_CPUTIME_ID; + case 6: + return CLOCK_THREAD_CPUTIME_ID; + } +} + +// Init state. +void clock_hack_init() { + check_cfg_mod(); + + gettimeofday_REAL = dlsym(RTLD_NEXT, "gettimeofday"); + clock_gettime_REAL = dlsym(RTLD_NEXT, "clock_gettime"); + + if (gettimeofday_REAL(>od_time, NULL)) { + fprintf(stderr, "[HOOK] !!! SANITY ERROR !!! - real gettimeofday cannot be used, aborting\n"); + exit(-1); + } + + gtod_base.tv_sec = gtod_time.tv_sec; + gtod_base.tv_usec = gtod_time.tv_usec; + + if (clock_gettime_REAL(CLOCK_MONOTONIC, &clkgt_m_time)) { + fprintf(stderr, "[HOOK] !!! SANITY ERROR !!! - real gettimeofday cannot be used, aborting\n"); + exit(-1); + } + + clkgt_m_base.tv_sec = clkgt_m_time.tv_sec; + clkgt_m_base.tv_nsec = clkgt_m_time.tv_nsec; + + if (!ENABLE_TIME_HACKS) + fprintf(stderr, "[HOOK] Speedhack is disabled.\n", time_speedhack); + else + fprintf(stderr, "[HOOK] Speedhack is applied. Time factor is %lf.\n", time_speedhack); + + initd_hack = 1; +} + +// Transform time based on fraction. +void xform_timeval(struct timeval *tv, struct timeval *orig) { + // What is this? Well, on init we save the initial point. From there, + // time moves at half-speed. So if I run a game at 2:00 PM, it's not back in 1990. + + // PROBLEM - Time changes. We add a different value for the 'base'. + tv->tv_sec = gtod_base.tv_sec + ((tv->tv_sec - orig->tv_sec) * SLOW_FACTOR); + tv->tv_usec = gtod_base.tv_usec + ((tv->tv_usec - orig->tv_usec) * SLOW_FACTOR); + +} + +void xform_timespec(struct timespec *tv, struct timespec *orig) { + tv->tv_sec = clkgt_m_base.tv_sec + ((tv->tv_sec - orig->tv_sec) * SLOW_FACTOR); + tv->tv_nsec = clkgt_m_base.tv_nsec + ((tv->tv_nsec - orig->tv_nsec) * SLOW_FACTOR); +} + +int done_gt_print = 0; + +// Fake gettimeofday. +int gettimeofday(struct timeval *tv, struct timezone *tz) { + if (!initd_hack) clock_hack_init(); + + int r = gettimeofday_REAL(tv, NULL); + if (ENABLE_TIME_HACKS && ENABLE_GETTIMEOFDAY_HOOK) + xform_timeval(tv, >od_time); + + if (check_cfg_mod()) { + gtod_base.tv_sec = tv->tv_sec; + gtod_base.tv_usec = tv->tv_usec; + } + + if (tv != NULL && tv->tv_sec % 5 == 0) { + if (!done_gt_print) printf("[gettimeofday] %llu\t%llu\n", tv->tv_sec, tv->tv_usec); + done_gt_print = 1; + } else + done_gt_print = 0; + + return r; +} + +int done_clkgt_print = 0; + +// Fake clock_gettime. +int clock_gettime(clockid_t clk_id, struct timespec *tp) { + if (!initd_hack) clock_hack_init(); + + int r = clock_gettime_REAL(clk_id, tp); + if (ENABLE_TIME_HACKS && ENABLE_CLOCK_GETTIME_HOOK) + xform_timespec(tp, &clkgt_m_time); + + if (check_cfg_mod()) { + clkgt_m_base.tv_sec = tp->tv_sec; + clkgt_m_base.tv_nsec = tp->tv_nsec; + } + + if (tp != NULL && tp->tv_sec % 5 == 0) { + mode_to_int(clk_id); + if (!done_clkgt_print) printf("[clock_gettime] %llu\t%llu\n", tp->tv_sec, tp->tv_nsec); + done_clkgt_print = 1; + } else + done_clkgt_print = 0; + + return r; +} diff --git a/info_window.c b/info_window.c new file mode 100644 index 0000000..93eb31e --- /dev/null +++ b/info_window.c @@ -0,0 +1,83 @@ +#include "config.h" + +// Types. +typedef __GLXextFuncPtr (*glXGetProcAddressARB_t) (const GLubyte*); +typedef __GLXextFuncPtr (*glXSwapBuffers_t)(Display* dpy, GLXDrawable drawable); +typedef GLXContext (*glXCreateContext_t)(Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct); + +// Function hooks. +static glXSwapBuffers_t glXSwapBuffers_REAL = NULL; +static glXGetProcAddressARB_t glXGetProcAddressARB_REAL = NULL; +static glXCreateContext_t glXCreateContext_REAL = NULL; + +void pre_hook() { +/* char speed[64]; + speed[0] = '\0'; + if (ENABLE_TIME_HACKS) + snprintf(speed, 64, "speedhack: %02lfx", SLOW_FACTOR); + uint8_t* buf = string_to_buf(speed); + glDrawPixels(8*strlen(speed), 8, GL_RGBA, GL_UNSIGNED_BYTE, buf); + free(buf); */ +} + +void post_hook() {} + +GLXContext hook_context; +Display* hook_display; +XVisualInfo* hook_visinfo; + +// glXSwapBuffers hook. +void glXSwapBuffers_fake(Display* dpy, GLXDrawable drawable) { + pre_hook(); + glXSwapBuffers_REAL(dpy, drawable); + post_hook(); +} + +// glXCreateContext hook. +GLXContext glXCreateContext_fake( Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct ) { + hook_context = glXCreateContext_REAL(dpy, vis, shareList, direct); + hook_display = dpy; + hook_visinfo = vis; + + fprintf(stderr, "[HOOK] Obtained GL context pointers successfully.\n"); + + return hook_context; +} + +// Same-ish thing, thin shim. +__GLXextFuncPtr glXGetProcAddress (const GLubyte* procName) { + return glXGetProcAddressARB(procName); +} + +// glXGetProcAddressARB +__GLXextFuncPtr glXGetProcAddressARB (const GLubyte* procName) +{ + __GLXextFuncPtr result; + + // Fetch pointer of actual glXGetProcAddressARB() function + if(!glXGetProcAddressARB_REAL) { + char* errorstr; + fprintf(stderr, "[HOOK] Overrided glXGetProcAddress.\n"); + glXGetProcAddressARB_REAL = (glXGetProcAddressARB_t) dlsym(RTLD_NEXT, "glXGetProcAddressARB"); + if((errorstr = dlerror())) { + fprintf(stderr, "dlsym fail: %s\n", errorstr); + exit(1); + } + } + + // Return our own function pointers for things. + if (!strcmp( (const char*) procName, "glXSwapBuffers" )) { + fprintf(stderr, "[HOOK] Hooked glXSwapBuffers pointer.\n"); + + glXSwapBuffers_REAL = (glXSwapBuffers_t) glXGetProcAddressARB_REAL(procName); + return (__GLXextFuncPtr) glXSwapBuffers_fake; + } else if (!strcmp( (const char*) procName, "glXCreateContext" )) { + fprintf(stderr, "[HOOK] Hooked glXCreateContext pointer.\n"); + + glXCreateContext_REAL = (glXCreateContext_t) glXGetProcAddressARB_REAL(procName); + return (__GLXextFuncPtr) glXCreateContext_fake; + } + + // Return default function pointer + return glXGetProcAddressARB_REAL(procName); +} -- 2.39.5