源码调试Glibc
系统自带glibc的缺点
在我们尝试观察延迟绑定机制时,
需要观察_dl_runtime_resolve和dl_fixup这两个函数
这两个函数都位于ld-linux.so.2 动态库中
默认情况下使用gcc编译程序时,动态链接的glibc动态库文件,都在/lib下
1 | root@Destroyer:/usr/src/CTF-All-In-One/src/writeup/6.1.3_pwn_xdctf2015_pwn200 |
libc | ld | |
---|---|---|
32位程序 | /lib/i386-linux-gnu/libc.so.6 | /lib/ld-linux.so.2 |
64位程序 | /lib/x86_64-linux-gnu/libc.so.6 | /lib64/ld-linux-x86-64.so.2 |
这是安装ubuntu这种发行版时系统自带的glibc-release版
1 | root@Destroyer:/lib/i386-linux-gnu |
可以看到调试符号信息已经被strip了
就算我们编译一个c程序时加入了-g选项,也只是保留了程序领空内的所有符号信息,该程序链接的glibc照样是没有调试符号的
1 | gef➤ info sharedlibrary |
两个库都标着*,意思是缺乏调试信息
如果我们想要调试glibc,或者说能够保留glibc中的符号,比如函数名,变量名之类,需要带有符号的glibc
可以自己编译一个玩
以我的wsl kali-linux为例,装机自带的libc版本号是2.38
1 | ┌──(dustball㉿Destroyer)-[~] |
下面我们编译一个新的带有调试符号的2.38版本的glibc
编译64位glibc
下载glibc
首先下载glibc源码
1 | apt install glibc-source |
执行完后会在/usr/src下面生成glibc目录
1 | ┌──(root㉿Destroyer)-[/usr/src/glibc] |
这个方法不太靠谱,我在另一台机器的wsl上这样整,缺少texinfo源代码
还是到仓库下载源代码稳妥
或者到glibc仓库http://ftp.gnu.org/gnu/glibc/挑一个下载
或者到镜像仓库下载https://mirrors.aliyun.com/gnu/glibc/
比如
1 | curl http://ftp.gnu.org/gnu/glibc/glibc-2.38.tar.xz -o glibc-2.38.tar.xz |
解压glibc
1 | tar -xf glibc-2.38.tar.xz |
建立bulid目录
1 | cd glibc-2.38 |
配置编译选项
在build路径下
1 | ../configure --prefix=/home/glibc/ --enable-add-ons=nptl --with-tls --with-__thread --enable-kernel=2.6.32 --enable-debug --disable-werror |
1 | ../configure --prefix=/home/glibc32/glibc-2.38 --enable-add-ons=nptl --with-tls --with-__thread --enable-kernel=2.6.32 --enable-debug --disable-werror |
1 | ../configure --prefix=/home/dustball/glibc/glibc-2.35 --enable-add-ons=nptl --with-tls --with-__thread --enable-kernel=2.6.32 --enable-debug --disable-werror |
1 | ../configure --prefix=/home/dustball/glibc/glibc-2.27 --enable-add-ons=nptl --with-tls --with-__thread --enable-kernel=2.6.32 --enable-debug --disable-werror |
1 | ../configure --prefix=/home/dustball/glibc/glibc-2.31 --enable-add-ons=nptl --with-tls --with-__thread --enable-kernel=2.6.32 --enable-debug --disable-werror |
1 | ../configure --prefix=/home/dustball/glibc/glibc-2.35 --enable-add-ons=nptl --with-tls --with-__thread --enable-kernel=2.6.32 --enable-debug --disable-werror |
这里--prefix=/home/glibc
是决定make install
的安装地址
–-enable-debug
允许调试,实际上就是给gcc传递-g编译选项
编译&安装
1 | make -j `nproc` |
如果配置编译选项时prefix没有写或者写错了也没关系
1 make install DESTDIR=/home/glibc/这样安装也可以
如果编译和安装都没有错误,会在/home/glibc/
下生成我们的货
1 | ┌──(root㉿Destroyer)-[/home/glibc] |
动态库在lib下面放着了
1 | ┌──(root㉿Destroyer)-[/home/glibc/lib] |
非常滴漂亮
交叉编译32位glibc(可选)
在x86_64 linux上,也兼容32位的程序
如果想要32位带符号glibc的支持,需要再编译一个32位的glibc
下载和解压不用做了,还是利用之前编译64位glibc时的源码即可
建立build32目录
1 | cd glibc-2.38 |
配置32位编译选项
首先让gcc能够编译32位程序,需要安装一些依赖
1 | apt-get install build-essential module-assistant gcc-multilib g++-multilib |
然后配置编译选项
1 | ../configure --prefix=/home/glibc32 --host=i686-pc-linux-gnu --enable-add-ons=nptl --with-tls --with-__thread --enable-kernel=2.6.32 --with-binutils=/usr/bin --with-headers=/usr/include --build=i686-linux-gnu CC="gcc -m32" CXX="g++ -m32" |
编译&安装
1 | make -j`nproc` |
如果编译安装都没有错误,会在/home/glibc32下面生成我们的货
1 | ┌──(root㉿Destroyer)-[/home] |
1 | ┌──(root㉿Destroyer)-[/home/glibc32/lib] |
漂亮滴很
编译链接glibc
现在系统里面有两个glibc
一个系统自带的没有调试符号的glibc在/lib下面
一个我们自己编译的有调试符号的glibc在/home/glibc下面
但是天无二日,程序只能链接一个glibc,并且程序默认链接到/lib下的老太阳
1 | ┌──(root㉿Destroyer)-[/home/glibc-test] |
如何让程序链接到我们自己编译的glibc呢?
gcc有一个编译选项可以指定链接libc的位置
1 | gcc main.c -o main -Wl,--rpath=/home/glibc/lib -Wl,--dynamic-linker=/home/glibc/lib/ld-linux-x86-64.so.2 -I/home/glibc/include -g -no-pie |
-Wl,--rpath=/home/glibc/lib
指定glibc路径
-Wl,--dynamic-linker=/home/glibc/lib/ld-linux-x86-64.so.2
指定动态链接器的路径
-I/home/glibc/include
指定头文件路径
1 | ┌──(root㉿Destroyer)-[/home/glibc-test] |
libc和动态链接器都改好了
如果想要编译成32位程序
1 | gcc main.c -o main -Wl,--rpath=/home/glibc32/lib -Wl,--dynamic-linker=/home/glibc32/lib/ld-linux.so.2 -I/home/glibc/include -g -no-pie -m32 |
1 | ┌──(root㉿Destroyer)-[/home/glibc-test] |
libc和动态链接器都改好了
此时使用gdb调试程序也是可以看到动态链接器的源码的
也可以看到ld中的符号link_map
1 | pwndbg> info types link_map |
patch-elf修改程序链接glibc
对于一个已经编译链接完毕,默认链接系统自带glibc的程序,如何让它使用我们编译的glibc呢
可以使用patch-elf修改程序,一是修改libc所在目录的位置,二是修改使用的链接器的绝对地址
1 | patchelf --set-rpath /home/glibc/lib/ --set-interpreter /home/glibc/lib/ld-linux-x86-64.so.2 main |
1 | ┌──(root㉿Executor)-[/home/glibc-test] |
改完了之后ldd观察结果和在编译链接时指定我们的glibc效果是一样的
虽然改完了ld的地址看上去还是指向原来那个/home/glibc/lib/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2
但是调试观察实际上已经改过了
1 | gef➤ info sharedlibrary |