kernel环境配置

入门写的一些笔记,此文绝对会有很多问题(希望大佬指出 :V

关于编译内核

下载kernel源代码:https://www.kernel.org/

随意选个版本,比如4.15.0
https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/linux-4.15.tar.gz

安装些必要的依赖

1
2
sudo apt-get update
sudo apt-get install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc

解压源码后进目录

1
make menuconfig

基本不需要改什么,直接save。

sakura的说法是

1
2
3
4
5
6
进入kernel hacking
勾选以下项目
Kernel debugging
Compile-time checks and compiler options —> Compile the kernel with debug info和Compile the kernel with frame pointers
KGDB
然后保存退出

不过貌似这些默认都是已选的。

1
make bzImage

玩几局游戏以后能看到如下信息就是编译OK了。

1
2
3
4
Setup is 17244 bytes (padded to 17408 bytes).
System is 7666 kB
CRC 5c77cbfe
Kernel: arch/x86/boot/bzImage is ready (#1)

./arch/x86/boot/拿到bzImage,从源码根目录拿到vmlinux

关于添加syscall

添加一个helloworld的syscall做示例。

以4.15.0版本的kernel为例子。

源码根目录创建helloworld目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# veritas @ ubuntu in ~/sources/linux-mod-4.15/helloworld [13:56:08] 
$ tree
.
├── helloworld.c
└── Makefile

0 directories, 2 files

# veritas @ ubuntu in ~/sources/linux-mod-4.15/helloworld [13:56:14]
$ cat helloworld.c
#include <linux/kernel.h>

asmlinkage long sys_helloworld(void){
printk("{==kernel==} hello world\n");
return 0;
}
# veritas @ ubuntu in ~/sources/linux-mod-4.15/helloworld [13:56:19]
$ cat Makefile
obj-y=helloworld.o

编辑源码根目录下的Makefile,添加helloworld/

编辑include/linux/syscalls.h,添加函数原型

编辑arch/x86/entry/syscalls/syscall_32.tblarch/x86/entry/syscalls/syscall_64.tbl,添加系统调用号

然后编译kernel。

1
make bzImage

./arch/x86/boot/拿到bzImage

编译busybox

上官网下载源代码编译https://busybox.net/

以1.28.4为例。http://busybox.net/downloads/busybox-1.28.4.tar.bz2

解压后到根目录

1
make menuconfig

进Settings,勾上Build static binary (no shared libs)

make install -j4

编译完成后跟目录多了一个_install的目录,就是我们编译的结果了。

1
2
3
4
5
cd _install
mkdir proc
mkdir sys
touch init
chmod +x init

其中init中添加如下内容

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/sh
echo "{==DBG==} INIT SCRIPT"
mkdir /tmp
mount -t proc none /proc
mount -t sysfs none /sys
mount -t debugfs none /sys/kernel/debug
mount -t tmpfs none /tmp
# insmod /xxx.ko # load ko
mdev -s # We need this to find /dev/sda later
echo -e "{==DBG==} Boot took $(cut -d' ' -f1 /proc/uptime) seconds"
setsid /bin/cttyhack setuidgid 1000 /bin/sh #normal user
# exec /bin/sh #root

可以写这样一个脚本来打包rootfs。

1
2
3
4
5
#!/bin/sh
echo "Generate rootfs.img"
cd busybox # fs folder
find . | cpio -o --format=newc > ../rootfs.img

##启动qemu

通过上面两步,我们得到了含有helloworld syscall的kernel bzImage和用busybox打包的fs。

接下来只要用qemu启动就ok了。

在这之前,可以先写一个测试程序来测试我们写的syscall。

1
2
3
4
5
6
7
//gcc test.c -static -o test
#include <unistd.h>

int main(void){
syscall(1337);
return 0;
}

放在fs目录下,重新打包得到新的rootfs.img。

可以写一个脚本来启动qemu。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/sh
qemu-system-x86_64 \
-m 64M \
-kernel ./vmlinuz-4.15.0-22-generic \
-initrd ./rootfs.img \
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 kalsr" \
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
-nographic \
-monitor /dev/null \
-smp cores=2,threads=1 \
-enable-kvm \
-cpu kvm64,+smep \
# -gdb tcp::1234 \
# -S

运行结果

1
2
3
4
5
6
/ $ ls
bin etc linuxrc root sys test.c usr
dev init proc sbin test tmp
/ $ ./test
[ 7.920605] {==kernel==} hello world
/ $

##加载ko

ko的载入很简单,只需要

1
$ insmod xxx.ko

但ko需要指定的kernel版本才能正常载入。

例如0CTF final的baby kernel,他要求的kernel版本为4.15.0-22-generic SMP mod_unload

可以使用apt download 相应内核的deb包,然后解包得到bzImage。

1
$ apt download linux-image-4.15.0-22-generic

修改fs的init脚本,加入insmod xxxx.ko即可。

载入系统后可以使用lsmod来查看载入的ko以及他的所在的内核地址

##调试ko

一般来说加nokaslr把kaslr关了调试起来会方便一些。

把启动脚本的最后两行注释取消,则qemu在启动后会等待调试器的连接。

写这样一个脚本来快速连接

1
2
3
4
5
6
7
8
#!/bin/sh
gdb \
-ex "target remote localhost:1234" \
-ex "continue" \
-ex "disconnect" \
-ex "set architecture i386:x86-64:intel" \
-ex "target remote localhost:1234" \
-ex "add-symbol-file ./busybox/baby.ko 0xdeadbeef" \

Reference