Skip to the content.

hookso

Hookso is a Linux dynamic link library injection modification search tool, used to modify the dynamic link library behavior of other processes.

Features

Compile

Git clone code, run scripts, generate hookso and test programs

# ./build.sh
# cd test && ./build.sh

Example

First look at the test code, the code is very simple, test.cpp constantly calls the libtest function of libtest.so

int n = 0;
while (1) {
    if (libtest (n ++)) {
        break;
    }
    sleep (1);
}

And the libtest function of libtest.so just prints to standard output

Note that several different ways to call puts are used here. The reason is that different writing methods will cause the position of puts in elf to be different. Multiple writing methods are used here, so that the subsequent search and replacement can be covered. For details, you can readelf -r libtest.so to view the details.

typedef int (*PutsFunc)(const char *s);

PutsFunc f = &puts;

extern "C" bool libtest(int n) {
    char buff[128] = {0};
    snprintf(buff, sizeof(buff), "libtest %d", n);
    if (n % 3 == 0) {
        puts(buff);
    } else if (n % 3 == 1) {
        f(buff);
    } else {
        PutsFunc ff = &puts;
        ff(buff);
    }
    return false;
}

At this time, test is not loaded with libtestnew.so, it will be injected with hookso later, the code of libtestnew.cpp is as follows

extern "C" bool libtestnew (int n) {
    char buff [128] = {0};
    snprintf (buff, sizeof (buff), "libtestnew% d", n);
    puts (buff);
    return false;
}

extern "C" bool putsnew (const char * str) {
    char buff [128] = {0};
    snprintf (buff, sizeof (buff), "putsnew% s", str);
    puts (buff);
    return false;
}

libtestnew.cpp defines two functions, one to replace the puts function of libtest.so and one to replace libtest.so

Now we start to compile and run it

# cd test
# ./build.sh
# ./test
libtest 1
libtest 2
...
libtest 10

The program starts to run, you can see that it keeps printing to the output, assuming that the test pid is 11234

So here is equivalent to calling write (1, “haha”, 4) in C language, which is to print a sentence on the standard output

Usage

hookso: type pid params

eg:

do syscall: 
# ./hookso syscall pid syscall-number i=int-param1 s="string-param2" 

call .so function: 
# ./hookso call pid target-so target-func i=int-param1 s="string-param2" 

dlopen .so: 
# ./hookso dlopen pid target-so-path 

dlclose .so: 
# ./hookso dlclose pid handle 

open .so and call function and close: 
# ./hookso dlcall pid target-so-path target-func i=int-param1 s="string-param2" 

replace src.so old-function to target.so new-function: 
# ./hookso replace pid src-so src-func target-so-path target-func 

replace target-function-addr to target.so new-function: 
# ./hookso replacep pid func-addr target-so-path target-func 

set target.so target-function new value : 
# ./hookso setfunc pid target-so target-func value 

set target-function-addr new value : 
# ./hookso setfuncp pid func-addr value 

find target.so target-function : 
# ./hookso find pid target-so target-func 

get target.so target-function call argument: 
# ./hookso arg pid target-so target-func arg-index 

get target-function-addr call argument: 
# ./hookso argp pid func-addr arg-index 

before call target.so target-function, do syscall/call/dlcall/dlopen/dlclose with params: 
# ./hookso trigger pid target-so target-func syscall syscall-number @1 i=int-param2 s="string-param3" 
# ./hookso trigger pid target-so target-func call trigger-target-so trigger-target-func @1 i=int-param2 s="string-param3" 
# ./hookso trigger pid target-so target-func dlcall trigger-target-so trigger-target-func @1 i=int-param2 s="string-param3" 
# ./hookso trigger pid target-so target-func dlopen target-so-path
# ./hookso trigger pid target-so target-func dlclose handle

before call target-function-addr, do syscall/call/dlcall/dlopen/dlclose with params: 
# ./hookso triggerp pid func-addr syscall syscall-number @1 i=int-param2 s="string-param3" 
# ./hookso triggerp pid func-addr call trigger-target-so trigger-target-func @1 i=int-param2 s="string-param3" 
# ./hookso triggerp pid func-addr dlcall trigger-target-so trigger-target-func @1 i=int-param2 s="string-param3" 
# ./hookso triggerp pid func-addr dlopen target-so-path
# ./hookso triggerp pid func-addr dlclose handle

QA

Why is there a main.cpp of 3K+ lines?

Because things are simple, reduce unnecessary packaging, increase readability

What does this thing actually do?

Like the Swiss Army Knife, it is much more useful. Can be used to hot update, or monitor the behavior of certain functions, or turn on debugging

What are the limitations of function calls?

syscall, call, and dlcall only support function calls with a maximum of 6 parameters, and the parameters can only support integers and characters Replace is not limited, but you must ensure that the new function and the old function have the same parameters, otherwise they will core out

Some so functions will report errors?

Some so is too large to be fully loaded into memory, resulting in unresolved and failed operation, such as

# ./hookso find 11234 libstdc ++. so.6.0.28 __dynamic_cast
[ERROR] [2020.4.28,14: 26: 55,161] main.cpp: 172, remote_process_read: remote_process_read fail 0x7fc375714760 5 Input / output error

Modify the so parameter to the file path, so that the so information will be read from the file

# ./hookso find 11234 /usr/local/lib64/libstdc++.so.6.0.28 __dynamic_cast
0x7fc37475cea0    140477449227936

As you can see, the find command has been successfully executed, the same is true for other commands such as call, dlopen, and replace

Who is using

Lua code coverage tool

Lua performance analysis tool

Lua debug tool

Lua watch tool