A Linux kernel development and exploitation lab.
The goal is to make it easy to build a kernel + BusyBox initramfs, boot it in QEMU, and iterate on LKMs/exploit dev with a working GDB setup.
You are using QEMU, but the hard part is reproducibly building a kernel + a bootable root filesystem and having a workflow that supports kernel debugging and fast iteration.
- Docker makes the build reproducible: kernel/BusyBox builds depend on toolchain and system packages; Docker pins the environment so it builds consistently across machines.
- This repo gives you a ready initramfs and boot config:
fs/+./launchproduce a bootable initramfs, mount your host$HOMEinto the guest, and set a sane kernel cmdline. - It is configured for debugging and research: QEMU can expose a GDB stub and lets you run with KASLR enabled or disabled, while the kernel build enables debug info/frame pointers to make debugging workable.
- It supports fast LKM iteration:
src/+./build_lkmbuilds.komodules against the same kernel tree and drops them into the initramfs so you can load them in the guest.
- Docker (with BuildKit enabled)
- QEMU (
qemu-system-x86_64) for launching the VM
Build the kernel and BusyBox inside a Docker environment:
./installDefaults:
- Kernel:
5.4 - BusyBox:
1.36.1
You can override versions:
./install --kernel 5.4 --busybox 1.36.1
# or
KERNEL_VERSION=5.4 BUSYBOX_VERSION=1.36.1 ./installIf signature verification fails in your environment (for example due to a proxy/mirror issue), you can skip kernel signature verification:
./install --no-verify
# or
VERIFY_KERNEL_SIG=0 ./installWarning: this disables integrity verification for the kernel tarball.
./install downloads sources into ./downloads/ and then runs a Docker build that produces:
linux-<version>.tar.gzbusybox-<version>.tar.gzfs/(initramfs staging tree)
After build, it'll result 2 release files in this directory, you should extract kernel by:
tar -zxvf linux-5.4.tar.gzRunning the kernel:
./launchNotes:
- Your host
$HOMEis mounted in the guest as/home/oops(via virtio-9p). - If you built LKMs and copied them into
fs/, they will show up in the guest as/*.ko. - KASLR toggle:
KASLR=0 ./launchdisables KASLR (default, addsnokaslr).KASLR=1 ./launchenables KASLR.
This repo supports two debugging styles:
- QEMU GDB stub (external): debugs the whole VM from the outside (works without any kernel KGDB setup).
- KGDB (in-kernel): kernel’s debugger stub, carried over a serial line (good if you specifically want the KGDB workflow).
By default ./launch exposes a GDB endpoint on :1234. In another terminal:
gdb ./linux-5.4/vmlinux
(gdb) target remote :1234Useful toggles:
GDB_PORT=1234 ./launch # change port
GDBWAIT=1 ./launch # QEMU starts paused until you attach (adds -S)
GDB=0 ./launch # disable the QEMU gdb stub
KASLR=0 ./launch # disable KASLR (default)
KASLR=1 ./launch # enable KASLRBasic test flow (recommended for first run):
- Start the VM paused:
GDBWAIT=1 ./launch- In another terminal, attach and break early:
gdb ./linux-5.4/vmlinux
(gdb) set pagination off
(gdb) target remote :1234
(gdb) hbreak start_kernel
(gdb) c- Verify debugging works:
(gdb) x/10i $rip
(gdb) info registers
(gdb) bt
(gdb) si
(gdb) niNote: avoid broad commands like info functions on kernel symbols; output is very large and can look like a hang.
This uses a second serial device (ttyS1) exposed over TCP (default :4444) and boots the kernel with kgdboc=ttyS1,115200.
Terminal 1:
KGDB=1 ./launchTerminal 2:
gdb ./linux-5.4/vmlinux
(gdb) target remote :4444Useful toggles:
KGDB_PORT=4444 KGDB=1 ./launch # change kgdb tcp port
KGDBWAIT=0 KGDB=1 ./launch # boot normally (no early kgdbwait)Note: you can expose both endpoints at the same time (QEMU gdb stub + KGDB), but a single gdb session can only be connected to one remote target at once. Pick the one you want to use for that session.
If src/example.c is built and available as /example.ko in guest:
(gdb) b log_init
(gdb) cThen in guest shell:
insmod /example.koGDB should break at log_init.
If you use pwndbg, it works fine on top of gdb for registers/stack/disasm/breakpoints. Some userland-oriented helpers (like vmmap) can be unreliable for kernel targets.
Put your own LKM module source files in src/ and build them:
./build_lkm
./launchInside the guest, load modules with:
insmod /example.ko
dmesg | tailIf your vulnerable driver sources are in Vulnerable-Drivers/Linux as multiple .c/.h files, copy them into src/ and build one combined module.
- Copy sources:
cp Vulnerable-Drivers/Linux/*.{c,h} src/- Use this
src/Makefilepattern:
KERNEL_VERSION ?= 5.4
obj-m += example.o
obj-m += hevd.o
hevd-objs := \
HackSysExtremeVulnerableDriver.o \
ArbitraryWrite.o \
BufferOverflowStack.o \
IntegerOverflow.o \
UninitializedMemoryStack.o
all:
make -C ../linux-$(KERNEL_VERSION) M=$(PWD) modules
clean:
make -C ../linux-$(KERNEL_VERSION) M=$(PWD) clean- Build and stage into initramfs:
cd src
make clean && make -j"$(nproc)"
cd ..
cp src/*.ko fs/- Boot and load:
./launchInside guest:
ls -l /*.ko
insmod /hevd.ko
dmesg | tail -n 100Expected: driver banner prints and HackSys Extreme Vulnerable Driver Loaded appears in dmesg.
Q: pwndbg's vmmap is broken when debugging the kernel ?
A: You should turning ptrace_scope level to 0 in your host machine by echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope.
