所以你想现场重装铁锈

2020-09-27 12:05:27

早上好!。现在还是2020年,世界着火了,所以我想我们都需要散散心。

这篇文章延续了我被无耻的书呆子狙击的传统-一次是Pascal关于小字符串的攻击,第二次是Twitch查看器关于Rust枚举大小的攻击。

不如您教我们如何在文件更改时重新加载dylib?

比方说我们想问候许多不同的事物和人,我们可能想要greet函数:

//在`main.c`中#include<;stdio.h>;void greet(char*name){printf(";hello,%s!\n";,name);}int main(){greet(";Moon";);return 0;}。

$GCC-wall-c greet.c$file greet.ogreet.o:ELF 64位lsb可重定位,x86-64,版本1(Sysv),未剥离。

然后,在main.c中,Pinky承诺将来会有一个名为greet的函数存在:

$GCC-wall-c main.c$file main.omain.o:ELF 64位lsb可重定位,x86-64,版本1(Sysv),未剥离

如果我们试图只用greet.o来制作一个可执行文件,那么.。它不工作,因为没有提供main,而其他一些对象(GCC在制作可执行文件时神奇地链接到该对象)需要它:

$GCC greet.o-o woops/usr/bin/ld:函数`_/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../lib/Scrt1.o:';中的开始:(.text+0x24):未定义的对`main';集合的引用2:错误:ld返回1个退出状态。

如果我们尝试只使用main.o创建可执行文件,那么...。它也不起作用,因为我们承诺过问候会在那里,但它不是:

$GCC GCC main.o-o woops/usr/bin/ld:main.o:在函数`main';:main.c:(.text+0xc):未定义的`greet';集合引用2:错误:ld返回1个退出状态。

$GCC main.o greet.o-o main$file mainmain:ELF 64位LSB py可执行文件,x86-64,版本1(Sysv),动态链接,解释器/lib64/ld-linux-x86-64.so.2,BuildID[sha1]=e1915df00b8bf67e121fbd30f0eaf1fd81ecdeb6,for GNU/Linux3.2.0,不剥离$。/mainHello,STARS!

我们有一个可执行文件。再来一次。但是那里仍然没有涉及到(我们的)动态库。

$nm--未定义-仅Main w__CXA_Finalize@@GLIBC_2.2.5 w__gmon_start__w_ITM_deregisterTMCloneTable w_ITM_registerTMCloneTable U__libc_start_main@@GLIBC_2.2.5 U printf@@GLIBC_2.2.5。

好的,让我们暂时忽略弱(W)符号-主要是,它需要…。一些启动例程和printf。好的。

$nm--仅已定义的main000000000002e8 r__abi_tag0000000000004030 B__bss_start0000000000004030 b已完成。00000000000004020 D__DATA_start00000000004020 W data_start0000000000004020 W data_start0000000000001070 t deregister_tm_duones 000000000010e0 t__DO_GLOBAL_DERBAL_Dtors_AUX00000000000000003df0 d__do_global_dtors_aux_fini_array_entry0000000000004028 D__frame_dummy_init_array_entry000000000000214c_00000000003d8 d_DYNIC000000000038 B_END000000000011f8_fini0000000011fone00000000000010e0 t__DO_GLOBAL_FRAME_DLERS_00000000003df0 d_DYNIC000000003d8 d_DYNIC000000000038B_end0000000011f8_fini0000000011fnes0000000000001010e0 t__DO_GLOBAL_FRAME_DLES_000000000000003df0 d_DYNIC000000003d8 d_DYNAM000000000038 B_END0000000011f8。_GNU_EH_FRAME_HDR00000000001150 T greet00000000001000 t_init0000000000003df0 d__init_array_end0000000000003de8 d__init_array_start00000000002000 R_IO_stdin_used000000000011f0 T_libc_csu_fini00000000001180 T__libc_csu_init0000000000001139 T main00000000000010a0 t register_t0000000000000030D_TMC_end_end_t000000004030。

$nm--仅定义。/main|grep';T';000000000011f8 T_fini0000000000001150 T greet00000000000011f0 T__libc_csu_fini00000000001180 T__libc_csu_init00000000001139 T main00000000001040 T_start。

我们如何在运行时加载库?这就是动态链接器的工作。这一次,我们将使用glibc的动态链接器,而不是制作我们自己的动态链接器:

//在`load.c`中//我最大的猜测是`dlfcn`代表`动态加载函数`#include<;dlfcn.h>;#include<;stdio.h>;//C函数指针语法是...。某物。//让我们的类型定义我们走出这条路。Tyecif void(*greet_t)(char*name);int main(){//我们想要什么?符号!//我们什么时候要?在实现定义的时间!Void*lib=dlopen(";./main";,rtld_lazy);如果(!Lib){fprintf(stderr,";无法加载库\n";);返回1;}greet_t greet=(Greet_T)dlsym(lib,";greet";);如果(!Greet){fprintf(stderr,&34;无法查找符号';greet&39;\n";);return 1;}greet(";venus&34;);dlclose(Lib);return 0;}。

$GCC-Wall load.c-o load/usr/bin/ld:/tmp/ccnvYzh7.o:in函数`main';:load.c:(.text+0x15):未定义的`dlopen';/usr/bin/ld:load.c:(.text+0x5a):未定义的`dlsym';集合2的引用:错误:ld返回1退出状态。

假如我真的想担心他们,我在哪里可以读到更多关于他们的报道呢?

您可以查看LSB核心规范-但请注意,它存在1.5个阿拉比特漏洞。

因此,由于libdl.so包含了我们需要的符号,并且它在GCC的库路径中,我们应该能够用-ldl链接到它:

$GCC-wall load.c-o load-ldl$file loadload:ELF 64位LSBie可执行文件,x86-64,版本1(SYSV),动态链接,解释器/lib64/ld-linux-x86-64.so.2,适用于GNU/Linux3.2.0的BuildID[sha1]=0d246f67c894d7032d0d5093ec01625e58711034,,未剥离。

$LD_DEBUG=ALL./LOAD 160275:Symbol=__vdso_Clock_Gettime;在文件中查找=linux-vdso.so.1[0]160275:将linux-vdso.so.1[0]绑定到linux-vdso.so.1[0]:正常符号`__vdso_lock_gettime';[linux_2.6]160275:Symbol=__vdso_gettimeofday;在文件中查找=linux-vdso.so.1[0]160275:将文件linux-vdso.so.1[0]绑定到linux-vdso.so.1[0]:正常符号`__vdso_gettimeofday';[linux_2.6]160275:Symbol=__vdso_time;在文件中查找=linux-vdso.so.1[0]160275:将文件linux-vdso.so.1[0]绑定到linux-vdso.so.1[0]:正常符号`__vdso_time';160275:Symbol=__vdso_getcpu;在文件中查找=linux-vdso.so.1[0]160275:将linux-vdso.so.1[0]文件绑定到linux-vdso.so.1[0]:正常符号`__vdso_getcpu';[linux_2.6]160275:Symbol=__vdso_lock_getres;在文件=linux-vdso.so.1[0]160275中查找:将文件linux-vdso.so.1[0]绑定到linux-vdso.so.1[0]:正常符号`__vdso_lock_getres';[linux_2.6]。

VDSO适用于虚拟动态共享对象&简而言之,它使某些系统调用速度更快。答案很长,你可以在LWN上阅读。

160275:file=libdl.so.2[0];需要。/Load[0]160275:查找库=libdl.so.2[0];搜索160275:搜索缓存=/etc/ld.so.cache 160275:正在尝试文件=/usr/lib/libdl.so.2 160275:160275:file=libdl.so.2[0];生成链接映射160275:动态:0x00007f5be513dcf0 base:0x00007f5be5139000大小:0x00007f5be513a210phdr:0x00007f5be513a210phdr:11x00007f5be513a210phdr:11x00007f5be513a210phdr:0x00007f5be513a210phdr:0x00007f5be513a210phdr:0x00007f5be513a210phdr:11。

那么,libdl.so是一个动态库,所以它是在运行时加载的,所以动态链接器必须首先找到它。

$cat/etc/ld.so.conf#动态链接器/加载器配置。#有关详细信息,请参阅ld.so(8)和ldconfig(8)。include/etc/ld.so.conf.d/*.conf$cat/etc/ld.so.conf.d/*.conf/usr/lib/libfakeroot/usr/lib32/usr/lib/openmpi。

$xxd/etc/ld.so.cache|ail-60|head00030bb0:4641 7564 696f 2e73 6f00 6c69 6246 4175 FAudio.so.libFAu00030bc0:6469 6f2e 736f 002f 7573 722f 6c69 6233 dio.so./usr/lib300030bd0:322f 6c69 6246 4175 6469 6f2e 736f 006c 2/libFAudio.so.l00030be0:6962 4547 4c5f 6e6976 6961 2e73 6f2e iL_nvia.so.00030b0:2f75 732f72 6c 6962 6fc 69645 47/usb/libFEG30c00:694 4c5f 6EG76 6964 2e73 6f2e i73 6f2e iL_nvia.so.00030b0。612e 736f 2E30 Begl_nvidia.so.000030c20:002f 7573 722f 6c69 6233 322f 6c69 6245。/usr/lib32/libE00030c30:474c 5f6e 7669 6469 612e 736f 2E30 006c GL_nvidia.so.0.l00030c40:6962 4547 4c5f 6e76 6964 6961 2e73 6f00 ibEGL_nvidia.so。

160275:file=libc.so.6[0];需要./Load[0]160275:find library=libc.so.6[0];搜索160275:搜索缓存=/etc/ld.so.cache 160275:正在尝试文件=/usr/lib/libc.so.6 160275:160275:file=libc.so.6[0];生成链接映射160275:动态:0x00007f2d14b7a9c0 base:0x00007f2d149b9000大小:0x000000001c82a0 160275:entry:0x00007f2d149e1290 phdr:0x00007f2d149e1290 phdr:0x00007f2d149b90b40Phnum:14

160275:正在检查文件/usr/lib/libdl.so.2[0]中是否有文件要求的版本`GLIBC_2.2.5';。/LOAD[0]160275:正在检查文件/usr/lib/libc.so.6[0]中是否有文件要求的版本`GLIBC_2.2.5';。/LOAD[0]160275:正在检查文件是否有版本`GLIBC_PRIVATE';在/usr/lib/libdl.so.2[0]文件要求的文件/lib64/ld-linux-x86-64.so.2[0]中:检查文件/usr/lib/libc.so.6[0]中的版本`GLIBC_PRIVATE';/usr/lib/libdl.so.2[0]160275:检查版本`GLIBC_2.4';在文件/usr/lib/libc.so.6[0]中/usr/lib/libdl.so.2[0]160275:检查文件/usr/lib/libc.so.6[0]中的版本`GLIBC_2.2.5';/usr/lib/libc.so.6[0]文件/usr/lib/libdl.so.2[0]160275:检查版本`GLIBC_2.2.5';在文件/lib64/ld-linux-x86-64.so.64.so.2[0]中/usr/lib/libc.so.6[0]160275:检查文件/lib64/ld-linux-x86-64.so.2[0]中的版本`GLIBC_2.3';/usr/lib/libc.so.6[0]160275:检查文件/lib64/ld-linux-x86-64.so.2[0]中的文件所需的版本`GLIBC_PRIVATE';/usr/lib/libc.so.6[0]。

是的。如你所见,有一堆这样的东西。另外,我很确定我的私人生活不是很好,但是我们不要分心。

160275:初始对象作用域160275:Object=./Load[0]160275:作用域0:./load/usr/lib/libdl.so.2/usr/lib/libc.so.6/lib64/ld-linux-x86-64.so.2 160275:160275:Object=linux-vdso.so.1[0]160275:作用域0:./load/usr/lib/libdl.so.2/usr/lib/libc.so.6/lib64/ld-linux-x86-64.so.2 160275:作用域1:linux。-vdso.so.1 160275:160275:Object=/usr/lib/libdl.so.2[0]160275:作用域0:./Load/usr/lib/libc.so.2/usr/lib/libc.so.6/lib64/ld-linux-x86-64.so.2 160275:160275:Object=/usr/lib/libc.so.6[0]160275:作用域0:./load/usr/lib/libdl.so.2/usr/lib/libc.so.6/lib64/ld-linux。-x86-64.so.2 160275:160275:Object=/lib64/ld-linux-x86-64.so.2[0]160275:无作用域。

在这里,动态链接器只是告诉我们它将在各种目标文件中查找符号的顺序。请注意,每个目标文件都有一个特定的顺序-它们只是碰巧在这里基本相同。

对于./load,它将首先查看./load,这是我们重新加载的可执行文件,然后是libdl,然后是libc,然后是..。动态链接器本身。

160275:重定位处理:/usr/lib/libc.so.6 160275:Symbol=_res;在文件中查找=./Load[0]160275:Symbol=_res;在文件中查找=/usr/lib/libdl.so.2[0]160275:Symbol=_res;在文件中查找=/usr/lib/libc.so.6[0]160275:将文件/usr/lib/libc.so.6[0]绑定到/usr/lib/libc.so.6[0]:正常符号`_res';[GLIBC_2.2.5]160275:Symbol=stderr;在文件中查找=./LOAD[0]160275:将文件/USR/lib/libc.so.6[0]绑定到./LOAD[0]:正常符号`stderr';[GLIBC_2.2.5]160275:Symbol=ERROR_ONE_PER_LINE;在文件中查找=./LOAD[0]160275:Symbol=ERROR_ONE_PER_LINE;在文件中查找=/USR/LIB/libdl.so.2[0]160275:Symbol=ERROR_ONE_PER_LINE;在file=/usr/lib/libc.so.6[0]160275中查找:将文件/usr/lib/libc.so.6[0]绑定到/usr/lib/libc.so.6[0]:正常符号`error_one_per_line';[glibc_2.2.5](等)。

好了,这样的东西很多,我们跳过吧。但是您可以看到它按照它之前确定的顺序看起来像sup:首先是./load,然后是libdl,然后是libc。

160275:调用init:/lib64/ld-linux-x86-64.so.2 160275:160275:160275:调用init:/usr/lib/libc.so.6 160275:160275:160275:调用init:/usr/lib/libdl.so.2160275:160275:160275:初始化程序:./Load160275:160275:160275:转移控制:./Load

在这一点上,它已经完成了加载动态库并初始化它们,并且它已经将控制权移交给了我们的程序./load。

160275:Symbol=dlopen;在文件中查找=./LOAD[0]160275:Symbol=dlopen;在文件中查找=/usr/lib/libdl.so.2[0]160275:绑定文件。/LOAD[0]到/usr/lib/libdl.so.2[0]:正常符号`dlopen';[glibc_2.2.5]。

啊,已经做得够多了。还记得我们传递给dlopen的RTLD_LAZY标志吗?在我的Linux发行版上,它是dynamic icloader的默认设置。

是的,因为我们叫了dlopen!它甚至说它由我们的测试可执行文件./load动态加载。

不幸的是,没有。它只是查找fwrite(我假设这是我们的fprintf调用的编译目标),这样我们就可以打印我们自己的错误消息,然后调用finalizer并退出:

160275:Symbol=fwrite;在文件中查找=./LOAD[0]160275:Symbol=fwrite;在文件中查找=/usr/lib/libdl.so.2[0]160275:Symbol=fwrite;在文件中查找=/usr/lib/libc.so.6[0]160275:绑定文件。/LOAD[0]to/usr/lib/libc.so.6[0]:正常符号`fwrite';[GLIBC_2.2.5]无法加载库160275:160275:调用Fini:./Load[0]160275:160275:160275:调用Fini:/usr/lib/libdl.so.2[0]160275:

嗯..。还记得我们试图确保libdl.so有dlopen和朋友吗?我们不得不使用纳米S-D旗帜。

$nm-D main w__CXA_Finalize@@GLIBC_2.2.5 w__gmon_start__w_ITM_deregisterTMCloneTable w_ITM_registerTMCloneTable U__libc_start_main@@GLIBC_2.2.5 U printf@@GLIBC_2.2.5

哦.。因此,对于main,greet位于其中一个符号表中,而不是动态符号表中。

$nm main|grep";T";000000000011f8 T_fini0000000000001150 T greet00000000000011f0 T__libc_csu_fini00000000001180 T__libc_csu_init00000000001139 T main0000000000001040 T_start$stat-c';%s字节';main16664字节。

$strie main$nm main|grep";T";nm:main:无符号$stat-c';%s字节';main14328字节。

$nm-D main w__CXA_Finalize@@GLIBC_2.2.5 w__gmon_start__w_ITM_deregisterTMCloneTable w_ITM_registerTMCloneTable U__libc_start_main@@GLIBC_2.2.5 U printf@@GLIBC_2.2.5。

#include<;unistd.h>;#include<;stdio.h>;//这会告诉GCC创建一个名为`.interp`的区段,并将//`/lib64/ld-linux-x86-64.so.2`(动态链接器的路径)存放在其中。/(通常它会自己执行此操作,但由于我们将使用//`-shared`标志,因此它不会执行此操作。)。Const char解释器[]__ATTRIBUTE__((SECTION(";.interp";)=";/lib64/ld-linux-x86-64.so.2";;void greet(char*name){printf(";hello,%s!\n";,name);}//通常情况下,我们链接有自己入口点的目标文件,//然后**调用`main`,但是因为我们';在使用`-shared`标志时,我们';重新//链接到*另一个*目标文件,并且我们需要提供我们自己的入口点。/与main不同,它不返回`int`,//我们永远不能从它返回,需要调用`_exit`否则就会崩溃。Void entry(){greet(";rain";);_exit(0);}。

$GCC-墙共享main.c-o libmain.so-wl,-soname,libmain.so-wl,-e,entry$file libmain.so libmain.so:ELF 64位LSB共享对象,x86-64,版本1(SYSV),动态链接,解释器/lib64/ld-linux-x86-64.so.2,BuildID[sha1]=460bf95f9cd22afa074399512bd9290c20b552ff,未剥离。

-WL,-Some-Option是我们告诉GCC传递链接器选项的方式。-wl,-foo将-foo传递给GNU ld。-wl,-foo,bar将传递-foo=bar。

-Soname在技术上不是这个演示运行所必需的,但它是一个东西,所以我们不妨设置它。

至于-e=条目,该条目是必需的,否则我们将无法将单元作为可执行文件运行。记住,我们将自带入口点!

//在`load.c`int main(){//WAS";main";void*lib=dlopen(";./libmain.so";,rtld_lazy);//等}。

……但是这一次,我们能不能把它过滤掉一点,这样它就可以放进一块或两块屏幕里?

当设置LD_DEBUG时,动态链接器(ld-linux-x86-64.so.2,它也是可执行文件/动态库的混合体)将调试信息输出到文件描述符号为2的标准错误(Stderr),因此-如果我们想要过滤它,我们需要将标准错误重定向到标准输出(2&>&;1-let&39;s试用):

$LD_DEBUG=ALL。/LOAD 2&>&;1|grep#39;strcpy';172425:Symbol=strcpy;在文件中查找=./LOAD[0]172425:Symbol=strcpy;在文件中查找=/usr/lib/libdl.so.2[0]172425:Symbol=strcpy;在文件中查找=/usr/lib/libc.so.6[0]172425:将文件/usr/lib/libdl.so.2[0]绑定到/usr/lib/libc.so.6[0]:正常符号`strcpy';[glbc_2.2.5]。

接下来-all有点冗长,让我们尝试将LD_DEBUG设置为files.。此外,让‘s将所有内容都输入wc-l,以计算行数。

$LD_DEBUG=文件。/LOAD 2>;&;1|HEAD-10 173292:173292:FILE=libdl.so.2[0];需要。/LOAD[0]173292:FILE=libdl.so.2[0];生成链接映射173292:DYNAMIC:0x00007f3a1df6fc0 base:0x00007f3a1df6b000 size:0x000000000000005090 173292:Entry:0x00007f3a1df6c210 phdr:0x00007f3a1df6b040 Phnum:11 173292:173292:173292:file=libc.so.6[0];需要/Load[0]173292:file=lic.b.b6[0];生成链接映射17x00007f3a1df6c210 phdr:0x00007f3a1df6b040 Phnum:11 173292:173292:file=libc.so.6;需要。/load[0]173292:file=lic.b.b6[0];生成链接映射17x00007f3a1df6c210 phdr:0x00007f3f3a1df6b040 Phnum:11 173292:173292:173292。

输出以PID(进程标识符,这里是172709)作为前缀的mhh有点烦人,我们可以使用sed(流编辑器)来解决这个问题。

$LD_DEBUG=文件。/LOAD 2>;&;1|sed-E-e';s/^[[:blank:]]+[[:digit:]]+:[[:blank:]]*//';|HeadFile=libdl.so.2[0];所需。/Load[0]File=libdl.so.2[0];生成链接映射动态:0x00007fe98d502cf0 base:0x00007fe98d4fe000 size:0x000000000000005090 entry:0x00007fe98d4ff210 phdr:0x00007fe98d4fe040 Phnum:11file=libc.so.6[0];所需/Load[0]file=libc.so.6[0];生成链接地图动态:0x00007fe98d4f69c0 base:0x00007fe98d335000大小:0x00000000001c82a0。

让我们把它分解一下。-E标志启用扩展正则表达式。我的建议是?不要费心学习非扩展正则表达式。

-e指定sed要运行的脚本。在这里,我们的脚本有s/pattern/Replace/命令,它用替换替换Pattern。

您可能只需使用一张小抄就能理解该模式,但下面是它:

[[:位数:]]+一个或多个小数位(0,1,2,3,4,5,6,7,8,9)。

嘿,愚蠢的问题-为什么我们要在sedscript中使用';(单引号)?你不是经常用双引号吗?

因为可能会有一堆对我的shell有意义的奇怪字符,所以我不希望我的shell插入其中的任何字符,所以,使用单引号。

我们已经过滤掉了很多杂音,但是我们仍然得到那些空行-我们可以使用另一个sed命令来过滤掉它们:/pattern/d-其中d代表DELETE&34;。

我们的模式将是^$-它匹配行首和行尾,中间没有任何东西,因此,只有空行(应该?)。火柴。

$LD_DEBUG=FILES。/LOAD 2>;&;1|sed-E-e';s/^[[:blank:]]+[[:digit:]]+:[[:blank:]]*//';-e';/^$/d';FILE=libdl.so.2[0];所需。/LOAD[0]FILE=libdl.so.2[0];生成链接映射动态:0x00007fc870f7cf0 base:0x00007fc870f6f000大小:0x00000000000000005090entry:0x00007fc870f6f040 Phnum:11file=libc.so.6[0];需要./load[0]file=libc.so.6[0];生成链接mapdynamic:0x00007fc870f679c0 base:0x00007fc870da6000 size:0x0000000000001c82a0entry:0x00007fc870dce290 phdr:0x00007fc870da6040 Phnum:14调用init:/lib64/ld-linux-x86-64.so.2调用init:/usr/lib/libc.so.6调用init:/usr/lib/libdl.so.2initialize program:./load control:/load control。

File=./libmain.so[0];由./load[0]file=./libmain.so[0]动态加载;生成链接mapdynamic:0x00007fc870fa6e10 base:0x00007fc870fa6e10 base:0x0000000000004040entry:0x00000000000040entry:0x00007fc870fa4150 phdr:0x00007fc870fa3040 Phnum:11调用init:./libmain.so[0];direct_opencount=1调用fini:./libmain.so[0]file=./libmain.so[0];销毁链接调用fini:./load[0]调用fini:/usr/lib/libdl.so.2[0],Hello venus!

所以,这里的输出有点乱了--stder。

.