Skip to the content.

hookso

hookso是一个linux动态链接库的注入修改查找工具,用来修改其他进程的动态链接库行为。

Readme EN

功能

编译

git clone代码,运行脚本,生成hookso以及测试程序

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

示例

先看下测试代码,代码很简单,test.cpp不停的调用libtest.so的libtest函数

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

而libtest.so的libtest函数只是打印到标准输出。

注意这里使用了几种不同的方式调用puts,原因是不同的写法,会导致puts在elf中的位置不太一样,这里使用多种写法,使得后面的查找替换都能够覆盖到。具体可以readelf -r libtest.so查看细节。

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;
}

这时候,test是没有加载libtestnew.so的,后面会用hookso来注入,libtestnew.cpp的代码如下

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定义了两个函数,一个用来替换libtest.so的puts函数,一个用来替换libtest.so的libtest函数

现在我们开始编译并运行它

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

程序开始运行,可以看到,不停的打印到输出,假设test的pid是11234

所以这里等价于C语言调用了write(1, “haha”, 4),也就是在标准输出打印一句话

用法

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

为什么就一个3k行+的main.cpp?

因为东西简单,减少无谓的封装,增加可读性

这东西实际有什么作用?

如同瑞士军刀一样,用处很多。可以用来热更新,或者监控某些函数行为,或者开启调试

函数调用有什么限制?

syscall、call、dlcall只支持最大6个参数的函数调用,并且参数只能支持整形、字符
replace不受限制,但是必须确保新的函数和旧函数,参数一致,不然会core掉

有些so的函数会报错?

某些so太大无法被全部load进内存,导致无法解析,运行失败,如

# ./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

把so参数修改成文件路径,这样就会从文件读取so信息

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

可以看到,find命令已成功执行,对于其他的命令如call、dlopen、replace同理

应用

Lua 代码覆盖率工具 cLua

Lua 性能分析工具 pLua

Lua 调试工具 dLua

Lua 监控工具 wLua