前言
最近想复现一个关于linux kernel的漏洞,无奈之前没有接触过内核调试,搭建环境还是废了一些功夫的,这里把我自己一路躺过来的坑记录一下分享给大家,如有不正确的,还请大佬不吝赐教^ ^
环境
qemu+gdb
Ubuntu16.04 架构:x86_64 内核:linux5.11.1 qemu:2.5.0 gdb:10.2 busybox:1.33.1
双机调试
debugging:ubuntu18.04 (调试端) debuggee:ubuntu18.04 (被调试端) 架构:x86_64 内核:linux5.11.1 gdb:8.0
qemu+gdb调试
编译内核
调试目标要清楚,要调试哪些内容,需要具备哪些条件,内核版本变化,然后编译内核,内和编译步骤如下(踩过坑之后的正确步骤)
- 确定以下依赖已经安装,可直接安装
make[2]: *** No rule to make target needed by ‘certs/x509_certificate_list’. Stop 内核目录下修改.config文件将下面这行注释掉CONFIG_SYSTEM_TRUSTED_KEYS="debian/certs/benh@debian.org.cert.pem" scripts/Makefile.host:90: recipe for target 'scripts/sign-file' failed 修改scripts 目录下的 Makefile文件
HOSTLDLIBS_sign-file = -lcrypto
HOSTLDLIBS_extract-cert = -lcrypto
改成
HOSTLDLIBS_sign-file = -L/usr/local/lib -lcrypto
HOSTLDLIBS_extract-cert = -L/usr/local/lib -lcrypto
之后编译应该没问题了,如有问题,按提示安装相应依赖:
- cd linux5.11.1
- make menuconfig 配置内核,打开或关闭你所需要的配置
- make -j4 (单独编译bzImage可执行 make bzImage -j4)
- 安装
make modules_install && make install
编译busybox
可以指定makefile中 ARCH=?、CROSS_COMPILE = ?来指定编译架构,默认为本机架构。
- cd busybox-1.28.0
- make menuconfig
Settings —> [*] Build static binary (no shared libs) - make && make install
编译完成后在busybox目录下会有_install目录:
-> ls _install
bin linuxrc sbin usr
制作根文件系统
可以编译好各个架构的文件系统(mips32、64的大小端,arm32、64的大小端,gdb、gdbserver)备用
busybox制作最小根文件系统
以下是脚本:
mkdir initramfs
cd initramfs
cp ../busybox-1.33.1/_install/* -rf ./
ln -s bin/busybox init
mkdir -pv bin sbin etc proc sys usr dev
mkdir usr/bin usr/sbin
cd etc
touch inittab
echo "::sysinit:/etc/init.d/rcS" >> inittab
echo "::askfirst:-/bin/sh" >> inittab
echo "::restart:/sbin/init" >> inittab
echo "::ctrlaltdel:/sbin/reboot" >> inittab
echo "::shutdown:/bin/umount -a -r" >> inittab
echo "::shutdown:/sbin/swapoff -a" >> inittab
chmod +x inittab
mkdir init.d
cd init.d
touch rcS
echo "#!/bin/sh" >> rcS
echo "mount proc" >> rcS
echo "mount -o remount,rw /" >> rcS
echo "mount -a" >> rcS
echo "clear " >> rcS
echo "echo 'My Tiny Linux Starting, press enter to active'" >> rcS
chmod +x rcS
cd ..
touch fstab
echo "proc /proc proc defaults 0 0" >> fstab
echo "sysfs /sys sysfs defaults 0 0" >> fstab
echo "devtmpfs /dev devtmpfs defaults 0 0" >> fstab
cd ..
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.img
这个是最基础的脚本,如果还需要什么依赖可以添加copy进去,busybox用的是ash,不支持bash,用的话需要把bash和他的依赖拷进去。
buildroot制作根文件系统
获取buildroot源码并设置配置文件
git clone git://git.buildroot.net/buildroot
cd buildroot
make menuconfig
在跳出的UI界面依次选择
Target options
Target Architecture (x86_64)
在x86_64 上按空格键,以选择该选项
- 再返回与Target options同一级的界面
选择Filesystem images
在ext2/3/4 root filesystem 按Y,并进入ext2/3/4 variant (ext4) 选择ext4
退出并保存配置.
编译buildroot
make -j4
编译完成之后,在buildroot/output/images/ 目录中可以获得文件系统镜像rootfs.ext2 和rootfs.ext4 参考 如果想要增加一些可执行文件,sudo mount rootfs.ext2 tmp/ 需要将编译好的文件和相应的依赖放进去,umount tmp/ 缺点编译时间太长。
启动调试
我们有2种文件系统,用哪一种都行.
第一种:使用rootfs.ext2作为文件系统运行qemu
qemu-system-x86_64 -kernel bzImage -boot c -m 1024 -hda rootfs.ext2 -append "root=/dev/sda rw console=ttyS0, 115200 acpi=off nokaslr" -serial stdio -display none -s -S
第二种:使用initramfs.img作为文件系统运行qemu
qemu-system-x86_64 -kernel bzImage -boot c -m 1024 -initrd initramfs.img -append "root=/dev/sda rw console=ttyS0, 115200 acpi=off nokaslr" -serial stdio -display none -s -S
如果需要调试内核的kvm模块,则向上述命令添加 -enable-kvm
gdb vmlinux
但是gdb调试还是没有成功,没有符号表,调试出问题。
Remote 'g' packet reply is too long: 4092d9b3ffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000203e80b4ffffffff203e80b4ffffffff00f72edc02000000e0d1c1c7cf97ffff0000000000000000fa5acc03690000000000000000000000802481b4ffffffff000000000000000000000000000000008695d9b3ffffffff4602000010000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007f030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffff000000ffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
我想要的效果是gdb可以看到反编译的源码进行调试。但目前还没有实现。 问题解决:这个错误是当目标程序执行时发生模式切换(real mode 16bit -> protected mode 32bit -> long mode 64bit) 的时候,gdb server (此处就是 qemu)发送了不同长度的信息,gdb 无法正确的处理这种情况,所以直接就报错。架构解析的问题,将架构设置成i386-x86_64:intel.问题会消失。参考 如果不是架构问题,就可能是gdb版本低了,编译一个最新版本的gdb。
双机调试
前提机器上已经安装好以上所需环境,编译过内核,具有bzImage、vmlinux。
配置双机
debuggee(被调试端)
双机调试主要是虚拟机通过串口进行调试,在debuggee的虚拟机设置上添加串口硬件,使用命令管道一栏填入//./pipe/com_1 (宿主机为win),/tmp/serial (宿主机为linux),选择该端是服务器,另一端是虚拟机。(网上都说该端是client端,我将配置改成客户端出现调试不了,换成服务端就行了) 进入虚拟机,修改/etc/grub.d/40_custom 文件,文件内容参考/boot/grub/grub.cfg ,将里面你要调试的内核的menuentry复制出来,填入/etc/grub.d/40_custom 文件,只需要添加kgdbwait kgdb8250=io,03f8,ttyS0,115200,4 kgdboc=ttyS0,115200 kgdbcon nokaslr
#!/bin/sh
exec tail -n +3 $0
menuentry 'Ubuntu, KGDB with nokaslr' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-bf306d0a-28c8-49c6-bffc-446be272ddcf' {
recordfail
load_video
gfxmode $linux_gfx_mode
insmod gzio
if [ x$grub_platform = xxen ]; then insmod xzio; insmod lzopio; fi
insmod part_msdos
insmod ext2
set root='hd0,msdos1'
if [ x$feature_platform_search_hint = xy ]; then
search --no-floppy --fs-uuid --set=root --hint-bios=hd0,msdos1 --hint-efi=hd0,msdos1 --hint-baremetal=ahci0,msdos1 bf306d0a-28c8-49c6-bffc-446be272ddcf
else
search --no-floppy --fs-uuid --set=root bf306d0a-28c8-49c6-bffc-446be272ddcf
fi
echo 'Loading Linux 4.10.0-19 with KGDB built by GEDU lab...'
linux /boot/vmlinuz-4.10.0-19-generic root=UUID=bf306d0a-28c8-49c6-bffc-446be272ddcf ro find_preseed=/preseed.cfg auto noprompt priority=critical locale=en_US quiet kgdbwait kgdb8250=io,03f8,ttyS0,115200,4 kgdboc=ttyS0,115200 kgdbcon nokaslr
echo 'Loading initial ramdisk ...'
initrd /boot/initrd.img-4.10.0-19-generic
}
修改完后,执行命令:sudo update-grub ,重启,一直按shift进入高级选项,选择最后一项Ubuntu, KGDB with nokaslr 进入kgdb等待另一端gdb的连接
debugging
该虚拟机可由配置前的debuggee虚拟机复制过来,和debuggee一样,设置串口,使用命令管道一栏填入//./pipe/com_1 (宿主机为win),/tmp/serial (宿主机为linux),选择该端是客户端,另一端是虚拟机。 然后启动虚拟机,正常使用gdb连接
调试配置
debuggee
双机调试比qemu慢了许多许多,单步调试很可能出现解析出错,我每次调试都只直接下断点,尽量少用单步。 当debuggee进入kgdb等待时,debugging使用gdb连接 vmlinux默认使用绝对路径搜索源码,如果了你在其他虚拟机调试可以使用set substitute-path /home/yrl/linux-5.11.1 /root/linuxkernel/linux-5.11.1 修改源码路径,第一个path是vmlinux中原有路径,第二个是替换的路径
sudo gdb -s vmlinux
set architecture i386:x86-64:intel
gef config context.layout "-legend -regs -stack code -args source -memory -threads trace -extra"
target remote /dev/ttyS0
c
至此debuggee端已经进入系统,debugging是gdb continuing状态,此时就需要使用sysrq来使debugging的gdb断下,我们就可以在执行exp调试前下断点
su
echo g > "/proc/sysrq-trigger"
./exp
|