TL;DR

0x00 Prologue

I am not familiar with linux kernel so it’s hard to tell the size of a structure when playing the challenges. Some of the structures would have different sizes in different versions. It’s painful to remember all of them. Thus, I decided to compile a kernel module to print the size of the structs when it’s needed.

To compile a kernel module and run it on a specific linux kernel, we need:

I generate this cheatsheet according to CTF-Wiki

0x01 Compile Linux Kernel

First, we should know the kernel version we want to compile. Normally, we can just run the challenge and run uname -a to get the kernel version and go to this website to download the same version of the kernel.

wget https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.14.16.tar.gz
tar xvf ./linux-5.14.16.tar.gz
cd linux-5.14.16/
make menuconfig
make -j`nproc`

Then, we need to config it and compile it with the above commands. To make compiling faster, I only selected “Enable loadable module support”.

(Optional) If you want to debug the kernel with symbols you can also select “Kernel Hacking -> Compile-time checks and compiler options -> Compile the kernel with debug info”

After compiling the kernel, you can find the bzimage file at arch/x86/boot/bzImage.

0x02 Compile Debug Kernel Module

To compile a kernel module, we need the source code of the module and the make file as follows.

The template of kernel module source code(n132.c):

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/msg.h>
MODULE_LICENSE("Dual BSD/GPL");
static int ko_n132_init(void)
{
    struct msg_msg p;
    printk("n132>\n%d\n",sizeof(p));
    return 0;
}
static void ko_n132_exit(void)
{
    printk("Bye Bye~\n");
}
module_init(ko_n132_init);
module_exit(ko_n132_exit);

Makefile:

obj-m += ko_n132.o

KDIR =/root/KCP/linux-5.14.16/
all:
        $(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
        rm -rf *.o *.ko *.mod.* *.symvers *.order

After executing make, you’ll get the kernel module ko_n132.ko.

0x03 Build FileSystem

To build a file system, we need to config and compile busybox by following commands. Also, if you don’t want to prepare shared libraries, you can select Settings-> Build Options-> Build static binary (no shared libs)

wget wget https://busybox.net/downloads/busybox-1.32.1.tar.bz2
tar xvf ./busybox-1.32.1.tar.bz2
cd busybox-1.32.1
make menuconfig
make -j`nproc`
make install
cd _install
mkdir -p proc sys dev etc
touch init
chmod +x init

The following script is a template for /init file.

#!/bin/sh
echo "[---------------[n132]---------------]"
mkdir /tmp
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
mount -t debugfs none /sys/kernel/debug
mount -t tmpfs none /tmp
echo -e "Boot took $(cut -d' ' -f1 /proc/uptime) seconds"
setsid /bin/cttyhack setuidgid 0 /bin/sh
poweroff -f

Then, we can move the kernel module we compiled to the file system and pack it with cpio:

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

0x03 Qemu Script

Run the following template you’ll get a shell of your compiled linux kernel.

qemu-system-x86_64 \
  -m 2G \
  -initrd  ./rootfs.cpio \
  -kernel ./bzImage -nographic \
  -monitor /dev/null -s \
  -append "kpti=1 +smep +smap nokaslr root=/dev/ram rw console=ttyS0 oops=panic panic=0 init=/init quiet"

After getting the shell, you can run /sbin/insmod ko_n132.ko && dmesg to check the size of struct msg_msg.

0x04 pahole

I got a tool to get all the structures’ size while reading other’s article: https://manpages.ubuntu.com/manpages/impish/man1/pahole.1.html.

You can install and use it by

apt install pahole
pahole < bzImage > SS

By default, it’s shows x64 version of result and you can also set --header elf32_hdr to get the result of x32 version.

0x03 Epilogue

This is a cheat sheet for kernel module compilation.