simple injection with LD_PRELOAD

Tonight I've played with code injection on Linux and want to share what I've learned so far. As rootguy suggested the LD_PRELOAD path, I decided to give it a try.

A nice introduction to this kind of hook can be found in this old Linux Journal article: Modifying a Dynamic Library Without Changing the Source Code. LD_PRELOAD is basically an environment variable that tells the DLL loader to load your library before any DLLs that the binary's linked to. This allows you to hook into any library function that's used by the binary, in our case WINE.

Updates:
- Added 2. glXSwapBuffers() hook.

1. Simple glClear() hook
For my first and successful test, I've chosen the OpenGL function glClear(). It's surprisingly simple and works well enough to demonstrate LD_PRELOAD. Beware that WoW calls glClear() multiple times per frame though. I recommend to use the glXSwapBuffers() hook which is described below.

/*
* eve.c: Simple LD_PRELOAD injection for OpenGL applications.
*
* gcc -std=c99 -Wall -Werror -m32 -O0 -fpic -shared -ldl -lGL -o eve.so eve.c
*
* How to use with WINE:
*   wineserver -k
*   export LD_PRELOAD=/path/to/eve.so
*   wine Wow.exe
*
*/

/* These libraries are necessary for the hook */
#include <dlfcn.h>
#include <stdlib.h>
#include <GL/gl.h>

/* "Injected" stuff */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
void doevil();

/* Hook function */
void glClear(GLbitfield mask) {
static void (*lib_glClear)(GLbitfield mask) = NULL;
void* handle;
char* errorstr;

if(!lib_glClear) {
/* Load real libGL */
handle = dlopen("/usr/lib32/libGL.so", RTLD_LAZY);
if(!handle) {
fputs(dlerror(), stderr);
exit(1);
}

/* Fetch pointer of real glClear() func */
lib_glClear = dlsym(handle, "glClear");
if( (errorstr = dlerror()) != NULL ) {
fprintf(stderr, "dlsym fail: %s\n", errorstr);
exit(1);
}
}

/* Woot */
doevil();

/* Call real glClear() */
lib_glClear(mask);
}

/* Here be dragons */
void doevil() {
static int framecnt = 0;
framecnt++;

uint32_t read_uint( uint32_t addr ) { return *((uint32_t*) addr); }
float read_float( uint32_t addr ) { return *((float*) addr); }

// calling game functions works too! (WoW 3.3.0a)
static int (*ClntObjMgrGetActivePlayer)() = (void*) 0x0047A2B0;

printf("doevil(), frame %d... ", framecnt);
if( ClntObjMgrGetActivePlayer() == 0 ) {
printf("not logged in.\n");
} else {
char p_name[16];
uint32_t p_base;
float p_x, p_y;

strncpy( p_name, (char*) 0x00C923F8, 16 );
if( read_uint(0x00CF7C00) ) {
p_base = read_uint(read_uint( read_uint( 0x00CF7C00 ) + 0x34 ) + 0x24);
p_x    = read_float( p_base + 0x798 );
p_y    = read_float( p_base + 0x79C );
}

printf("p_name: %s, x/y: %.2f/%.2f\n", p_name, p_x, p_y);
}
}

 

Example output:

Code:
 
 
...
doevil(), frame 1037... not logged in.
doevil(), frame 1038... not logged in.
doevil(), frame 1039... p_name: Somedude, x/y: -14444.90/464.05
doevil(), frame 1040... p_name: Somedude, x/y: -14444.90/464.05
doevil(), frame 1041... p_name: Somedude, x/y: -14444.90/464.05
...
 
Enjoy.

2. glXSwapBuffers() hook
void glXSwapBuffers(Display *dpy, GLXDrawable drawable) is probably the best OpenGL method to hook because it gets called exactly once per frame and WoW's engine is in a consistent state at that point. However there is one downside: You need to patch WINE in order to hook it with LD_PRELOAD. Credits to RoKFenris for supplying that patch.

Patch:
Code:
 
diff -Nru wine-1.1.39.orig/dlls/winex11.drv/opengl.c wine-1.1.39/dlls/winex11.drv/opengl.c
--- wine-1.1.39.orig/dlls/winex11.drv/opengl.c 2010-02-27 02:21:02.973526893 +0100
+++ wine-1.1.39/dlls/winex11.drv/opengl.c 2010-02-27 02:27:11.231401387 +0100
@@ -412,7 +412,7 @@
         return FALSE;
     }

-    pglXGetProcAddressARB = wine_dlsym(opengl_handle, "glXGetProcAddressARB", NULL, 0);
+    pglXGetProcAddressARB = wine_dlsym(RTLD_DEFAULT, "glXGetProcAddressARB", NULL, 0);
     if (pglXGetProcAddressARB == NULL) {
         ERR("Could not find glXGetProcAddressARB in libGL, disabling OpenGL.\n");
         goto failed;
 
 
If you're not familiar with the OpenGL extension mechanism: applications that wish to use an extension function call void* glXGetProcAddressARB(const GLubyte *procName) with the name of the desired extension as a parameter and get a pointer to the function in return. In order to hook glXSwapBuffers, you actually hook glXGetProcAddressARB, and return the pointer to your function as soon as procName equals "glXSwapBuffers".

hook-glx.c:
Code:
#include "hook-glx.h"

#define _GNU_SOURCE
#include <dlfcn.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <GL/gl.h>
#include <GL/glx.h>

void hookfunc();

/* glXSwapBuffers() is very appropriate as an "end scene" hook because
* WoW calls it exactly once, at the end of each frame.
*
* Unfortunately WINE needs to be patched to allow hooking this via
* LD_PRELOAD.
*
* 1) Instead of using the default RTLD loader, WINE retrieves the address
*    for glXGetProcAddressARB directly from the OpenGL library.
*    This is done to enable binary compatibility to systems with or without
*    OpenGL.
*
*    Solution: patch WINE to use the default loader
*
* 2) WINE gets the pointers to glx functions via glXGetProcAddressARB
*   
*    => I'm hooking glXGetProcAddressARB and return my own
*       pointer to glXSwapBuffers.
*
*/

typedef __GLXextFuncPtr (*fp_glXGetProcAddressARB) (const GLubyte*);
typedef __GLXextFuncPtr (*fp_glXSwapBuffers)(Display* dpy, GLXDrawable drawable);

// glXSwapBuffers
fp_glXSwapBuffers real_glXSwapBuffers;

void my_glXSwapBuffers(Display* dpy, GLXDrawable drawable) {
real_glXSwapBuffers(dpy, drawable);
hookfunc();
}

// glXGetProcAddressARB
__GLXextFuncPtr glXGetProcAddressARB (const GLubyte* procName)
{
__GLXextFuncPtr result;
printf("* hook-glx.c: glXGetProcAddressARB(\"%s\")\n", procName);

// Fetch pointer of actual glXGetProcAddressARB() function
static fp_glXGetProcAddressARB lib_getprocaddr = NULL;
if(!lib_getprocaddr)
{
char* errorstr;
lib_getprocaddr = (fp_glXGetProcAddressARB)
dlsym(RTLD_NEXT, "glXGetProcAddressARB");
if( (errorstr = dlerror()) != NULL )
{
fprintf(stderr, "dlsym fail: %s\n", errorstr);
exit(1);
}
}
result = lib_getprocaddr(procName);

// Return our own function pointers
if( strcmp( (const char*) procName, "glXSwapBuffers" ) == 0 )
{
real_glXSwapBuffers = (fp_glXSwapBuffers) result;
return (__GLXextFuncPtr) my_glXSwapBuffers;
}

// Return default function pointer
return lib_getprocaddr(procName);
}
 
hook-glx.h:
Code:
 
#pragma once
// http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html

/* In your C++ source:
*
* void hookfunc() { ... }
*
*/

#ifdef __cplusplus

extern "C" void hookfunc();

#else

void hookfunc();

#endif
 
=> Link hook-glx.c with the rest of your code and define void hookfunc() in another .c or .cpp file.

 

你可能感兴趣的:(linux,职场,hook,休闲)