在做 pwn 题时,更换 ELF 文件的 libc 版本一直让人头疼,所以写文记录关于 glibc 的下载,替换,调试的一些问题。
通过镜像源可以下载到常见版本的 glibc 及其符号表。
通过 Ubuntu 的 old-releases 镜像站 或者 清华的镜像站 可以下载到不同版本的 glibc。然后通过 dpkg -x *.deb
来解压 deb 包得到 libc。
当然也可以通过 Debian 等发行版的镜像源来下载 glibc。
有两个项目可以实现自动下载 libc:https://github.com/niklasb/libc-database和https://github.com/matrix1001/glibc-all-in-one,前者不会下载符号表,而后者会将符号表存入对应 libc 的 ".debug" 文件夹中。
因为 ld.so 和 libc.so 不匹配的原因,所以直接设置 LD_PRELOAD 可能会炸,就如下所示...
可以将配套的 ld 和 libc 一起使用即可实现动态加载 libc。只需将下面代码中 LD_PRELOAD 后面的 "/path/to/libc.so.6" (要加载的 libc 的路径)和第二行的 "/path/to/ld.so" (要加载的 ld 的路径)替换成相应文件的路径就行了。
在 pwntools 启动程序时,可以按照下面的代码进行设置。
在 github 上有一个项目叫 patchelf,通过这个项目可以实现修改 ELF 中硬编码的 libc 和 ld 的路径。
一般 ELF 文件的 ldd
和 file
结果与下面类似,可以看到 libc 等动态库的路径被写死在文件中,而 libc.so.6 是一个符号链接,所指向的文件是真正的 libc。
我们通过 patchelf 修改 ELF 文件达到加载指定版本 libc。我们先用 "--set-interpreter" 这个选项来将旧的 ld.so 替换为要加载的 ld.so,然后使用 "--replace-needed" 这个选项将旧的 libc.so 替换成要加载的 libc.so。在使用 "--replace-needed" 时,第 2 个参数是程序原本的动态库的路径,可以由 ldd $目标文件
得到,第 3 个参数是新的动态库的路径,第 4 个参数为要修改文件的路径。
这里我们修改 "./patchelf" 这个文件的的 libc.so 和 ld.so。根据上面 ldd
的结果,可以知道 ELF 中的 libc 的路径为 "libc.so.6",所以替换 libc 时所使用的第 2 个参数为 "libc.so.6"
然后再用 ldd 和 file 命令查看程序,可以看到 libc 和 ld 都修改成功了。
我写了个简单脚本,使用 "patchelf" 给 ELF 文件 Patch 上 "glibc all in one" 下载的 libc。将下面脚本以 "chlibc.sh" 保存后,只需要使用 ./chlibc.sh $libc所在目录 $目标文件
就能修改 libc 了。例如 ./chlibc.sh /opt/libs/2.23-0ubuntu10_i386 echo3
就是将 "echo3" 这个 ELF 文件的 libc 和 ld 换成 /opt/libs/2.23-0ubuntu10_i386
这个目录下的 libc 和 ld。
需要注意的是 patchelf 可能会有 Bug,所以使用从 Github 上的代码进行编译得到的 patchelf 能避开一些 Bug。
在看 LIEF 文档时发现了一篇老外的博文,讲的是通过 LIEF 来使用不是系统自带的 libc。
如果只是直接 Patch 了 ELF 文件的 ld 和 libc,而没有放置符号表的话,那么在使用 GDB 调试的时候就不容易看到 libc 中的各种结构。所以我们需要加载符号表来方便调试。
Ubuntu 的软件维护者在编译对应的 ELF 文件时,会将符号表与 ELF 文件分离,将符号表命名为 "*-dbg.deb"。这样我们就可以通过手动下载符号表来方便调试。
如果是使用 "glibc all in one" 下载的 libc,会在 libc 等库放置的位置使用 ".debug" 文件夹存放好了 libc 的符号表,使用 gdb 可以自动加载。但是碰到 "glibc all in one" 没有的 libc 版本时就需要手动下载 debug 文件,并手动加载了。
在放置 libc 的目录下新建 ".debug"文件夹,将 debug 文件放入其中即可。原理见参考文章 2,glibc 的官方文档。
在参考文章1可以看到关于 gdb 如何加载分离的 debug 文件,我们只要将 libc 等库的 debug 文件放入对应文件夹,并通过 set debug-file-directory $directories
命令将文件夹设为分离的 debug 文件目录,就可以让 gdb 加载 debug 文件。
gdb 的 python api 中有一个Objfile.add_separate_debug_file (file)
来让对应的二进制文件加载分离的 debug file,这里我将其封装成了命令,在程序运行起来后通过修改下面的命令即可加载。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2020-3-19 01:45
被富强民主和谐编辑
,原因: 丰富细节,增加说明