hookso Linux 动态链接库工具开源项目

我要开发同款
匿名用户2020年04月30日
92阅读

技术信息

开源地址
https://github.com/esrrhs/hookso
授权协议
MIT

作品详情

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

功能让某个进程执行系统调用让某个进程执行.so的某个函数给某个进程挂接新的.so卸载某个进程的.so把旧.so的函数替换为新.so的函数复原.so的函数替换查找.so的函数地址编译

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

#./build.sh#cdtest&&./build.sh示例启动test目录下的测试程序

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

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

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

exter"C"boollibtest(it){charbuff[128]={0};spritf(buff,sizeof(buff),"libtest%d",);puts(buff);returfalse;}

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

exter"C"boollibtestew(it){charbuff[128]={0};spritf(buff,sizeof(buff),"libtestew%d",);puts(buff);returfalse;}exter"C"boolputsew(costchar*str){charbuff[128]={0};spritf(buff,sizeof(buff),"putsew%s",str);puts(buff);returfalse;}

libtestew.cpp定义了两个函数,一个用来替换libtest.so的puts函数,一个用来替换libtest.so的libtest函数

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

#cdtest#./build.sh#./testlibtest1libtest2...libtest10

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

示例1:让test在屏幕上打印一句话#./hooksosyscall112341i=1s="haha"i=44

注意这里的输出4,表示是系统调用的返回值。然后观察test的输出,可以看到haha输出

libtest12699libtest12700hahalibtest12701libtest12702libtest12703

这里的几个参数说明:1是系统调用的号码,1表示的是write,i=1意思是一个it类型值为1的参数,s="haha"则为字符串内容为haha

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

示例2:让test调用libtest.so的libtest函数#./hooksocall11234libtest.solibtesti=12340

这里的参数和返回值,和示例1syscall同理。然后观察test的输出,可以看到输出

libtest12713libtest12714libtest12715libtest1234libtest12716libtest12717

libtest1234则为我们插入的一次调用输出结果

示例3:让test加载libtestew.so#./hooksodlope11234./test/libtestew.so13388992

注意这里的输出13388992,表示是dlope的hadle,这个hadle后面卸载so会用到。然后查看系统/proc/11234/maps

#cat/proc/11234/maps00400000-00401000r-xp00000000fc:01678978/home/project/hookso/test/test00600000-00601000r--p00000000fc:01678978/home/project/hookso/test/test00601000-00602000rw-p00001000fc:01678978/home/project/hookso/test/test01044000-01076000rw-p0000000000:000[heap]7fb351aa9000-7fb351aaa000r-xp00000000fc:01678977/home/project/hookso/test/libtestew.so7fb351aaa000-7fb351ca9000---p00001000fc:01678977/home/project/hookso/test/libtestew.so7fb351ca9000-7fb351caa000r--p00000000fc:01678977/home/project/hookso/test/libtestew.so7fb351caa000-7fb351cab000rw-p00001000fc:01678977/home/project/hookso/test/libtestew.so

可以看到libtestew.so已经成功加载

示例4:让test卸载libtestew.so#./hooksodlclose112341338899213388992

这个13388992是示例3dlope返回的hadle值(多次dlope的值是一样,并且dlope多次就得dlclose多次才能真正卸载掉)。然后查看系统/proc/11234/maps

#cat/proc/16992/maps00400000-00401000r-xp00000000fc:01678978/home/project/hookso/test/test00600000-00601000r--p00000000fc:01678978/home/project/hookso/test/test00601000-00602000rw-p00001000fc:01678978/home/project/hookso/test/test01044000-01076000rw-p0000000000:000[heap]7fb3525ab000-7fb352765000r-xp00000000fc:0125054/usr/lib64/libc-2.17.so7fb352765000-7fb352964000---p001ba000fc:0125054/usr/lib64/libc-2.17.so7fb352964000-7fb352968000r--p001b9000fc:0125054/usr/lib64/libc-2.17.so7fb352968000-7fb35296a000rw-p001bd000fc:0125054/usr/lib64/libc-2.17.so

可以看到已经没用libtestew.so了

示例5:让test加载libtestew.so,执行libtestew,然后卸载libtestew.so#./hooksodlcall11234./test/libtestew.solibtestewi=12340

同理,这里的输出0为函数返回值。然后观察test的输出,可以看到libtestew.so的libtestew函数输出

libtest151libtest152libtest153libtestew1234libtest154libtest155

libtestew1234就是libtestew.so的函数libtestew输出,dlcall相当于执行了前面的dlope、call、dlclose三步操作

示例6:让test加载libtestew.so,并把libtest.so的puts函数调用,修改为调用libtestew.so的putsew#./hooksoreplace11234libtest.soputs./test/libtestew.soputsew13388992140573454638880

注意这里的输出结果13388992表示hadle,140573454638880表示替换之前的旧值,后面我们复原会用到。然后观察test的输出,可以看到已经调用到了libtestew.so的putsew方法

libtest3313libtest3314libtest3315libtest3316libtest3317putsewlibtest3318putsewlibtest3319putsewlibtest3320

现在开始,libtest.so内部调用puts函数,就变成了调用libtestew.so的putsew函数了,libtest.so之外调用puts函数,还是以前的没有变

示例7:让test的libtest.so的puts函数,恢复到之前,这里的140573454638880就是之前示例6replace输出的backup旧值#./hooksosetfuc11234libtest.soputs140573454638880140573442652001

注意这里的setfuc也会输出旧值140573442652001,方便下次再还原。然后观察test的输出,可以看到又重新回到了puts方法

putsewlibtest44putsewlibtest45putsewlibtest46libtest47libtest48libtest49

注意这时候libewtest.so仍然在内存中,如果不需要可以用dlclose卸载它,这里不再赘述

示例8:让test加载libtestew.so,并把libtest.so的libtest函数,跳转到libtestew的libtestew,这个和示例6的区别是libtest是libtest.so内部实现的函数,puts是libtest.so调用的外部函数#./hooksoreplace2936libtest.solibtest./test/libtestew.solibtestew1338899210442863786053945429

这里的输出和示例6同理。然后观察test的输出,可以看到调用了libtestew.so的libtestew函数

libtest31714libtest31715libtest31716libtest31717libtest31718libtestew31719libtestew31720libtestew31721libtestew31722libtestew31723

现在整个进程所有调用libtest的地方,都跳转到了libtestew函数

示例9:让test的libtest.so的libtest函数,恢复到之前,这里的10442863786053945429就是之前示例8replace输出的替换旧值#./hooksosetfuc11234libtest.solibtest104428637860539454291092601523177

然后观察test的输出,可以看到又回到了libtest.so的libtest函数

libtestew26libtestew27libtestew28libtestew29libtest30libtest31libtest32示例10:查找test的libtest.so的libtest函数地址#./hooksofid11234libtest.solibtest0x7fd9cfb91668140573469644392

0x7fd9cfb91668即为地址,140573469644392是地址转成了uit64_t的值

 QA

 

为什么就一个1500行的mai.cpp?

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

 

这东西实际有什么作用?

典型的用法是来监控某些进程的底层函数,打打日志,不用修改原始代码。或者拿来做c++的热更新补丁也可以

 

函数调用有什么限制?

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

 

有些so的函数会报错?

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

#./hooksofid11234libstdc++.so.6.0.28__dyamic_cast[ERROR][2020.4.28,14:26:55,161]mai.cpp:172,remote_process_read:remote_process_readfail0x7fc3757147605Iput/outputerror

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

#./hooksofid11234/usr/local/lib64/libstdc++.so.6.0.28__dyamic_cast[INFO][2020.4.28,14:26:47,274]mai.cpp:1529,program_fid:/usr/local/lib64/libstdc++.so.6.0.28__dyamic_cast=0x7fc37475cea0140477449227936

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

功能介绍

hookso是一个Linux动态链接库的注入修改查找工具,用来修改其他进程的动态链接库行为。 功能 让某个进程执行系统调用 让某个进程执行.so的某个函数 给某个进程挂接新的.so 卸载某个进程...

声明:本文仅代表作者观点,不代表本站立场。如果侵犯到您的合法权益,请联系我们删除侵权资源!如果遇到资源链接失效,请您通过评论或工单的方式通知管理员。未经允许,不得转载,本站所有资源文章禁止商业使用运营!
下载安装【程序员客栈】APP
实时对接需求、及时收发消息、丰富的开放项目需求、随时随地查看项目状态

评论