[Linux] LD_PRELOAD injection without patching Wine menu

User Tag List

Results 1 to 5 of 5
  1. #1
    kouteiheika's Avatar Private
    Reputation
    14
    Join Date
    May 2011
    Posts
    7
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    [Linux] LD_PRELOAD injection without patching Wine

    If you are hacking on Linux chances are you are using LD_PRELOAD method described here. It's a very powerful, and easy, way to hook into another process. Unfortunately, when it comes to hooking into Wine *and* glXSwapBuffers (aka. endscene hook) it requires patching its source code. Not anymore! (If you don't care about my brief-yet-mostly-useless explanation and you're here to just copy and paste the code then scroll down to the bottom.)

    To understand why Wine needs patching we need to take a peek into its source code:

    Code:
        #define SONAME_LIBGL "libGL.so"
        (...snip...)
        opengl_handle = wine_dlopen(SONAME_LIBGL, RTLD_NOW|RTLD_GLOBAL, buffer, sizeof(buffer));
        (...snip...)
        pglXGetProcAddressARB = wine_dlsym(opengl_handle, "glXGetProcAddressARB", NULL, 0);
        (...snip...)
        pglXSwapBuffers = pglXGetProcAddressARB("glXSwapBuffers");
    Basically, Wine directly opens libGL.so and directly requests glXGetProcAddress from it and then uses that to load glXSwapBuffers; to force Wine into using our version of glXSwapBuffers defined in our LD_PRELOAD library we had to patch this "opengl_handle" to RTLD_DEFAULT, which basically means "I don't care from where it comes from, just give me the damn pointer".

    Let's take a closer look at wine_dlsym():

    Code:
    void *wine_dlsym( void *handle, const char *symbol, char *error, size_t errorsize )
    {
        (...snip...)
        ret = dlsym( handle, symbol );
        (...error handling...)
        (...snip...)
    }
    We can see that this is just a simple wrapper over dlsym with some error handling throw in the mix. Now, an idea - we could get away without patching Wine if only we could trick dlsym() to return our version of glXGetProcAddress. How? By hooking into dlsym() with LD_PRELOAD!

    Code:
    static void * (*dlsym_real)( void * handle, const char * symbol ) = nullptr;
    
    void * dlsym( void * handle, const char * symbol )
    {
        if( dlsym_real == nullptr )
            dlsym_real = ( decltype(dlsym_real) )dlsym( RTLD_NEXT, "dlsym" );
            
        if( !strcmp( symbol, "glXGetProcAddressARB" ) )
            return ( void * )glXGetProcAddressARB_fake;
            
        return dlsym_real( handle, symbol );
    }
    There is just one little problem - this obviously won't work. Our dlsym() will just call dlsym(), and that will call dlsym() again and again. What we need is a function that does what dlsym() does but isn't dlsym(). A quick look at "man dlsym" turned out this:

    Code:
       Glibc extensions: dladdr() and dlvsym()
           Glibc adds two functions not described by POSIX, with prototypes
    
           (...snip...)
    
           void *dlvsym(void *handle, char *symbol, char *version);
    
           (...snip...)
    
           The function dlvsym(), provided by glibc since version 2.1, does the same as dlsym() but takes a version string as an additional argument.
    Precisely what we need. Now we just need to figure out what the hell we're supposed to put in the version string; fortunately, objdump comes to the rescue:

    Code:
    kou@rin:~$ objdump -T /lib32/libdl.so.2 
    
    /lib32/libdl.so.2:     file format elf32-i386
    
    DYNAMIC SYMBOL TABLE:
    (...snip...)
    00001940 g    DF .text  00000067 (GLIBC_2.0)  dlopen
    00001430 g    DF .text  000000a8  GLIBC_2.3.3 dladdr1
    00000000 g    DO *ABS*  00000000  GLIBC_PRIVATE GLIBC_PRIVATE
    00000d50 g    DF .text  00000094  GLIBC_2.0   dlsym
    00000000 g    DO *ABS*  00000000  GLIBC_2.0   GLIBC_2.0
    00000000 g    DO *ABS*  00000000  GLIBC_2.1   GLIBC_2.1
    00000cd0 g    DF .text  0000003f  GLIBC_2.0   dlclose
    (...snip...)
    We have out version string - "GLIBC_2.0", so we can rewrite that code as:

    Code:
    /* This definition is required to get dlvsym(). */
    #define _GNU_SOURCE
    #include <dlfcn.h>
    
    static void * (*dlsym_real)( void * handle, const char * symbol ) = nullptr;
    
    void * dlsym( void * handle, const char * symbol )
    {
        if( dlsym_real == nullptr )
            dlsym_real = ( decltype(dlsym_real) )dlvsym( RTLD_DEFAULT, "dlsym", "GLIBC_2.0" );
            
        if( !strcmp( symbol, "glXGetProcAddressARB" ) )
            return ( void * )glXGetProcAddressARB_fake;
            
        return dlsym_real( handle, symbol );
    }
    And this is how you LD_PRELOAD without patching Wine.

    [Linux] LD_PRELOAD injection without patching Wine
  2. #2
    flo8464's Avatar Active Member
    Reputation
    30
    Join Date
    Apr 2009
    Posts
    434
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    An easy and clean solution, thank you!
    Hey, it compiles! Ship it!

  3. #3
    SKU's Avatar Contributor
    Reputation
    306
    Join Date
    May 2007
    Posts
    565
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Neat, thanks a lot.

  4. #4
    argh44z's Avatar Member
    Reputation
    19
    Join Date
    Nov 2007
    Posts
    93
    Thanks G/R
    0/1
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by kouteiheika View Post
    Precisely what we need. Now we just need to figure out what the hell we're supposed to put in the version string; fortunately, objdump comes to the rescue:
    Nice work. You can also set the env variable LD_DEBUG=all before launching wine to get the version number:
    Code:
          2444: symbol=dlsym;  lookup in file=/usr/lib/fglrx/libGL.so.1 [0]
          2444: symbol=dlsym;  lookup in file=/usr/local/bin/wineserver [0]
          2444: symbol=dlsym;  lookup in file=/home/sb/hook/libsas.so [0]
          2444: symbol=dlsym;  lookup in file=/usr/local/bin/../lib/libwine.so.1 [0]
          2444: symbol=dlsym;  lookup in file=/lib/tls/i686/cmov/libc.so.6 [0]
          2444: symbol=dlsym;  lookup in file=/lib/tls/i686/cmov/libdl.so.2 [0]
          2444: binding file /usr/lib/fglrx/libGL.so.1 [0] to /lib/tls/i686/cmov/libdl.so.2 [0]: normal symbol `dlsym' [GLIBC_2.0]
    and

    Code:
     2447: symbol=wine_dlsym;  lookup in file=/usr/local/bin/wine [0]
          2447: symbol=wine_dlsym;  lookup in file=/home/sb/hook/libsas.so [0]
          2447: symbol=wine_dlsym;  lookup in file=/usr/local/bin/../lib/libwine.so.1 [0]
          2447: binding file /usr/local/bin/../lib/libwine.so.1 [0] to /usr/local/bin/../lib/libwine.so.1 [0]: normal symbol `wine_dlsym' [WINE_1.0]
          2447: symbol=dlsym;  lookup in file=/usr/local/bin/wine [0]
          2447: symbol=dlsym;  lookup in file=/home/sb/hook/libsas.so [0]
          2447: symbol=dlsym;  lookup in file=/usr/local/bin/../lib/libwine.so.1 [0]
          2447: symbol=dlsym;  lookup in file=/lib/tls/i686/cmov/libpthread.so.0 [0]
          2447: symbol=dlsym;  lookup in file=/lib/tls/i686/cmov/libc.so.6 [0]
          2447: symbol=dlsym;  lookup in file=/lib/tls/i686/cmov/libdl.so.2 [0]
          2447: binding file /usr/local/bin/../lib/libwine.so.1 [0] to /lib/tls/i686/cmov/libdl.so.2 [0]: normal symbol `dlsym' [GLIBC_2.0]
          2447: symbol=_dl_sym;  lookup in file=/usr/local/bin/wine [0]
          2447: symbol=_dl_sym;  lookup in file=/home/sb/hook/libsas.so [0]
          2447: symbol=_dl_sym;  lookup in file=/usr/local/bin/../lib/libwine.so.1 [0]
          2447: symbol=_dl_sym;  lookup in file=/lib/tls/i686/cmov/libpthread.so.0 [0]
          2447: symbol=_dl_sym;  lookup in file=/lib/tls/i686/cmov/libc.so.6 [0]
          2447: binding file /lib/tls/i686/cmov/libdl.so.2 [0] to /lib/tls/i686/cmov/libc.so.6 [0]: normal symbol `_dl_sym' [GLIBC_PRIVATE]

  5. #5
    Sednogmah's Avatar Contributor
    Reputation
    129
    Join Date
    Oct 2009
    Posts
    158
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hey kouteiheika!
    Very clever solution!

    I just returned after a looong break and updated my injection method to hook dlsym().
    Also linked this thread in my old Linux glx hook thread:
    http://www.ownedcore.com/forums/worl...d_preload.html

    Also relevant (check the latest answers for useful info):
    http://www.ownedcore.com/forums/worl...n32-mingw.html

    P.S.: Any Linux botters still around?
    Feel free to PM me
    I'm working on a pattern editor for function pointers. (Linux / Qt).
    Last edited by Sednogmah; 06-16-2013 at 03:33 PM.

Similar Threads

  1. Is there a possible way to do .NET injection without C++?
    By zys924 in forum WoW Memory Editing
    Replies: 4
    Last Post: 12-25-2012, 08:55 AM
  2. [Linux] simple injection with LD_PRELOAD
    By Sednogmah in forum WoW Memory Editing
    Replies: 37
    Last Post: 04-24-2012, 07:07 PM
  3. Unlock Protected LUA with a Patch? (Without Injecting code?)
    By Zeroi9 in forum WoW Memory Editing
    Replies: 15
    Last Post: 03-30-2009, 05:58 PM
  4. how do i log in wow without downloading patch 2.3?
    By pavis in forum World of Warcraft General
    Replies: 11
    Last Post: 11-18-2007, 10:31 AM
  5. Using DLL's to inject values without CE.
    By Matsy in forum World of Warcraft Bots and Programs
    Replies: 7
    Last Post: 06-29-2007, 02:26 PM
All times are GMT -5. The time now is 11:28 AM. Powered by vBulletin® Version 4.2.3
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved. User Alert System provided by Advanced User Tagging (Pro) - vBulletin Mods & Addons Copyright © 2024 DragonByte Technologies Ltd.
Digital Point modules: Sphinx-based search