对于linux内核exploit的调试,始终不得要领,终于挖到了muhe师傅和swing师傅的linux kernel beginners系列的几篇文章。后来了解到,最早是joker师傅指引,令我等后来人能够如是复盘,如获至宝。

Linux kernel beginners - syscall

这一篇主要讲环境的搭建与配置、syscall的添加与测试。内核版本选择2.6.32.1,采用qemu来调试内核,宿主机系统为Ubuntu 14.04 x86。

我在两个系统里尝试过,另一个是Ubuntu Kylin 14.04 x86。在Ubuntu 16.04 x64上由于内核版本过老,所以编译时问题太多而暂时没有继续下去。

环境搭建

第一步自然是需要获取内核源码,URL为:https://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.32.1.tar.gz

如果没有vpn或代理的话,可能下载速度很慢,由于我开了lantern vip,所以秒下,如果读者有需要的话,可以找我要一份。

解包:tar -zvxf linux-2.6.32.1.tar.gz

由于menuconfig需要libncurses5-dev,所以首先要安装libncurses5-dev,在ubuntu下可以:

sudo apt-get install libncurses5-dev

我在Ubuntu 14.04使用时,由于源的问题,存在依赖关系的问题,对此,可以通过如下方式尝试修正:

sudo apt-get -f install
sudo apt-get install --resinstall name=version

如果依赖耦合过度,则直接下载libncurses5-dev以及其依赖的libncurses5和ncurses5-bin。

此外,还需要qemu:

sudo apt-get install qemu qemu-system

这之后,可以进行对内核的编译:

make menuconfig
make 
make all
make modules

编译内核时,会有几个问题,这几个问题在swing的博文上都有说明,请参考附录链接。编译时产生的错误,都是因为新旧工具的兼容性问题,有兴趣的可以一一查阅。

增加syscall

对于内核来说,系统调用是尤为重要的,我们添加两个自定义的系统调用并测试一下。

对于linux内核来说,添加系统调用有3个位置需要处理:

  1. arch/x86/kernel/syscall_table_32.S 在系统调用表中添加序号索引

增加两个新的系统调用,序号顺延。

  1. arch/x86/include/asm/unistd_32.h 在unistd的32位库中添加系统调用序列号的宏

定义新系统调用宏,其值为在syscall_table.S中的索引号。注意要相应递增下面的NR_syscalls宏。

  1. include/linux/syscalls.h 在syscalls.h中给出系统调用的声明

声明这两个新的系统调用。

  1. 定义自己的系统调用,修改主Makefile,在linux根目录下增加r00tk1t_test目录,建r00tk1t_test.c文件以及Makefile:

重新Make内核。

busybox编译

使用busybox是为了方便的添加文件到文件系统中,这东西大名鼎鼎,不知道的自己百度。

下载busybox-1.22.1:https://busybox.net/downloads/busybox-1.22.1.tar.bz2。老规矩:

make menuconfig
make
make install

在menuconfig中勾选上静态库,避免后续的各种动态库缺失的问题:

此后,对busybox的文件系统做些配置。

$ cd _install
$ mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin}}
$ cat init
#!/bin/sh
echo "INIT SCRIPT"
mount -t proc none /proc
mount -t sysfs none /sys
mount -t debugfs none /sys/kernel/debug
mkdir /tmp
mount -t tmpfs none /tmp
mdev -s # We need this to find /dev/sda later
echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
exec /bin/sh
$ chmod +x init
$ find . -print0 \
    | cpio --null -ov --format=newc \
    | gzip -9 > /tmp/initramfs-busybox-x86.cpio.gz
$ qemu-system-i386 -kernel arch/i386/boot/bzImage -initrd /tmp/initramfs-busybox-x86.cpio.gz

此时就可以用生成的文件系统测试linux内核了:qemu-system-i386 -kernel arch/i386/boot/bzImage -initrd /tmp/initramfs-busybox-x86.cpio.gz

测试syscall

编写一个二进制程序,测试syscall:

编译时要加-static参数静态编译,依然是绕开各种的动态库缺失问题。gcc r00tk1t.c -o r00tk1t -static

将编译好的二进制r00tk1t拷贝到busybox下的_install/usr/下,然后重新生成文件系统,依然是:

$ find . -print0 \
    | cpio --null -ov --format=newc \
    | gzip -9 > /tmp/initramfs-busybox-x86.cpio.gz

注意,每次有了文件系统的改动,initramfs-busybox-x86.cpio.gz都需要重新生成,并用新的文件系统重启qemu。

启动qemu后就可以执行usr下的r00tk1t了。

qemu出了点毛病,待续。。