# Linux Kernel Debug Enviroment with buildroot
任意のバージョンのLinux KernelをQemu上で動かしてkgdbでデバッグするまでのメモ
## ビルド環境
buildrootのバージョンごとに扱えるgccのバージョンの範囲が限られている
しかし経験上、**Linux Kernelは特定の範囲のバージョンのgccを使用しないとビルドに成功しない**事が多い
参考: https://qiita.com/masami256/items/a97917761d6df137f817
そのため、カーネルのリリース日とguessを元に適したバージョンのbuildrootを持ってくる
なお今回は**Linux Kernel 3.13.5**をビルドする事を前提とする
(このメモはCVE-2014-3153の解析環境の構築が最終目標なので)
対応しているbuildrootは**buildroot-2014.08**だった
https://buildroot.org/downloads/buildroot-2014.08.tar.gz
また、念の為Dockerを使用してビルドするホスト環境も調整してみた
使用したのは`Ubuntu 16.04`のコンテナ
`$ docker run -it ubuntu:16.04`
でコンテナを動かしたらホスト側の環境を整備する
```
# apt update
# apt install wget build-essential bc libncurses5-dev rsync cpio unzip python vim
```
また、このバージョンのbuildrootの場合、pkgconfのリンクが切れているようなので修正する
```diff
package/pkgconf/pkgconf.mk
8c8
< PKGCONF_SITE = http://distfiles.ariadne.space/pkgconf
---
> PKGCONF_SITE = http://rabbit.dereferenced.org/~nenolod/distfiles
```
## buildroot
buildrootを展開し、中に入ってQemu用のconfigを持ってくる
```
# wget https://buildroot.org/downloads/buildroot-2014.08.tar.gz
# tar xvf ./buildroot-2014.08.tar.gz
# cd buildroot-2014.08
# make qemu_x86_64_defconfig
```
menuconfigを使用して追加で設定していく
以降、buildrootのバージョンの違いによって設定名や設定までのパスが異なるかもしれないので注意
```
# make menuconfig
```
ビルドしたいLinux Kernelのバージョンを指定し、それに適したリソースを持ってくるようにする
```
Toolchain -> Custom kernel headers series -> 3.13.x
Kernel -> Kernel version -> 3.13.5
```
今回作成する環境に適する形式でかカーネルを出力する (要 出典 / 検証)
```
Kernel -> Install kernel image to /boot in target
```
rootユーザーのパスワードを設定する
```
System Configuration -> Root password
```
rootfsの設定を行う
```
Filesystem images -> cpio the root filesystem
ext2/3/4 filesystem -> ext2/3/4 variant -> ext4
```
## busybox
今回は権限昇格の脆弱性を検証する予定であるため、ユーザー権限でシェルを動かすためのアプレットを追加する
```
# make busybox-menuconfig
```
```
Shells -> cttyhack
Runit Utilities -> setuidgid
```
## Linux Kernel
そのままでも動くが、デバッグしやすいようにkgdbや各シンボルの保持を設定する
```
# make linux-menuconfig
```
```
Kernel hacking -> Kernel debugging
Kernel hacking -> Compile-time checks and compiler options -> Enable unused/obsolete exported symbols
Kernel hacking -> Compile-time checks and compiler options -> Compile the kernel with debug info
Kernel hacking -> Compile-time checks and compiler options -> Compile the kernel with frame pointers
Kernel hacking -> Compile-time checks and compiler options -> Generate readable assembler code
Kernel hacking -> KGDB: kernel debugger -> KGDB: use kgdb over the serial console
Kernel hacking -> KGDB: kernel debugger -> KGDB: internal test suite
Kernel hacking -> KGDB: kernel debugger -> KGDB: Allow debugging with traps in notifiers
Kernel hacking -> KGDB: kernel debugger -> KGDB_KDB: include kdb frontend for kgdb
```
デバッグに便利なgdb向けスクリプトを作ってくれる`Provide GDB scripts for kernel debugging`なるオプションがあるらしいが、このメモで使用しているカーネルはかなり古く実装されていないようなので今回は使用しない
参考: https://troushoo.blog.fc2.com/blog-entry-499.html
## Build
やや強めに祈ると失敗しないが、強すぎると変なところでコケる
職人の祈祷力が試される
```
# make BR2_JLEVEL=4
```
祈りが届いたら熱いうちに以下を持ってくる
```
# Kernel
output/build/linux-X.X.X/vmlinux
# Kernel (Compressed)
output/images/bzImage
# rootfs
output/images/rootfs.cpio
```
ソースコードを見ながらデバッグしたい場合は
```
# cd output/build/linux-X.X.X
# make clean
```
の後に
```
output/build/linux-X.X.X
```
をデバッグ環境の手元(後でgdbを走らせる)に置いておく
kernel.orgから直接持ってきてもいいかもしれない
参考: https://cdn.kernel.org/pub/linux/kernel/v3.x/linux-3.13.5.tar.xz
## rootfs
rootfsを展開して変更を加える
適当に展開用のディレクトリを作り、中で展開
```
$ mkdir rootfs && cd rootfs
$ sudo cpio -idv < ../rootfs.cpio
```
initd.dにスクリプトを記述してユーザー権限でのシェルを動かすようにする
```
$ sudo vim ./etc/init.d/S99shell
```
```shell!
#!/bin/sh
mdev -s
mount -t proc none /proc
mkdir -p /dev/pts
mount -vt devpts -o gid=4,mode=620 none /dev/pts
chmod 666 /dev/ptmx
stty -opost
uname -a
setsid cttyhack setuidgid 1000 sh
umount /proc
poweroff -d 0 -f
```
書き込んだら権限を調整してcpioに戻す
```
$ sudo chmod 775 ./etc/init.d/S99shell
$ sudo find . -print0 | sudo cpio -o --format=newc --null > ../rootfs.cpio
```
## Debug
Qemuの起動スクリプトを書く
```shell!
#!/bin/sh
qemu-system-x86_64 \
-m 64M \
-nographic \
-kernel bzImage \
-append "console=ttyS0 loglevel=3 oops=panic panic=-1 pti=on nokaslr" \
-no-reboot \
-cpu qemu64,+smap,+smep \
-smp 1 \
-monitor /dev/null \
-initrd rootfs_debug.cpio \
-gdb tcp::1234
```
KASLR(カーネル空間のランダマイズ機構)はオフにしないとkgdbを使用する際にdebugger側の想定しているアドレスとdebuggee側での実態のアドレスが異なり、エラーが発生するためデバッグの際は切っておく
(`-append nokaslr`の記述)
スクリプトを動かすとdebuggee側のQemuが動くため、それに接続するdebugger側も起動させる
```
$ gdb -q -ex 'target remote localhost:1234' ./vmlinux
```
あとは任意の関数にブレークポイントを設置するなりしてデバッグを楽しむ
余談だが、gdb extensionはLinux Kernel向けの機能が盛り込まれたbataさん作のgefのforkが個人的には一番のオススメ
参考: https://github.com/bata24/gef
