0x4 使用 qemu + gdb 调试Linux内核

使用 qemu + gdb 调试Linux内核

一、载入内核符号表

直接使用 gdb 载入之前在源码根目录下编译出来的未压缩内核镜像 vmlinux 即可

1
sudo gdb vmlinux

什么?!你说你sudo gdb不是pwndbg?

image-20240929161310985

现在知道怎么做了吧

二、remote连接

还记得当时编写的boot.sh吗?

1
2
3
4
5
6
7
8
9
10
11
#!/bin/sh
qemu-system-x86_64 \
-m 128M \
-kernel ./bzImage \
-initrd ./rootfs.cpio \
-monitor /dev/null \
-append "root=/dev/ram rdinit=/sbin/init console=ttyS0 oops=panic panic=1 loglevel=3 quiet kaslr" \
-cpu kvm64,+smep \
-smp cores=2,threads=1 \
-nographic \
-s

-s:相当于-gdb tcp::1234的简写(也可以直接这么写),后续我们可以通过gdb连接本地端口进行调试

我们启动时已经将内核映射到了本地的1234端口

只需要gdb连接上就行

1
2
3
4
pwndbg> set architecture i386:x86-64
The target architecture is set to "i386:x86-64".
pwndbg> target remote localhost:1234
Remote debugging using localhost:1234
image-20240929161522166

三、解压 bzImage 镜像

有的时候我们只有压缩后的内核镜像bzImage (例如在一些 CTF 题目中),此时我们可以使用如下脚本进行解压(来自github):

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-only
# ----------------------------------------------------------------------
# extract-vmlinux - Extract uncompressed vmlinux from a kernel image
#
# Inspired from extract-ikconfig
# (c) 2009,2010 Dick Streefland <dick@streefland.net>
#
# (c) 2011 Corentin Chary <corentin.chary@gmail.com>
#
# ----------------------------------------------------------------------

check_vmlinux()
{
# Use readelf to check if it's a valid ELF
# TODO: find a better to way to check that it's really vmlinux
# and not just an elf
readelf -h $1 > /dev/null 2>&1 || return 1

cat $1
exit 0
}

try_decompress()
{
# The obscure use of the "tr" filter is to work around older versions of
# "grep" that report the byte offset of the line instead of the pattern.

# Try to find the header ($1) and decompress from here
for pos in `tr "$1\n$2" "\n$2=" < "$img" | grep -abo "^$2"`
do
pos=${pos%%:*}
tail -c+$pos "$img" | $3 > $tmp 2> /dev/null
check_vmlinux $tmp
done
}

# Check invocation:
me=${0##*/}
img=$1
if [ $# -ne 1 -o ! -s "$img" ]
then
echo "Usage: $me <kernel-image>" >&2
exit 2
fi

# Prepare temp files:
tmp=$(mktemp /tmp/vmlinux-XXX)
trap "rm -f $tmp" 0

# That didn't work, so retry after decompression.
try_decompress '\037\213\010' xy gunzip
try_decompress '\3757zXZ\000' abcde unxz
try_decompress 'BZh' xy bunzip2
try_decompress '\135\0\0\0' xxx unlzma
try_decompress '\211\114\132' xy 'lzop -d'
try_decompress '\002!L\030' xxx 'lz4 -d'
try_decompress '(\265/\375' xxx unzstd

# Finally check for uncompressed images or objects:
check_vmlinux $img

# Bail out:
echo "$me: Cannot find vmlinux." >&2

用法如下:

1
$ ./extract-vmlinux ./bzImage > vmlinux

四、寻找gadget

用ROPgadget或者ropper都行,笔者比较喜欢使用ROPgadget

1
2
3
4
5
$ ROPgadget --binary ./vmlinux > gadget.txt

or

$ ropper --file ./vmlinuz --nocolor > gadget2.txt

一般出来大概有个几十MB

arttnba3师傅说他直接用pwntools暴力搜。。怎么搜?

五、调试内核模块

使用 gdb 调试内核模块和调试普通的可执行文件大致上是相同的,不过我们需要额外指定各个需要被我们调试的 section 的载入地址

首先通过读取 /sys/module/模块名/sections/ 目录下对应的文件获取对应 section 在内核中的载入地址,例如我们调试时需要用到 .text.data.bss 这三个段的数据

img

之后在使用 gdb 连接上 qemu 后,使用 add-symble-file 命令载入内核模块信息,默认指定为 .text

1
(gdb) add-symbol-file ./arttnba3_module.ko 0xffffffffc0002000 -s .data 0xffffffffc0004000 -s .bss 0xffffffffc000
image.png

之后就可以正常调试内核模块了

image.png
image.png

若是你只需要对代码段进行调试,也可以通过 lsmod 命令或者读取 /proc/modules 以获取代码段的载入地址

image.png