在做 pwn 题时,glibc 更换一直让人头疼,所以写文记录关于 glibc 中一些问题。
参考文章:
https://wiki.ubuntu.com/Debug%20Symbol%20Packages
https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
通过镜像源可以下载到常见版本的 glibc 及其符号表。
通过 Ubuntu 的镜像源http://old-releases.ubuntu.com/ubuntu/pool/main/g/glibc/和https://mirrors.tuna.tsinghua.edu.cn/ubuntu/pool/main/g/glibc/可以下载到不同版本的 glibc。
当然也可以通过 Debian 等发行版的镜像源来下载 glibc。
有两个项目可以实现自动下载 libc:https://github.com/niklasb/libc-database和https://github.com/matrix1001/glibc-all-in-one,前者不会下载符号表,而后者会将符号表存入 ".debug" 文件夹中。
因为 ld.so 不匹配的原因,所以直接设置 LD_PRELOAD 可能会炸,就如下所示...
$ LD_PRELOAD=./libc.so.6 ./baby_tcache 段错误 (核心已转储)
我试着修改了 LD_LIBRARY_PATH,然后也凉了...
$ LD_LIBRARY_PATH=/opt/libs/2.27-3ubuntu1_amd64/ ./baby_tcache 段错误 (核心已转储)
求大佬指点指点...
在 github 上有一个项目叫 patchelf https://github.com/NixOS/patchelf,通过这个项目可以实现修改 ELF 中硬编码的 libc 和 ld 的路径。
一般 ELF 文件的 ldd 和 file 结果与下面类似,可以看到 libc 等动态库的路径被写死在文件中,而 libc.so.6 是一个符号链接,所指向的文件是真正的 libc。
$ ldd /usr/local/bin/patchelf linux-vdso.so.1 (0x00007ffc497e5000) libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f2d46aee000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f2d4696b000) libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f2d46951000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2d46790000) /lib64/ld-linux-x86-64.so.2 (0x00007f2d46caf000) $ file /usr/local/bin/patchelf /usr/local/bin/patchelf: ELF 64-bit LSB pie executable, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=cfa182b059312c4c03e401efc8efe47a373d348c, with debug_info, not stripped $ file /lib/x86_64-linux-gnu/libc.so.6 /lib/x86_64-linux-gnu/libc.so.6: symbolic link to libc-2.28.so
我们通过 patchelf 强行修改 ELF 文件达到加载指定版本 libc。使用 "--replace-needed" 时,第2个参数是程序原本的动态库路径,第三个参数是新的动态库的路径。
$ patchelf --set-interpreter /opt/libs/2.27-3ubuntu1_amd64/ld-2.27.so ./patchelf $ patchelf --replace-needed libc.so.6 /opt/libs/2.27-3ubuntu1_amd64/libc-2.27.so ./patchelf
然后再用 ldd 和 file 命令查看程序,可以看到修改成功。
$ ldd ./patchelf linux-vdso.so.1 (0x00007fff785a0000) libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fb408672000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fb4084ef000) libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fb4084d5000) /opt/libs/2.27-3ubuntu1_amd64/libc-2.27.so (0x00007fb4080e4000) /opt/libs/2.27-3ubuntu1_amd64/ld-2.27.so => /lib64/ld-linux-x86-64.so.2 (0x00007fb408a67000) $ file ./patchelf ./patchelf: ELF 64-bit LSB pie executable, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /opt/libs/2.27-3ubuntu1_amd64/ld-2.27.so, for GNU/Linux 3.2.0, BuildID[sha1]=cfa182b059312c4c03e401efc8efe47a373d348c, with debug_info, not stripped
如果是使用 "glibc all in one" 下载的 libc,会在 libc 等库放置的位置使用 ".debug" 文件夹存放好了 libc 的符号表,使用 gdb 可以自动加载。但是碰到 "glibc all in one" 没有的 libc 版本时就需要手动下载 debug 文件,并手动加载了。下面的方法三选一就行...
在放置 libc 的目录下新建 ".debug"文件夹,将 debug 文件放入其中即可。原理见参考文章2。
在参考文章2可以看到关于 gdb 如何加载分离的 debug 文件,我们只要将 libc 等库的 debug 文件放入对应文件夹,并通过 "set debug-file-directory $directories" 命令将文件夹设为分离的 debug 文件目录,就可以让 gdb 加载 debug 文件。
gdb 的 python api 中有一个Objfile.add_separate_debug_file (file)
来让对应的二进制文件加载分离的 debug file,这里我将其封装成了命令,在程序运行起来后通过修改下面的命令即可加载。
pwndbg> source '/path/to/loadsym.py' pwndbg> loadsym '/path/to/usr/lib/debug/lib/x86_64-linux-gnu/libc-2.27.so' [*] symbol file path: /path/to/usr/lib/debug/lib/x86_64-linux-gnu/libc-2.27.so [+] load debug file success!
这是 loadsym.py 文件
import gdb class loadsym(gdb.Command): """ load symbol file to glibc Usage: loadsym {symbol file} Example: (gdb) loadsym '/path/to/usr/lib/debug/lib/x86_64-linux-gnu/libc-2.27.so' """ def __init__(self): ''' register command in constructer function ''' super(self.__class__, self).__init__("loadsym", gdb.COMMAND_USER) def invoke(self, args, from_tty): ''' in invoke method, we add command's features ''' # using string_to_argv to convert args to list argv = gdb.string_to_argv(args) if len(argv) != 1: raise gdb.GdbError( 'Fail to execute command, use "help loadsym" for help') print('[*] symbol file path: {}'.format(argv[0])) # traverse objfiles to find libc for i in gdb.objfiles(): if 'libc' in i.filename: self.add_debug_file(i, argv[0]) return print('[-] fail to find libc!') def add_debug_file(self, objfile, debugfile_path): ''' add debug file and check debug file's status ''' objfile.add_separate_debug_file(debugfile_path) # check symbol file is loading if gdb.lookup_symbol('main_arena') == None: print('[-] load debug file fail!') return False else: print('[+] load debug file success!') return True if __name__ == "__main__": loadsym()
[公告]安全服务和外包项目请将项目需求发到看雪企服平台:https://qifu.kanxue.com
最后于 2019-10-4 22:49 被富强民主和谐编辑 ,原因: 添加说明,修复错误