├── .github ├── actions │ └── build-kernel │ │ └── action.yml └── workflows │ └── build.yml ├── .gitignore ├── .gitmodules ├── Makefile ├── README.md ├── build.py ├── config ├── config.fragment └── rootfs-overlay │ ├── .profile │ └── etc │ ├── init.d │ └── setup │ ├── inittab │ ├── rc │ └── systemd │ └── system │ ├── multi-user.target.wants │ └── setup.service │ ├── serial-getty@ttyS0.service.d │ └── override.conf │ └── setup.service ├── rootfs ├── alpine-minirootfs-3.17.1-arm64.tar.gz ├── alpine-minirootfs-3.17.1-x86_64.tar.gz └── alpine-minirootfs-3.18.0-i386.tar.gz └── scripts ├── arch_specific.sh ├── check_merged_config.sh ├── checkout_linux.sh ├── download_linux.sh ├── gdb.sh ├── gdbinit.gdb └── ubuntu_debootstrap.sh /.github/actions/build-kernel/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Build Kernel' 2 | description: 'Build the Linux kernel' 3 | inputs: 4 | kernel-version: 5 | description: 'Kernel branch or tag name to checkout' 6 | required: true 7 | os: 8 | description: 'Build machine OS' 9 | required: true 10 | arch: 11 | description: 'Architecture to build' 12 | required: true 13 | ack: 14 | description: 'Whether to build the Android Common Kernel' 15 | required: true 16 | runs: 17 | using: "composite" 18 | steps: 19 | - name: Checkout kernel version 20 | shell: bash 21 | env: 22 | ACK: ${{ inputs.ack }} 23 | ARCH: ${{ inputs.arch }} 24 | VERSION: ${{ inputs.kernel-version }} 25 | run: make linux_download 26 | 27 | - name: Install common dependencies 28 | shell: bash 29 | run: | 30 | sudo apt update && sudo apt install -y \ 31 | build-essential \ 32 | bc \ 33 | bison \ 34 | flex \ 35 | libelf-dev \ 36 | libssl-dev \ 37 | ncurses-dev \ 38 | llvm \ 39 | clang-tools \ 40 | lld \ 41 | lz4 \ 42 | xz-utils 43 | 44 | - name: Install pahole 45 | if: ${{ inputs.os == 'ubuntu-22.04' }} 46 | shell: bash 47 | run: sudo apt install pahole 48 | 49 | - name: Install pahole 50 | if: ${{ inputs.os == 'ubuntu-20.04' }} 51 | shell: bash 52 | run: sudo apt install dwarves 53 | 54 | - name: Install x86_64 dependencies 55 | if: ${{ inputs.arch == 'x86_64' }} 56 | shell: bash 57 | run: sudo apt update && sudo apt install -y qemu-system-x86 58 | 59 | - name: Install arm64 dependencies 60 | if: ${{ inputs.arch == 'arm64' }} 61 | shell: bash 62 | run: | 63 | sudo apt update && sudo apt install -y \ 64 | binutils-aarch64-linux-gnu \ 65 | gcc-aarch64-linux-gnu 66 | 67 | - name: Set Linux source directory 68 | if: ${{ inputs.ack == 0 }} 69 | shell: bash 70 | run: echo "LINUX_SRC=linux-${{ inputs.kernel-version }}" >> $GITHUB_ENV 71 | 72 | - name: Set ACK source directory 73 | if: ${{ inputs.ack == 1 }} 74 | shell: bash 75 | run: echo "LINUX_SRC=${{ inputs.kernel-version }}" >> $GITHUB_ENV 76 | 77 | - name: Build kernel 78 | shell: bash 79 | env: 80 | ACK: ${{ inputs.ack }} 81 | ARCH: ${{ inputs.arch }} 82 | run: | 83 | make -j$(nproc) linux 84 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [workflow_dispatch] 3 | 4 | jobs: 5 | build: 6 | strategy: 7 | fail-fast: false 8 | matrix: 9 | os: [ubuntu-22.04, ubuntu-20.04] 10 | kernel: [{ack: 0, version: 5.10.y}, {ack: 0, version: 5.15.y}, {ack: 0, version: 6.1.y}, {ack: 0, version: 6.6.y}, {ack: 1, version: android13-5.10-lts}, {ack: 1, version: android14-5.15-lts}] 11 | arch: [arm64, x86_64] 12 | runs-on: ${{ matrix.os }} 13 | timeout-minutes: 100 14 | env: 15 | ACK: ${{ matrix.kernel.ack }} 16 | ARCH: ${{ matrix.arch }} 17 | outputs: 18 | SHORT_HASH: ${{ steps.vars.outputs.SHORT_HASH }} 19 | steps: 20 | 21 | - uses: actions/checkout@v3 22 | 23 | - name: Set output variables 24 | id: vars 25 | run: | 26 | set -x 27 | 28 | SHORT_HASH=$(git rev-parse --short HEAD) 29 | 30 | if [ $ACK -eq 0 ]; then 31 | KERNEL_TYPE=linux 32 | OUT_DIR=out/linux/${ARCH} 33 | else 34 | KERNEL_TYPE=ack 35 | OUT_DIR=out/ack/common/${ARCH} 36 | fi 37 | 38 | if [ $ARCH == x86_64 ]; then 39 | # We need to use the real image in the x86 folder because caching 40 | # doesn't work with symlinks 41 | KERNEL_IMAGE="${OUT_DIR}/arch/x86/boot/bzImage" 42 | elif [ $ARCH == arm64 ]; then 43 | KERNEL_IMAGE="${OUT_DIR}/arch/${ARCH}/boot/Image" 44 | fi 45 | 46 | echo "SHORT_HASH=$SHORT_HASH" >> $GITHUB_OUTPUT 47 | echo "OUT_DIR=$OUT_DIR" >> $GITHUB_OUTPUT 48 | echo "KERNEL_TYPE=$KERNEL_TYPE" >> $GITHUB_OUTPUT 49 | echo "KERNEL_IMAGE=$KERNEL_IMAGE" >> $GITHUB_OUTPUT 50 | 51 | - name: Get real kernel version 52 | run: | 53 | LINUX_URL=https://cdn.kernel.org/pub/linux/kernel 54 | VERSION="${{ matrix.kernel.version }}" 55 | first_char=${VERSION:0:1} 56 | last_char=${VERSION: -1:1} 57 | if [ $last_char == y ]; then 58 | major_minor=${VERSION: 0:-1} 59 | VERSION=$(curl -s $LINUX_URL/v${first_char}.x/ \ 60 | | sed -e 's/<[^>]*>//g' \ 61 | | grep -oP "linux-${major_minor}[0-9]+" \ 62 | | sort -r -V \ 63 | | head -n1 \ 64 | | cut -d '-' -f2) 65 | fi 66 | echo "VERSION=$VERSION" >> $GITHUB_ENV 67 | 68 | - name: Cache kernel image 69 | id: cache-kernel 70 | uses: actions/cache@v3 71 | with: 72 | key: ${{ matrix.os }}-${{ env.VERSION }}-${{ matrix.arch }} 73 | path: | 74 | ${{ steps.vars.outputs.KERNEL_IMAGE }} 75 | ${{ steps.vars.outputs.OUT_DIR }}/vmlinux 76 | 77 | - run: ls -lR 78 | if: ${{ steps.cache-kernel.outputs.cache-hit == 'true' }} 79 | 80 | - name: Build kernel 81 | if: ${{ steps.cache-kernel.outputs.cache-hit != 'true' }} 82 | uses: ./.github/actions/build-kernel 83 | with: 84 | os: ${{ matrix.os }} 85 | arch: ${{ matrix.arch }} 86 | ack: ${{ matrix.kernel.ack }} 87 | kernel-version: ${{ env.VERSION }} 88 | 89 | - name: Rename kernel images 90 | run: | 91 | SUFFIX=${{ steps.vars.outputs.KERNEL_TYPE }}-${{ env.VERSION }}-${{ matrix.arch }} 92 | cp ${{ steps.vars.outputs.KERNEL_IMAGE }} ${{ steps.vars.outputs.KERNEL_IMAGE }}-$SUFFIX 93 | cp ${{ steps.vars.outputs.OUT_DIR }}/vmlinux ${{ steps.vars.outputs.OUT_DIR }}/vmlinux-$SUFFIX 94 | 95 | echo "SUFFIX=$SUFFIX" >> $GITHUB_ENV 96 | 97 | - name: Upload kernel image artifact 98 | if: ${{ matrix.os == 'ubuntu-22.04' }} 99 | uses: actions/upload-artifact@v3 100 | with: 101 | name: kernel-image 102 | path: ${{ steps.vars.outputs.KERNEL_IMAGE }}-${{ env.SUFFIX }} 103 | 104 | - name: Upload vmlinux artifact 105 | if: ${{ matrix.os == 'ubuntu-22.04' }} 106 | uses: actions/upload-artifact@v3 107 | with: 108 | name: vmlinux 109 | path: ${{ steps.vars.outputs.OUT_DIR }}/vmlinux-${{ env.SUFFIX }} 110 | 111 | run: 112 | needs: build 113 | strategy: 114 | fail-fast: false 115 | matrix: 116 | kernel: [{ack: 0, version: 5.10.y}, {ack: 0, version: 5.15.y}, {ack: 0, version: 6.1.y}, {ack: 1, version: android13-5.10-lts}] 117 | arch: [arm64, x86_64] 118 | timeout-minutes: 20 119 | runs-on: ubuntu-22.04 120 | env: 121 | ACK: ${{ matrix.kernel.ack }} 122 | ARCH: ${{ matrix.arch }} 123 | steps: 124 | - uses: actions/checkout@v3 125 | 126 | - name: Download all workflow run artifacts 127 | uses: actions/download-artifact@v3 128 | 129 | - name: Install common dependencies 130 | shell: bash 131 | run: sudo apt update && sudo apt install -y debootstrap 132 | 133 | - name: Install x86_64 dependencies 134 | if: ${{ matrix.arch == 'x86_64' }} 135 | shell: bash 136 | run: sudo apt install -y qemu-system-x86 137 | 138 | - name: Install arm64 dependencies 139 | if: ${{ matrix.arch == 'arm64' }} 140 | shell: bash 141 | run: sudo apt install -y qemu-system-arm qemu-user-static binfmt-support 142 | 143 | - name: Get real kernel version 144 | run: | 145 | LINUX_URL=https://cdn.kernel.org/pub/linux/kernel 146 | VERSION="${{ matrix.kernel.version }}" 147 | first_char=${VERSION:0:1} 148 | last_char=${VERSION: -1:1} 149 | if [ $last_char == y ]; then 150 | major_minor=${VERSION: 0:-1} 151 | VERSION=$(curl -s $LINUX_URL/v${first_char}.x/ \ 152 | | sed -e 's/<[^>]*>//g' \ 153 | | grep -oP "linux-${major_minor}[0-9]+" \ 154 | | sort -r -V \ 155 | | head -n1 \ 156 | | cut -d '-' -f2) 157 | fi 158 | echo "VERSION=$VERSION" >> $GITHUB_ENV 159 | 160 | - name: Set output variables 161 | id: vars 162 | run: | 163 | set -x 164 | 165 | if [ $ACK -eq 0 ]; then 166 | KERNEL_TYPE=linux 167 | else 168 | KERNEL_TYPE=ack 169 | fi 170 | 171 | SUFFIX=$KERNEL_TYPE-${{ env.VERSION }}-${{ matrix.arch }} 172 | 173 | if [ $ARCH == x86_64 ]; then 174 | IMAGE_NAME=bzImage-$SUFFIX 175 | else 176 | IMAGE_NAME=Image-$SUFFIX 177 | fi 178 | 179 | echo "IMAGE_NAME=$IMAGE_NAME" >> $GITHUB_ENV 180 | 181 | - name: Initialize rootfs and initramfs 182 | run: | 183 | set -x 184 | make rootfs-init 185 | ROOTFS=./alpine-${{ matrix.arch }}.img make rootfs-init 186 | 187 | scripts/ubuntu_debootstrap.sh jammy ${{ matrix.arch }} 188 | SUDO=1 ROOTFS_DIR=./rootfs/ubuntu-jammy-${{ matrix.arch }} CPIO_FILE=ubuntu-jammy-${{ matrix.arch }}.cpio.gz make rootfs-overlay 189 | 190 | - name: Setup shared/init.sh 191 | run: | 192 | mkdir shared 193 | echo poweroff > shared/init.sh 194 | chmod +x shared/init.sh 195 | 196 | - name: Run kernel 197 | run: | 198 | QEMU_KERNEL_IMAGE=./kernel-image/$IMAGE_NAME make run 199 | QEMU_KERNEL_IMAGE=./kernel-image/$IMAGE_NAME ROOTFS=./rootfs/ubuntu-jammy-${{ matrix.arch }}.img make run 200 | QEMU_KERNEL_IMAGE=./kernel-image/$IMAGE_NAME INITRD=1 make run 201 | QEMU_KERNEL_IMAGE=./kernel-image/$IMAGE_NAME INITRD=./ubuntu-jammy-${{ matrix.arch }}.cpio.gz make run 202 | QEMU_KERNEL_IMAGE=./kernel-image/$IMAGE_NAME CPU=2 MEM=2048M QEMU_EXTRA_ARGS="" QEMU_EXTRA_KERNEL_CMDLINE="nokaslr" ROOTFS=./alpine-${{ matrix.arch }}.img make run 203 | 204 | - name: Upload rootfs artifact 205 | uses: actions/upload-artifact@v3 206 | with: 207 | name: rootfs 208 | path: rootfs/alpine-${{ matrix.arch }}.img 209 | 210 | release: 211 | runs-on: ubuntu-22.04 212 | needs: [run, build] 213 | permissions: 214 | contents: write 215 | steps: 216 | 217 | - name: Download all workflow run artifacts 218 | uses: actions/download-artifact@v3 219 | 220 | - name: Compute hashsums and create hashsums.txt 221 | run: | 222 | (cd vmlinux && sha256sum *) > hashsums.txt 223 | (cd kernel-image && sha256sum *) >> hashsums.txt 224 | (cd rootfs && sha256sum *) >> hashsums.txt 225 | 226 | - run: ls -lR 227 | 228 | - name: Set output variables 229 | id: vars 230 | run: | 231 | set -x 232 | 233 | echo "DATE=$(date +'%Y.%m.%d')" >> $GITHUB_OUTPUT 234 | 235 | - name: Publish release 236 | uses: softprops/action-gh-release@v1 237 | with: 238 | prerelease: true 239 | tag_name: ${{ steps.vars.outputs.DATE }}-${{ needs.build.outputs.SHORT_HASH }} 240 | files: | 241 | vmlinux/* 242 | kernel-image/* 243 | rootfs/* 244 | hashsums.txt 245 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /rootfs/*.img 2 | /rootfs/*.cpio.gz 3 | /rootfs/alpine-*/ 4 | /rootfs/ubuntu-*/ 5 | 6 | /toolchain 7 | /shared 8 | /out 9 | /.cache 10 | compile_commands.json 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ack/common-modules/virtual-device"] 2 | path = ack/common-modules/virtual-device 3 | url = https://android.googlesource.com/kernel/common-modules/virtual-device 4 | branch = android14-5.15 5 | [submodule "ack/common"] 6 | path = ack/common 7 | url = https://android.googlesource.com/kernel/common 8 | branch = android14-5.15 9 | [submodule "linux"] 10 | path = linux 11 | url = https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ 12 | branch = master 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifneq (,$(wildcard ./.env)) 2 | include .env 3 | endif 4 | 5 | ROOT_DIR := $(CURDIR) 6 | OUT_DIR := $(ROOT_DIR)/out 7 | SCRIPT_DIR := $(ROOT_DIR)/scripts 8 | CONFIG_DIR := $(ROOT_DIR)/config 9 | CLANG_DIR ?= $(ROOT_DIR)/toolchain/clang 10 | 11 | CLANG_URL := https://github.com/llvm/llvm-project/releases/download/llvmorg-15.0.6/clang+llvm-15.0.6-x86_64-linux-gnu-ubuntu-18.04.tar.xz 12 | 13 | SHARED_DIR := $(ROOT_DIR)/shared 14 | 15 | VERBOSE ?= 0 16 | 17 | ARCH ?= x86_64 18 | 19 | GREEN := $(shell tput setaf 2) 20 | YELLOW := $(shell tput setaf 3) 21 | NC := $(shell tput sgr0) 22 | 23 | ifeq ($(ARCH),aarch64) 24 | $(warning $(YELLOW)Incorrect architecture 'aarch64', using 'arm64' instead $(NC)) 25 | ARCH := arm64 26 | else ifeq ($(ARCH),x86) 27 | $(warning $(YELLOW)Incorrect architecture 'x86', using 'x86_64' instead $(NC)) 28 | ARCH := x86_64 29 | else ifeq ($(filter x86_64 arm64 i386,$(ARCH)),) 30 | $(error Invalid architecture $(ARCH)) 31 | endif 32 | 33 | .PHONY: default 34 | default: linux tools-vm 35 | 36 | .PHONY: clean 37 | clean: linux_clean tools-vm_clean 38 | 39 | .PHONY: help 40 | help: 41 | @echo '$(GREEN)General Environment Variables:$(NC)' 42 | @echo ' ARCH - Specify one of the supported architectures: x86_64, i386, arm64 (default: x86_64)' 43 | @echo ' ACK - Set to 1 to build ACK instead of the Linux kernel. Does not need to be set for `ack` and `run-ack` targets (default: 0)' 44 | @echo ' VERBOSE - Set to 1 to enable verbose output (default: 0)' 45 | @echo '' 46 | @echo '$(GREEN)Build/Config:$(NC)' 47 | @echo ' Targets:' 48 | @echo ' linux (default) - Build the Linux kernel' 49 | @echo ' linux_defconfig - Run `make defconfig`' 50 | @echo ' linux_menuconfig - Run `make menuconfig`' 51 | @echo ' linux_modules - Build the Linux kernel modules' 52 | @echo ' linux_debpkg - Creates a Debian package for the kernel' 53 | @echo ' ack - Build the Android Common Kernel' 54 | @echo ' tools-vm - Build linux/tools/vm' 55 | @echo '' 56 | @echo ' Environment Variables:' 57 | @echo ' LINUX_DEFCONFIG - The defconfig to use when building the kernel (default: defconfig, ACK default: gki_defconfig)' 58 | @echo ' LINUX_SRC - The path to the kernel source directory (default: linux, ACK default: ack/common)' 59 | @echo ' LINUX_OUT - The path where the kernel build output should be stored (default: out/linux/$$ARCH, ACK default: out/ack/common/$$ARCH)' 60 | @echo ' LINUX_CONFIG_FRAGMENT - A kernel config fragment to merge with the defconfig (default: config/config.fragment)' 61 | @echo '' 62 | @echo '$(GREEN)Clean:$(NC)' 63 | @echo ' Targets:' 64 | @echo ' clean - Clean output from default build targets' 65 | @echo ' _clean - Clean output for , where is one of: ack, linux, tools-vm, rootfs' 66 | @echo '' 67 | @echo '$(GREEN)Run/Debug:$(NC)' 68 | @echo ' Targets:' 69 | @echo ' run - Run QEMU with the built kernel and rootfs image' 70 | @echo ' run-ack - Same as `run` but runs ACK instead' 71 | @echo '' 72 | @echo ' Environment Variables:' 73 | @echo ' GDB - Set to 1 to start a gdbserver and wait for GDB when running QEMU (default: 0)' 74 | @echo ' CPU - The number of CPUs to use when running QEMU (default: 4)' 75 | @echo ' MEM - The memory size in MB to use when running QEMU (default: 1024)' 76 | @echo ' QEMU_EXTRA_ARGS - Additional arguments to pass to QEMU (default: "")' 77 | @echo ' QEMU_EXTRA_KERNEL_CMDLINE - Additional arguments to pass to the kernel (default: "")' 78 | @echo ' QEMU_KERNEL_IMAGE - The path to the kernel image to run (x86_64/i386 default: $$LINUX_OUT/arch/$$ARCH/boot/bzImage, arm64 default: $$LINUX_OUT/arch/$$ARCH/boot/Image)' 79 | @echo ' ROOTFS - The path to the rootfs image file (default: rootfs/alpine-$$ARCH.img)' 80 | @echo ' ROOTFS_FORMAT - The format of the rootfs image file: raw, qcow2 (default: qcow2)' 81 | @echo ' INITRD - Set to 1 to use the $$CPIO_FILE initramfs instead of the $$ROOTFS image as the rootfs, or specify the path to an alternative CPIO file (default: "")' 82 | @echo ' RDINIT - The value of the `rdinit` kernel command line parameter (default: "", default if INITRD is set: /sbin/init)' 83 | @echo ' ECHR - The value of the QEMU `-echr` flag (default: 1)' 84 | @echo ' ROOT - The value of the `root` kernel command line parameter (default: /dev/vda)' 85 | @echo ' RW - Whether to mount the rootfs as read-write or read-only: ro, rw (default: rw)' 86 | @echo ' KASLR - Set to 1 to enable KASLR (default: 0)' 87 | @echo '' 88 | @echo '$(GREEN)rootfs:$(NC)' 89 | @echo ' Targets:' 90 | @echo ' rootfs-init - Extract the Alpine Linux rootfs to $$ROOTFS_DIR and then run the `rootfs-overlay` target' 91 | @echo ' rootfs-overlay - Apply arch-specific changes to $$ROOTFS_DIR and run the `rootfs` target' 92 | @echo ' rootfs - Run the `ext4` and `cpio` targets' 93 | @echo ' ext4 - Build a $$ROOTFS $$ROOTFS_FORMAT (default qcow2) image with an ext4 filesystem from $$ROOTFS_DIR' 94 | @echo ' cpio - Build a $$CPIO_FILE gzipped initramfs CPIO file from $$ROOTFS_DIR' 95 | @echo ' initramfs - An alias for the `cpio` target' 96 | @echo ' uncpio - Extract $$CPIO_FILE to $$ROOTFS_DIR' 97 | @echo ' rootfs-mount - Mount $$ROOTFS image at /tmp/rootfs' 98 | @echo ' rootfs-unmount - Unmount rootfs image from /tmp/rootfs' 99 | @echo ' chroot - chroot into $$ROOTFS_DIR' 100 | @echo '' 101 | @echo ' Environment Variables:' 102 | @echo ' EXT4_SIZE - The disk size of the rootfs image to build' 103 | @echo ' ROOTFS_DIR - The directory to create the ext4 rootfs image and initramfs CPIO from (default: rootfs/alpine-$$ARCH)' 104 | @echo ' ROOTFS - The path to the rootfs image file (default: rootfs/alpine-$$ARCH.img)' 105 | @echo ' ROOTFS_FORMAT - The format of the rootfs image file: raw, qcow2 (default: qcow2)' 106 | @echo ' CPIO_FILE - The path to the CPIO file to create (default: rootfs/alpine-$$ARCH.cpio.gz)' 107 | @echo '' 108 | @echo '$(GREEN)Miscellaneous:$(NC)' 109 | @echo ' Targets:' 110 | @echo ' linux_download - Downloads an archive of the Linux kernel source for the version specified in $$VERSION' 111 | @echo ' linux_checkout - Checks out the version specified by $$VERSION of the linux kernel in $$LINUX_SRC' 112 | @echo '' 113 | @echo ' Environment Variables:' 114 | @echo " VERSION - The version to download or checkout. For checkout only, if the third number in the version string 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" 115 | 116 | $(SHARED_DIR): 117 | mkdir -p $(SHARED_DIR) 118 | 119 | ## 120 | ## Linux Kernel and Android Common Kernel 121 | ## 122 | 123 | ACK_TARGETS := ack ack_clean run-ack 124 | ACK ?= 0 125 | 126 | ACK_SRC := $(ROOT_DIR)/ack/common 127 | ACK_OUT := $(OUT_DIR)/ack/common/$(ARCH) 128 | 129 | ifneq ($(filter $(ACK_TARGETS),$(MAKECMDGOALS)),) 130 | ACK := 1 131 | endif 132 | 133 | # If we're building ACK instead of Linux, set the appropriate variables 134 | ifeq ($(ACK),1) 135 | LINUX_SRC ?= $(ACK_SRC) 136 | LINUX_OUT ?= $(ACK_OUT) 137 | LINUX_DEFCONFIG ?= gki_defconfig 138 | else 139 | LINUX_SRC ?= $(ROOT_DIR)/linux 140 | LINUX_OUT ?= $(OUT_DIR)/linux/$(ARCH) 141 | LINUX_DEFCONFIG ?= defconfig 142 | endif 143 | 144 | LINUX_CONFIG_FRAGMENT ?= $(CONFIG_DIR)/config.fragment 145 | LINUX_OUT_MODULES_DEP := $(LINUX_OUT)/modules_install.stamp 146 | LINUX_MODULES_INSTALL_PATH := $(LINUX_OUT)/modules_install 147 | LINUX_CONFIG := $(LINUX_OUT)/.config 148 | 149 | ifeq ($(ARCH),x86_64) 150 | TARGET := x86_64-pc-linux-gnu 151 | KERNEL_IMAGE := $(LINUX_OUT)/arch/$(ARCH)/boot/bzImage 152 | else ifeq ($(ARCH),i386) 153 | TARGET := i386-pc-linux-gnu 154 | KERNEL_IMAGE := $(LINUX_OUT)/arch/$(ARCH)/boot/bzImage 155 | else ifeq ($(ARCH),arm64) 156 | TARGET := aarch64-linux-gnu 157 | KERNEL_IMAGE := $(LINUX_OUT)/arch/$(ARCH)/boot/Image 158 | endif 159 | 160 | LINUX_MAKE := \ 161 | PATH=$(CLANG_DIR)/bin:$(PATH) \ 162 | $(MAKE) \ 163 | -C $(LINUX_SRC) \ 164 | ARCH=$(ARCH) \ 165 | CROSS_COMPILE=$(TARGET)- \ 166 | LLVM=1 LLVM_IAS=1 \ 167 | V=$(VERBOSE) \ 168 | O=$(LINUX_OUT) \ 169 | -j `nproc` 170 | 171 | $(CLANG_DIR): 172 | $(warning $(YELLOW)Clang directory $(CLANG_DIR) does not exist, downloading prebuilt binaries $(NC)) 173 | wget --no-verbose --show-progress $(CLANG_URL) -O clang.tar.xz 174 | mkdir -p $(CLANG_DIR) 175 | tar -xf clang.tar.xz -C $(CLANG_DIR) --strip-components=1 176 | 177 | .PHONY: linux_defconfig 178 | linux_defconfig $(LINUX_CONFIG): $(LINUX_CONFIG_FRAGMENT) | $(CLANG_DIR) 179 | + $(LINUX_MAKE) $(LINUX_DEFCONFIG) 180 | KCONFIG_CONFIG=$(LINUX_CONFIG) \ 181 | $(LINUX_SRC)/scripts/kconfig/merge_config.sh \ 182 | -m \ 183 | $(LINUX_CONFIG) \ 184 | $(LINUX_CONFIG_FRAGMENT) 185 | + $(LINUX_MAKE) olddefconfig 186 | $(SCRIPT_DIR)/check_merged_config.sh $(LINUX_CONFIG) $(LINUX_CONFIG_FRAGMENT) 187 | 188 | .PHONY: linux_menuconfig 189 | linux_menuconfig: 190 | + $(LINUX_MAKE) menuconfig 191 | $(SCRIPT_DIR)/check_merged_config.sh $(LINUX_CONFIG) $(LINUX_CONFIG_FRAGMENT) 192 | 193 | .PHONY: linux 194 | linux $(KERNEL_IMAGE): $(LINUX_CONFIG) | $(CLANG_DIR) 195 | + $(LINUX_MAKE) 196 | # Older versions of Linux don't have this script 197 | ifneq (,$(wildcard ./.env)) 198 | $(LINUX_SRC)/scripts/clang-tools/gen_compile_commands.py -d $(LINUX_OUT) 199 | endif 200 | 201 | 202 | .PHONY: linux_modules 203 | linux_modules $(LINUX_OUT_MODULES_DEP): $(KERNEL_IMAGE) 204 | + $(LINUX_MAKE) modules 205 | + $(LINUX_MAKE) INSTALL_MOD_PATH=$(LINUX_MODULES_INSTALL_PATH) modules_install 206 | @find $(LINUX_MODULES_INSTALL_PATH) -type f | sort | \ 207 | xargs sha1sum > $(LINUX_OUT_MODULES_DEP).tmp 208 | @cmp $(LINUX_OUT_MODULES_DEP).tmp $(LINUX_OUT_MODULES_DEP) || \ 209 | mv $(LINUX_OUT_MODULES_DEP).tmp $(LINUX_OUT_MODULES_DEP) 210 | 211 | .PHONY: linux_clean 212 | linux_clean: 213 | + $(LINUX_MAKE) mrproper 214 | 215 | .PHONY: linux_download 216 | linux_download: 217 | ifndef VERSION 218 | $(error VERSION environment variable is not defined) 219 | endif 220 | $(SCRIPT_DIR)/download_linux.sh $(VERSION) $(ACK) 221 | 222 | .PHONY: linux_checkout 223 | linux_checkout: 224 | ifndef VERSION 225 | $(error VERSION environment variable is not defined) 226 | endif 227 | $(SCRIPT_DIR)/checkout_linux.sh $(VERSION) $(LINUX_SRC) 228 | 229 | # Use xz compression instead of zstd, as the latter is not supported by some 230 | # versions of dpkg. 231 | .PHONY: linux_debpkg 232 | linux_debpkg: $(KERNEL_IMAGE) 233 | + $(LINUX_MAKE) KDEB_COMPRESS=xz bindeb-pkg 234 | 235 | 236 | # These targets do the same thing as the `linux` targets, but because they're in 237 | # `ACK_TARGETS`, the `ACK` variable is set to 1 and the ACK source and output 238 | # directories are used instead of the Linux directories. The same thing can be 239 | # achieved by setting the `ACK=1` environment variable, so these are just 240 | # provided for convenience. 241 | .PHONY: ack 242 | ack: linux 243 | 244 | .PHONY: ack_clean 245 | ack_clean: linux_clean 246 | 247 | # Because of how `tools/vm/Makefile` is designed, we can't use `$(LINUX_MAKE)` 248 | # here, as specifying the `O` variable causes compilation errors, and `clang` 249 | # won't properly cross-compile the binary so we need to use GCC 250 | TOOLS_MAKE := $(MAKE) \ 251 | -C $(LINUX_SRC)/tools \ 252 | DESTDIR=$(SHARED_DIR) \ 253 | sbindir=/tools/$(ARCH) 254 | 255 | # Assuming we're building on an x86 system, we should only set `CROSS_COMPILE` 256 | # if we're building arm64. 257 | ifeq ($(ARCH),arm64) 258 | TOOLS_MAKE += CROSS_COMPILE=$(TARGET)- 259 | endif 260 | 261 | .PHONY: tools-vm 262 | tools-vm: | $(SHARED_DIR) 263 | + $(TOOLS_MAKE) vm_install 264 | 265 | .PHONY: tools-vm_clean 266 | tools-vm_clean: 267 | + $(TOOLS_MAKE) vm_clean 268 | rm -f $(SHARED_DIR)/tools/$(ARCH)/{page_owner_sort,page-types,slabinfo} 269 | 270 | ## 271 | ## Generate rootfs images 272 | ## 273 | 274 | ROOTFS_PARENT_DIR := $(ROOT_DIR)/rootfs 275 | ROOTFS ?= $(ROOTFS_PARENT_DIR)/$$(basename $(ROOTFS_DIR)).img 276 | ROOTFS_FORMAT ?= qcow2 277 | ROOTFS_DIR ?= $(ROOTFS_PARENT_DIR)/alpine-$(ARCH) 278 | CPIO_FILE ?= $(ROOTFS_PARENT_DIR)/$$(basename $(ROOTFS_DIR)).cpio.gz 279 | EXT4_SIZE ?= 1G 280 | ifeq ($(SUDO),1) 281 | SUDO := sudo 282 | endif 283 | 284 | # The user might set a relative path for `CPIO_FILE`, so we need to get the 285 | # absolute path, as we need to reference it after we've changed directories 286 | CPIO_FILE := $(shell realpath $(CPIO_FILE)) 287 | 288 | $(ROOTFS_DIR): 289 | mkdir -p $(ROOTFS_DIR) 290 | 291 | .PHONY: rootfs-init 292 | rootfs-init: | $(ROOTFS_DIR) 293 | tar -xf $(ROOTFS_PARENT_DIR)/alpine-minirootfs-*-$(ARCH).tar.gz -C $(ROOTFS_DIR) 294 | $(MAKE) rootfs-overlay 295 | 296 | .PHONY: rootfs-overlay 297 | rootfs-overlay: | $(ROOTFS_DIR) 298 | $(SUDO) cp -f -r $(CONFIG_DIR)/rootfs-overlay/. $(ROOTFS_DIR) 299 | $(SUDO) $(SCRIPT_DIR)/arch_specific.sh $(ARCH) $(ROOTFS_DIR) 300 | $(MAKE) rootfs 301 | 302 | .PHONY: ext4 303 | ext4: | $(ROOTFS_DIR) 304 | dd if=/dev/zero of=$(ROOTFS) bs=1 count=0 seek=$(EXT4_SIZE) 305 | $(SUDO) mkfs.ext4 -b 4096 -d $(ROOTFS_DIR) -F $(ROOTFS) 306 | qemu-img convert -O $(ROOTFS_FORMAT) $(ROOTFS) $(ROOTFS).tmp 307 | mv $(ROOTFS).tmp $(ROOTFS) 308 | 309 | .PHONY: cpio initramfs 310 | initramfs cpio: | $(ROOTFS_DIR) 311 | (cd $(ROOTFS_DIR) && $(SUDO) find . -print0 \ 312 | | $(SUDO) cpio --null --create --verbose --format=newc) \ 313 | | gzip --best > $(CPIO_FILE) 314 | 315 | .PHONY: uncpio 316 | uncpio: | $(ROOTFS_DIR) 317 | cd $(ROOTFS_DIR) && zcat $(CPIO_FILE) | $(SUDO) cpio --extract --make-directories --format=newc --no-absolute-filenames 318 | 319 | .PHONY: rootfs 320 | rootfs: ext4 cpio 321 | 322 | .PHONY: rootfs-mount 323 | rootfs-mount: 324 | sudo modprobe nbd 325 | sudo qemu-nbd -c /dev/nbd0 -f $(ROOTFS_FORMAT) $(ROOTFS) 326 | mkdir -p /tmp/rootfs 327 | sudo mount /dev/nbd0 /tmp/rootfs 328 | 329 | .PHONY: rootfs-unmount 330 | rootfs-unmount: 331 | sudo umount /dev/nbd0 332 | sudo qemu-nbd -d /dev/nbd0 333 | 334 | .PHONY: chroot 335 | chroot: 336 | sudo chroot $(ROOTFS_DIR) /bin/sh 337 | 338 | .PHONY: rootfs_clean 339 | rootfs_clean: 340 | $(SUDO) rm -rf $(ROOTFS_DIR) 341 | $(SUDO) rm -f $(CPIO_FILE) 342 | $(SUDO) rm -f $(ROOTFS) 343 | 344 | ## 345 | ## Run QEMU 346 | ## 347 | 348 | CPU ?= 4 349 | MEM ?= 1024 350 | QEMU_EXTRA_ARGS ?= 351 | QEMU_EXTRA_KERNEL_CMDLINE ?= 352 | GDB ?= 0 353 | INITRD ?= 354 | RDINIT ?= /sbin/init 355 | QEMU_KERNEL_IMAGE ?= $(KERNEL_IMAGE) 356 | ECHR ?= 1 357 | ROOT ?= /dev/vda 358 | RW ?= rw 359 | KASLR ?= 0 360 | 361 | QEMU_KERNEL_CMDLINE := selinux=0 362 | 363 | QEMU_ARGS := \ 364 | -m $(MEM) \ 365 | -smp $(CPU) \ 366 | -nographic \ 367 | -no-reboot \ 368 | -kernel $(QEMU_KERNEL_IMAGE) \ 369 | -netdev user,id=eth0,hostfwd=tcp::7777-:7777,hostfwd=tcp::2222-:22,hostfwd=tcp::2223-:23 -device virtio-net-pci,netdev=eth0 \ 370 | -virtfs local,security_model=mapped-xattr,path=$(SHARED_DIR),mount_tag=shared \ 371 | -echr $(ECHR) \ 372 | $(QEMU_EXTRA_ARGS) 373 | 374 | ifneq ($(INITRD),) 375 | ifeq ($(INITRD),1) 376 | INITRD := $(CPIO_FILE) 377 | endif 378 | QEMU_KERNEL_CMDLINE += rdinit=$(RDINIT) 379 | QEMU_ARGS += -initrd $(INITRD) 380 | else 381 | QEMU_ARGS += -drive file=$(ROOTFS),if=virtio,format=$(ROOTFS_FORMAT) 382 | QEMU_KERNEL_CMDLINE += root=$(ROOT) $(RW) 383 | endif 384 | 385 | ifeq ($(GDB),1) 386 | QEMU_ARGS += -s -S 387 | endif 388 | 389 | ifeq ($(ARCH),x86_64) 390 | QEMU_BIN := qemu-system-x86_64 391 | QEMU_KERNEL_CMDLINE += console=ttyS0 kpti no5lvl 392 | 393 | QEMU_ARGS += -cpu kvm64,+smep,+smap 394 | 395 | # We can't use KVM with GitHub Actions CI 396 | ifneq ($(CI),true) 397 | QEMU_ARGS += -accel kvm 398 | endif 399 | else ifeq ($(ARCH),i386) 400 | QEMU_BIN := qemu-system-i386 401 | QEMU_KERNEL_CMDLINE += console=ttyS0 402 | else 403 | QEMU_BIN := qemu-system-aarch64 404 | QEMU_KERNEL_CMDLINE += console=ttyAMA0 405 | 406 | QEMU_ARGS += \ 407 | -M virt \ 408 | -cpu cortex-a53 \ 409 | -semihosting-config enable=on,target=native 410 | endif 411 | 412 | ifeq ($(KASLR),0) 413 | QEMU_KERNEL_CMDLINE += nokaslr 414 | endif 415 | 416 | QEMU_ARGS += -append "$(QEMU_KERNEL_CMDLINE) $(QEMU_EXTRA_KERNEL_CMDLINE)" 417 | 418 | RUN_DEPS := $(QEMU_KERNEL_IMAGE) 419 | 420 | .PHONY: run 421 | run: $(RUN_DEPS) | $(SHARED_DIR) 422 | @echo "$(GREEN)Running QEMU, press 'ctrl-a x' to quit $(NC)" 423 | ifeq ($(GDB),1) 424 | @echo "$(ARCH) $(ACK)" > $(OUT_DIR)/.gdb 425 | @echo "$(GREEN)Waiting for GDB, attach with \`scripts/gdb.sh\` $(NC)" 426 | 427 | ifdef TERMINAL_CMD 428 | $(TERMINAL_CMD) $(SCRIPT_DIR)/gdb.sh 429 | endif 430 | 431 | endif 432 | @echo '' 433 | $(QEMU_BIN) $(QEMU_ARGS) 434 | 435 | .PHONY: run-ack 436 | run-ack: run 437 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Linux Exploit Development Environment 2 | 3 | ## Quickstart 4 | 5 | The only supported host OS is Ubuntu 22.04, things may or may not work on any other system. 6 | 7 | First install the dependencies and clone the project: 8 | ```bash 9 | sudo apt update 10 | 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 11 | git clone --recursive https://github.com/gsingh93/linux-exploit-dev-env 12 | cd linux-exploit-dev-env 13 | ``` 14 | 15 | This will clone the Linux kernel and Android Common Kernel (ACK) submodules. You can checkout any version of the kernel you want, i.e. 16 | ```bash 17 | VERSION=5.10.107 make linux_checkout # Checkout a specific version 18 | VERSION=5.10.y make linux_checkout # Checkout the latest version of an LTS kernel 19 | ``` 20 | 21 | Create the default rootfs images: 22 | ```bash 23 | make rootfs-init 24 | ``` 25 | 26 | Build and run the Linux kernel: 27 | ```bash 28 | make 29 | make run 30 | ``` 31 | 32 | To exit QEMU, use `ctrl+a x` (`ctrl-c` will not work). 33 | 34 | To attach with GDB, set the `GDB` environment variable before running: 35 | ```bash 36 | GDB=1 make run 37 | ``` 38 | 39 | And then in another terminal, attach with: 40 | ```bash 41 | scripts/gdb.sh 42 | ``` 43 | 44 | The default architecture is x86_64, but arm64 is also supported: 45 | ```bash 46 | ARCH=arm64 make rootfs-init 47 | ARCH=arm64 make run 48 | ``` 49 | 50 | ## All Targets and Options 51 | 52 | ``` 53 | $ make help 54 | General Environment Variables: 55 | ARCH - Specify one of the supported architectures: x86_64, i386, arm64 (default: x86_64) 56 | ACK - Set to 1 to build ACK instead of the Linux kernel. Does not need to be set for `ack` and `run-ack` targets (default: 0) 57 | VERBOSE - Set to 1 to enable verbose output (default: 0) 58 | 59 | Build/Config: 60 | Targets: 61 | linux (default) - Build the Linux kernel 62 | linux_defconfig - Run `make defconfig` 63 | linux_menuconfig - Run `make menuconfig` 64 | linux_modules - Build the Linux kernel modules 65 | linux_debpkg - Creates a Debian package for the kernel 66 | ack - Build the Android Common Kernel 67 | tools-vm - Build linux/tools/vm 68 | 69 | Environment Variables: 70 | LINUX_DEFCONFIG - The defconfig to use when building the kernel (default: defconfig, ACK default: gki_defconfig) 71 | LINUX_SRC - The path to the kernel source directory (default: linux, ACK default: ack/common) 72 | LINUX_OUT - The path where the kernel build output should be stored (default: out/linux/$ARCH, ACK default: out/ack/common/$ARCH) 73 | LINUX_CONFIG_FRAGMENT - A kernel config fragment to merge with the defconfig (default: config/config.fragment) 74 | 75 | Clean: 76 | Targets: 77 | clean - Clean output from default build targets 78 | _clean - Clean output for , where is one of: ack, linux, tools-vm, rootfs 79 | 80 | Run/Debug: 81 | Targets: 82 | run - Run QEMU with the built kernel and rootfs image 83 | run-ack - Same as `run` but runs ACK instead 84 | 85 | Environment Variables: 86 | GDB - Set to 1 to start a gdbserver and wait for GDB when running QEMU (default: 0) 87 | CPU - The number of CPUs to use when running QEMU (default: 4) 88 | MEM - The memory size in MB to use when running QEMU (default: 1024) 89 | QEMU_EXTRA_ARGS - Additional arguments to pass to QEMU (default: "") 90 | QEMU_EXTRA_KERNEL_CMDLINE - Additional arguments to pass to the kernel (default: "") 91 | QEMU_KERNEL_IMAGE - The path to the kernel image to run (x86_64/i386 default: $LINUX_OUT/arch/$ARCH/boot/bzImage, arm64 default: $LINUX_OUT/arch/$ARCH/boot/Image) 92 | ROOTFS - The path to the rootfs image file (default: rootfs/alpine-$ARCH.img) 93 | ROOTFS_FORMAT - The format of the rootfs image file: raw, qcow2 (default: qcow2) 94 | INITRD - Set to 1 to use the $CPIO_FILE initramfs instead of the $ROOTFS image as the rootfs, or specify the path to an alternative CPIO file (default: "") 95 | RDINIT - The value of the `rdinit` kernel command line parameter (default: "", default if INITRD is set: /sbin/init) 96 | ECHR - The value of the QEMU `-echr` flag (default: 1) 97 | ROOT - The value of the `root` kernel command line parameter (default: /dev/vda) 98 | RW - Whether to mount the rootfs as read-write or read-only: ro, rw (default: rw) 99 | KASLR - Set to 1 to enable KASLR (default: 0) 100 | 101 | rootfs: 102 | Targets: 103 | rootfs-init - Extract the Alpine Linux rootfs to $ROOTFS_DIR and then run the `rootfs-overlay` target 104 | rootfs-overlay - Apply arch-specific changes to $ROOTFS_DIR and run the `rootfs` target 105 | rootfs - Run the `ext4` and `cpio` targets 106 | ext4 - Build a $ROOTFS $ROOTFS_FORMAT (default qcow2) image with an ext4 filesystem from $ROOTFS_DIR 107 | cpio - Build a $CPIO_FILE gzipped initramfs CPIO file from $ROOTFS_DIR 108 | initramfs - An alias for the `cpio` target 109 | uncpio - Extract $CPIO_FILE to $ROOTFS_DIR 110 | rootfs-mount - Mount $ROOTFS image at /tmp/rootfs 111 | rootfs-unmount - Unmount rootfs image from /tmp/rootfs 112 | chroot - chroot into $ROOTFS_DIR 113 | 114 | Environment Variables: 115 | EXT4_SIZE - The disk size of the rootfs image to build 116 | ROOTFS_DIR - The directory to create the ext4 rootfs image and initramfs CPIO from (default: rootfs/alpine-$ARCH) 117 | ROOTFS - The path to the rootfs image file (default: rootfs/alpine-$ARCH.img) 118 | ROOTFS_FORMAT - The format of the rootfs image file: raw, qcow2 (default: qcow2) 119 | CPIO_FILE - The path to the CPIO file to create (default: rootfs/alpine-$ARCH.cpio.gz) 120 | 121 | Miscellaneous: 122 | Targets: 123 | linux_download - Downloads an archive of the Linux kernel source for the version specified in $VERSION 124 | linux_checkout - Checks out the version specified by $VERSION of the linux kernel in $LINUX_SRC 125 | 126 | Environment Variables: 127 | VERSION - The version to download or checkout. For checkout only, if the third number in the version string 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 128 | ``` 129 | 130 | ## Building 131 | 132 | You can customize the path to the linux source and output directories with the `LINUX_SRC` and `LINUX_OUT` environment variables: 133 | ```bash 134 | LINUX_SRC=/path/to/src LINUX_OUT=/path/to/out make linux 135 | ``` 136 | 137 | 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. 138 | 139 | ### Android Common Kernel (ACK) 140 | 141 | Instead of the Linux kernel, you can set the `ACK` variable to build the Android Common Kernel: 142 | ```bash 143 | ACK=1 make linux 144 | ACK=1 make run 145 | ``` 146 | 147 | ### defconfig 148 | 149 | To generate the default kernel config with the default `defconfig`, run: 150 | ```bash 151 | make linux_defconfig 152 | ``` 153 | 154 | The `ARCH` and `ACK` variables can also be used here: 155 | ```bash 156 | ACK=1 ARCH=arm64 make linux_defconfig 157 | ``` 158 | 159 | 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`. 160 | 161 | ## rootfs 162 | 163 | The default rootfs is based on [Alpine Linux's](https://alpinelinux.org/downloads/) 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: 164 | ```bash 165 | ROOTFS_DIR=/path/to/rootfs/dir ROOTFS=/path/to/output/rootfs.img ROOTFS_FORMAT=qcow2 make rootfs 166 | ``` 167 | 168 | 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: 169 | ```bash 170 | INITRAMFS_DIR=/path/to/initramfs/dir make cpio 171 | ``` 172 | 173 | Remember to set the correct `ARCH` variable for these commands if you are working with an architecture other than x86_64. 174 | 175 | ## Running 176 | 177 | When running a kernel, you can specify an alternative rootfs or initramfs (but not both) with the `ROOTFS` and `INITRD` variables: 178 | ```bash 179 | ROOTFS=/path/to/rootfs-qcow2.img make run 180 | ROOTFS=/path/to/rootfs-raw.img ROOTFS_FORMAT=raw make run 181 | INITRD=/path/to/initramfs.cpio.gz make run 182 | ``` 183 | 184 | 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: 185 | ```bash 186 | CPU=4 MEM=2048M QEMU_EXTRA_KERNEL_CMDLINE="nokaslr" QEMU_EXTRA_ARGS="-S -s" make run 187 | ``` 188 | 189 | As shown earlier, `GDB=1` can be used instead of `QEMU_EXTRA_ARGS="-S -s"`. 190 | 191 | ## Miscellaneous Commands 192 | 193 | `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: 194 | ```bash 195 | VERSION=5.10.107 make linux_download 196 | VERSION=5.10.107 make linux_checkout 197 | ``` 198 | 199 | 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: 200 | ```bash 201 | VERSION=5.10.y make linux_download 202 | VERSION=5.10.y make linux_checkout 203 | ``` 204 | 205 | The `LINUX_SRC` variable can be used to customize the source directory for `linux_checkout`. 206 | 207 | 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. 208 | -------------------------------------------------------------------------------- /build.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # @echo 'Generic Targets:' 4 | # @echo ' bl - builds U-Boot and TF-A and copies them to the bl output directory' 5 | # @echo ' tfa - builds Arm Trusted Firmware-A (BL1, BL2, BL31)' 6 | # @echo ' bl33/uboot - builds U-Boot (BL33)' 7 | # @echo ' tools-vm - builds linux/tools/vm' 8 | 9 | # TODO: Shallow clone 10 | 11 | import os 12 | import argparse 13 | from argparse import ArgumentParser, _HelpAction 14 | from enum import Enum 15 | import subprocess 16 | 17 | class Arch(str, Enum): 18 | X86_64 = "x86_64" 19 | ARM64 = "arm64" 20 | 21 | def handle_build(args, env): 22 | subprocess.run(["make", args.target], env=env) 23 | 24 | 25 | def handle_run(args, env): 26 | if args.gdb: 27 | env['GDB'] = '1' 28 | if args.mem: 29 | env['MEM'] = args.mem 30 | if args.cpu: 31 | env['CPU'] = str(args.cpu) 32 | if args.extra_qemu_args: 33 | env["QEMU_EXTRA_ARGS"] = args.extra_qemu_args 34 | if args.extra_kernel_cmdline: 35 | env["QEMU_EXTRA_KERNEL_CMDLINE"] = args.extra_kernel_cmdline 36 | 37 | subprocess.run(["make", "run"], env=env) 38 | 39 | 40 | def handle_clean(args, env): 41 | if args.all: 42 | subprocess.run(["make", "clean"], env=env) 43 | else: 44 | subprocess.run(["make", f"{args.target}_clean"], env=env) 45 | 46 | 47 | def handle_rootfs(args, env): 48 | print(args) 49 | 50 | class HelpAction(argparse.Action): 51 | def __init__(self, option_strings, subparsers, parent_parser, *args, **kwargs): 52 | self.subparsers = subparsers 53 | self.parent_parser = parent_parser 54 | super(HelpAction, self).__init__(option_strings=option_strings, *args, **kwargs) 55 | 56 | def __call__(self, parser, namespace, values, option_string=None): 57 | parser.print_help() 58 | 59 | f = self.parent_parser._get_formatter() 60 | f.add_arguments(self.parent_parser._actions) 61 | lines = f.format_help().split('\n') 62 | for l in lines: 63 | print(" " + l) 64 | 65 | for k, v in self.subparsers.choices.items(): 66 | f = v._get_formatter() 67 | f.start_section(f"{k} options") 68 | actions = [a for a in v._actions if a not in self.parent_parser._actions and not isinstance(a, _HelpAction)] 69 | f.add_arguments(actions) 70 | f.end_section() 71 | print(f.format_help()) 72 | 73 | exit(0) 74 | 75 | 76 | def parse_args(): 77 | parser = ArgumentParser(description='TODO', add_help=False) 78 | subparsers = parser.add_subparsers() 79 | parent_parser = ArgumentParser(add_help=False) 80 | 81 | parser.add_argument('-h', '--help', action=HelpAction, subparsers=subparsers, parent_parser=parent_parser, nargs=0) 82 | 83 | # TODO: How to add parent_parser defaults? 84 | parent_parser.add_argument('-v', '--verbose', action='store_true') 85 | parent_parser.add_argument('-a', '--arch', type=Arch, choices=[a.value for a in Arch], default=Arch.X86_64.value) 86 | parent_parser.add_argument('--ack', action='store_true') 87 | parent_parser.add_argument('-t', '--target', choices=['linux', 'ack'], default='linux') 88 | 89 | 90 | build_parser = subparsers.add_parser('build', parents=[parent_parser], help="Build a kernel image") 91 | build_parser.set_defaults(func=handle_build) 92 | # TODO: mutually exclusive 93 | build_parser.add_argument('-c', '--defconfig', action='store_true') 94 | build_parser.add_argument('-m', '--modules', action='store_true') 95 | 96 | run_parser = subparsers.add_parser('run', parents=[parent_parser], help="Run a kernel") 97 | run_parser.set_defaults(func=handle_run) 98 | 99 | run_parser.add_argument('-g', '--gdb', action='store_true') 100 | run_parser.add_argument('-c', '--cpu', type=int) 101 | run_parser.add_argument('-m', '--mem') 102 | run_parser.add_argument('--rootfs') 103 | run_parser.add_argument('--initramfs') 104 | run_parser.add_argument('--extra-qemu-args') 105 | run_parser.add_argument('--extra-kernel-cmdline') 106 | 107 | clean_parser = subparsers.add_parser('clean', parents=[parent_parser], help="Clean built artifacts") 108 | clean_parser.set_defaults(func=handle_clean) 109 | clean_parser.add_argument('--all', action='store_true') 110 | 111 | rootfs_parser = subparsers.add_parser('rootfs') 112 | rootfs_parser.set_defaults(func=handle_rootfs) 113 | 114 | rootfs_subparser = rootfs_parser.add_subparsers(dest="rootfs_command") 115 | mount_subparser = rootfs_subparser.add_parser('mount') 116 | unmount_subparser = rootfs_subparser.add_parser('unmount') 117 | initramfs_subparser = rootfs_subparser.add_parser('initramfs') 118 | image_subparser = rootfs_subparser.add_parser('image') 119 | 120 | return parser.parse_args() 121 | 122 | 123 | def main(): 124 | args = parse_args() 125 | print(args) 126 | env = os.environ.copy() 127 | # env['ARCH'] = args.arch 128 | # if args.verbose: 129 | # env['VERBOSE'] = '1' 130 | # if args.target == "ack": 131 | # env['ACK'] = '1' 132 | 133 | args.func(args, env) 134 | 135 | if __name__ == '__main__': 136 | main() 137 | -------------------------------------------------------------------------------- /config/config.fragment: -------------------------------------------------------------------------------- 1 | # Extra kernel configuration options. This will be merged into the main kernel 2 | # config (`defconfig` or `gki_defconfig`). This config should work for Linux and 3 | # ACK, for both x86_64 and arm64. If an option doesn't exist for particular 4 | # platform, it's automatically removed. 5 | 6 | # Make debugging and testing easier 7 | CONFIG_FRAME_POINTER=y 8 | CONFIG_DEBUG_INFO_DWARF5=y 9 | CONFIG_DEBUG_INFO_BTF=y 10 | #CONFIG_DEBUG_INFO=y 11 | #CONFIG_GDB_SCRIPTS=y 12 | CONFIG_DEBUG_FS=y 13 | CONFIG_KGDB=y 14 | CONFIG_KGDB_KDB=y 15 | CONFIG_KDB_KEYBOARD=y 16 | CONFIG_DEBUG_KERNEL=y 17 | CONFIG_DEBUG_WX=y 18 | CONFIG_PTDUMP_DEBUGFS=y 19 | CONFIG_IKCONFIG=y 20 | CONFIG_IKCONFIG_PROC=y 21 | # CONFIG_STRICT_DEVMEM is not set 22 | CONFIG_VT=y 23 | CONFIG_SECURITY_SELINUX=y 24 | CONFIG_SECURITY_SELINUX_BOOTPARAM=y 25 | CONFIG_MODULE_FORCE_LOAD=y 26 | 27 | # LTO is required for CFI, but even LTO_CLANG_THIN is incredibly slow. Only 28 | # enable this if it's necessary 29 | # CONFIG_LTO_CLANG_FULL is not set 30 | # CONFIG_LTO_CLANG_THIN=y 31 | # CONFIG_CFI_PERMISSIVE=y 32 | 33 | # Shared directories 34 | CONFIG_9P_FS=y 35 | CONFIG_NET_9P=y 36 | CONFIG_NET_9P_VIRTIO=y 37 | 38 | # Other virtio configuration 39 | CONFIG_VIRTIO_BLK=y 40 | CONFIG_VIRTIO_BALLOON=y 41 | CONFIG_VIRTIO_CONSOLE=y 42 | CONFIG_VIRTIO_DMA_SHARED_BUFFER=y 43 | CONFIG_VIRTIO_INPUT=y 44 | CONFIG_VIRTIO_NET=y 45 | CONFIG_VIRTIO_PCI=y 46 | CONFIG_VIRTIO_PMEM=y 47 | CONFIG_VIRTIO_VSOCKETS=y 48 | 49 | # devtmpfs needs to be mounted on boot for Android 50 | CONFIG_DEVTMPFS=y 51 | CONFIG_DEVTMPFS_MOUNT=y 52 | # CONFIG_KASAN is not set 53 | 54 | # Dynamic Tracing (http://events17.linuxfoundation.org/sites/events/files/slides/ELC_2017_NA_dynamic_tracing_tools_on_arm_aarch64_platform.pdf slide 40) 55 | CONFIG_BPF=y 56 | CONFIG_BPF_JIT=y 57 | CONFIG_BPF_SYSCALL=y 58 | CONFIG_BPF_EVENTS=y 59 | CONFIG_KPROBES=y 60 | CONFIG_KPROBE_EVENTS=y 61 | CONFIG_UPROBES=y 62 | CONFIG_UPROBE_EVENTS=y 63 | CONFIG_BRANCH_PROFILE_NONE=y 64 | CONFIG_CONTEXT_SWITCH_TRACER=y 65 | CONFIG_DYNAMIC_FTRACE=y 66 | CONFIG_EVENT_TRACING=y 67 | CONFIG_FTRACE=y 68 | CONFIG_FTRACE_MCOUNT_RECORD=y 69 | CONFIG_FTRACE_SYSCALLS=y 70 | CONFIG_FUNCTION_GRAPH_TRACER=y 71 | CONFIG_FUNCTION_PROFILER=y 72 | CONFIG_FUNCTION_TRACER=y 73 | CONFIG_BOOTTIME_TRACING=y 74 | CONFIG_GENERIC_TRACER=y 75 | CONFIG_NOP_TRACER=y 76 | CONFIG_PREEMPT_TRACER=y 77 | CONFIG_RELAY=y 78 | CONFIG_RING_BUFFER=y 79 | CONFIG_RING_BUFFER_ALLOW_SWAP=y 80 | CONFIG_SCHED_TRACER=y 81 | CONFIG_STACKTRACE=y 82 | CONFIG_STACK_TRACER=y 83 | CONFIG_TRACEPOINTS=y 84 | CONFIG_TRACER_MAX_TRACE=y 85 | CONFIG_TRACER_SNAPSHOT=y 86 | CONFIG_TRACER_SNAPSHOT_PER_CPU_SWAP=y 87 | CONFIG_TRACE_CLOCK=y 88 | CONFIG_TRACING=y 89 | # Mount tracefs at /sys/kernel/debug/tracing in addition to /sys/kernel/tracing. 90 | # Some tools still require the older debugfs path 91 | # CONFIG_TRACEFS_DISABLE_AUTOMOUNT is not set 92 | 93 | # Allow lvl5 paging 94 | CONFIG_X86_5LEVEL=y 95 | -------------------------------------------------------------------------------- /config/rootfs-overlay/.profile: -------------------------------------------------------------------------------- 1 | export HISTFILE=/.sh_history 2 | export PROMPT_COMMAND="history -a" 3 | 4 | # https://unix.stackexchange.com/a/629996 5 | alias rsz='resize >/dev/null' 6 | if [ "$SHELL" != "/bin/sh" ] && [ $(tty) == '/dev/ttyS0' ]; then 7 | trap rsz DEBUG 8 | fi 9 | -------------------------------------------------------------------------------- /config/rootfs-overlay/etc/init.d/setup: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: setup 4 | # Required-Start: $network 5 | # Required-Stop: 6 | # Default-Start: 2 3 4 5 7 | # Default-Stop: 8 | # Short-Description: Linux Exploit Dev Environment Setup 9 | # Description: Linux Exploit Dev Environment Setup 10 | ### END INIT INFO 11 | 12 | case "$1" in 13 | start) 14 | # Setup networking 15 | ETH_IF="$(ls /sys/class/net | grep -E '^(eth|enp)')" 16 | ip link set "$ETH_IF" up 17 | if [ -x "$(command -v udhcpc)" ]; then 18 | udhcpc # Busybox DHCP 19 | elif [ -x "$(command -v dhclient)" ]; then 20 | dhclient # Ubuntu DHCP 21 | fi 22 | 23 | # If the user already exists this won't cause any problems 24 | adduser user --disabled-password 2>/dev/null 25 | 26 | mkdir -p /shared 27 | mount -t 9p -o trans=virtio,version=9p2000.L shared /shared 28 | 29 | mount -t debugfs none /sys/kernel/debug 30 | [ ! -e /d ] && ln -s /sys/kernel/debug /d 31 | 32 | mount -t tracefs nodev /sys/kernel/tracing 33 | [ ! -e /t ] && ln -s /sys/kernel/tracing /t 34 | 35 | # Only mount binder if the kernel supports it 36 | cat /proc/filesystems | grep binder > /dev/null 37 | if [ $? -eq 0 ]; then 38 | mkdir -p /dev/binderfs 39 | mount -t binder binder /dev/binderfs 40 | ln -s /dev/binderfs/binder /dev/binder 41 | echo 16383 > /sys/module/binder/parameters/debug_mask 42 | fi 43 | 44 | [ -x /shared/init.sh ] && /shared/init.sh 45 | ;; 46 | *) ;; 47 | esac 48 | 49 | exit 0 50 | -------------------------------------------------------------------------------- /config/rootfs-overlay/etc/inittab: -------------------------------------------------------------------------------- 1 | # /etc/inittab 2 | # 3 | # Copyright (C) 2001 Erik Andersen 4 | # 5 | # Note: BusyBox init doesn't support runlevels. The runlevels field is 6 | # completely ignored by BusyBox init. If you want runlevels, use 7 | # sysvinit. 8 | # 9 | # Format for each entry: ::: 10 | # 11 | # id == tty to run on, or empty for /dev/console 12 | # runlevels == ignored 13 | # action == one of sysinit, respawn, askfirst, wait, and once 14 | # process == program to run 15 | 16 | # Startup the system 17 | ::sysinit:/bin/mount -t devtmpfs devtmpfs /dev 18 | ::sysinit:/bin/mount -t proc proc /proc 19 | ::sysinit:/bin/mount -t sysfs nodev /sys 20 | ::sysinit:/bin/mount -o remount,rw / 21 | ::sysinit:/bin/mkdir -p /dev/pts /dev/shm 22 | ::sysinit:/bin/mount -a 23 | ::sysinit:/sbin/swapon -a 24 | null::sysinit:/bin/ln -sf /proc/self/fd /dev/fd 25 | null::sysinit:/bin/ln -sf /proc/self/fd/0 /dev/stdin 26 | null::sysinit:/bin/ln -sf /proc/self/fd/1 /dev/stdout 27 | null::sysinit:/bin/ln -sf /proc/self/fd/2 /dev/stderr 28 | ::sysinit:/bin/hostname -F /etc/hostname 29 | # now run any rc scripts 30 | ::sysinit:/etc/rc 31 | 32 | # Instead of running getty on the serial port, we can directly run a bash login 33 | # shell so we're immediately logged in as root 34 | ##ARCH_TTY##::respawn:-/bin/sh 35 | # ##ARCH_TTY##::respawn:/sbin/getty -L ##ARCH_TTY## 0 vt100 # GENERIC_SERIAL 36 | 37 | # Stuff to do before rebooting 38 | ::shutdown:/etc/init.d/rcK 39 | ::shutdown:/sbin/swapoff -a 40 | ::shutdown:/bin/umount -a -r 41 | -------------------------------------------------------------------------------- /config/rootfs-overlay/etc/rc: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # TODO: Properly run all rc scripts 4 | /etc/init.d/setup start 5 | -------------------------------------------------------------------------------- /config/rootfs-overlay/etc/systemd/system/multi-user.target.wants/setup.service: -------------------------------------------------------------------------------- 1 | /etc/systemd/system/setup.service -------------------------------------------------------------------------------- /config/rootfs-overlay/etc/systemd/system/serial-getty@ttyS0.service.d/override.conf: -------------------------------------------------------------------------------- 1 | [Service] 2 | Type=simple 3 | ExecStart= 4 | ExecStart=-/sbin/agetty --autologin root --noclear %I 38400 linux 5 | -------------------------------------------------------------------------------- /config/rootfs-overlay/etc/systemd/system/setup.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Documentation=man:systemd-sysv-generator(8) 3 | SourcePath=/etc/init.d/setup 4 | Description=Linux Exploit Dev Environment Setup 5 | 6 | [Service] 7 | Type=forking 8 | Restart=no 9 | TimeoutSec=5min 10 | IgnoreSIGPIPE=no 11 | KillMode=process 12 | GuessMainPID=no 13 | RemainAfterExit=yes 14 | SuccessExitStatus=5 6 15 | ExecStart=/etc/init.d/setup start 16 | ExecStop=/etc/init.d/setup stop 17 | 18 | [Install] 19 | WantedBy=multi-user.target 20 | -------------------------------------------------------------------------------- /rootfs/alpine-minirootfs-3.17.1-arm64.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwndbg/linux-exploit-dev-env/20a2cc181f16e91b05e19022c63c9fc706b7fc8c/rootfs/alpine-minirootfs-3.17.1-arm64.tar.gz -------------------------------------------------------------------------------- /rootfs/alpine-minirootfs-3.17.1-x86_64.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwndbg/linux-exploit-dev-env/20a2cc181f16e91b05e19022c63c9fc706b7fc8c/rootfs/alpine-minirootfs-3.17.1-x86_64.tar.gz -------------------------------------------------------------------------------- /rootfs/alpine-minirootfs-3.18.0-i386.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwndbg/linux-exploit-dev-env/20a2cc181f16e91b05e19022c63c9fc706b7fc8c/rootfs/alpine-minirootfs-3.18.0-i386.tar.gz -------------------------------------------------------------------------------- /scripts/arch_specific.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # usage: ./arch_specific.sh ARCH ROOTFS_DIR 4 | # 5 | # Replaces the following arch-specific variables in the rootfs: 6 | # 7 | # Variable x86_64 arm64 8 | # ------------ ------ ------- 9 | # ##ARCH_TTY## ttyS0 ttyAMA0 10 | 11 | set -o errexit 12 | 13 | ARCH="$1" 14 | ROOTFS_DIR="$2" 15 | 16 | INITTAB_PATH="${ROOTFS_DIR}/etc/inittab" 17 | 18 | TTY=ttyS0 19 | if [[ "$ARCH" == arm64 ]]; then 20 | TTY=ttyAMA0 21 | fi 22 | 23 | sed -i "s/##ARCH_TTY##/${TTY}/g" "$INITTAB_PATH" 24 | sed -i "s/##ARCH_TTY##/${TTY}/g" "${ROOTFS_DIR}/.profile" 25 | 26 | sed -i "s/root:x:/root::/g" "${ROOTFS_DIR}/etc/passwd" 27 | -------------------------------------------------------------------------------- /scripts/check_merged_config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | CONFIG_PREFIX=${CONFIG_:-CONFIG_} 6 | SED_CONFIG_EXP1="s/^\(${CONFIG_PREFIX}[a-zA-Z0-9_]*\)=.*/\1/p" 7 | SED_CONFIG_EXP2="s/^# \(${CONFIG_PREFIX}[a-zA-Z0-9_]*\) is not set$/\1/p" 8 | 9 | YELLOW='\033[0;33m' 10 | NC='\033[0m' 11 | 12 | warn() { 13 | echo -e "${YELLOW}$1${NC}" 14 | } 15 | 16 | usage() { 17 | echo "Usage: $0 CONFIG_FILE FRAGMENT_FILE" 18 | echo 19 | echo "Used prefix: '$CONFIG_PREFIX'. You can redefine it with \$CONFIG_ environment variable." 20 | } 21 | 22 | if [ "$#" -ne 2 ] ; then 23 | usage 24 | exit 1 25 | fi 26 | 27 | CONFIG_FILE="$1" 28 | FRAGMENT_FILE="$2" 29 | 30 | warn "Checking merged config" 31 | 32 | for CFG in $(sed -n -e "$SED_CONFIG_EXP1" -e "$SED_CONFIG_EXP2" "$FRAGMENT_FILE"); do 33 | REQUESTED_VAL=$(grep -w -e "$CFG" "$FRAGMENT_FILE") 34 | ACTUAL_VAL=$(grep -w -e "$CFG" "$CONFIG_FILE" || true) 35 | if [ "x$REQUESTED_VAL" != "x$ACTUAL_VAL" ] ; then 36 | warn "Value requested for $CFG not in final .config" 37 | warn "Requested value: $REQUESTED_VAL" 38 | warn "Actual value: $ACTUAL_VAL" 39 | echo "" 40 | fi 41 | done 42 | 43 | warn "Finished checking merged config" 44 | -------------------------------------------------------------------------------- /scripts/checkout_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | 5 | VERSION="$1" 6 | LINUX_SRC="${2:-linux}" 7 | 8 | if [ -z "$VERSION" ]; then 9 | echo "usage: $0 VERSION" 10 | exit 1 11 | fi 12 | 13 | function check_version_and_checkout { 14 | local REF="$1" 15 | if git -C "$LINUX_SRC" show-ref "$REF" > /dev/null; then 16 | git -C "$LINUX_SRC" checkout $REF 17 | return 0 18 | else 19 | return 1 20 | fi 21 | } 22 | 23 | check_version_and_checkout "$VERSION" && exit 0 24 | check_version_and_checkout "v$VERSION" && exit 0 25 | check_version_and_checkout "linux-$VERSION" && exit 0 26 | 27 | echo "No tag or branch corresponding to version $VERSION found" 28 | exit 1 29 | -------------------------------------------------------------------------------- /scripts/download_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | 5 | VERSION="$1" 6 | ACK="${2:-0}" 7 | MIN_VERSION=2 8 | MAX_VERSION=6 9 | 10 | LINUX_URL=https://cdn.kernel.org/pub/linux/kernel 11 | ACK_URL=https://android.googlesource.com/kernel/common/+archive/refs/heads 12 | 13 | if [ -z "$VERSION" ]; then 14 | echo "usage: $0 VERSION [0 | 1]" 15 | exit 1 16 | fi 17 | 18 | if [ $ACK -eq 0 ]; then 19 | first_char=${VERSION:0:1} 20 | 21 | # Strip any leading 'v' 22 | if [ $first_char == v ]; then 23 | VERSION=${VERSION:1} 24 | fi 25 | first_char=${VERSION:0:1} 26 | 27 | # If patch version is 'y', we need to find the latest version of the major.minor release 28 | last_char=${VERSION: -1:1} 29 | if [ $last_char == y ]; then 30 | major_minor=${VERSION: 0:-1} 31 | VERSION=$(curl -s $LINUX_URL/v${first_char}.x/ \ 32 | | sed -e 's/<[^>]*>//g' \ 33 | | grep -oP "linux-${major_minor}[0-9]+" \ 34 | | sort -r -V \ 35 | | head -n1 \ 36 | | cut -d '-' -f2) 37 | fi 38 | 39 | echo "Downloading kernel version $VERSION" 40 | 41 | if [ $first_char -ge $MIN_VERSION ] && [ $first_char -le $MAX_VERSION ]; then 42 | wget --no-verbose --show-progress $LINUX_URL/v${first_char}.x/linux-$VERSION.tar.xz 43 | tar -xf linux-$VERSION.tar.xz 44 | else 45 | echo "$first_char is not a valid kernel major version number" 46 | exit 1 47 | fi 48 | else 49 | wget $ACK_URL/$VERSION.tar.gz 50 | mkdir -p $VERSION 51 | tar -xf $VERSION.tar.gz -C $VERSION 52 | fi 53 | -------------------------------------------------------------------------------- /scripts/gdb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CWD=$(dirname -- "$0") 4 | ROOT_DIR=$CWD/.. 5 | OUT_DIR=$ROOT_DIR/out 6 | GDB_FILE=$OUT_DIR/.gdb 7 | GDBINIT=$CWD/gdbinit.gdb 8 | 9 | ptrace_scope=$(cat /proc/sys/kernel/yama/ptrace_scope) 10 | if [[ $ptrace_scope -ne 0 && $(id -u) -ne 0 ]]; then 11 | cat << EOF 12 | WARNING: You are not running as root and ptrace_scope is not set to zero. If you 13 | run into issues when using pwndbg or gdb-pt-dump, rerun this script as root, or 14 | alternatively run the following command: 15 | 16 | echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope 17 | 18 | EOF 19 | fi 20 | 21 | IFS=" " read -r ARCH ACK < $GDB_FILE 22 | 23 | if [[ "$ACK" -eq 1 ]]; then 24 | LINUX_OUT=$OUT_DIR/ack/common/$ARCH 25 | else 26 | LINUX_OUT=$OUT_DIR/linux/$ARCH 27 | fi 28 | 29 | OUTPUT=$(mktemp) 30 | sed "s|##LINUX_OUT##|${LINUX_OUT}|g" "$GDBINIT" > "$OUTPUT" 31 | 32 | if [[ $ARCH == "x86_64" ]]; then 33 | GDB=gdb 34 | else 35 | GDB=gdb-multiarch 36 | fi 37 | 38 | exec "${GDB}" -q -ex "source ${OUTPUT}" "$@" 39 | -------------------------------------------------------------------------------- /scripts/gdbinit.gdb: -------------------------------------------------------------------------------- 1 | file ##LINUX_OUT##/vmlinux 2 | source ##LINUX_OUT##/vmlinux-gdb.py 3 | target remote :1234 4 | # add-symbol-file ##LINUX_OUT##/modules_install/lib/modules/5.10.107/extra/my_module.ko -s .text 0xffffffc0091b0800 5 | -------------------------------------------------------------------------------- /scripts/ubuntu_debootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | 5 | CWD=$(dirname -- "$0") 6 | ROOT_DIR="$(realpath ${CWD}/..)" 7 | 8 | NAME="${1:-jammy}" 9 | ARCH="${2:-x86_64}" 10 | 11 | DIR="${ROOT_DIR}/rootfs/ubuntu-${NAME}-${ARCH}" 12 | 13 | if [[ "$ARCH" == x86_64 ]]; then 14 | ARCH=amd64 15 | elif [[ "$ARCH" != arm64 ]]; then 16 | echo "usage: $0 [NAME] [x86_64 | arm64]" 17 | exit 1 18 | fi 19 | 20 | mkdir -p $DIR 21 | sudo debootstrap --arch $ARCH $NAME $DIR 22 | 23 | echo "Ubuntu ${NAME} debootstrap installed in ${DIR}" 24 | --------------------------------------------------------------------------------