​ 有时候需要看库函数哪里出问题了,有源码调试的话,看起来会更直观和方便

glibc源代码调试

​ 开启gdb后 dir xxxx目录 即可

​ 但dir命令加载源码只能指定单个目录或文件,在gdb启动的时候用这个命令来加载glibc源码进去就好了

1
gdb `find ~/path/to/glibc/source -type d -printf '-d %p '` ./a.out

当前机器版本libc

​ 安装带调试版本libc

1
2
apt install libc6-dbg  
apt install libc6-dbg:i386

​ 下载对应的源代码

1
2
3
1. 修改/etc/apt/sources.list 开启deb-src (不知道是不是都要开)
2. apt update
3. apt source libc6-dev

​ 注意第三步会在当前目录进行下载

​ 调试的时候进行指定即可: gdb -q ./pwn -d /xxx/xxx/glibc-2.31/

不同版本glibc下载与编译

官方下载与编译

https://ftp.gnu.org/gnu/glibc/

1
2
3
4
5
6
git clone git://sourceware.org/git/glibc.git && cd glibc
git checkout glibc-2.31
mkdir build && cd build
../configure --prefix=/usr/local/glibc-2.31-debug --enable-debug=yes
make -j16
make install # 这时候/usr/local/glibc-2.31-debug/就有文件了 包含库文件和头文件

查看当前分支: git branch

查看有哪些分支: git branch -a

指定编译好的库进行文件编译

​ –rpath指定共享库路径 -I指定动态链接器

​ 就可以用指定的libc来编译代码了, 这样的话, 也会很自然就有glibc的源代码可以调试了

1
gcc -L/usr/local/glibc-2.31-debug/lib -Wl,--rpath=/usr/local/glibc-2.31-debug/lib -Wl,-I/usr/local/glibc-2.31-debug/lib/ld-2.31.so hello.c -o hello

​ 为什么不用加 -g了? (需要的,如果要看hello.c的源代码的话)

​ 但是用glibcallinone下载的不行..有问题, 那是因为它只有编译好的库,没有头文件之类的吧?

glibc-all-in-one工具

​ 这里是编译好的,一般都是带符号的,但是如果需要源代码调试还是需要下载源码并加载进gdb里面进行调试

https://blog.csdn.net/csdn546229768/article/details/122691241

​ 如果网站里没有的话,就修改download源代码, 换个source看看,google精准搜索搜索一下

debug信息

​ 符号文件在哪呢, 被编译进程序里了吧,我记得之前有单独的符号文件的

image-20231023223614611

ls -al /usr/lib/debug/.build-id/这里有

1
2
3
4
5
6
7
8
drwxr-xr-x   2 root root  4096 727 10:48 .
drwxr-xr-x 236 root root 4096 727 10:48 ..
-rw-r--r-- 1 root root 23496 47 2022 329f3d85e153a01672b77b853beda0faf0dee6.debug
-rw-r--r-- 1 root root 20104 47 2022 9f5043bbf7cfbf4dfd27684d9c3cbcbb835bd9.debug
-rw-r--r-- 1 root root 19468 47 2022 b0728b23be4032704a004d8066a268c4b6924a.debug
-rw-r--r-- 1 root root 20052 47 2022 bbb582f3d2cd3464764682f1937b4e3d9c2641.debug
-rw-r--r-- 1 root root 22200 47 2022 c4ae3a65bc87ea96986b3b2441e892c8a433f0.debug
-rw-r--r-- 1 root root 22168 47 2022 cd9124f765fe93560701d55d5c61c37be4657a.debug

但是glibcallinone里没找到这个呀…

他们之间是什么关系呢?

libc编译案例

​ 做一道题需要23版本的libc,用ubuntu20编译的有问题..换18试试 (也有很多报错, 难道得换debian?)

​ source是源码,右边是编译好的,但是没有符号信息

​ 难道版本不对? 这个source看起来是没有什么问题的,下载后看了下源码文件

image-20231023213302220

https://launchpad.net/ubuntu/xenial/amd64/libc6/2.23-0ubuntu11

http://launchpadlibrarian.net/409875491/libc6_2.23-0ubuntu11_amd64.deb

1
2
3
4
mkdir build && cd build
../configure --prefix=/usr/local/glibc-2.23-debug --enable-debug=yes
make -j16
make install # 这时候/usr/local/glibc-2.23-debug/就有文件了 包含库文件和头文件

罢了……..还是报错….处理不了……..(回头试试16, 或者说 还是得换debian? 不纠结这个编译问题了…)

转机

https://launchpad.net/ubuntu/xenial/amd64/libc6-dbg/2.23-0ubuntu11

image-20231106210245051

嘶,用了一下还是不太行

不过实际做题也未必要一模一样的环境,反正给了libc,可以从里面找偏移

报错处理

之前报错说要加–disable-werror, 但是好像也没啥用,

../configure –disable-werror –prefix=/usr/local/glibc-2.23 –enable-debug=yes

1
2
error: argument 1 of type ‘struct __jmp_buf_tag *’ declared as a pointer [-Werror=array-parameter=]
743 | extern int __sigsetjmp (struct __jmp_buf_tag *__env,

https://stackoverflow.com/questions/76079071/when-i-compile-glibc-2-28-with-the-make-command-on-centos-7-5-i-got-the-error-l

make[1]: *** [Makefile:214: stdio-common/subdir_lib] Error

https://www.cnblogs.com/zq10/p/14314952.html

https://gist.github.com/stefan1wan/5e4b3973aae578ac39f94d30a5555f19

make[2]: *** No rule to make target ‘../manual/errno.texi’, needed by ‘../sysdeps/gnu/errlist.c’. Stop

Inconsistency detected by ld.so: dl-call-libc-early-init.c: 37: _dl_call_libc_early_init: Assertion `sym != NULL’ failed!

问: 这里有现成的libc可以用,但是没有符号文件, 符号和源代码又是两回事吧

答: 是的,两回事

问: 没有符号咋办, 只能进行编译吗..

答: 我记得之前做海大ctf,ida可以导入符号文件,网上有编译好的符号文件,这个网站上也有symbol, 但是不全

问: gcc直接用这里的库编译会有问题,需要install?

问: 如何编译符号文件呢

调试案例

printf的调用路径

1
2
3
4
5
#include <stdio.h>
void main()
{
printf("hello,%s\n","world"); //防止优化为puts
}

​ 调试, bt追踪栈

1
2
3
4
#0  0x00007ffff7e5ca69 in __printf (format=0x55555555600a "hello,%s\n") at printf.c:28
#1 0x0000555555555169 in main ()
#2 0x00007ffff7e2d013 in __libc_start_main (main=0x555555555149 <main>, argc=1, argv=0x7fffffffe5a8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe598) at ../csu/libc-start.c:308
#3 0x000055555555508e in _start ()

​ 会发现进入的是__printf函数,为什么不是printf函数呢? 需要看libc源代码

​ 进到这里是因为ldbl_strong_alias (__printf, printf); 将printf和 _ _ printf进入了的函数统一为 _ _printf

​ stdio-common/printf.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <libioP.h>
#include <stdarg.h>
#include <stdio.h>

#undef printf

/* Write formatted output to stdout from the format string FORMAT. */
/* VARARGS1 */
int
__printf (const char *format, ...)
{
va_list arg;
int done;

va_start (arg, format);
done = vfprintf (stdout, format, arg);
va_end (arg);

return done;
}

#undef _IO_printf
ldbl_strong_alias (__printf, printf);
/* This is for libg++. */
ldbl_strong_alias (__printf, _IO_printf);

​ __printf又主要调用了vfprintf函数,在stdio-common/vfprintf.c中, 这个函数有点长

函数完整加载过程

恩….比较复杂..有时间专门学习下..

image of the callgraph for all the routines involved in program startup on linux

原文地址: http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html

翻译: https://zhuanlan.zhihu.com/p/52054044

https://zhuanlan.zhihu.com/p/521205296

其他参考:

https://xuanxuanblingbling.github.io/ctf/pwn/2021/12/12/csu/

程序员的自我修养里面也有

_start

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0000000000001060 <_start>:
1060: f3 0f 1e fa endbr64
1064: 31 ed xor %ebp,%ebp
1066: 49 89 d1 mov %rdx,%r9
1069: 5e pop %rsi
106a: 48 89 e2 mov %rsp,%rdx
106d: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
1071: 50 push %rax
1072: 54 push %rsp
1073: 4c 8d 05 66 01 00 00 lea 0x166(%rip),%r8 # 11e0 <__libc_csu_fini>
107a: 48 8d 0d ef 00 00 00 lea 0xef(%rip),%rcx # 1170 <__libc_csu_init>
1081: 48 8d 3d c1 00 00 00 lea 0xc1(%rip),%rdi # 1149 <main>
1088: ff 15 52 2f 00 00 callq *0x2f52(%rip) # 3fe0 <__libc_start_main@GLIBC_2.2.5>
108e: f4 hlt
108f: 90 nop

__libc_start_main

starti可以从最开始启动时下断点

image-20231105211445286

可以慢慢地去一探究竟程序的过程了!

https://refspecs.linuxbase.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/baselib---libc-start-main-.html

​ 它会处理执行环境的初始化工作, 然后调用main函数, 并且处理main函数的返回,

​ 它可能干的事如下

  • performing any necessary security checks if the effective user ID is not the same as the real user ID.
  • initialize the threading subsystem.
  • registering the *rtld_fini* to release resources when this dynamic shared object exits (or is unloaded).
  • registering the *fini* handler to run at program exit.
  • calling the initializer function (**init*)().
  • calling main() with appropriate arguments.
  • calling exit() with the return value from main().

参考

https://xuanxuanblingbling.github.io/ctf/tools/2020/03/20/gdb/

[原创]关于不同版本 glibc 更换的一些问题: https://bbs.kanxue.com/thread-254868.htm

https://gist.github.com/stefan1wan/5e4b3973aae578ac39f94d30a5555f19

问答

有无符号的区别在哪呢….蒙圈了

函数、变量符号

有符号的话是可以看到定义的那些变量、函数等,但是不知道行数、具体的代码

符号文件在哪?

deb如何解包 dpkx -x xxxxx.deb ./

debug文件为什么会有单独的,为什么glibcallinone里的不用呢