Can gdb make a function pointer point to another location?

前端 未结 10 1408
庸人自扰
庸人自扰 2020-12-23 14:47

I\'ll explain:

Let\'s say I\'m interested in replacing the rand() function used by a certain application.

So I attach gdb to this process and ma

相关标签:
10条回答
  • 2020-12-23 15:20

    I followed this post and this presentation and came up with the following set of gdb commands for OSX with x86-64 executable, which can be loaded with -x option when attaching to the process:

    set $s = dyld_stub_rand
    set $p = ($s+6+*(int*)($s+2))
    call (void*)dlsym((void*)dlopen("myrand.dylib"), "my_rand")
    set *(void**)$p = my_rand
    c
    

    The magic is in set $p = ... command. dyld_stub_rand is a 6-byte jump instruction. Jump offset is at dyld_stub_rand+2 (4 bytes). This is a $rip-relative jump, so add offset to what $rip would be at this point (right after the instruction, dyld_stub_rand+6).

    This points to a symbol table entry, which should be either real rand or dynamic linker routine to load it (if it was never called). It is then replaced by my_rand.

    Sometimes gdb will pick up dyld_stub_rand from libSystem or another shared library, if that happens, unload them first with remove-symbol-file before running other commands.

    0 讨论(0)
  • I'm not sure how to do this in a running program, but perhaps LD_PRELOAD will work for you. If you set this environment variable to a list of shared objects, the runtime loader will load the shared object early in the process and allow the functions in it to take precedence over others.

    LD_PRELOAD=path_to_library/asdf.so path/to/prog 
    

    You do have to do this before you start the process but you don't have to rebuild the program.

    0 讨论(0)
  • 2020-12-23 15:23

    You can still us LD_PRELOAD if you make the preloaded function understand the situations it's getting used in. Here is an example that will use the rand() as normal, except inside a forked process when it will always return 42. I use the dl routines to load the standard library's rand() function into a function pointer for use by the hijacked rand().

    // -*- compile-command: "gcc -Wall -fPIC -shared my_rand.c -o my_rand.so -ldl"; -*-
    //my_rand.c
    #include <sys/types.h>
    #include <unistd.h>
    
    #include <dlfcn.h>
    
    
    int pid = 0;
    int (*real_rand)(void) = NULL;
    
    void f(void) __attribute__ ((constructor));
    
    void f(void) {
        pid = getpid();
        void* dl = dlopen("libc.so.6", RTLD_LAZY);
        if(dl) {
            real_rand = dlsym(dl, "rand");
        }
    }
    
    int rand(void) 
    {
        if(pid == getpid() && real_rand)
            return real_rand();
        else
            return 42;
    }
    

    //test.c
    #include <dlfcn.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    int main(int argc, char** argv)
    {
    
        printf("Super random number: %d\n", rand());
        if(fork()) {
            printf("original process rand: %d\n", rand());
    
        } else {
            printf("forked process rand: %d\n", rand());
        }
    
        return 0;
    }
    

    jdizzle@pudding:~$ ./test
    Super random number: 1804289383
    original process rand: 846930886
    forked process rand: 846930886
    
    jdizzle@pudding:~$ LD_PRELOAD="/lib/ld-linux.so.2 ./my_rand.so" ./test
    Super random number: 1804289383
    original process rand: 846930886
    forked process rand: 42
    
    0 讨论(0)
  • 2020-12-23 15:25

    I have a new solution, based on the new original constraints. (I am not deleting my first answer, as others may find it useful.)

    I have been doing a bunch of research, and I think it would work with a bit more fiddling.

    1. In your .so rename your replacement rand function, e.g my_rand
    2. Compile everything and load up gdb
    3. Use info functions to find the address of rand in the symbol table
    4. Use dlopen then dlsym to load the function into memory and get its address

      call (int) dlopen("my_rand.so", 1) -> -val-

      call (unsigned int) dlsym(-val-, "my_rand") -> my_rand_addr

    5. -the tricky part- Find the hex code of a jumpq 0x*my_rand_addr* instruction
    6. Use set {int}*rand_addr* = *my_rand_addr* to change symbol table instruction
    7. Continue execution: now whenever rand is called, it will jump to my_rand instead

    This is a bit complicated, and very round-about, but I'm pretty sure it would work. The only thing I haven't accomplished yet is creating the jumpq instruction code. Everything up until that point works fine.

    0 讨论(0)
提交回复
热议问题