├── .gitattributes ├── .gitignore ├── .gitmodules ├── README.md ├── src ├── app.c ├── func.c ├── hook.c └── repl.c └── tests ├── dr ├── CMakeLists.txt ├── client.c ├── docker.sh └── test.sh ├── elf_hook ├── mwe │ ├── app.c │ ├── hook.c │ ├── libfunc.c │ ├── test.sh │ ├── testA.c │ ├── testB.c │ ├── testC.c │ └── testD.c ├── test.sh ├── testA.c ├── testB.c ├── testC.c ├── testD.c └── wrapfunc.c ├── ld_preload └── test.sh ├── mambo └── mambo_plugin.c └── testenv /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=-crlf 2 | 3 | *.c text 4 | *.gitattributes text 5 | *.gitignore text 6 | *.gitmodules text 7 | *.json text 8 | *.md text 9 | *.sh text 10 | *.txt text 11 | *.yml text 12 | 13 | *.gif binary 14 | *.ico binary 15 | *.jpeg binary 16 | *.png binary 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.o 3 | *.so 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tests/001/ELF-Hook"] 2 | path = tests/elf_hook/ELF-Hook 3 | url = https://github.com/shoumikhin/ELF-Hook 4 | [submodule "tests/poke/poke"] 5 | path = tests/poke/poke 6 | url = https://git.savannah.gnu.org/git/poke.git 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **binhook** is a survey of techniques to hook and/or replace functions in executable binaries or shared libraries. These allow to change the behaviour of programs, without requiring access to source code and without recompilation. 2 | 3 | Given a pre-built application ([app](src/app.c)) that uses a function ([func](src/func.c)) with a known signature (e.g. `char* func(char* A, char* B, uint32_t length)`), the target of these techniques is to execute an alternative implementation defined in independent C sources ([replacement](src/replacement.c)). 4 | 5 | # dynamic/runtime modification 6 | 7 | ## [ld_preload](tests/ld_preload/) 8 | 9 | > supported os (?) platforms (any) 10 | 11 | `LD_PRELOAD` is an environment variable supported by the dynamic linker on GNU/Linux systems. 12 | 13 | > http://man7.org/linux/man-pages/man8/ld.so.8.html 14 | > 15 | > A list of additional, user-specified, ELF shared objects to be loaded before all others. 16 | > This feature can be used to selectively override functions in other shared objects. 17 | 18 | ``` 19 | +------------+ +-------------+ +--------------+ 20 | | app | | patched_app | | app_lib | 21 | | +--------+ | | +---------+ | | +----------+ | 22 | | | func.o | +-----+ | | repl.o | | | |libfunc.so| +-----+ 23 | | +--------+ | | | +---------+ | | +----------+ | | 24 | +------------+ | +-------------+ +--------------+ | 25 | | v | | v 26 | | +----+-----+ | | +----+-----+ 27 | LD_PRELOAD: | |librepl.so| | | |librepl.so| 28 | | +----+-----+ | | +----+-----+ 29 | v v v v v 30 | output: default default replacement default replacement 31 | ``` 32 | 33 | In this test suite, the following artifacts are built: 34 | 35 | - `[func|replacement][.o|.so]`: both the original and the replacement function are independently built as objects and as shared libraries. 36 | - `app`: `app.c` and `func.o`. 37 | - `app_lib`: `app.c` and `func.so`. 38 | - `patched_app`: `app.c` and `replacement.o`. 39 | 40 | And the following tests are executed: 41 | 42 | - `app` without `LD_PRELOAD`. Regular execution of the app with built in `func`. 43 | - `app` with `LD_PRELOAD`. When the function is built in the app, `LD_PRELOAD` has no effect at all. 44 | - `patched_app` without `LD_PRELOAD`. Execution of the patched app, should the user have access to app sources to built it. 45 | - `app_lib` without `LD_PRELOAD`. Regular execution of the app with `func` loaded from a shared lib. 46 | - `app_lib` with `LD_PRELOAD`. Execution of the `replacement` function, since `replacement.so` is loaded before `libfunc.so`. 47 | 48 | Notes: 49 | 50 | - `LD_PRELOAD` allows to easily replace functions that the app uses from shared libraries. However, it is not suitable for functions that are built in the app. By the same token, it is not suitable for statically compiled bineries. 51 | - Both the app and/or the shared libraries can be built with `gcc -Os -s`. No additional symbol info is required. 52 | 53 | ## Hook by Import Address Table (IAT) / Procedure Linkage Table (PLT) patching 54 | 55 | ### [elf_hook](tests/elf_hook/) 56 | 57 | > supported os (?) platforms (?) 58 | 59 | In this testsuite, library [shoumikhin/ELF-Hook](https://github.com/shoumikhin/ELF-Hook) is used. ELF-Hook allows to replace a function which is called from another function defined in a shared library. 60 | 61 | ``` 62 | A/B C/D 63 | 64 | +------+ +------+ +------+ +------+ +---+ 65 | |hook.c| |test.c| |hook.c| |test.c| |app| 66 | +-----++ ++-----+ +-----++ ++-----+ +-+-+ 67 | | | | | | 68 | >-+-< >-+-< |rename 69 | build | build | v 70 | v +----------+ v +-----+----+ 71 | test <-+libfunc.so| test <-+libfunc.so| 72 | +----------+ +----------+ 73 | 74 | ``` 75 | 76 | The following artifacts are built: 77 | 78 | - `libfunc.so`: the target ([func](src/func.c)) is wrapped ([wrapfunc](src/wrapfunc.c)). 79 | - `testA`: `libfunc.so` is loaded with `dlopen` and `get_module_base_address` and `elf_hook` are used to replace `func` with `hook` (defined in [testA](tests/elf_hook/testA.c)). 80 | - `testB`: same as `testA`, but `dlsym` is used to get a pointer to the entrypoint. 81 | - `testC`: same as `testB`, but an exebutable is loaded instead of a shared library. 82 | - `testD`: same as `testC`, but the entrypoint is `main` (from the loaded app). 83 | 84 | And the following tests are executed: 85 | 86 | - `testA`: `wrapfunc` is executed before and after calling `elf_hook`. 87 | - `testB`: `wrapfunc` is executed before and after calling `elf_hook`. 88 | - `testC`: `func` is executed before calling `elf_hook`. Setting the redirect fails. 89 | - `testD`: `main` is execute before calling `elf_hook`. Setting the redirect fails. 90 | 91 | Notes: 92 | 93 | - Compared to LD_PRELOAD, ELF-Hook allows to apply modifications to a single shared library. 94 | - Replacing a function in a shared library is supported (testA, testB), but the same approach fails with a PIE executable (testC, testD). 95 | - ELF-Hook allows to optionally execute the original function from inside the hook. Hence, `wrapfunc` is the entrypoint, and whenever `func` is used, `hook` can execute instructions before and/or after. 96 | 97 | ### [kubo/plthook](https://github.com/kubo/plthook) 98 | 99 | > supported os (GNU/Linux, Windows, macOS, solaris, FreeBSD) platforms (x64, x86, arm, aarch64, powerpc, powerpc64le) 100 | 101 | to do... 102 | 103 | ## Hook by JMP instruction insertion 104 | 105 | ### [kubo/funchook](https://github.com/kubo/funchook) 106 | 107 | > supported os (GNU/Linux, Windows, macOS) platforms (x64, x86) 108 | 109 | to do... 110 | 111 | - [List of API Hook Libraries](https://github.com/kubo/funchook/wiki/List-of-API-Hook-Libraries) 112 | - [x86 API Hooking Demystified](http://jbremer.org/x86-api-hooking-demystified/) by Jurriaan Bremer 113 | 114 | ## Dynamic Binary Modification (DBM) 115 | 116 | ### MAMBO 117 | 118 | > supported os (GNU/Linux) platforms (armv7, aarch32, aarch64) 119 | 120 | work in progress... 121 | 122 | ### DynamoRIO 123 | 124 | > supported os (?) platforms (?) 125 | 126 | work in progress... 127 | 128 | # static/file modification 129 | 130 | ## [GNU poke](http://www.jemarch.net/poke.html) 131 | 132 | > supported os (?) platforms (?) 133 | 134 | work in progress... the extensible editor for structured binary data 135 | 136 | ## [Egalito](https://egalito.org/) 137 | 138 | > supported os (?) platforms (?) 139 | 140 | - [columbia/egalito](https://github.com/columbia/egalito) 141 | - [ASPLOS'20 - Session 2B - Egalito: Layout-Agnostic Binary Recompilation](https://www.youtube.com/watch?v=9Mv0-PfiXeg) 142 | 143 | ## [lief-project/LIEF](https://github.com/lief-project/LIEF) 144 | 145 | > supported os (?) platforms (?) 146 | 147 | to do... LIEF - Library to Instrument Executable Formats 148 | 149 | - https://lief.quarkslab.com/ 150 | - https://2018.pass-the-salt.org/files/talks/03-static-instrumentation.pdf 151 | 152 | # injection into a running process 153 | 154 | ## [kubo/injector](https://github.com/kubo/injector) 155 | 156 | > supported os (GNU/Linux, Windows) platforms (x64, arm64, ?) 157 | 158 | to do... 159 | 160 | # references 161 | 162 | - Fixing/Making Holes in Binaries by Shaun Clowes ([slides](https://www.blackhat.com/presentations/bh-asia-02/Clowes/bh-asia-02-clowes.pdf), [video](https://www.youtube.com/watch?v=18DKETYfvjg)) 163 | - [Dynamic linker tricks: Using LD_PRELOAD to cheat, inject features and investigate programs](https://rafalcieslak.wordpress.com/2013/04/02/dynamic-linker-tricks-using-ld_preload-to-cheat-inject-features-and-investigate-programs/) by Rafał Cieślak 164 | - [Redirecting Functions in Shared ELF Libraries](https://www.codeproject.com/Articles/70302/Redirecting-functions-in-shared-ELF-libraries) by [Anthony Shoumikhin](https://github.com/shoumikhin) ([shoumikhin/ELF-Hook](https://github.com/shoumikhin/ELF-Hook)) 165 | - [cea-sec/miasm](https://github.com/cea-sec/miasm) 166 | - [s3team/uroboros](https://github.com/s3team/uroboros) 167 | - [PEBIL: Static Binary Instrumentation for x86/Linux](https://www.sdsc.edu/pmac/tools/pebil.html) 168 | - [asciiflow.com](http://asciiflow.com/) 169 | -------------------------------------------------------------------------------- /src/app.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | extern char* func(char* A, char* B, uint32_t length); 6 | 7 | int main() { 8 | printf("Hello world!\n"); 9 | 10 | uint32_t length = 3; 11 | char *A = malloc(length*sizeof(int32_t)); 12 | char *B = malloc(length*sizeof(int32_t)); 13 | 14 | int i; 15 | for ( i=0; i 2 | #include 3 | #include 4 | 5 | char* func(char* A, char* B, uint32_t length) { 6 | printf("func: %p %p %d\n", A, B, length); 7 | 8 | char *O = malloc(length*sizeof(int32_t));; 9 | int i; 10 | for ( i=0; i 2 | #include 3 | 4 | extern char* func(char* A, char* B, uint32_t length); 5 | 6 | char* hook(char* A, char* B, uint32_t length) { 7 | printf("pre-hook!\n"); 8 | char* O = func(A, B, length); 9 | printf("post-hook!\n"); 10 | return O; 11 | } 12 | -------------------------------------------------------------------------------- /src/repl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | char* func(char* A, char* B, uint32_t length) { 6 | printf("replacement: %p %p %d\n", A, B, length); 7 | 8 | char *O = malloc(length*sizeof(int32_t));; 9 | int i; 10 | for ( i=0; i 5 | 6 | #define orig_name "func" 7 | #define func_name hook 8 | #define func_argd char* A, char* B, uint32_t length 9 | #define func_args A, B, length 10 | 11 | extern void func_name (func_argd); 12 | void *dbhi_ctx; 13 | 14 | void dbhi_func (func_argd) { 15 | func_name (func_args); 16 | drwrap_replace_native_fini(dbhi_ctx); 17 | } 18 | 19 | static void module_load_event(void *drcontext, const module_data_t *mod, bool loaded) { 20 | dbhi_ctx = drcontext; 21 | app_pc orig_pc = (app_pc)dr_get_proc_address(mod->handle, orig_name); 22 | if (orig_pc != NULL) { 23 | drwrap_replace_native ( orig_pc , (app_pc)dbhi_func , false , 0 , NULL , false ); 24 | } 25 | } 26 | 27 | static void event_exit(void) { 28 | drwrap_exit(); 29 | drmgr_exit(); 30 | } 31 | 32 | DR_EXPORT void dr_client_main(client_id_t id, int argc, const char *argv[]) { 33 | dr_set_client_name("DynamoRIO Sample Client 'wrap'", "http://dynamorio.org/issues"); 34 | dr_log(NULL, DR_LOG_ALL, 1, "Client 'wrap' initializing\n"); 35 | drmgr_init(); 36 | drwrap_init(); 37 | dr_register_exit_event(event_exit); 38 | drmgr_register_module_load_event(module_load_event); 39 | } 40 | -------------------------------------------------------------------------------- /tests/dr/docker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | winpty docker run --rm -it \ 6 | -v /$(pwd)/../..://wrk \ 7 | -w //wrk/tests/dr \ 8 | aptman/dbhi:bionic-dr bash -c "$(cat test.sh)" 9 | 10 | # $(command -v winpty) docker run --rm -itu "$(id -u)" \ 11 | # -e DBHI_CASE="$DBHI_CASE" \ 12 | # -e DBHI_ORIG="$DBHI_ORIG" \ 13 | # -e DBHI_UUT="$DBHI_UUT" \ 14 | # -e DBHI_FUNC="$DBHI_FUNC" \ 15 | # -e DBHI_ARGD="$DBHI_ARGD" \ 16 | # -e DBHI_DRARGS="$DBHI_DRARGS" \ 17 | # -e DBHI_HAND="$DBHI_HAND" \ 18 | # -e BUILD_DIR="//src/examples/$DBHI_BASE/build" \ 19 | # -v "/$(pwd)/..://src" \ 20 | # -w "//src/examples/$DBHI_BASE/build" \ 21 | # "aptman/dbhi:bionic-$1" \ 22 | # sh -c "../../run b_$1" -------------------------------------------------------------------------------- /tests/dr/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | if [ "$DYNAMORIO_HOME" = "" ]; then 6 | echo "DYNAMORIO_HOME not set." 7 | exit 1 8 | fi 9 | 10 | #plugin="./src/drclient.c" 11 | #sed_defines 12 | 13 | mkdir -p build 14 | cd build 15 | 16 | SRCS="../../../src/" 17 | 18 | #cp "$SRCS"hook.c ./ 19 | 20 | gcc -Wall -o app "$SRCS"func.c "$SRCS"app.c 21 | #-Os -s 22 | 23 | cmake SHOW_RESULTS \ 24 | -L$SRCS \ 25 | -DDynamoRIO_DIR=$DYNAMORIO_HOME/cmake \ 26 | ../ 27 | 28 | #-DPLUGIN_ARGS="$(get_plugin_args)" ./src 29 | 30 | make 31 | 32 | "$DYNAMORIO_HOME"/bin64/drrun -c libdrclient.so -- app 33 | 34 | bash 35 | 36 | # -Dorig_name="func" \ 37 | # -Dfunc_name="hook" \ 38 | # -Dfunc_argd='void' \ 39 | # -Dfunc_args='void' \ 40 | 41 | 42 | #define func_name {{ .Func }} 43 | #define func_argd {{ .Argd }} 44 | #define func_args {{ .Hand }} -------------------------------------------------------------------------------- /tests/elf_hook/mwe/app.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern void func(); 4 | 5 | int main() { 6 | printf("Hello world!\n"); 7 | func(); 8 | printf("Bye!\n"); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /tests/elf_hook/mwe/hook.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern void func(); 4 | 5 | void hook() { 6 | printf("pre-hook!\n"); 7 | func(); 8 | printf("post-hook!\n"); 9 | return; 10 | } 11 | -------------------------------------------------------------------------------- /tests/elf_hook/mwe/libfunc.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void func() { 4 | printf("func!\n"); 5 | } 6 | 7 | void wrapfunc() { 8 | printf("begin wrap\n"); 9 | func(); 10 | printf("end wrap\n"); 11 | } -------------------------------------------------------------------------------- /tests/elf_hook/mwe/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | enable_color() { 6 | ENABLECOLOR='-c ' 7 | ANSI_RED="\033[31m" 8 | ANSI_GREEN="\033[32m" 9 | ANSI_YELLOW="\033[33m" 10 | ANSI_BLUE="\033[34m" 11 | ANSI_MAGENTA="\033[35m" 12 | ANSI_GRAY="\033[90m" 13 | ANSI_CYAN="\033[36;1m" 14 | ANSI_DARKCYAN="\033[36m" 15 | ANSI_NOCOLOR="\033[0m" 16 | } 17 | 18 | disable_color() { unset ENABLECOLOR ANSI_RED ANSI_GREEN ANSI_YELLOW ANSI_BLUE ANSI_MAGENTA ANSI_CYAN ANSI_DARKCYAN ANSI_NOCOLOR; } 19 | 20 | enable_color 21 | 22 | gstart () { 23 | printf "${ANSI_MAGENTA}${1}$ANSI_NOCOLOR\n" 24 | } 25 | 26 | cd $(dirname $0) 27 | 28 | mkdir -p build 29 | cd build 30 | 31 | gcc -g3 -fPIC -shared -o libfunc.so ../libfunc.c 32 | 33 | gstart "· Test A" 34 | gcc -g3 -I../../ELF-Hook -L${PWD} -o testA ../hook.c ../testA.c ../../ELF-Hook/elf_hook.c -lfunc -ldl 35 | LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./testA 36 | 37 | gstart "· Test B" 38 | gcc -g3 -I../../ELF-Hook -L${PWD} -o testB ../hook.c ../testB.c ../../ELF-Hook/elf_hook.c -lfunc -ldl 39 | LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./testB 40 | 41 | gstart "· Build app" 42 | gcc -g3 -fPIC -rdynamic -Wall -o app ../libfunc.c ../app.c 43 | 44 | gstart "· Test C" 45 | gcc -g3 -I../../ELF-Hook -L${PWD} -o testC ../hook.c ../testC.c ../../ELF-Hook/elf_hook.c -lfunc -ldl 46 | LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./testC 47 | 48 | gstart "· Test D" 49 | gcc -g3 -I../../ELF-Hook -L${PWD} -o testD ../hook.c ../testD.c ../../ELF-Hook/elf_hook.c -lfunc -ldl 50 | LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./testD 51 | -------------------------------------------------------------------------------- /tests/elf_hook/mwe/testA.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "elf_hook.h" 6 | 7 | #define LIBFUNC_PATH "./libfunc.so" //position dependent code 8 | 9 | void wrapfunc(); 10 | void hook(); 11 | 12 | int set_hook(void * handle) { 13 | void *base = NULL; 14 | if (NULL == handle) { 15 | fprintf(stderr, "Failed to open \"%s\"! %s\n", LIBFUNC_PATH, dlerror()); exit(1); 16 | } 17 | if(get_module_base_address(LIBFUNC_PATH, handle, &base)) { 18 | fprintf(stderr, "Failed to get module base addresses\n"); exit(1); 19 | } 20 | printf("base: %p\n", base); 21 | void *orig = elf_hook(LIBFUNC_PATH, base, "func", hook); 22 | if (NULL == orig) { 23 | fprintf(stderr, "Redirection failed!\n"); exit(1); 24 | } 25 | } 26 | 27 | int main() { 28 | wrapfunc(); 29 | 30 | void *handle = dlopen(LIBFUNC_PATH, RTLD_LAZY); 31 | printf("----\n"); 32 | set_hook(handle); 33 | printf("----\n"); 34 | 35 | wrapfunc(); 36 | 37 | dlclose(handle); 38 | return 0; 39 | } -------------------------------------------------------------------------------- /tests/elf_hook/mwe/testB.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "elf_hook.h" 6 | 7 | #define LIBFUNC_PATH "./libfunc.so" //position dependent code 8 | 9 | void hook(); 10 | 11 | int set_hook(void * handle) { 12 | void *base = NULL; 13 | if(get_module_base_address(LIBFUNC_PATH, handle, &base)) { 14 | fprintf(stderr, "Failed to get module base addresses\n"); 15 | exit(1); 16 | } 17 | printf("base: %p\n", base); 18 | void *orig = elf_hook(LIBFUNC_PATH, base, "func", hook); 19 | if (NULL == orig) { 20 | fprintf(stderr, "Redirection failed!\n"); 21 | exit(1); 22 | } 23 | } 24 | 25 | int main() { 26 | void *handle = dlopen(LIBFUNC_PATH, RTLD_LAZY); 27 | if (!handle){ 28 | fprintf(stderr, "%s\n", dlerror()); 29 | exit(1); 30 | } 31 | 32 | typedef int func_t(); 33 | 34 | func_t *wrapfunc = (func_t*)dlsym(handle, "wrapfunc"); 35 | if (!wrapfunc){ 36 | fprintf(stderr, "%s\n", dlerror()); 37 | exit(2); 38 | } 39 | 40 | wrapfunc(); 41 | 42 | printf("----\n"); 43 | set_hook(handle); 44 | printf("----\n"); 45 | 46 | wrapfunc(); 47 | 48 | dlclose(handle); 49 | 50 | return 0; 51 | } -------------------------------------------------------------------------------- /tests/elf_hook/mwe/testC.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "elf_hook.h" 6 | 7 | #define LIBFUNC_PATH "./app" //position dependent code 8 | 9 | void hook(); 10 | 11 | void set_hook(void * handle) { 12 | void *base = NULL; 13 | if(get_module_base_address(LIBFUNC_PATH, handle, &base)) { 14 | fprintf(stderr, "Failed to get module base addresses\n"); 15 | exit(1); 16 | } 17 | printf("base: %p\n", base); 18 | void *orig = elf_hook(LIBFUNC_PATH, base, "func", hook); 19 | if (NULL == orig) { 20 | fprintf(stderr, "Redirection failed!\n"); 21 | //exit(1); 22 | } 23 | } 24 | 25 | int main() { 26 | void *handle = dlopen(LIBFUNC_PATH, RTLD_LAZY); 27 | if (!handle){ 28 | fprintf(stderr, "%s\n", dlerror()); 29 | exit(1); 30 | } 31 | 32 | typedef int main_t(int, char**); 33 | main_t *client_main = (main_t*)dlsym(handle, "main"); 34 | if (!client_main){ 35 | fprintf(stderr, "%s\n", dlerror()); 36 | exit(2); 37 | } 38 | 39 | client_main(1, (char*[]){"client", 0}); 40 | 41 | printf("----\n"); 42 | set_hook(handle); 43 | printf("----\n"); 44 | 45 | client_main(1, (char*[]){"client", 0}); 46 | 47 | dlclose(handle); 48 | 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /tests/elf_hook/mwe/testD.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "elf_hook.h" 6 | 7 | #define LIBFUNC_PATH "./app" //position dependent code 8 | 9 | void hook(); 10 | 11 | void set_hook(void * handle) { 12 | void *base = NULL; 13 | if(get_module_base_address(LIBFUNC_PATH, handle, &base)) { 14 | fprintf(stderr, "Failed to get module base addresses\n"); 15 | exit(1); 16 | } 17 | printf("base: %p\n", base); 18 | void *orig = elf_hook(LIBFUNC_PATH, base, "func", hook); 19 | if (NULL == orig) { 20 | fprintf(stderr, "Redirection failed!\n"); 21 | //exit(1); 22 | } 23 | } 24 | 25 | int main() { 26 | void *handle = dlopen(LIBFUNC_PATH, RTLD_LAZY); 27 | if (!handle){ 28 | fprintf(stderr, "%s\n", dlerror()); 29 | exit(1); 30 | } 31 | 32 | typedef int func_t(); 33 | 34 | func_t *ffunc = (func_t*)dlsym(handle, "func"); 35 | if (!ffunc){ 36 | fprintf(stderr, "%s\n", dlerror()); 37 | exit(2); 38 | } 39 | 40 | ffunc(); 41 | 42 | printf("----\n"); 43 | set_hook(handle); 44 | printf("----\n"); 45 | 46 | ffunc(); 47 | 48 | dlclose(handle); 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /tests/elf_hook/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | cd $(dirname $0) 6 | 7 | . ../testenv 8 | 9 | mkdir -p build 10 | cd build 11 | 12 | SRCS="../../../src/" 13 | 14 | set +e 15 | rm *.so *.o *.log > /dev/null 16 | set -e 17 | 18 | gstart "· Build libfunc.so" 19 | gcc -Os -s -Wall -shared -o libfunc.so "${SRCS}func.c" ../wrapfunc.c 20 | gend 21 | 22 | build_test () { 23 | gcc -Wall -I../ELF-Hook -L${PWD} -o "$@" ../ELF-Hook/elf_hook.c -lfunc -ldl 24 | } 25 | #-Os -s 26 | 27 | gstart "· Build testA" 28 | build_test testA "${SRCS}hook.c" ../testA.c 29 | gend 30 | 31 | gstart "· Run testA" 32 | LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./testA 33 | gend 34 | 35 | gstart "· Build testB" 36 | build_test testB "${SRCS}hook.c" ../testB.c 37 | gend 38 | 39 | gstart "· Run testB" 40 | LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./testB 41 | gend 42 | 43 | gstart "· Build app" 44 | gcc -g3 -fPIC -rdynamic -Wall -o app "$SRCS"func.c "$SRCS"app.c 45 | gend 46 | 47 | cp app libfunc.so 48 | 49 | gstart "· Build testC" 50 | build_test testC "${SRCS}hook.c" ../testC.c 51 | gend 52 | 53 | gstart "· Run testC" 54 | LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./testC 55 | gend 56 | 57 | gstart "· Build testD" 58 | build_test testD "${SRCS}hook.c" ../testD.c 59 | gend 60 | 61 | gstart "· Run testD" 62 | LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./testD 63 | gend 64 | -------------------------------------------------------------------------------- /tests/elf_hook/testA.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "elf_hook.h" 7 | 8 | #define LIBFUNC_PATH "./libfunc.so" //position dependent code 9 | 10 | char* wrapfunc(char* A, char* B, uint32_t length); 11 | char* hook(char* A, char* B, uint32_t length); 12 | 13 | void set_hook(void * handle) { 14 | void *base = NULL; 15 | if (NULL == handle) { 16 | fprintf(stderr, "Failed to open \"%s\"! %s\n", LIBFUNC_PATH, dlerror()); exit(1); 17 | } 18 | if(get_module_base_address(LIBFUNC_PATH, handle, &base)) { 19 | fprintf(stderr, "Failed to get module base addresses\n"); 20 | exit(1); 21 | } 22 | printf("base: %p\n", base); 23 | void *orig = elf_hook(LIBFUNC_PATH, base, "func", hook); 24 | if (NULL == orig) { 25 | fprintf(stderr, "Redirection failed!\n"); 26 | exit(1); 27 | } 28 | } 29 | 30 | int main() { 31 | uint32_t length = 3; 32 | char *A = malloc(length*sizeof(int32_t)); 33 | char *B = malloc(length*sizeof(int32_t)); 34 | 35 | int i; 36 | for ( i=0; i 2 | #include 3 | #include 4 | #include 5 | 6 | #include "elf_hook.h" 7 | 8 | #define LIBFUNC_PATH "./libfunc.so" //position dependent code 9 | 10 | char* hook(char* A, char* B, uint32_t length); 11 | 12 | void set_hook(void * handle) { 13 | void *base = NULL; 14 | if(get_module_base_address(LIBFUNC_PATH, handle, &base)) { 15 | fprintf(stderr, "Failed to get module base addresses\n"); 16 | exit(1); 17 | } 18 | printf("base: %p\n", base); 19 | void *orig = elf_hook(LIBFUNC_PATH, base, "func", hook); 20 | if (NULL == orig) { 21 | fprintf(stderr, "Redirection failed!\n"); 22 | exit(1); 23 | } 24 | } 25 | 26 | int main() { 27 | void *handle = dlopen(LIBFUNC_PATH, RTLD_LAZY); 28 | 29 | if (!handle){ 30 | fprintf(stderr, "%s\n", dlerror()); 31 | exit(1); 32 | } 33 | 34 | typedef int func_t(char* A, char* B, uint32_t length); 35 | 36 | func_t *wrapfunc = (func_t*)dlsym(handle, "wrapfunc"); 37 | if (!wrapfunc){ 38 | fprintf(stderr, "%s\n", dlerror()); 39 | exit(2); 40 | } 41 | 42 | uint32_t length = 3; 43 | char *A = malloc(length*sizeof(int32_t)); 44 | char *B = malloc(length*sizeof(int32_t)); 45 | 46 | int i; 47 | for ( i=0; i 2 | #include 3 | #include 4 | #include 5 | 6 | #include "elf_hook.h" 7 | 8 | #define LIBFUNC_PATH "./app" //position dependent code 9 | 10 | char* hook(char* A, char* B, uint32_t length); 11 | 12 | void set_hook(void * handle) { 13 | void *base = NULL; 14 | if(get_module_base_address(LIBFUNC_PATH, handle, &base)) { 15 | fprintf(stderr, "Failed to get module base addresses\n"); 16 | exit(1); 17 | } 18 | printf("base: %p\n", base); 19 | void *orig = elf_hook(LIBFUNC_PATH, base, "func", hook); 20 | if (NULL == orig) { 21 | fprintf(stderr, "Redirection failed!\n"); 22 | //exit(1); 23 | } 24 | } 25 | 26 | int main() { 27 | void *handle = dlopen(LIBFUNC_PATH, RTLD_LAZY); 28 | 29 | if (!handle){ 30 | fprintf(stderr, "%s\n", dlerror()); 31 | exit(1); 32 | } 33 | 34 | typedef int func_t(char* A, char* B, uint32_t length); 35 | 36 | func_t *ffunc = (func_t*)dlsym(handle, "func"); 37 | if (!ffunc){ 38 | fprintf(stderr, "%s\n", dlerror()); 39 | exit(2); 40 | } 41 | 42 | uint32_t length = 3; 43 | char *A = malloc(length*sizeof(int32_t)); 44 | char *B = malloc(length*sizeof(int32_t)); 45 | 46 | int i; 47 | for ( i=0; i 2 | #include 3 | #include 4 | #include 5 | 6 | #include "elf_hook.h" 7 | 8 | #define LIBFUNC_PATH "./app" //position dependent code 9 | 10 | char* hook(char* A, char* B, uint32_t length); 11 | 12 | void set_hook(void * handle) { 13 | void *base = NULL; 14 | if (NULL == handle) { 15 | fprintf(stderr, "Failed to open \"%s\"! %s\n", LIBFUNC_PATH, dlerror()); 16 | exit(1); 17 | } 18 | if(get_module_base_address(LIBFUNC_PATH, handle, &base)) { 19 | fprintf(stderr, "Failed to get module base addresses\n"); 20 | exit(1); 21 | } 22 | printf("base: %p\n", base); 23 | void *orig = elf_hook(LIBFUNC_PATH, base, "func", hook); 24 | if (NULL == orig) { 25 | fprintf(stderr, "Redirection failed!\n"); 26 | //exit(1); 27 | } 28 | } 29 | 30 | int main() { 31 | void *handle = dlopen(LIBFUNC_PATH, RTLD_LAZY); 32 | 33 | if (!handle){ 34 | fprintf(stderr, "%s\n", dlerror()); 35 | exit(1); 36 | } 37 | 38 | typedef int main_t(int, char**); 39 | main_t *client_main = (main_t*)dlsym(handle, "main"); 40 | if (!client_main){ 41 | fprintf(stderr, "%s\n", dlerror()); 42 | exit(2); 43 | } 44 | 45 | client_main(1, (char*[]){"client", 0}); 46 | 47 | printf("----\n"); 48 | set_hook(handle); 49 | printf("----\n"); 50 | 51 | client_main(1, (char*[]){"client", 0}); 52 | 53 | dlclose(handle); 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /tests/elf_hook/wrapfunc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | char* func(char* A, char* B, uint32_t length); 5 | 6 | char* wrapfunc(char* A, char* B, uint32_t length) { 7 | printf("begin wrap\n"); 8 | char* O = func(A, B, length); 9 | printf("end wrap\n"); 10 | return O; 11 | } 12 | -------------------------------------------------------------------------------- /tests/ld_preload/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | cd $(dirname $0) 6 | 7 | . ../testenv 8 | 9 | mkdir -p build 10 | cd build 11 | 12 | SRCS="../../../src/" 13 | 14 | set +e 15 | rm *.so *.o *.log > /dev/null 16 | set -e 17 | 18 | cmd_gcc () { 19 | gstart "· Build $1" 20 | gcc -Os -s -Wall -o "$@" 21 | gend 22 | } 23 | 24 | for i in func repl; do 25 | cmd_gcc "${i}.o" -c "${SRCS}${i}.c" 26 | cmd_gcc "lib${i}.so" -shared "${SRCS}${i}.c" 27 | done 28 | 29 | cmd_gcc app func.o "$SRCS"app.c 30 | cmd_gcc app_lib "$SRCS"app.c -L$(pwd) -lfunc 31 | cmd_gcc patched_app repl.o "$SRCS"app.c 32 | 33 | gstart "· Run app (without LD_PRELOAD)" 34 | # output: default 35 | ./app | tee app.log 36 | gend 37 | 38 | cmd_preload () { 39 | LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" LD_PRELOAD="$(pwd)/librepl.so" "$@" 40 | } 41 | 42 | gstart "· Run app (with LD_PRELOAD)" 43 | # output: default 44 | cmd_preload ./app | tee app_pre.log 45 | gend 46 | 47 | gstart "· Run patched_app" 48 | # output: replacement 49 | ./patched_app | tee patched_app.log 50 | gend 51 | 52 | gstart "· Run app_lib (without LD_PRELOAD)" 53 | # output: default 54 | LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" ./app_lib | tee app_lib.log 55 | gend 56 | 57 | gstart "· Run app_lib (with LD_PRELOAD)" 58 | # output: replacement 59 | cmd_preload ./app_lib | tee app_lib_pre.log 60 | gend 61 | -------------------------------------------------------------------------------- /tests/mambo/mambo_plugin.c: -------------------------------------------------------------------------------- 1 | #ifdef PLUGINS_NEW 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define orig_name "{{ .Orig }}" 9 | #define func_name {{ .Func }} 10 | #define func_argd {{ .Argd }} 11 | #define func_handler {{ .Hand }} 12 | 13 | extern void func_name(func_argd); 14 | 15 | int func_handler(mambo_context *ctx) { 16 | uint32_t *wrp = (uint32_t *)ctx->code.write_p; 17 | #ifdef __arm__ 18 | int inst_set = mambo_get_inst_type(ctx); 19 | if (inst_set == ARM_INST) { 20 | uint32_t *write_p = wrp; 21 | arm_push_regs((1 << es) | (1 << lr)); 22 | arm_copy_to_reg_32bit(&write_p, es, (uintptr_t)func_name); 23 | arm_blx(&write_p, es); 24 | write_p++; 25 | arm_pop_regs((1 << es) | (1 << lr)); 26 | arm_inline_hash_lookup(ctx->thread_data, (uint32_t**)&write_p, ctx->code.fragment_id, lr); 27 | wrp = write_p; 28 | } else if (inst_set == THUMB_INST) { 29 | uint16_t *write_p = (uint16_t *)wrp; 30 | thumb_push_regs(&write_p, (1 << es) | (1 << lr)); 31 | copy_to_reg_32bit(&write_p, es, (uintptr_t)func_name); 32 | thumb_blx16(&write_p, es); 33 | write_p++; 34 | thumb_pop_regs(&write_p, (1 << es) | (1 << lr)); 35 | thumb_inline_hash_lookup(ctx->thread_data, &write_p, ctx->code.fragment_id, lr); 36 | wrp = (uint32_t*)write_p; 37 | } 38 | 39 | #elif __aarch64__ 40 | uint32_t *write_p = wrp; 41 | a64_push_pair_reg(es, lr) 42 | a64_copy_to_reg_64bits(&write_p, es, (uintptr_t)func_name); 43 | a64_BLR(&write_p, es); 44 | write_p++; 45 | a64_pop_pair_reg(es, lr) 46 | a64_inline_hash_lookup(ctx->thread_data, ctx->code.fragment_id, &write_p, 47 | ctx->code.read_address, lr, false, true); 48 | wrp = write_p; 49 | #endif 50 | ctx->code.write_p = (void *)wrp; 51 | return 0; 52 | } 53 | 54 | void mambo_register_function_replace(mambo_context *ctx, char *func, void *replace) { 55 | // TODO `void *replace` is not used ATM. It'd be interesing to pass `*replace` (`func_name`) to `func_handler` here, instead of using defines. 56 | mambo_register_function_cb(ctx, func, func_handler, NULL); 57 | } 58 | 59 | __attribute__((constructor)) void ghdl_plugin() { 60 | mambo_context *ctx = mambo_register_plugin(); 61 | assert(ctx != NULL); 62 | mambo_register_function_replace(ctx, orig_name, func_name); 63 | setlocale(LC_NUMERIC, ""); 64 | } 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /tests/testenv: -------------------------------------------------------------------------------- 1 | enable_color() { 2 | ENABLECOLOR='-c ' 3 | ANSI_RED="\033[31m" 4 | ANSI_GREEN="\033[32m" 5 | ANSI_YELLOW="\033[33m" 6 | ANSI_BLUE="\033[34m" 7 | ANSI_MAGENTA="\033[35m" 8 | ANSI_GRAY="\033[90m" 9 | ANSI_CYAN="\033[36;1m" 10 | ANSI_DARKCYAN="\033[36m" 11 | ANSI_NOCOLOR="\033[0m" 12 | } 13 | 14 | disable_color() { unset ENABLECOLOR ANSI_RED ANSI_GREEN ANSI_YELLOW ANSI_BLUE ANSI_MAGENTA ANSI_CYAN ANSI_DARKCYAN ANSI_NOCOLOR; } 15 | 16 | enable_color 17 | 18 | print_start() { 19 | if [ "x$2" != "x" ]; then 20 | COL="$2" 21 | elif [ "x$BASE_COL" != "x" ]; then 22 | COL="$BASE_COL" 23 | else 24 | COL="$ANSI_MAGENTA" 25 | fi 26 | printf "${COL}${1}$ANSI_NOCOLOR\n" 27 | } 28 | 29 | gstart () { 30 | print_start "$@" 31 | } 32 | gend () { 33 | : 34 | } 35 | 36 | if [ -n "$GITHUB_EVENT_PATH" ]; then 37 | export CI=true 38 | fi 39 | 40 | [ -n "$CI" ] && { 41 | gstart () { 42 | printf '::[group]' 43 | print_start "$@" 44 | SECONDS=0 45 | } 46 | 47 | gend () { 48 | duration=$SECONDS 49 | echo '::[endgroup]' 50 | printf "${ANSI_GRAY}took $(($duration / 60)) min $(($duration % 60)) sec.${ANSI_NOCOLOR}\n" 51 | } 52 | } || echo "INFO: not in CI" 53 | --------------------------------------------------------------------------------