/linux-exploit-dev-env

Everything you need to build and run Linux and Android kernels for exploit development

Primary LanguageMakefile

Linux Exploit Development Environment

Quickstart

The only supported host OS is Ubuntu 22.04, things may or may not work on any other system.

First install the dependencies and clone the project:

sudo apt update
sudo apt install -y bc bison build-essential flex git libelf-dev libssl-dev ncurses-dev gdb gdb-multiarch qemu qemu-system-x86 qemu-system-arm qemu-user-static binfmt-support llvm clang clang-tools lld lz4 binutils-aarch64-linux-gnu gcc-aarch64-linux-gnu pahole dwarves
git clone --recursive https://github.com/gsingh93/linux-exploit-dev-env
cd linux-exploit-dev-env

This will clone the Linux kernel and Android Common Kernel (ACK) submodules. You can checkout any version of the kernel you want, i.e.

VERSION=5.10.107 make linux_checkout # Checkout a specific version
VERSION=5.10.y make linux_checkout # Checkout the latest version of an LTS kernel

Create the default rootfs images:

make rootfs-init

Build and run the Linux kernel:

make
make run

To exit QEMU, use ctrl+a x (ctrl-c will not work).

To attach with GDB, set the GDB environment variable before running:

GDB=1 make run

And then in another terminal, attach with:

scripts/gdb.sh

The default architecture is x86_64, but arm64 is also supported:

ARCH=arm64 make rootfs-init
ARCH=arm64 make run

All Targets and Options

$ make help
General Environment Variables:
   ARCH                             - Specify one of the supported architectures: x86_64, arm64 (default: x86_64)
   ACK                              - Set to 1 if building ACK instead of the Linux kernel. Does not need to be set for `ack` and `run-ack` targets (default: 0)
   VERBOSE                          - Set to 1 to enable verbose output (default: 0)

Build/Config:
   Targets:
      linux (default)               - Builds the Linux kernel
      linux_defconfig               - Builds the Linux kernel
      linux_modules                 - Builds the Linux kernel modules
      ack                           - Builds the Android Common Kernel
      tools-vm                      - Builds linux/tools/vm

   Environment Variables:
      LINUX_DEFCONFIG               - The defconfig to use when building the kernel (default: defconfig, ACK default: gki_defconfig)
      LINUX_SRC                     - The path to the kernel source directory (default: linux, ACK default: ack/common)
      LINUX_OUT                     - The path where the kernel build output should be stored (default: out/linux/$ARCH, ACK default: out/ack/common/$ARCH)
      LINUX_CONFIG_FRAGMENT         - A kernel config fragment to merge with the defconfig (default: config/config.fragment, ACK default: config/config.fragment)

Clean:
   Targets:
      clean                         - Cleans output from default build targets
      <target>_clean                - Cleans output for <target>

Run/Debug:
   Targets:
      run                           - Run QEMU with the built kernel, bootloader, and rootfs image
      run-ack                       - Same as `run` but runs ACK instead

   Environment Variables:
      GDB                           - Set to 1 to start a gdbserver and wait for GDB when running QEMU (default: 0)
      CPU                           - Specify the number of CPUs to use when running QEMU (default: 4)
      MEM                           - Specify the memory size in MB to use when running QEMU (default: 1024)
      QEMU_EXTRA_ARGS               - Specify additional arguments to pass to QEMU (default: "")
      QEMU_EXTRA_KERNEL_CMDLINE     - Specify additional arguments to pass to the kernel (default: "")
      KERNEL_IMAGE                  - The path to the kernel image to run (x86_64 default: $LINUX_OUT/arch/$ARCH/boot/bzImage, arm64 default: $LINUX_OUT/arch/$ARCH/boot/Image)
      ROOTFS                        - The path to the rootfs image file (default: rootfs/rootfs-$ARCH.img)
      ROOTFS_FORMAT                 - The format of the rootfs image file (default: qcow2)
      INITRD                        - The path to a gziped initramfs CPIO file to use instead of a rootfs image (default: "")
      RDINIT                        - The value of the `rdinit` kernel command line paramter (default: "", default if INITRD is set: /sbin/init)

rootfs:
   Targets:
      rootfs-init                   - Extracts the Alpine Linux rootfs to rootfs/alpine-$ARCH, makes arch-specific changes, and builds a rootfs image at rootfs/alpine-$ARCH.img
      rootfs                        - An alias for the `ext4` target
      ext4                          - Builds a QCOW2 rootfs image at rootfs/alpine-$ARCH.img with an ext4 filesystem
      cpio                          - Builds a gziped initramfs CPIO file from the $INITRAMFS_DIR directory
      rootfs-mount                  - Mount rootfs image at /tmp/rootfs
      rootfs-unmount                - Unmount rootfs image

   Environment Variables:
      EXT4_SIZE                     - The disk size of the rootfs image to build
      INITRAMFS_DIR                 - The directory to create the initramfs from (default: rootfs/alpine-$ARCH)
      ROOTFS_DIR                    - The directory to create the ext4/rootfs image from (default: rootfs/alpine-$ARCH)
      ROOTFS                        - The path to the rootfs image file (default: rootfs/rootfs-$ARCH.img)
      ROOTFS_FORMAT                 - The format of the rootfs image file (default: qcow2)

Miscellaneous:
   Targets:
      linux_download                - Downloads an archive of the Linux kernel source for the version specified in $VERSION
      linux_checkout                - Checks out the version specified by $VERSION of the linux kernel in $LINUX_SRC

   Environment Variables:
      VERSION                       - The version to download or checkout. If the patch version is a 'y', the latest version of the kernel with that major and minor version is used. Examples: 5.10, 5.10.107, v5.10, 5.10.y, linux-5.10.y, android13-5.10

Building

You can customize the path to the linux source and output directories with the LINUX_SRC and LINUX_OUT environment variables:

LINUX_SRC=/path/to/src LINUX_OUT=/path/to/out make linux

Note that the default output directory is out/$KERNEL_DIR/$ARCH, which means that switching between architecture or kernel types (i.e. ACK or Linux) will not overwrite builds of another architecture or kernel type.

Android Common Kernel (ACK)

Instead of the Linux kernel, you can set the ACK variable to build the Android Common Kernel:

ACK=1 make linux
ACK=1 make run

defconfig

To generate the default kernel config with the default defconfig, run:

make linux_defconfig

The ARCH and ACK variables can also be used here:

ACK=1 ARCH=arm64 make linux_defconfig

If ACK is set gki_defconfig is used instead of defconfig. By default, config/config.fragment is merged into the generated config to create the final kernel config. This can be customized by setting LINUX_CONFIG_FRAGMENT.

rootfs

The default rootfs is based on Alpine Linux's mini root filesystem. make rootfs-init will automatically extract the file system to rootfs/alpine-$ARCH and create the filesystem image at rootfs/rootfs-$ARCH.img. You can modify the file system by modifying the files in rootfs/alpine-$ARCH and then running make rootfs to regenerate the image. If you have another directory where you keep your rootfs, or you'd like to customize where the output image is stored, you can use the ROOTFS_DIR and ROOTFS variables:

ROOTFS_DIR=/path/to/rootfs/dir ROOTFS=/path/to/output/rootfs.img ROOTFS_FORMAT=qcow2 make rootfs

If you'd like to use an initramfs instead of a disk image, you can use make cpio, which will create rootfs/alpine-$ARCH.cpio.gz by default. To build an initramfs from a different directory, use the INITRAMFS_DIR variable:

INITRAMFS_DIR=/path/to/initramfs/dir make cpio

Remember to set the correct ARCH variable for these commands if you are working with an architecture other than x86_64.

Running

When running a kernel, you can specify an alternative rootfs or initramfs (but not both) with the ROOTFS and INITRD variables:

ROOTFS=/path/to/rootfs-qcow2.img make run
ROOTFS=/path/to/rootfs-raw.img ROOTFS_FORMAT=raw make run
INITRD=/path/to/initramfs.cpio.gz make run

The number of CPUs and the amount of memory can be configured with the CPU and MEM variables. You can pass additional arguments to QEMU or the kernel command line with QEMU_EXTRA_ARGS and QEMU_EXTRA_KERNEL_CMDLINE, respectively:

CPU=4 MEM=2048M QEMU_EXTRA_KERNEL_CMDLINE="nokaslr" QEMU_EXTRA_ARGS="-S -s" make run

As shown earlier, GDB=1 can be used instead of QEMU_EXTRA_ARGS="-S -s".

Miscellaneous Commands

make linux_download (which downloads a source archive) and make linux_checkout can be used to more easily switch between kernel versions. The kernel version to download or checkout must be specified with the VERSION environment variable:

VERSION=5.10.107 make linux_download
VERSION=5.10.107 make linux_checkout

The version string can be specified as 5.10.107 or v5.10.107 (the name of the version tag in the Linux git repo). You can additionally set the last number to y to checkout or download the latest version of that kernel:

VERSION=5.10.y make linux_download
VERSION=5.10.y make linux_checkout

The LINUX_SRC variable can be used to customize the source directory for linux_checkout.

These make targets are just wrappers around the ./scripts/download_linux.sh and ./scripts/checkout_linux.sh scripts, so if you prefer those can be used instead.