关于不同版本 glibc 更换的一些问题
2019-10-14 01:54:26 Author: bbs.pediy.com(查看原文) 阅读量:959 收藏

在做 pwn 题时,glibc 更换一直让人头疼,所以写文记录关于 glibc 中一些问题。

参考文章:

https://wiki.ubuntu.com/Debug%20Symbol%20Packages

https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html

如何获取不同版本的 glibc

手动下载

通过镜像源可以下载到常见版本的 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-databasehttps://github.com/matrix1001/glibc-all-in-one,前者不会下载符号表,而后者会将符号表存入 ".debug" 文件夹中。

让程序强行加载特定版本 glibc

通过 LD_LIBRARY_PATH 或者 LD_PRELOAD

因为 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
段错误 (核心已转储)

求大佬指点指点...

通过修改 ELF 文件

在 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

在 gdb 中加载 debug 文件/符号表

如果是使用 "glibc all in one" 下载的 libc,会在 libc 等库放置的位置使用 ".debug" 文件夹存放好了 libc 的符号表,使用 gdb 可以自动加载。但是碰到 "glibc all in one" 没有的 libc 版本时就需要手动下载 debug 文件,并手动加载了。下面的方法三选一就行...

将 debug 文件放入 ".debug"文件夹

在放置 libc 的目录下新建 ".debug"文件夹,将 debug 文件放入其中即可。原理见参考文章2。

通过 gdb 命令 set debug-file-directory directories

在参考文章2可以看到关于 gdb 如何加载分离的 debug 文件,我们只要将 libc 等库的 debug 文件放入对应文件夹,并通过 "set debug-file-directory $directories" 命令将文件夹设为分离的 debug 文件目录,就可以让 gdb 加载 debug 文件。

通过 gdb python api 的 add_separate_debug_file() 加载

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 被富强民主和谐编辑 ,原因: 添加说明,修复错误


文章来源: https://bbs.pediy.com/thread-254868.htm
如有侵权请联系:admin#unsafe.sh