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
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.
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.
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
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.
info functions
to find the address of rand
in the symbol tableUse 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
jumpq 0x*my_rand_addr*
instructionset {int}*rand_addr* = *my_rand_addr*
to change symbol table instructionContinue
execution: now whenever rand
is called, it will jump to my_rand
insteadThis 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.