Can gdb make a function pointer point to another location?

前端 未结 10 1412
庸人自扰
庸人自扰 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:17

    I frequently use code injection as a method of mocking for automated testing of C code. If that's the sort of situation you're in -- if your use of GDB is simply because you're not interested in the parent processes, and not because you want to interactively select the processes which are of interest -- then you can still use LD_PRELOAD to achieve your solution. Your injected code just needs to determine whether it is in the parent or child processes. There are several ways you could do this, but on Linux, since your child processes exec(), the simplest is probably to look at the active executable image.

    I produced two executables, one named a and the other b. Executable a prints the result of calling rand() twice, then fork()s and exec()s b twice. Executable b print the result of calling rand() once. I use LD_PRELOAD to inject the result of compiling the following code into the executables:

    // -*- compile-command: "gcc -D_GNU_SOURCE=1 -Wall -std=gnu99 -O2 -pipe -fPIC -shared -o inject.so inject.c"; -*-
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define constructor __attribute__((__constructor__))
    
    typedef int (*rand_t)(void);
    
    typedef enum {
        UNKNOWN,
        PARENT,
        CHILD
    } state_t;
    
    state_t state = UNKNOWN;
    rand_t rand__ = NULL;
    
    state_t
    determine_state(void)
    {
        pid_t pid = getpid();
        char linkpath[PATH_MAX] = { 0, };
        char exepath[PATH_MAX] = { 0, };
        ssize_t exesz = 0;
    
        snprintf(linkpath, PATH_MAX, "/proc/%d/exe", pid);
        exesz = readlink(linkpath, exepath, PATH_MAX);
        if (exesz < 0)
            return UNKNOWN;
    
        switch (exepath[exesz - 1]) {
        case 'a':
            return PARENT;
        case 'b':
            return CHILD;
        }
    
        return UNKNOWN;
    }
    
    int
    rand(void)
    {
        if (state == CHILD)
            return 47;
        return rand__();
    }
    
    constructor static void
    inject_init(void) 
    {
        rand__ = dlsym(RTLD_NEXT, "rand");
        state = determine_state();
    }
    

    The result of running a with and without injection:

    $ ./a
    a: 644034683
    a: 2011954203
    b: 375870504
    b: 1222326746
    $ LD_PRELOAD=$PWD/inject.so ./a
    a: 1023059566
    a: 986551064
    b: 47
    b: 47
    

    I'll post a gdb-oriented solution later.

提交回复
热议问题