This post is for my future self, but maybe it will help you to. I will outline the instructions for building a kernel, running it in qemu, and debugging with GDB. The same info is at this github repo which is probably a better format for this info: https://github.com/w0ot-net/KernelBuildingAndDebug
Build the kernel
First use this script to download and build the kernel. Make sure to update KERNEL_VERSION
to your target kernel version:
########################################################
# - Which kernel version to download
# - need to update KERNEL_VERSION
########################################################
export KERNEL_VERSION=5.15.102
export KERNEL_LINK=https://cdn.kernel.org/pub/linux/kernel/v${KERNEL_VERSION:0:1}.x/linux-$KERNEL_VERSION.tar.gz
########################################################
# Install packages needed to build/run the custom kernel
########################################################
# - debootstrap -- Debootstrap is a tool used in Debian-based Linux distributions
# to install a basic Debian or Ubuntu system into a target directory
apt update -y
apt install -y debootstrap qemu qemu-system-x86 git build-essential qemu-system-x86
########################################################
# Download the kernel sources
########################################################
wget $KERNEL_LINK
tar xfvz linux-$KERNEL_VERSION.tar.gz
########################################################
# Build the kernel
########################################################
# - The make defconfig command generates a default configuration file named .config
# in the current directory that specifies the default values for all the configuration options
# - The first sed command disables KASLR in the config, this is good for debugging/exploit dev;
# it can be re-enabled when testing production exploit
# - CONFIG_DEBUG_KERNEL seems to already be set to `y` but if not be sure to enable it;
# we definitely want debugging symbols
cd linux-$KERNEL_VERSION
make defconfig
sed -i 's/CONFIG_RANDOMIZE_BASE=y/CONFIG_RANDOMIZE_BASE=n/g' .config
make -j`nproc`
cd ..
########################################################
# Make sure build succeeded
########################################################
file linux-$KERNEL_VERSION/vmlinux | grep 'not stripped' && echo '[+] we got a debug kernel!'
ls linux-$KERNEL_VERSION/arch/x86/boot/bzImage 2>&1 > /dev/null && '[+] echo got a bzImage'
Create the OS Image
Next, use this slightly modified syzkaller script to create an OS image. Make sure to update the RELEASE
variable to your target Ubuntu version. Also, run this script in a folder adjacent to the kernel sources, I named folder ubuntu-image
:
#!/usr/bin/env bash
# Copyright 2016 syzkaller project authors. All rights reserved.
# Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
# create-image.sh creates a minimal Debian Linux image suitable for syzkaller.
# -eux means `exit on error`, `error on undefined variable`, `xtrace (print each command to terminal)`
set -eux
# Create a minimal Debian distribution in a directory.
DIR=chroot
PREINSTALL_PKGS=openssh-server,curl,tar,gcc,libc6-dev,time,strace,sudo,less,psmisc,net-tools
# If ADD_PACKAGE is not defined as an external environment variable, use our default packages
if [ -z ${ADD_PACKAGE+x} ]; then
ADD_PACKAGE="make,sysbench,git,vim,tmux,usbutils,tcpdump"
fi
# Variables affected by options
# http://archive.ubuntu.com/ubuntu/
ARCH=$(uname -m)
RELEASE=focal # (UPDATE THIS!! focal, xenial, etc.)
FEATURE=minimal
SEEK=2047
PERF=false
# Display help function
display_help() {
echo "Usage: $0 [option...] " >&2
echo
echo " -a, --arch Set architecture"
echo " -d, --distribution Set on which debian distribution to create"
echo " -f, --feature Check what packages to install in the image, options are minimal, full"
echo " -s, --seek Image size (MB), default 2048 (2G)"
echo " -h, --help Display help message"
echo " -p, --add-perf Add perf support with this option enabled. Please set envrionment variable \$KERNEL at first"
echo
}
while true; do
if [ $# -eq 0 ];then
echo $#
break
fi
case "$1" in
-h | --help)
display_help
exit 0
;;
-a | --arch)
ARCH=$2
shift 2
;;
-d | --distribution)
RELEASE=$2
shift 2
;;
-f | --feature)
FEATURE=$2
shift 2
;;
-s | --seek)
SEEK=$(($2 - 1))
shift 2
;;
-p | --add-perf)
PERF=true
shift 1
;;
-*)
echo "Error: Unknown option: $1" >&2
exit 1
;;
*) # No more options
break
;;
esac
done
# Handle cases where qemu and Debian use different arch names
case "$ARCH" in
ppc64le)
DEBARCH=ppc64el
;;
aarch64)
DEBARCH=arm64
;;
arm)
DEBARCH=armel
;;
x86_64)
DEBARCH=amd64
;;
*)
DEBARCH=$ARCH
;;
esac
# Foreign architecture
FOREIGN=false
if [ $ARCH != $(uname -m) ]; then
# i386 on an x86_64 host is exempted, as we can run i386 binaries natively
if [ $ARCH != "i386" -o $(uname -m) != "x86_64" ]; then
FOREIGN=true
fi
fi
if [ $FOREIGN = "true" ]; then
# Check for according qemu static binary
if ! which qemu-$ARCH-static; then
echo "Please install qemu static binary for architecture $ARCH (package 'qemu-user-static' on Debian/Ubuntu/Fedora)"
exit 1
fi
# Check for according binfmt entry
if [ ! -r /proc/sys/fs/binfmt_misc/qemu-$ARCH ]; then
echo "binfmt entry /proc/sys/fs/binfmt_misc/qemu-$ARCH does not exist"
exit 1
fi
fi
# Double check KERNEL when PERF is enabled
if [ $PERF = "true" ] && [ -z ${KERNEL+x} ]; then
echo "Please set KERNEL environment variable when PERF is enabled"
exit 1
fi
# If full feature is chosen, install more packages
if [ $FEATURE = "full" ]; then
PREINSTALL_PKGS=$PREINSTALL_PKGS","$ADD_PACKAGE
fi
sudo rm -rf $DIR
sudo mkdir -p $DIR
sudo chmod 0755 $DIR
# 1. debootstrap stage
DEBOOTSTRAP_PARAMS="--arch=$DEBARCH --include=$PREINSTALL_PKGS --components=main,contrib,non-free $RELEASE $DIR"
if [ $FOREIGN = "true" ]; then
DEBOOTSTRAP_PARAMS="--foreign $DEBOOTSTRAP_PARAMS"
fi
sudo debootstrap $DEBOOTSTRAP_PARAMS http://archive.ubuntu.com/ubuntu/
# 2. debootstrap stage: only necessary if target != host architecture
if [ $FOREIGN = "true" ]; then
sudo cp $(which qemu-$ARCH-static) $DIR/$(which qemu-$ARCH-static)
sudo chroot $DIR /bin/bash -c "/debootstrap/debootstrap --second-stage"
fi
# Set some defaults and enable promtless ssh to the machine for root.
sudo sed -i '/^root/ { s/:x:/::/ }' $DIR/etc/passwd
echo 'T0:23:respawn:/sbin/getty -L ttyS0 115200 vt100' | sudo tee -a $DIR/etc/inittab
#printf '\nauto eth0\niface eth0 inet dhcp\n' | sudo tee -a $DIR/etc/network/interfaces
printf 'network:\n version: 2\n ethernets:\n eth0:\n dhcp4: true' | sudo tee -a $DIR/etc/netplan/eth0.yaml
echo '/dev/root / ext4 defaults 0 0' | sudo tee -a $DIR/etc/fstab
echo 'debugfs /sys/kernel/debug debugfs defaults 0 0' | sudo tee -a $DIR/etc/fstab
echo 'securityfs /sys/kernel/security securityfs defaults 0 0' | sudo tee -a $DIR/etc/fstab
echo 'configfs /sys/kernel/config/ configfs defaults 0 0' | sudo tee -a $DIR/etc/fstab
echo 'binfmt_misc /proc/sys/fs/binfmt_misc binfmt_misc defaults 0 0' | sudo tee -a $DIR/etc/fstab
echo -en "127.0.0.1\tlocalhost\n" | sudo tee $DIR/etc/hosts
echo "nameserver 8.8.8.8" | sudo tee -a $DIR/etc/resolve.conf
echo "ngunibantu" | sudo tee $DIR/etc/hostname
ssh-keygen -f $RELEASE.id_rsa -t rsa -N ''
sudo mkdir -p $DIR/root/.ssh/
cat $RELEASE.id_rsa.pub | sudo tee $DIR/root/.ssh/authorized_keys
# Add perf support
if [ $PERF = "true" ]; then
cp -r $KERNEL $DIR/tmp/
BASENAME=$(basename $KERNEL)
sudo chroot $DIR /bin/bash -c "apt-get update; apt-get install -y flex bison python-dev libelf-dev libunwind8-dev libaudit-dev libslang2-dev libperl-dev binutils-dev liblzma-dev libnuma-dev"
sudo chroot $DIR /bin/bash -c "cd /tmp/$BASENAME/tools/perf/; make"
sudo chroot $DIR /bin/bash -c "cp /tmp/$BASENAME/tools/perf/perf /usr/bin/"
rm -r $DIR/tmp/$BASENAME
fi
# Add udev rules for custom drivers.
# Create a /dev/vim2m symlink for the device managed by the vim2m driver
echo 'ATTR{name}=="vim2m", SYMLINK+="vim2m"' | sudo tee -a $DIR/etc/udev/rules.d/50-udev-default.rules
# Build a disk image
dd if=/dev/zero of=$RELEASE.img bs=1M seek=$SEEK count=1
sudo mkfs.ext4 -F $RELEASE.img
sudo mkdir -p /mnt/$DIR
sudo mount -o loop $RELEASE.img /mnt/$DIR
sudo cp -a $DIR/. /mnt/$DIR/.
sudo umount /mnt/$DIR
sudo rm -rf /mnt/$DIR
Make a Wrapper Script to Launch the QEMU VM
export KERNEL_VERSION="5.15.102"
export IMAGE="ubuntu-image"
# -m -- sets the amount of memory for the vm
# -s -- enables kernel debugging
# -smp -- sets the number of processors (1 may be ideal for kernel debugging)
# -kerenl -- path to the kernl
# -append -- boot options
# -drive -- path to the OS image
# -net -- set up port forwarding for ssh
# -pidfile -- Store the QEMU process PID in file
qemu-system-x86_64 \
-m 2G \
-s \
-smp 1 \
-kernel linux-$KERNEL_VERSION/arch/x86/boot/bzImage \
-append "console=ttyS0 root=/dev/sda earlyprintk=serial net.ifnames=0" \
-drive file=$IMAGE/focal.img,format=raw \
-net user,host=10.0.2.10,hostfwd=tcp:127.0.0.1:10021-:22 \
-net nic,model=e1000 \
-nographic \
-pidfile vm.pid \
2>&1 | tee vm.log
Make a Wrapper Script to Connect to SSH
IMAGE="ubuntu-image"
ssh -i $IMAGE/focal.id_rsa -p 10021 -o "StrictHostKeyChecking no" root@localhost
Install GEF and Update .gdbinit File
bash -c "$(curl -fsSL https://gef.blah.cat/sh)"
file /path/to/your/vmlinux
target remote localhost:1234
References:
– https://www.josehu.com/memo/2021/01/02/linux-kernel-build-debug.html
– https://github.com/google/syzkaller/blob/master/tools/create-image.sh
– greetz to @fabiusartrel