首页
社区
课程
招聘
[原创]关于不同版本 glibc 更换的一些问题
2019-10-4 18:24 51031

[原创]关于不同版本 glibc 更换的一些问题

2019-10-4 18:24
51031

关于不同版本 glibc 更换的一些问题

在做 pwn 题时,更换 ELF 文件的 libc 版本一直让人头疼,所以写文记录关于 glibc 的下载,替换,调试的一些问题。

如何获取不同版本的 glibc

手动下载

通过镜像源可以下载到常见版本的 glibc 及其符号表。

 

通过 Ubuntu 的 old-releases 镜像站 或者 清华的镜像站 可以下载到不同版本的 glibc。然后通过 dpkg -x *.deb 来解压 deb 包得到 libc。

 

当然也可以通过 Debian 等发行版的镜像源来下载 glibc。

自动化工具

有两个项目可以实现自动下载 libc:https://github.com/niklasb/libc-databasehttps://github.com/matrix1001/glibc-all-in-one,前者不会下载符号表,而后者会将符号表存入对应 libc 的 ".debug" 文件夹中。

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

通过 LD_LIBRARY_PATH 或者 LD_PRELOAD

因为 ld.so 和 libc.so 不匹配的原因,所以直接设置 LD_PRELOAD 可能会炸,就如下所示...

$ LD_PRELOAD=./libc.so.6 ./baby_tcache
段错误 (核心已转储)

可以将配套的 ld 和 libc 一起使用即可实现动态加载 libc。只需将下面代码中 LD_PRELOAD 后面的 "/path/to/libc.so.6" (要加载的 libc 的路径)和第二行的 "/path/to/ld.so" (要加载的 ld 的路径)替换成相应文件的路径就行了。

$ LD_PRELOAD=/path/to/libc.so.6;
$ /path/to/ld.so ./test

在 pwntools 启动程序时,可以按照下面的代码进行设置。

p = process(["/path/to/ld.so", "./test"],
            env={"LD_PRELOAD":"/path/to/libc.so.6"})

通过 patchelf 修改 ELF 文件

在 github 上有一个项目叫 patchelf,通过这个项目可以实现修改 ELF 中硬编码的 libc 和 ld 的路径。

 

一般 ELF 文件的 lddfile 结果与下面类似,可以看到 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。我们先用 "--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"

$ 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 命令查看程序,可以看到 libc 和 ld 都修改成功了。

$ 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

我写了个简单脚本,使用 "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。

set -x
libc_path=$1
elf_path=$2
patchelf_bin_path="/path/to/patchelf"
if [ -f ${libc_path}/ld-[2].[0-9][0-9].so ]; then
    $patchelf_bin_path --set-interpreter $libc_path/ld-[2].[0-9][0-9].so $elf_path
fi
if [ -f $libc_path/libc-[2].[0-9][0-9].so ]; then
    $patchelf_bin_path --replace-needed libc.so.6 $libc_path/libc-[2].[0-9][0-9].so $elf_path
fi
set +x

需要注意的是 patchelf 可能会有 Bug,所以使用从 Github 上的代码进行编译得到的 patchelf 能避开一些 Bug。

使用 LIEF

在看 LIEF 文档时发现了一篇老外的博文,讲的是通过 LIEF 来使用不是系统自带的 libc。

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

如果只是直接 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 文件,并手动加载了。

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

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

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

在参考文章1可以看到关于 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://wiki.ubuntu.com/Debug%20Symbol%20Packages

 

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

 

http://man7.org/linux/man-pages/man8/ld.so.8.html


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2020-3-19 01:45 被富强民主和谐编辑 ,原因: 丰富细节,增加说明
收藏
点赞16
打赏
分享
最新回复 (14)
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jackeyQ 2019-12-25 14:45
2
0
这么好的文章居然没人留言。。。
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jackeyQ 2019-12-25 14:48
3
0
推荐个工具是pwn_debug  https://github.com/ray-cp/pwn_debug
雪    币: 207
活跃值: (66)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
留恋恋恋恋 2020-3-18 21:19
4
0
好文章,支持一下!
雪    币: 1187
活跃值: (63)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
我是alpha 2020-8-17 17:24
5
0
专门注册了一个账号就是为了赞你
雪    币: 11
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
00D 2020-9-25 23:11
6
0
很有用啊,救了我
雪    币:
活跃值: (168)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
k1ll3rQ 2020-10-9 15:45
7
1
好文,不过我在实践的时候发现文章提到的结合pwntool的用法
p = process(["/path/to/ld.so", "./test"], env={"LD_PRELOAD":"/path/to/libc.so.6"})
会导致system执行出错。
patchelf的方法不会。
雪    币: 220
活跃值: (103)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xxs_bxs 2020-10-27 10:37
8
0
wx_LONELYGOD 好文,不过我在实践的时候发现文章提到的结合pwntool的用法 p = process(["/path/to/ld.so", "./test"], env={ ...
同问,想知道为什么会这样
雪    币: 188
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
96613686 2021-1-10 23:53
9
0
非常感谢大佬啊,一直在寻找更好libc后gdb无法识别的问题的解答
雪    币: 234
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
a3uRa 2021-2-22 01:28
10
0
已经有一个libc-2.19.so的文件 能否得到对应的版本 对应ld的版本呢
雪    币: 4165
活跃值: (15932)
能力值: (RANK:710 )
在线值:
发帖
回帖
粉丝
Roland_ 12 2021-2-23 22:59
11
0
很有用的实用技巧,支持!
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
kr0emer 2021-3-15 16:54
12
0

感谢楼主


最后于 2021-3-30 23:54 被kr0emer编辑 ,原因:
雪    币: 27
活跃值: (91)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
常喑 2021-4-13 02:43
13
0
不知道楼主和各位大佬有没有遇到过这种问题,我最近重编了一个libc2.23,用里面的loader去加载以前2.23题目给的libc,会segfault,这是为啥呢
雪    币: 20
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
zyLeo 2021-10-31 10:24
14
0
好文顶一下
雪    币: 74
活跃值: (569)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
GUANZHI_ 2023-12-29 08:31
15
0
a3uRa 已经有一个libc-2.19.so的文件 能否得到对应的版本 对应ld的版本呢
strings <libc.so文件> | grep 'ubuntu'命令会显示要用的ld版本
游客
登录 | 注册 方可回帖
返回