最近脑子一抽决定看《深入理解 Linux 内核》,后面又抽多了几下想试试 kernel pwn,于是开始痛苦地配环境
目前姑且算是完成了第零步 甚至还不是第一步,过程磕磕绊绊 还留下一堆坑没解决,总之先记录一下 以免后面忘了

先摆上参考的文章

编译内核

先装环境依赖

sudo apt-get update
sudo apt-get install git fakeroot build-essential ncurses-dev xz-utils qemu flex libncurses5-dev libssl-dev bc bison libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev libelf-dev

编译内核

获取源码

Linux Kernel Archive 下载对应版本的内核源码
以 kernel-5.11 为例

wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.11.tar.xz

解压

tar -xvf linux-5.11.tar.xz

编译选项配置

进入源码文件夹,执行如下命令开始配置编译选项并进入配置页面

make menuconfig

配置页面通过上下键移动配置选项,左右键移动功能界面功能选项,y 键选定选项,空格或回车进入次级目录

勾选以下配置:

  • Kernel hacking -> Kernel debugging
  • Kernel hacking -> Compile-time checks and compiler options -> Compile the kernel with debug info
  • Kernel hacking -> Generic Kernel Debugging Instruments -> KGDB: kernel debugger
  • kernel hacking -> Compile the kernel with frame pointers(似乎没有)

也可以根据你的需求手动更改一些编译选项,此是后话

通常保存的路径在当前目录下的 .config 文件中,如果你在生成配置文件后才想起来忘了改某个选项也可以直接编辑这个文件

编译

make -j$(nproc) bzImage
  • 用 WSL 编译很慢,中途还崩溃了
  • 改用实验室的工作站编译就嘎嘎快,十分解压(?)

编译结果

出现如下信息则说明编译完成

Kernel: arch/x86/boot/bzImage is ready  (#1)

主要生成两个文件
当前目录下的 vmlinux 为编译出来的原始内核文件
/arch/x86/boot目录下的 bzImage 为压缩后的内核文件,适用于大内核

下载内核镜像

如果不想花太多时间配置和编译,也可以下载现成的内核镜像文件

搜索可用镜像文件(会比现有的版本滞后些)

sudo apt search linux-image- 

下载镜像文件,以 kernel-5.8.0 为例

sudo apt download linux-image-5.8.0-43-generic

使用系统内核镜像

还可以使用/boot目录下现成的系统内核镜像(原生 WSL 没有 kernel)

制作文件系统

BusyBox 是一个封装了 Linux 下大部分常用命令的软件,具体可以看 JYY 的 OS 课(关注 JYY 谢谢喵)
我们可以利用 BusyBox 为 kernel 制作一个基本的用户环境

获取源码

busybox.net 下载对应版本
busybox-1.33.0 为例

wget https://busybox.net/downloads/busybox-1.33.0.tar.bz2

解压

tar -jxvf busybox-1.33.0.tar.bz2

编译

进入配置界面配置编译选项

make menuconfig

勾选 Settings -> Build static binary file(no shared lib)来静态编译,以免后面还要配置 libc
取消选中Networking Utilities -> inetd

编译

make install

编译完成后生成一个_install目录,将会用它来构建磁盘镜像

构建文件系统

初始化目录文件

建立一些系统启动所需的目录和文件

cd _install
mkdir -pv {bin,sbin,etc,proc,sys,home,lib64,lib/x86_64-linux-gnu,usr/{bin,sbin}}
touch etc/inittab
mkdir etc/init.d
touch etc/init.d/rcS
chmod +x ./etc/init.d/rcS

配置初始化脚本

配置/etc/inittab,其指定了系统初始化脚本为/etc/init.d/rcS

::sysinit:/etc/init.d/rcS
::askfirst:/bin/ash
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/init

配置/etc/init.d/rcS

#!/bin/busybox sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev
mount -t tmpfs tmpfs /tmp
mkdir /dev/pts
mount -t devpts devpts /dev/pts

echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
setsid cttyhack setuidgid 1000 sh

poweroff -d 0  -f

也可以在根目录下创建init文件

touch init
chmod +x ./init
#!/bin/busybox sh

mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev

exec 0</dev/console
exec 1>/dev/console
exec 2>/dev/console

echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
setsid cttyhack setuidgid 1000 sh

umount /proc
umount /sys
poweroff -d 0  -f

配置用户组

echo "root:x:0:0:root:/root:/bin/sh" > etc/passwd
echo "ctf:x:1000:1000:ctf:/home/ctf:/bin/sh" >> etc/passwd
echo "root:x:0:" > etc/group
echo "ctf:x:1000:" >> etc/group
echo "none /dev/pts devpts gid=5,mode=620 0 0" > etc/fstab

配置 glibc

暂时用不上,还没弄

打包 cpio 镜像

find . | cpio -o --format=newc > /path/to/rootfs.cpio

QEMU 运行内核

配置启动脚本

将 bzImage 和 rootfs.cpio 放到同一个目录下

编写启动脚本

touch boot.sh
#!/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

部分参数说明如下:

  • -m:虚拟机内存大小
  • -kernel:内存镜像路径
  • -initrd:磁盘镜像路径
  • -append:附加参数选项
    • nokalsr:关闭内核地址随机化,方便我们进行调试
    • rdinit:指定初始启动进程,/sbin/init进程会默认以 /etc/init.d/rcS 作为启动脚本
    • loglevel=3&quiet:不输出 log(loglevel=8则可以看到比较完整的启动过程)
    • console=ttyS0:指定终端为/dev/ttyS0,这样一启动就能进入终端界面
  • -monitor:将监视器重定向到主机设备/dev/null,这里重定向至null主要是防止CTF中被人给偷了qemu拿flag
  • -cpu:设置CPU安全选项,在这里开启了smep保护
  • -s:相当于-gdb tcp::1234的简写(也可以直接这么写),后续我们可以通过gdb连接本地端口进行调试

最后运行脚本即可

emmm……至少我看别人是可以了,但我这死活不行

报错1:启动到clocksource: Switched to clocksource tsc就卡住不动了
解决办法:似乎是在配置 BusyBox 编译选项时取消选中Networking Utilities ->inetd后就好了,我也不确定

报错2:No such file or directory
解决办法:似乎是启动脚本的问题,在删掉/etc/inittab后就没再出现了,删除后/sbin/init也会默认选择/etc/init.d/rcS为启动脚本,所以似乎影响不大

报错3:can't open /dev/tty2 3 4: No such file or directory
解决办法:最神必的报错,虽然已经启动完成了但不停的报错十分影响我们正常使用,最后在根目录下的启动脚本init中加上几行touch /dev/tty2 3 4就好了

报错4:applet not found
解决办法:不影响使用,先不排查了

启动

./boot.sh

虽然解决报错的手段十分粗暴,而且很可能已经为后面埋下了隐患,但是也算是启动成功了,我哭死 TAT