实现 glibc 源码调试

刚开始接触堆漏洞时,一直是直接对着 ctf-wiki 学习堆漏洞原理,虽然最终学会了如何利用各种堆漏洞,但却不知道如何跟入 libc 中的函数,从源码的角度一步一步观察漏洞利用的过程。最近调试 glibc 源码的念头又冒了出来,于是开始琢磨怎么达到这样的目的。经过一段时间的努力,终于成功了,这里记录一下大致的流程。

0x00 环境和准备

  • Ubuntu16.04
  • glibc2.23 源码
  • vscode

0x01 编译 glibc

下载 glibc 源码

首先下载 glibc2.23 的源码,使用 ftp 似乎太慢了,可以直接访问 http://ftp.gnu.org/gnu/glibc 下载 glibc-2.23.tar.gz

之后解压下好的源码,并新建一个 build 文件夹。

tar -xvf glibc-2.23.tar.gz && cd glibc-2.23
mkdir build && cd build

configure

在 build 文件夹下 configure,并且加上一些必要的参数使得编译时加上 symbol,并且指定输出文件夹:

# 如果希望编译 x64 的 libc 的话应使用 x64 这段设置,x32 同理。
# x64
CFLAGS="-g -g3 -ggdb -gdwarf-4 -Og -Wno-error" \
CXXFLAGS="-g -g3 -ggdb -gdwarf-4 -Og" \
../configure --prefix=/home/lukbash    # 这里填 lib 的安装路径,x32 同理。
# x32
CC="gcc -m32" CXX="g++ -m32" \
CFLAGS="-g -g3 -ggdb -gdwarf-4 -Og -Wno-error" \
CXXFLAGS="-g -g3 -ggdb -gdwarf-4 -Og" \
../configure --prefix=/home/lukbash --host=i686-linux-gnu

编译

configure 成功后便可以 sudo make && sudo make install 进行编译。

libc 编译成功后可以在安装路径下看到一个 lib 文件夹。

image-20200903212357332

P.S. 可能出现的坑

某些安装包缺失

configure 的时候可能会出现如下提示:

configure: error:
*** These critical programs are missing or too old: gawk
*** Check the INSTALL file for required versions.

这时应安装 gawk:

sudo apt install gawk

空间不足

因为用的是虚拟机,最初可能并没有分配多少空间给它。编译过程中需要足够的空间,当空间不足时会出现如下的提示:

mkdir: cannot create directory '/home/quizas/lib': No space left on device
Makerules:941: recipe for target '/home/quizas/lib/libc.a' failed
make[1]: *** [/home/quizas/lib/libc.a] Error 1
make[1]: Leaving directory '/home/quizas/glibc-2.23'
Makefile:12: recipe for target 'install' failed
make: *** [install] Error 2

解决办法是给虚拟机扩容或重新装一个空间足够的虚拟机。

0x02 安装 vscode

从官网上下载 deb 包,安装:

sudo  dpkg  -i code_1.31.1-1549938243_amd64.deb

之后可在终端中通过 code 启动 vscode。

0x03 配置

在之前解压出的放置 glibc 源码的文件夹中创建一个 test.c 文件作为测试,内容为:

#include <stdio.h>
#include <stdlib.h>

int main() {
    char *p;
    p = malloc(0x60);
    free(p);
    return 0;
}

指定编译器

编译时需要注意指定之前编译好的链接器。

sudo gcc test.c -o test -Wl,-I /home/lukbash/lib/ld-2.23.so

检查 test 使用的程序依赖库,这里如果使用 ldd 进行查看的话可能出现 not a dynamic executable 的提示,可使用以下命令进行代替:

LD_TRACE_LOADED_OBJECTS=1 ./test

可见链接器被成功替换。

image-20200903214054171

vscode 相关设置

在 vscode 中打开放置 glibc 源码的文件夹,配置 task.json 和 launch.json

task.json

// task.json
{
    "version": "2.0.0",
    "tasks": [
        {
            "type": "shell",
            "label": "build",
            "command": "/usr/bin/gcc",
            "args": [
                "-g",
                "${file}",
                "-o",
                "${fileDirname}/${fileBasenameNoExtension}",
                "-Wl,-I/home/lukbash/lib/ld-2.23.so"
            ],
            "options": {
                "cwd": "${workspaceFolder}"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            }
        }
    ]
}

launch.json

// launch.json
{
    // 使用 IntelliSense 了解相关属性。 
    // 悬停以查看现有属性的描述。
    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "gcc.exe - 生成和调试活动文件",
            "type": "cppdbg",
            "request": "launch",
            "program": "${fileDirname}\\test", // 改为要调试的可执行文件路径
            "args": [],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "miDebuggerPath": "/usr/bin/gdb",
            "setupCommands": [
                {
                    "description": "为 gdb 启用整齐打印",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ],
            "preLaunchTask": "build" // 注意此处应和 task.json 中的 label 相同
        }
    ]
}

接着在 test.c 中下断点,再按 F5 开始调试。

image-20200903214822389

会出现类似提示:

无法打开“malloc.c”: 无法读取文件'/build/glibc-LK5gWL/glibc-2.23/malloc/malloc.c' (Error: 无法解析不存在的文件"/build/glibc-LK5gWL/glibc-2.23/malloc/malloc.c")。

此时只要创建 /build/glibc-LK5gWL/ 文件夹,再把之前解压出来的源码复制到这个文件夹中:

sudo cp -r /home/lukbash/glibc-2.23 /build/glibc-LK5gWL

重新按 F5 进行调试,即可通过单步调试跟入 libc 中的函数进行源码调试。

image-20200903215355522

0x04 后记

虽然是针对 Ubuntu16.04 下 glibc2.23 的调试,但应该也能作为其他版本的参考。

之所以选择 vscode 是因为它的调试界面相对于直接使用 gdb 来进行源码调试更加直观。另外 vscode 的调试控制台似乎不能像终端那样输出带颜色的字体(实际上可以,但并不是我想要的结果),导致执行 gdb 命令时 gef 的输出乱七八糟,非常难看。目前还没有找到解决的办法,最后还是凑合了。

直接学习漏洞的利用方式固然迅速,但在学习漏洞利用的同时结合对源码的调试,才能对漏洞发生的过程和产生漏洞的根本原因有更深入的理解。

0x05 参考资料

2019-2020 @lukbash