# 前言
万事开头难……准备开始学习堆利用了,不过发现想要在 Arch Linux 上获得预期的源码级调
试体验有点有点费劲,没有像 ubuntu 那样的 `libc6-dbg` 软件包,试过配置 `debuginfo
d` 服务,也没有解决问题(很爆炸,等我把这个项目撸完后才发现,原来用 ubuntu 的 [d
ebuginfod](https://documentation.ubuntu.com/server/explanation/debugging/about-d
ebuginfod/index.html) 地址就行了,不能用 arch 的……)。glibc-all-in-one 下载下来
的 glibc 动态链接库默认也是 stripped 的,虽然它提供了 build 脚本,但那是针对 ubu
ntu 开发的,Arch 上使用会出现很多问题(比如我编译 glibc 2.23 就没成功)。
## 有关在宿主机上编译低版本 glibc 的问题
学习堆利用难免要自己写点程序,使用对应版本的 glibc 编译。起初我是用 glibc-all-in
-one 下载对应 glibc 共享库,然后编译的时候通过 `-Wl,--rpath="$GLIBC_PATH/lib"`
和 `-Wl,--dynamic-linker="$GLIBC_PATH/lib/ld-linux-x86-64.so.2"` 指定使用对应的
loader 和 libc. 但运行却遇到 `Version 'GLIBC_2.34' not found` 问题,程序跑不起来
,以致每次为了编译个程序还得启动个 docker,太麻烦了。
通过 `strings` 看了一下,编译出来的程序有 `__libc_start_main@GLIBC_2.34`,使用了
高版本 glibc, 其它函数倒是正确使用了低版本 glibc, 比如 `malloc@GLIBC_2.2.5`. 我
本来以为是宿主机 gcc 版本太高,而我需要的 glibc 是 2.23, 可能之间相差太大了,所
以不兼容。但是后来才知道,这和 gcc 毛关系都没有,而是因为 gcc 会从系统默认的 lib
c 动态链接库路径获得各种共享对象文件,虽然我编译的时候设置了 loader 和查找 libc
的路径,但是其它库文件还是会从默认路径去取。导致一部分是正确链接了低版本库,一部
分没找到的就链接了系统默认的高版本库。
总之期间走了不少弯路,花了两天去折腾怎么安装低版本 gcc,因为 Arch 是滚动更新,所
有软件都保持在最新版本,bro 想弄个低版本 gcc 太不容易了。AUR 上提供的 gcc 包安装
编译各种问题层出不穷。本以为看到 AUR 是看到了希望,现实却让人大失所望……
因为路线错误,一心以为换个低版本 gcc 就能解决问题,所以我还是坚持研究出来了怎么
在 Arch 上编译低版本 gcc,学到了如何写 PKGBUILD、如何使用 Arch build system 和如
何使用 chroot 等……虽然折腾期间学到了不少新知识,但很不幸,当我兴奋地将历经九九八
十一难编译出来的低版本 gcc 安装到机子上后……编译!运行!草!为毛还是 `Version 'GL
IBC_2.34' not found`!感觉受到了致命打击……研究了两天的成果并没有软用,但至少也说
明了和 gcc 版本没关系,而是另有所因……
最终,在我的严厉拷问<s>(软磨硬泡)</s>下,AI 给出了一个我从未见过的 `-B` 编译选
项。我也是非常渴望解决问题啊,直接就拿来测试了。再编译,运行……?居然成功了!之后
我 `gcc --help` 查了一下这个选项的含义,发现对其的描述是 `-B <directory> Add <d
irectory> to the compiler's search paths.`. 好家伙,搞了半天编译的时候只设置 loa
der 和 libc 路径并没有用啊,还得设置一个默认搜索路径……不过我也曾试过用 `-I` 和 `
-L` 设置库路径,却没成功,可能是编译不只是用到了库,还有别的东西?
至此,直接在宿主机下编译使用低版本 glibc 程序的问题,终于 tmd 解决了!
这里必须要吐槽一下,即便是 GPT-5 对于这些问题,知识面也不是很足啊,我天天问,各
种角度问,它就是给不出正确的解决方案,老是搁那儿一本正经地胡说八道……
## gdb 调试没有符号信息
解决了使用宿主机编译低版本 glibc 程序的问题,接下来就该解决一下没有 debug symbol
s 的问题了。glibc-all-in-one 下载的 glibc 都是 stripped 的,虽然里面提供了 `.deb
ug` 目录,保存了调试信息,但当我将这写调试信息加载到 gdb 中后,gdb 的 heap 命令
就被破坏了。原因可能是直接加载调试信息并不会自动设置 libc 基地址,加载出来的都是
各种符号在 libc 里面的偏移。而 heap 指令又依赖 glibc 信息和各种结构体信息,比如
`mp_` 什么的……因此如果只是加载了偏移地址,而没有设置基地址,那自然会破坏这些指令
的配置。这块儿我也研究了半天,试过加载的时候自动设置基地址,不过好像没有这样的指
令,也试过根据 `heap-config` 所需的信息手动计算物理地址修复 heap 指令的使用,但
是既麻烦,也没有成功……
让人心态爆炸的是,等我撸完了这个项目后才发现 glibc-all-in-one 下载下来的共享对象
文件里面的 `.debug` 目录,包含的并不是单纯的调试信息文件,而是 tmd 没 strip 的共
享对象文件……为此我之前还特地写了一个 gdb 插件 [load-symbols](https://github.com/
CuB3y0nd/load-symbols),迭代加载指定目录下的 debug 信息……只能说当时被别人的博客
误导了,然后自己也没注意看,眼睛瞎了,隔了那么就才发现……要不是在群里和其他师傅们
交流,我可能到现在都没发现也说不准……不过没事,手动 patchelf 指定它的路径写起来也
麻烦,更重要的是,它虽然也提供了没 strip 的 libc,但这只能解决已有二进制文件的运
行问题,如果想在宿主机上自己编译使用低版本 libc 的程序就不行了,还是需要有编译好
的版本。
无奈,摆在我面前的是两条路:
1. 调试的时候手动计算各种符号的物理地址,下断点
2. 使用别人配置好的虚拟机环境,或者自己整一个
老实说两个方法都很麻烦,我一个也不想用……我只是想本地能直接使用 `b *_int_free` 这
样的指令而已,为啥那么费劲呀……
被逼无奈,当时是准备搭一个 Docker 调试环境的,试了下 [pwndocker](https://github.
com/skysider/pwndocker),但是发现里面的工具版本都比较老了,于是强迫症+完美主义的
我想自己从头开始构建一个 Docker Pwn 环境。最后确实是搭好了,<s>但是我一次都没用
过……</s>心理那叫一个难受啊,哈哈哈,我本来就用的 Linux, 只想在本地解决所有问题,
而不是每次要做点什么的时候还必须放到虚拟机里面去做,那多麻烦,多奇怪啊,加之我平
时虚拟机用的就少,还得学习一下它的用法,感觉学习成本太高了,我只是想开开心心地直
接开始做事。还有一个原因就是虚拟机环境和我本地环境配置有很大出入,所以用起来一点
也不舒服,而我又懒得去配,让它和我本地统一……~懒成这样是不是没救了?~
## Dynabox
嗯……上面就是创建这个项目的原因了,虽然这个项目底层本质上还是用虚拟机,但这或许是
最好的解决本地不能编译的问题的方法了。不过我的脚本已经将虚拟机的操作完全封装好了
,不需要学习任何有关 Docker 的使用,所以还是很方便滴。
项目地址下面给出了,感兴趣的师傅可以去试试看,能给个 star 的话最好不过了哈哈。
GitHub: https://github.com/CuB3y0nd/Dynabox
至于这个工具怎么用我就不在这里赘述了,请师傅们移步项目 README. 本来写这篇博客也
就是为了随便记录一下折腾的过程,虽然不知道写这个有啥用,但就是想写哈哈哈。
后期打算用其它语言重构一下,因为考虑到后面子命令越来越多,shell 脚本可能会长到难
以维护。正好也能锻炼一下开发能力吧,因为平时自己写代码的时间太少了。
比较可惜的是,也是在我自己撸完这个项目后才发现的,有个叫 [pwninit](https://githu
b.com/io12/pwninit) 的项目已经实现了和我这个项目类似的功能,不过他的项目并没有解
决本地编译低版本 glibc 程序的问题,所以我这个项目还是有点不一样的东西的 xD