shoumikhin / elf-hook Goto Github PK
View Code? Open in Web Editor NEWELF shared library import table patching for function redirection.
ELF shared library import table patching for function redirection.
I've sucessfully used this library to replace func
in the following shared library:
#include <stdio.h>
void func() {
printf("func!\n");
}
void wrapfunc() {
func();
}
with the following test.c
:
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include "elf_hook.h"
#define LIBFUNC_PATH "./libfunc.so" //position dependent code
void wrapfunc();
void hook() {
printf("hook!\n");
}
void set_hook(void * handle) {
void *base = NULL;
if (NULL == handle) {
fprintf(stderr, "Failed to open \"%s\"! %s\n", LIBFUNC_PATH, dlerror()); exit(1);
}
if(get_module_base_address(LIBFUNC_PATH, handle, &base)) {
fprintf(stderr, "Failed to get module base addresses\n"); exit(1);
}
void *orig = elf_hook(LIBFUNC_PATH, base, "func", hook);
if (NULL == orig) {
fprintf(stderr, "Redirection failed!\n"); exit(1);
}
}
int main() {
wrapfunc();
void *handle = dlopen(LIBFUNC_PATH, RTLD_LAZY);
set_hook(handle);
wrapfunc();
dlclose(handle);
return 0;
}
through:
#!/usr/bin/env sh
set -e
cd $(dirname $0)
gcc -g3 -fPIC -shared -o libfunc.so libfunc.c
gcc -g3 -I../ELF-Hook -L${PWD} -o test test.c ../ELF-Hook/elf_hook.c -lfunc -ldl
LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./test
However, when I try to replace wrapfunc
instead of func
(i.e. elf_hook(LIBFUNC_PATH, base, "wrapfunc", hook)
), I get a Redirection failed!
error. Is this a supported use case?
Shared library does nothing:
class TestAPI
{
public:
void work() {}
};
Here I hook the socket
function in TestAPI
:
#include <iostream>
#include "test_api.h"
#include "elf_hook.h"
#include <dlfcn.h>
#include <string.h>
#include <sys/socket.h>
int hook_socket(int domain, int type, int protocol)
{
int fd = socket(domain, type, protocol);
printf("socket: fd(%d) domain(%d) type(%d) protocol(%d)\n", fd, domain, type, protocol);
return fd;
}
int main()
{
TestAPI api;
const char* filename = "/src/build/elfhook/libtest_api.so";
void* handle = dlopen(filename, RTLD_LAZY);
if (!handle)
return 1;
void* base_address = 0;
if (get_module_base_address(filename, handle, &base_address) != 0)
return 1;
elf_hook(filename, base_address, "socket", (void*)hook_socket);
api.work();
return 0;
}
Running it seg faults:
$ ./elfhook/elfhook.bin
Segmentation fault (core dumped)
GDB stack trace:
(gdb) where
#0 0xffff808868a96e0e in ?? ()
#1 0x00007f7797984c17 in _dl_fini () at dl-fini.c:235
#2 0x00007f7796720fe8 in __run_exit_handlers (status=0, listp=0x7f7796aaa5f8 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true) at exit.c:82
#3 0x00007f7796721035 in __GI_exit (status=<optimized out>) at exit.c:104
#4 0x00007f7796707837 in __libc_start_main (main=0x409c31 <main()>, argc=1, argv=0x7ffe2f6cd6f8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffe2f6cd6e8) at ../csu/libc-start.c:325
#5 0x0000000000409b19 in _start ()
I'm running glibc-2.23
:
$ /lib/x86_64-linux-gnu/libc.so.6
GNU C Library (Ubuntu GLIBC 2.23-0ubuntu3) stable release version 2.23, by Roland McGr
According to the glibc source
215 /* Is there a destructor function? */
216 if (l->l_info[DT_FINI_ARRAY] != NULL
217 || l->l_info[DT_FINI] != NULL)
218 {
219 /* When debugging print a message first. */
220 if (__builtin_expect (GLRO(dl_debug_mask)
221 & DL_DEBUG_IMPCALLS, 0))
222 _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n",
223 DSO_FILENAME (l->l_name),
224 ns);
225
226 /* First see whether an array is given. */
227 if (l->l_info[DT_FINI_ARRAY] != NULL)
228 {
229 ElfW(Addr) *array =
230 (ElfW(Addr) *) (l->l_addr
231 + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
232 unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
233 / sizeof (ElfW(Addr)));
234 while (i-- > 0)
235 ((fini_t) array[i]) (); // this is the line that seg faults
236 }
237
If I change TestAPI::work
to actually call socket
, then it works fine
class TestAPI
{
public:
void work()
{
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
}
};
Running now uses the hooked socket
function, and no seg fault
$ ./elfhook/elfhook.bin
socket: fd(3) domain(2) type(1) protocol(6)
Is there a way to hook functions that may not be called in the shared library, and not have the app seg fault?
I'm trying to hook the posix socket api
elf_hook(handle, base_address, "socket", hook_socket);
elf_hook(handle, base_address, "bind", hook_bind);
elf_hook(handle, base_address, "connect", hook_connect);
elf_hook(handle, base_address, "getpeername", hook_getpeername);
elf_hook(handle, base_address, "getsockname", hook_getsockname);
elf_hook(handle, base_address, "getsockopt", hook_getsockopt);
elf_hook(handle, base_address, "listen", hook_listen);
elf_hook(handle, base_address, "recv", hook_recv);
elf_hook(handle, base_address, "recvfrom", hook_recvfrom);
elf_hook(handle, base_address, "recvmsg", hook_recvmsg);
elf_hook(handle, base_address, "send", hook_send);
elf_hook(handle, base_address, "sendmsg", hook_sendmsg);
elf_hook(handle, base_address, "sendto", hook_sendto);
elf_hook(handle, base_address, "setsockopt", hook_setsockopt);
elf_hook(handle, base_address, "shutdown", hook_shutdown);
elf_hook(handle, base_address, "socketpair", hook_socketpair);
I can hook the first 6 functions
hooked(socket) original_function(0x7f8279c3c936)
hooked(bind) original_function(0xff04f3a7a808)
hooked(connect) original_function(0x4a8e56)
hooked(getpeername) original_function(0x4a8ff0)
hooked(getsockname) original_function(0x4a9126)
hooked(getsockopt) original_function(0x4a92c7)
and then it seg faults
#0 0x00000000004b2b59 in elf_hook \
(module_filename=0x1498200 "/src/build/elfhook/libtest_api.so", \
module_address=0x7f8279c3c000, \
name=0x5671b2 "listen", \
substitution=0x4a96a5 <hook_listen(int, int)>) at /src/elfhook/elf_hook.c:479
479 *(size_t *)(((size_t)module_address) + rel_plt_table[i].r_offset) = (size_t)substitution; \
//and replace it with the substitutional
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.