hookso是一个Linux动态链接库的注入修改查找工具,用来修改其他进程的动态链接库行为。
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函数只是打印到标准输出
extern "C" bool libtest(int n) {
char buff[128] = {0};
snprintf(buff, sizeof(buff), "libtest %d", n);
puts(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
# ./hookso syscall 11234 1 i=1 s="haha" i=4
4
注意这里的输出4,表示是系统调用的返回值。然后观察test的输出,可以看到haha输出
libtest 12699
libtest 12700
hahalibtest 12701
libtest 12702
libtest 12703
这里的几个参数说明:1是系统调用的号码,1表示的是write,i=1意思是一个int类型值为1的参数,s="haha"则为字符串内容为haha
所以这里等价于C语言调用了write(1, "haha", 4),也就是在标准输出打印一句话
# ./hookso call 11234 libtest.so libtest i=1234
0
这里的参数和返回值,和示例1 syscall同理。然后观察test的输出,可以看到输出
libtest 12713
libtest 12714
libtest 12715
libtest 1234
libtest 12716
libtest 12717
libtest 1234则为我们插入的一次调用输出结果
# ./hookso dlopen 11234 ./test/libtestnew.so
13388992
注意这里的输出13388992,表示是dlopen的handle,这个handle后面卸载so会用到。然后查看系统/proc/11234/maps
# cat /proc/11234/maps
00400000-00401000 r-xp 00000000 fc:01 678978 /home/project/hookso/test/test
00600000-00601000 r--p 00000000 fc:01 678978 /home/project/hookso/test/test
00601000-00602000 rw-p 00001000 fc:01 678978 /home/project/hookso/test/test
01044000-01076000 rw-p 00000000 00:00 0 [heap]
7fb351aa9000-7fb351aaa000 r-xp 00000000 fc:01 678977 /home/project/hookso/test/libtestnew.so
7fb351aaa000-7fb351ca9000 ---p 00001000 fc:01 678977 /home/project/hookso/test/libtestnew.so
7fb351ca9000-7fb351caa000 r--p 00000000 fc:01 678977 /home/project/hookso/test/libtestnew.so
7fb351caa000-7fb351cab000 rw-p 00001000 fc:01 678977 /home/project/hookso/test/libtestnew.so
可以看到libtestnew.so已经成功加载
# ./hookso dlclose 11234 13388992
13388992
这个13388992是示例3 dlopen返回的handle值(多次dlopen的值是一样,并且dlopen多次就得dlclose多次才能真正卸载掉)。然后查看系统/proc/11234/maps
# cat /proc/16992/maps
00400000-00401000 r-xp 00000000 fc:01 678978 /home/project/hookso/test/test
00600000-00601000 r--p 00000000 fc:01 678978 /home/project/hookso/test/test
00601000-00602000 rw-p 00001000 fc:01 678978 /home/project/hookso/test/test
01044000-01076000 rw-p 00000000 00:00 0 [heap]
7fb3525ab000-7fb352765000 r-xp 00000000 fc:01 25054 /usr/lib64/libc-2.17.so
7fb352765000-7fb352964000 ---p 001ba000 fc:01 25054 /usr/lib64/libc-2.17.so
7fb352964000-7fb352968000 r--p 001b9000 fc:01 25054 /usr/lib64/libc-2.17.so
7fb352968000-7fb35296a000 rw-p 001bd000 fc:01 25054 /usr/lib64/libc-2.17.so
可以看到已经没用libtestnew.so了
# ./hookso dlcall 11234 ./test/libtestnew.so libtestnew i=1234
0
同理,这里的输出0为函数返回值。然后观察test的输出,可以看到libtestnew.so的libtestnew函数输出
libtest 151
libtest 152
libtest 153
libtestnew 1234
libtest 154
libtest 155
libtestnew 1234就是libtestnew.so的函数libtestnew输出,dlcall相当于执行了前面的dlopen、call、dlclose三步操作
# ./hookso replace 11234 libtest.so puts ./test/libtestnew.so putsnew
13388992 140573454638880
注意这里的输出结果13388992表示handle,140573454638880表示替换之前的旧值,后面我们复原会用到。然后观察test的输出,可以看到已经调用到了libtestnew.so的putsnew方法
libtest 3313
libtest 3314
libtest 3315
libtest 3316
libtest 3317
putsnew libtest 3318
putsnew libtest 3319
putsnew libtest 3320
现在开始,libtest.so内部调用puts函数,就变成了调用libtestnew.so的putsnew函数了,libtest.so之外调用puts函数,还是以前的没有变
# ./hookso setfunc 11234 libtest.so puts 140573454638880
140573442652001
注意这里的setfunc也会输出旧值140573442652001,方便下次再还原。然后观察test的输出,可以看到又重新回到了puts方法
putsnew libtest 44
putsnew libtest 45
putsnew libtest 46
libtest 47
libtest 48
libtest 49
注意这时候libnewtest.so仍然在内存中,如果不需要可以用dlclose卸载它,这里不再赘述
# ./hookso replace 2936 libtest.so libtest ./test/libtestnew.so libtestnew
13388992 10442863786053945429
这里的输出和示例6同理。然后观察test的输出,可以看到调用了libtestnew.so的libtestnew函数
libtest 31714
libtest 31715
libtest 31716
libtest 31717
libtest 31718
libtestnew 31719
libtestnew 31720
libtestnew 31721
libtestnew 31722
libtestnew 31723
现在整个进程所有调用libtest的地方,都跳转到了libtestnew函数
# ./hookso setfunc 11234 libtest.so libtest 10442863786053945429
1092601523177
然后观察test的输出,可以看到又回到了libtest.so的libtest函数
libtestnew 26
libtestnew 27
libtestnew 28
libtestnew 29
libtest 30
libtest 31
libtest 32
# ./hookso find 11234 libtest.so libtest
0x7fd9cfb91668 140573469644392
0x7fd9cfb91668即为地址,140573469644392是地址转成了uint64_t的值
为什么就一个1500行的main.cpp?
因为东西简单,减少无谓的封装,增加可读性
这东西实际有什么作用?
典型的用法是来监控某些进程的底层函数,打打日志,不用修改原始代码。或者拿来做c++的热更新补丁也可以
函数调用有什么限制?
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
[INFO][2020.4.28,14:26:47,274]main.cpp:1529,program_find: /usr/local/lib64/libstdc++.so.6.0.28 __dynamic_cast=0x7fc37475cea0 140477449227936
可以看到,find命令已成功执行,对于其他的命令如call、dlopen、replace同理
hookso hookso是一个linux动态链接库的注入修改查找工具,用来修改其他进程的动态链接库行为。hookso是一个linux动态链接库的注入修改查找工具,用来修改其他进程的动态链接库行为。 https://github.com/esrrhs/hooksogithub.com 功能 让某个进程执行系统调用 让某个进程执行.so的某个函数 给某个进程挂接新的.so 卸载某个进程的.so
function print_string(addr) { var base_hello_jni = Module.findBaseAddress("libxxxx.so"); var addr_str = base_hello_jni.add(addr); console.log("addr:", addr, " ", ptr(addr_str).readCString(
动态链接库(也称为DLL)是Microsoft Windows最重要的组成要素之一。大多数与Windows相关的磁盘文件如果不是程序模块,就是动态链接程序。迄今为止,我们都是在开发Windows应用程序;现在是尝试编写动态链接库的时候了。许多您已经学会的编写应用程序的规则同样适用于编写这些动态链接库模块,但也有一些重要的不同。 动态链接库的基本知识 正如前面所看到的,Windows应用程序是一个可
主要内容:静态链接库,动态链接库,总结我们知道,C、C++程序从源文件到生成可执行文件需经历 4 个阶段,分别为预处理、编译、汇编和链接,本节将重点围绕链接阶段,对静态链接库和动态链接库做详细的讲解。 有关链接操作的具体细节,感兴趣的读者可阅读《 到底什么是链接,它起到了什么作用?》和《 符号——链接的粘合剂》这两节。总的来说链接阶段要完成的工作,就是将同一项目中各源文件生成的目标文件以及程序中用到的库文件整合为一个可执行文件。 通过
我的Wampserver是橙色的,不会改变。 当我进入Apache错误日志时,我得到 我知道这是一个相当常见的问题,与php_curl.dll.然而,我尝试了许多解决方案,没有一个工作。我已经尝试了PHP 5.4.3的dll修复(anindya的博客),并在\wamp\bin\php\php5.4.3\exts中替换了dll文件。curl在php.ini文件中被勾选并处于活动状态。extensio
静态链接方法:静态链接的时候,载入代码就会把程序会用到的动态代码或动态代码的地址确定下来 静态库的链接可以使用静态链接,动态链接库也可以使用这种方法链接导入库 动态链接方法:使用这种方式的程序并不在一开始就完成动态链接,而是直到真正调用动态库代码时,载入程序才计算(被调用的那部分)动态代码的逻辑地址,然后等到某个时候,程序又需要调用另外某块动态代码时,载入程序又去计算这部分代码的逻辑地址,所以,这
在编译Linux程序时,我们经常会看到动态链接和静态链接这两个术语。这两个术语中是我Linux的共享函数库(shared libraries)相关的。共享函数库就象Windows系统里的.dll文件,它里面包含有很多程序常用的函数。为了方便程序开发和减少程序的冗余,程序当中就不用包含每个常用函数的拷贝,只是在需要时调用系统中共享函数库中常函数功能即可。这种方式我们称之为动态链接(Dynamical
在Postgresql版本10中创建扩展时出错 无法加载库"C:/Program Files/PostgreSQL/10/lib/plpython3.dll":找不到指定的模块 注意:在窗口10上使用Postgresql 10
我刚刚在Vista上安装了XAMMP 7.2.2,但是无法启动Apache。每次,我都会收到以下错误消息: «Apache 2正在启动。。。htttpd。exe:C:/xampp/apache/conf/httpd的第532行出现语法错误。conf:C:/xampp/apache/conf/extra/httpd xampp的第17行出现语法错误。conf:无法加载/xampp/php/php7t
我想运行PHP扩展,能够连接到火鸟数据库。在php.ini有启用的行扩展名=pdo_firebird.dll,文件存在于其他扩展名 /ext目录中。每次我重启Apache(和PHP),我都看不到phpinfo()中加载的扩展。为什么啊?我在Windows 8上运行Apache 2.2和PHP 5.4。