diff --git a/glinject/GLInject.cpp b/glinject/GLInject.cpp index 44aec180..692abe40 100644 --- a/glinject/GLInject.cpp +++ b/glinject/GLInject.cpp @@ -14,9 +14,9 @@ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH RE GLInject::GLInject() { #ifdef __x86_64__ - fprintf(stderr, "[SSR-GLInject] Library loaded (64-bit).\n"); + GLINJECT_PRINT("[SSR-GLInject] GLInject started (64-bit)."); #else - fprintf(stderr, "[SSR-GLInject] Library loaded (32-bit).\n"); + GLINJECT_PRINT("[SSR-GLInject] GLInject started (32-bit)."); #endif } @@ -28,7 +28,7 @@ GLInject::~GLInject() { m_glx_frame_grabbers.pop_back(); } - fprintf(stderr, "[SSR-GLInject] Library unloaded.\n"); + GLINJECT_PRINT("[SSR-GLInject] GLInject stopped."); } diff --git a/glinject/Hook.cpp b/glinject/Hook.cpp index 09671ff6..7709993d 100644 --- a/glinject/Hook.cpp +++ b/glinject/Hook.cpp @@ -25,19 +25,22 @@ extern char **environ; typedef void (*GLXextFuncPtr)(void); // hook replacement function prototypes +void* glinject_hook_dlopen(const char* filename, int flags); +void* glinject_hook_dlmopen(Lmid_t lmid, const char* filename, int flags); void* glinject_hook_dlsym(void* handle, const char* symbol); void* glinject_hook_dlvsym(void* handle, const char* symbol, const char* version); int glinject_hook_execl(const char* filename, const char* arg, ...); int glinject_hook_execlp(const char* filename, const char* arg, ...); int glinject_hook_execle(const char* filename, const char* arg, ...); int glinject_hook_execv(const char* filename, char* const argv[]); -int glinject_hook_execve(const char* filename, char* const argv[], char* const envp[]); int glinject_hook_execvp(const char* filename, char* const argv[]); +int glinject_hook_execve(const char* filename, char* const argv[], char* const envp[]); int glinject_hook_execvpe(const char* filename, char* const argv[], char* const envp[]); GLXWindow glinject_hook_glXCreateWindow(Display* dpy, GLXFBConfig config, Window win, const int* attrib_list); void glinject_hook_glXDestroyWindow(Display* dpy, GLXWindow win); int glinject_hook_XDestroyWindow(Display* dpy, Window win); void glinject_hook_glXSwapBuffers(Display* dpy, GLXDrawable drawable); +GLXextFuncPtr glinject_hook_glXGetProcAddress(const GLubyte *proc_name); GLXextFuncPtr glinject_hook_glXGetProcAddressARB(const GLubyte *proc_name); // hook table @@ -46,69 +49,204 @@ struct GLInjectHook { void *address; }; std::initializer_list glinject_hook_table = { + {"dlopen" , (void*) &glinject_hook_dlopen}, + {"dlmopen" , (void*) &glinject_hook_dlmopen}, {"dlsym" , (void*) &glinject_hook_dlsym}, {"dlvsym" , (void*) &glinject_hook_dlvsym}, {"execl" , (void*) &glinject_hook_execl}, {"execlp" , (void*) &glinject_hook_execlp}, {"execle" , (void*) &glinject_hook_execle}, {"execv" , (void*) &glinject_hook_execv}, - {"execve" , (void*) &glinject_hook_execve}, {"execvp" , (void*) &glinject_hook_execvp}, + {"execve" , (void*) &glinject_hook_execve}, {"execvpe" , (void*) &glinject_hook_execvpe}, {"glXCreateWindow" , (void*) &glinject_hook_glXCreateWindow}, {"glXDestroyWindow" , (void*) &glinject_hook_glXDestroyWindow}, {"XDestroyWindow" , (void*) &glinject_hook_XDestroyWindow}, {"glXSwapBuffers" , (void*) &glinject_hook_glXSwapBuffers}, + {"glXGetProcAddress" , (void*) &glinject_hook_glXGetProcAddress}, {"glXGetProcAddressARB", (void*) &glinject_hook_glXGetProcAddressARB}, }; -// main glinject object and mutex -static GLInject *g_glinject = NULL; -static std::mutex g_glinject_mutex; +// list of executables that should not be hooked +const char* g_glinject_filtered_execs[] = { + "ping", + "/bin/ping", + "/usr/bin/ping", +}; -// hook initializer -static struct GLInjectHooksInitializer { - GLInjectHooksInitializer() { +// list of libraries that should not be hooked +const char* g_glinject_filtered_libs[] = { + "libGL.so", + "libGLdispatch.so", + "libGLESv1.so", + "libGLESv1_", // vendor-specific libraries such as libGLESv1_CM_nvidia.so + "libGLESv2.so", + "libGLESv2_", // vendor-specific libraries such as libGLESv2_nvidia.so + "libGLU.so", + "libGLX.so", + "libGLX_", // vendor-specific libraries such as libGLX_nvidia.so + "libnvidia-", // more nvidia libraries including libnvidia-glcore.so + "libOpenGL.so", + "libX11.so", +}; - // get the link table of the glinject library (we can use any global variable for this) - Dl_info glinject_dlinfo; - struct link_map *glinject_lmap = NULL; - if(dladdr1((void*) &glinject_hook_table, &glinject_dlinfo, (void**) &glinject_lmap, RTLD_DL_LINKMAP) == 0) { - GLINJECT_PRINT("Error: Failed to get link map of glinject library!"); - return; +bool GLInjectShouldFilterExec(const char* filename) { + for(unsigned int i = 0; i < sizeof(g_glinject_filtered_execs) / sizeof(const char*); ++i) { + if(strcmp(g_glinject_filtered_execs[i], filename) == 0) { + return true; } + } + return false; +} - // replace PLT entries everywhere except in the glinject library - void *mainhandle = dlopen(NULL, RTLD_NOW); - if(mainhandle == NULL) { - GLINJECT_PRINT("Error: Failed to get main program handle!"); - return; - } - struct link_map *lmap = NULL; - if(dlinfo(mainhandle, RTLD_DI_LINKMAP, &lmap) != 0) { - GLINJECT_PRINT("Error: Failed to get link map of main program!"); - return; - } - while(lmap) { - if(lmap != glinject_lmap) { - plthook_t *plthook; - if(plthook_open_by_linkmap(&plthook, lmap) == 0) { - for(const GLInjectHook &hook : glinject_hook_table) { - void *oldfunc; - if(plthook_replace(plthook, hook.name, hook.address, &oldfunc) == 0) { - GLINJECT_PRINT("Hooked " << hook.name << " PLT entry in '" << lmap->l_name << "'."); - } - } - plthook_close(plthook); - } +bool GLInjectShouldFilterLib(const char* filename) { + if(filename[0] == '\0') + return false; // empty string means the main program + const char *slash = strrchr(filename, '/'); + if(slash == NULL) + return true; // not a normal library + for(unsigned int i = 0; i < sizeof(g_glinject_filtered_libs) / sizeof(const char*); ++i) { + if(strncmp(slash + 1, g_glinject_filtered_libs[i], strlen(g_glinject_filtered_libs[i])) == 0) + return true; + } + return false; +} + +std::vector GLInjectFilterEnv(char* const* envp) { + std::vector out; + while(*envp != NULL) { + if(strncmp(*envp, "LD_PRELOAD=", 11) != 0) + out.push_back(*envp); + ++envp; + } + out.push_back(NULL); + return out; +} + +bool GLInjectHookDebug_Init() { + const char *ssr_hook_debug = getenv("SSR_HOOK_DEBUG"); + if(ssr_hook_debug != NULL && atoi(ssr_hook_debug) > 0) { + GLINJECT_PRINT("Hook debugging enabled."); + return true; + } else { + return false; + } +} +bool GLInjectHookAdvanced_Init() { + const char *ssr_hook_advanced = getenv("SSR_HOOK_ADVANCED"); + if(ssr_hook_advanced != NULL && atoi(ssr_hook_advanced) > 0) { + return true; + } else { + return false; + } +} + +bool GLInjectHookDebug() { + static bool debug = GLInjectHookDebug_Init(); + return debug; +} +bool GLInjectHookAdvanced() { + static bool advanced = GLInjectHookAdvanced_Init(); + return advanced; +} + +// PLT hooking (called once for each object) +void GLInjectHookByLinkMap(struct link_map* lmap) { + bool advanced = GLInjectHookAdvanced(); + plthook_t *plthook; + if(plthook_open_by_linkmap(&plthook, lmap) == 0) { + if(GLInjectHookDebug()) + GLINJECT_PRINT(" dlsym = " << (void*) &dlsym); + for(const GLInjectHook &hook : glinject_hook_table) { + void *expect = dlsym(RTLD_DEFAULT, hook.name); + if(GLInjectHookDebug()) + GLINJECT_PRINT(" dlsym(" << hook.name << ") = " << expect); + + Dl_info target_dlinfo; + struct link_map *target_lmap = NULL; + if(dladdr1(expect, &target_dlinfo, (void**) &target_lmap, RTLD_DL_LINKMAP) != 0) { + if(GLInjectHookDebug()) + GLINJECT_PRINT(" -> " << target_dlinfo.dli_fname << " : " << ((target_dlinfo.dli_sname)? target_dlinfo.dli_sname : "(no symbol)")); + } + + void *oldfunc; + int res; + if(advanced) + res = plthook_replace_adv(plthook, hook.name, hook.address, &oldfunc, expect); + else + res = plthook_replace(plthook, hook.name, hook.address, &oldfunc); + if(res == 0) { + if(GLInjectHookDebug()) + GLINJECT_PRINT(" Hooked " << hook.name << " PLT entry in '" << lmap->l_name << "', was " << oldfunc << ", expected " << expect << "."); } - lmap = lmap->l_next; } - dlclose(mainhandle); + plthook_close(plthook); + } +} +void GLInjectHookByHandle(void* handle) { + struct link_map *lmap = NULL; + if(dlinfo(handle, RTLD_DI_LINKMAP, &lmap) != 0) { + GLINJECT_PRINT("Error: Failed to get link map from library handle!"); + } else { + GLInjectHookByLinkMap(lmap); + } +} +void GLInjectHookAll() { + + // get the link table of the glinject library (we can use any global variable for this) + Dl_info glinject_dlinfo; + struct link_map *glinject_lmap = NULL; + if(dladdr1((void*) &glinject_hook_table, &glinject_dlinfo, (void**) &glinject_lmap, RTLD_DL_LINKMAP) == 0) { + GLINJECT_PRINT("Error: Failed to get link map of glinject library!"); + return; + } + // replace PLT entries everywhere except in the glinject library + void *mainhandle = dlopen(NULL, RTLD_NOW); + if(mainhandle == NULL) { + GLINJECT_PRINT("Error: Failed to get main program handle!"); + return; + } + struct link_map *lmap = NULL; + if(dlinfo(mainhandle, RTLD_DI_LINKMAP, &lmap) != 0) { + GLINJECT_PRINT("Error: Failed to get link map of main program!"); + return; + } + while(lmap) { + if(GLInjectHookDebug()) + GLINJECT_PRINT("Link map " << lmap << " = '" << lmap->l_name << "'."); + if(lmap != glinject_lmap && !GLInjectShouldFilterLib(lmap->l_name)) { + GLInjectHookByLinkMap(lmap); + } else { + if(GLInjectHookDebug()) + GLINJECT_PRINT(" (filtered)") + } + lmap = lmap->l_next; + } + dlclose(mainhandle); + +} + +static std::mutex g_hook_mutex; + +// hook initializer +static struct GLInjectHooksInitializer { + GLInjectHooksInitializer() { + if(GLInjectHookDebug()) + GLINJECT_PRINT("Initializing hooks ..."); + std::lock_guard lock(g_hook_mutex); + GLInjectHookAll(); + } + ~GLInjectHooksInitializer() { + // GLInjectHookAll(); } } glinject_hooks_initializer; +// main glinject object and mutex +static GLInject *g_glinject = NULL; +static std::mutex g_glinject_mutex; + void GLInjectInit(); void GLInjectFree(); @@ -126,34 +264,45 @@ void GLInjectFree() { } } -void FilterEnviron(const char* filename, std::vector* out, char* const* in) { - const char* exec_blacklist[] = { - "ping", - "/bin/ping", - "/usr/bin/ping", - }; - bool filter = false; - for(unsigned int i = 0; i < sizeof(exec_blacklist) / sizeof(const char*); ++i) { - if(strcmp(exec_blacklist[i], filename) == 0) { - filter = true; - break; - } +void* glinject_hook_dlopen(const char* filename, int flags) { + // const char *str = "(In glinject_hook_dlopen)\n"; + // write(2, str, strlen(str)); + if(GLInjectHookDebug()) + GLINJECT_PRINT("Captured glinject_hook_dlopen(" << filename << ")"); + void *res = dlopen(filename, flags); + if(res != NULL) { + if(GLInjectHookDebug()) + GLINJECT_PRINT("Refreshing hooks ..."); + std::lock_guard lock(g_hook_mutex); + GLInjectHookAll(); + //GLInjectHookByHandle(res); } - while(*in != NULL) { - if(!filter || strncmp(*in, "LD_PRELOAD=", 11) != 0) - out->push_back(*in); - ++in; + return res; +} + +void* glinject_hook_dlmopen(Lmid_t lmid, const char* filename, int flags) { + // const char *str = "(In glinject_hook_dlmopen)\n"; + // write(2, str, strlen(str)); + if(GLInjectHookDebug()) + GLINJECT_PRINT("Captured glinject_hook_dlmopen(" << filename << ")"); + void *res = dlmopen(lmid, filename, flags); + if(res != NULL) { + if(GLInjectHookDebug()) + GLINJECT_PRINT("Refreshing hooks ..."); + std::lock_guard lock(g_hook_mutex); + GLInjectHookAll(); + //GLInjectHookByHandle(res); } - out->push_back(NULL); + return res; } void* glinject_hook_dlsym(void* handle, const char* symbol) { - const char *str = "(In glinject_hook_dlsym)\n"; - write(2, str, strlen(str)); + // const char *str = "(In glinject_hook_dlsym)\n"; + // write(2, str, strlen(str)); for(const GLInjectHook &hook : glinject_hook_table) { if(strcmp(hook.name, symbol) == 0) { - std::lock_guard lock(g_glinject_mutex); - GLINJECT_PRINT("Hooked dlsym(" << symbol << ")."); + if(GLInjectHookDebug()) + GLINJECT_PRINT("Hooked dlsym(" << symbol << ")."); return hook.address; } } @@ -161,12 +310,12 @@ void* glinject_hook_dlsym(void* handle, const char* symbol) { } void* glinject_hook_dlvsym(void* handle, const char* symbol, const char* version) { - const char *str = "(In glinject_hook_dlvsym)\n"; - write(2, str, strlen(str)); + // const char *str = "(In glinject_hook_dlvsym)\n"; + // write(2, str, strlen(str)); for(const GLInjectHook &hook : glinject_hook_table) { if(strcmp(hook.name, symbol) == 0) { - std::lock_guard lock(g_glinject_mutex); - GLINJECT_PRINT("Hooked dlvsym(" << symbol << ")."); + if(GLInjectHookDebug()) + GLINJECT_PRINT("Hooked dlvsym(" << symbol << ")."); return hook.address; } } @@ -174,8 +323,8 @@ void* glinject_hook_dlvsym(void* handle, const char* symbol, const char* version } int glinject_hook_execl(const char* filename, const char* arg, ...) { - const char *str = "(In glinject_hook_execl)\n"; - write(2, str, strlen(str)); + // const char *str = "(In glinject_hook_execl)\n"; + // write(2, str, strlen(str)); std::vector args; args.push_back((char*) arg); va_list vl; @@ -184,14 +333,17 @@ int glinject_hook_execl(const char* filename, const char* arg, ...) { args.push_back(va_arg(vl, char*)); } va_end(vl); - std::vector filtered_environ; - FilterEnviron(filename, &filtered_environ, environ); - return execve(filename, args.data(), filtered_environ.data()); + if(GLInjectShouldFilterExec(filename)) { + std::vector filtered_env = GLInjectFilterEnv(environ); + return execve(filename, args.data(), filtered_env.data()); + } else { + return execv(filename, args.data()); + } } int glinject_hook_execlp(const char* filename, const char* arg, ...) { - const char *str = "(In glinject_hook_execlp)\n"; - write(2, str, strlen(str)); + // const char *str = "(In glinject_hook_execlp)\n"; + // write(2, str, strlen(str)); std::vector args; args.push_back((char*) arg); va_list vl; @@ -200,14 +352,17 @@ int glinject_hook_execlp(const char* filename, const char* arg, ...) { args.push_back(va_arg(vl, char*)); } va_end(vl); - std::vector filtered_environ; - FilterEnviron(filename, &filtered_environ, environ); - return execvpe(filename, args.data(), filtered_environ.data()); + if(GLInjectShouldFilterExec(filename)) { + std::vector filtered_env = GLInjectFilterEnv(environ); + return execvpe(filename, args.data(), filtered_env.data()); + } else { + return execvp(filename, args.data()); + } } int glinject_hook_execle(const char* filename, const char* arg, ...) { - const char *str = "(In glinject_hook_execle)\n"; - write(2, str, strlen(str)); + // const char *str = "(In glinject_hook_execle)\n"; + // write(2, str, strlen(str)); std::vector args; args.push_back((char*) arg); va_list vl; @@ -217,46 +372,61 @@ int glinject_hook_execle(const char* filename, const char* arg, ...) { } char *const *envp = va_arg(vl, char* const*); va_end(vl); - std::vector filtered_environ; - FilterEnviron(filename, &filtered_environ, envp); - return execvpe(filename, args.data(), filtered_environ.data()); + if(GLInjectShouldFilterExec(filename)) { + std::vector filtered_env = GLInjectFilterEnv(envp); + return execve(filename, args.data(), filtered_env.data()); + } else { + return execve(filename, args.data(), envp); + } } int glinject_hook_execv(const char* filename, char* const argv[]) { - const char *str = "(In glinject_hook_execv)\n"; - write(2, str, strlen(str)); - std::vector filtered_environ; - FilterEnviron(filename, &filtered_environ, environ); - return execve(filename, argv, filtered_environ.data()); + // const char *str = "(In glinject_hook_execv)\n"; + // write(2, str, strlen(str)); + if(GLInjectShouldFilterExec(filename)) { + std::vector filtered_env = GLInjectFilterEnv(environ); + return execve(filename, argv, filtered_env.data()); + } else { + return execv(filename, argv); + } } -int glinject_hook_execve(const char* filename, char* const argv[], char* const envp[]) { - const char *str = "(In glinject_hook_execve)\n"; - write(2, str, strlen(str)); - std::vector filtered_environ; - FilterEnviron(filename, &filtered_environ, envp); - return execve(filename, argv, filtered_environ.data()); +int glinject_hook_execvp(const char* filename, char* const argv[]) { + // const char *str = "(In glinject_hook_execvp)\n"; + // write(2, str, strlen(str)); + if(GLInjectShouldFilterExec(filename)) { + std::vector filtered_env = GLInjectFilterEnv(environ); + return execvpe(filename, argv, filtered_env.data()); + } else { + return execvp(filename, argv); + } } -int glinject_hook_execvp(const char* filename, char* const argv[]) { - const char *str = "(In glinject_hook_execvp)\n"; - write(2, str, strlen(str)); - std::vector filtered_environ; - FilterEnviron(filename, &filtered_environ, environ); - return execvpe(filename, argv, filtered_environ.data()); +int glinject_hook_execve(const char* filename, char* const argv[], char* const envp[]) { + // const char *str = "(In glinject_hook_execve)\n"; + // write(2, str, strlen(str)); + if(GLInjectShouldFilterExec(filename)) { + std::vector filtered_env = GLInjectFilterEnv(envp); + return execve(filename, argv, filtered_env.data()); + } else { + return execve(filename, argv, envp); + } } int glinject_hook_execvpe(const char* filename, char* const argv[], char* const envp[]) { - const char *str = "(In glinject_hook_execvpe)\n"; - write(2, str, strlen(str)); - std::vector filtered_environ; - FilterEnviron(filename, &filtered_environ, envp); - return execvpe(filename, argv, filtered_environ.data()); + // const char *str = "(In glinject_hook_execvpe)\n"; + // write(2, str, strlen(str)); + if(GLInjectShouldFilterExec(filename)) { + std::vector filtered_env = GLInjectFilterEnv(envp); + return execvpe(filename, argv, filtered_env.data()); + } else { + return execvpe(filename, argv, envp); + } } GLXWindow glinject_hook_glXCreateWindow(Display* dpy, GLXFBConfig config, Window win, const int* attrib_list) { - const char *str = "(In glinject_hook_glXCreateWindow)\n"; - write(2, str, strlen(str)); + // const char *str = "(In glinject_hook_glXCreateWindow)\n"; + // write(2, str, strlen(str)); GLXWindow res = glXCreateWindow(dpy, config, win, attrib_list); if(res == 0) return 0; @@ -269,8 +439,8 @@ GLXWindow glinject_hook_glXCreateWindow(Display* dpy, GLXFBConfig config, Window } void glinject_hook_glXDestroyWindow(Display* dpy, GLXWindow win) { - const char *str = "(In glinject_hook_glXDestroyWindow)\n"; - write(2, str, strlen(str)); + // const char *str = "(In glinject_hook_glXDestroyWindow)\n"; + // write(2, str, strlen(str)); glXDestroyWindow(dpy, win); { std::lock_guard lock(g_glinject_mutex); @@ -280,8 +450,8 @@ void glinject_hook_glXDestroyWindow(Display* dpy, GLXWindow win) { } int glinject_hook_XDestroyWindow(Display* dpy, Window win) { - const char *str = "(In glinject_hook_XDestroyWindow)\n"; - write(2, str, strlen(str)); + // const char *str = "(In glinject_hook_XDestroyWindow)\n"; + // write(2, str, strlen(str)); int res = XDestroyWindow(dpy, win); { std::lock_guard lock(g_glinject_mutex); @@ -292,8 +462,8 @@ int glinject_hook_XDestroyWindow(Display* dpy, Window win) { } void glinject_hook_glXSwapBuffers(Display* dpy, GLXDrawable drawable) { - const char *str = "(In glinject_hook_glXSwapBuffers)\n"; - write(2, str, strlen(str)); + // const char *str = "(In glinject_hook_glXSwapBuffers)\n"; + // write(2, str, strlen(str)); { std::lock_guard lock(g_glinject_mutex); GLInjectInit(); @@ -307,13 +477,28 @@ void glinject_hook_glXSwapBuffers(Display* dpy, GLXDrawable drawable) { glXSwapBuffers(dpy, drawable); } +GLXextFuncPtr glinject_hook_glXGetProcAddress(const GLubyte *proc_name) { + // const char *str = "(In glinject_hook_glXGetProcAddress)\n"; + // write(2, str, strlen(str)); + for(const GLInjectHook &hook : glinject_hook_table) { + if(strcmp(hook.name, (const char*) proc_name) == 0) { + std::lock_guard lock(g_glinject_mutex); + if(GLInjectHookDebug()) + GLINJECT_PRINT("Hooked glXGetProcAddress(" << proc_name << ")."); + return (GLXextFuncPtr) hook.address; + } + } + return glXGetProcAddress(proc_name); +} + GLXextFuncPtr glinject_hook_glXGetProcAddressARB(const GLubyte *proc_name) { - const char *str = "(In glinject_hook_glXGetProcAddressARB)\n"; - write(2, str, strlen(str)); + // const char *str = "(In glinject_hook_glXGetProcAddressARB)\n"; + // write(2, str, strlen(str)); for(const GLInjectHook &hook : glinject_hook_table) { if(strcmp(hook.name, (const char*) proc_name) == 0) { std::lock_guard lock(g_glinject_mutex); - GLINJECT_PRINT("Hooked glXGetProcAddressARB(" << proc_name << ")."); + if(GLInjectHookDebug()) + GLINJECT_PRINT("Hooked glXGetProcAddressARB(" << proc_name << ")."); return (GLXextFuncPtr) hook.address; } } diff --git a/glinject/plthook.h b/glinject/plthook.h index 4b74d715..48062e90 100644 --- a/glinject/plthook.h +++ b/glinject/plthook.h @@ -56,7 +56,9 @@ int plthook_open_by_handle(plthook_t **plthook_out, void *handle); int plthook_open_by_address(plthook_t **plthook_out, void *address); int plthook_open_by_linkmap(plthook_t **plthook_out, void *linkmap); int plthook_enum(plthook_t *plthook, unsigned int *pos, const char **name_out, void ***addr_out); +int plthook_enum_adv(plthook_t *plthook, unsigned int *pos, const char **name_out, void ***addr_out, unsigned int *reltype_out); int plthook_replace(plthook_t *plthook, const char *funcname, void *funcaddr, void **oldfunc); +int plthook_replace_adv(plthook_t *plthook, const char *funcname, void *funcaddr, void **oldfunc, void *expect); void plthook_close(plthook_t *plthook); const char *plthook_error(void); diff --git a/glinject/plthook_elf.c b/glinject/plthook_elf.c index c82f705c..a249cfd0 100644 --- a/glinject/plthook_elf.c +++ b/glinject/plthook_elf.c @@ -41,6 +41,7 @@ #endif #include #include +#include #include #include #include @@ -84,6 +85,8 @@ #elif defined __i386__ || defined __i386 #define R_JUMP_SLOT R_386_JMP_SLOT #define R_GLOBAL_DATA R_386_GLOB_DAT +#define R_PC_RELATIVE R_386_PC32 +#define PC_RELATIVE_OFFSET 4 #define USE_REL #elif defined __arm__ || defined __arm #define R_JUMP_SLOT R_ARM_JUMP_SLOT @@ -312,7 +315,7 @@ int plthook_open_by_address(plthook_t **plthook_out, void *address) int plthook_open_by_linkmap(plthook_t **plthook_out, void *linkmap) { - return plthook_open_real(plthook_out, (struct link_map*)linkmap); + return plthook_open_real(plthook_out, (struct link_map*)linkmap); } static int plthook_open_executable(plthook_t **plthook_out) @@ -731,6 +734,23 @@ static int check_rel(const plthook_t *plthook, const Elf_Plt_Rel *plt, Elf_Xword return -1; } +static int check_rel_adv(const plthook_t *plthook, const Elf_Plt_Rel *plt, Elf_Xword r_type, const char **name_out, void ***addr_out, unsigned int *reltype_out) +{ + if (ELF_R_TYPE(plt->r_info) == r_type) { + size_t idx = ELF_R_SYM(plt->r_info); + idx = plthook->dynsym[idx].st_name; + if (idx + 1 > plthook->dynstr_size) { + set_errmsg("too big section header string table index: %" SIZE_T_FMT, idx); + return PLTHOOK_INVALID_FILE_FORMAT; + } + *name_out = plthook->dynstr + idx; + *addr_out = (void**)(plthook->plt_addr_base + plt->r_offset); + *reltype_out = r_type; + return 0; + } + return -1; +} + int plthook_enum(plthook_t *plthook, unsigned int *pos, const char **name_out, void ***addr_out) { while (*pos < plthook->rela_plt_cnt) { @@ -756,6 +776,43 @@ int plthook_enum(plthook_t *plthook, unsigned int *pos, const char **name_out, v return EOF; } +int plthook_enum_adv(plthook_t *plthook, unsigned int *pos, const char **name_out, void ***addr_out, unsigned int *reltype_out) +{ + // fprintf(stderr, "plthook_enum: started, pos = %u, plthook->rela_plt_cnt = %lu, plthook->rela_dyn_cnt = %lu\n", *pos, plthook->rela_plt_cnt, plthook->rela_dyn_cnt); + while (*pos < plthook->rela_plt_cnt) { + const Elf_Plt_Rel *plt = plthook->rela_plt + *pos; + int rv = check_rel_adv(plthook, plt, R_JUMP_SLOT, name_out, addr_out, reltype_out); + // fprintf(stderr, "plthook_enum: check_rel returned %d\n", rv); + (*pos)++; + if (rv >= 0) { + return rv; + } + } + +#ifdef R_GLOBAL_DATA + while (*pos < plthook->rela_plt_cnt + plthook->rela_dyn_cnt) { + const Elf_Plt_Rel *plt = plthook->rela_dyn + (*pos - plthook->rela_plt_cnt); + int rv = check_rel_adv(plthook, plt, R_GLOBAL_DATA, name_out, addr_out, reltype_out); +#ifdef R_PC_RELATIVE + if (rv == -1) { + rv = check_rel_adv(plthook, plt, R_PC_RELATIVE, name_out, addr_out, reltype_out); + // if (rv == 0) { + // fprintf(stderr, "plthook_enum: found R_PC_RELATIVE (%s, %p, %p)\n", *name_out, *addr_out, **addr_out); + // rv = -1; + // } + } +#endif + (*pos)++; + if (rv >= 0) { + return rv; + } + } +#endif + *name_out = NULL; + *addr_out = NULL; + return EOF; +} + int plthook_replace(plthook_t *plthook, const char *funcname, void *funcaddr, void **oldfunc) { size_t funcnamelen = strlen(funcname); @@ -800,6 +857,74 @@ int plthook_replace(plthook_t *plthook, const char *funcname, void *funcaddr, vo return rv; } +int plthook_replace_adv(plthook_t *plthook, const char *funcname, void *funcaddr, void **oldfunc, void *expect) +{ + size_t funcnamelen = strlen(funcname); + unsigned int pos = 0; + const char *name; + void **addr; + unsigned int reltype; + int rv; + unsigned int found = 0; + + if (plthook == NULL) { + set_errmsg("invalid argument: The first argument is null."); + return PLTHOOK_INVALID_ARGUMENT; + } + while ((rv = plthook_enum_adv(plthook, &pos, &name, &addr, &reltype)) == 0) { + if (strncmp(name, funcname, funcnamelen) == 0) { + if (name[funcnamelen] == '\0' || name[funcnamelen] == '@') { + int prot = get_memory_permission(addr); + if (prot == 0) { + return PLTHOOK_INTERNAL_ERROR; + } + if (!(prot & PROT_WRITE)) { + if (mprotect(ALIGN_ADDR(addr), page_size, PROT_READ | PROT_WRITE) != 0) { + set_errmsg("Could not change the process memory permission at %p: %s", + ALIGN_ADDR(addr), strerror(errno)); + return PLTHOOK_INTERNAL_ERROR; + } + } +#ifdef R_PC_RELATIVE + if (reltype == R_PC_RELATIVE) { + if (oldfunc) { + *oldfunc = (void*)(((char*)addr + PC_RELATIVE_OFFSET) + (ptrdiff_t)*addr); + } + *addr = (void*) ((char*)funcaddr - ((char*)addr + PC_RELATIVE_OFFSET)); + } else { +#endif + if (oldfunc) { + *oldfunc = *addr; + } + *addr = funcaddr; +#ifdef R_PC_RELATIVE + } +#endif + if (!(prot & PROT_WRITE)) { + mprotect(ALIGN_ADDR(addr), page_size, prot); + } +#ifdef R_PC_RELATIVE + if (reltype == R_PC_RELATIVE) { + if (oldfunc && *oldfunc != expect && *oldfunc != funcaddr) { + fprintf(stderr, "PLTHOOKS WARNING: relocation of %s at %p was %p, expected %p, changed to %p\n", funcname, addr, *oldfunc, expect, funcaddr); + } + } +#endif + found++; + } + } + } + if (rv == EOF) { + if (found == 0) { + set_errmsg("no such function: %s", funcname); + rv = PLTHOOK_FUNCTION_NOT_FOUND; + } else { + rv = 0; + } + } + return rv; +} + void plthook_close(plthook_t *plthook) { if (plthook != NULL) { diff --git a/scripts/ssr-glinject b/scripts/ssr-glinject index 23206902..c292ebeb 100755 --- a/scripts/ssr-glinject +++ b/scripts/ssr-glinject @@ -7,6 +7,9 @@ usage() { echo "" >& 2 echo "Options:" >& 2 echo " --help Show this help message." >& 2 + echo " --hook-debug Print details related to the function hooking process." >& 2 + echo " --hook-advanced Use advanced hooking techniques that can handle more" >& 2 + echo " unusual situations, but are more likely to break things." >& 2 echo " --glx-debug Enables GLX debugging. This may reduce the performance" >& 2 echo " and print lots of error messages, but it is useful to" >& 2 echo " track down bugs." >& 2 @@ -24,6 +27,8 @@ usage() { echo "a bug report." >& 2 } +export SSR_HOOK_DEBUG=0 +export SSR_HOOK_ADVANCED=0 export SSR_GLX_DEBUG=0 export SSR_STREAM_RELAX_PERMISSIONS=0 @@ -37,6 +42,14 @@ do then usage exit + elif [ x"$1" = x"--hook-debug" ] + then + export SSR_HOOK_DEBUG=1 + shift + elif [ x"$1" = x"--hook-advanced" ] + then + export SSR_HOOK_ADVANCED=1 + shift elif [ x"$1" = x"--glx-debug" ] then export SSR_GLX_DEBUG=1