etc~ scripts and random things |
| git clone https://github.com/djnnvx/etc.git |
| log | files | refs | archive |
setup.sh (12788B)
1 #!/bin/bash 2 # setup.sh — one-shot bootstrap for the usbipd fuzzing environment. 3 # 4 # What this does (in order): 5 # 1. Install system dependencies 6 # 2. Clone + build AFL++ with QEMU mode 7 # 3. Download + configure + build minimal Linux 6.12 LTS kernel 8 # 4. Build static busybox 9 # 5. Build usbipd from kernel tools (instrumented for AFL++, and plain for initramfs) 10 # 6. Assemble initramfs (busybox + usbipd + /init) 11 # 7. Compile net_send.c 12 # 13 # Re-running is safe: each step checks for existing artifacts and skips. 14 # Override KERNEL_VER or BUSYBOX_VER env vars to use different versions. 15 # 16 # Usage: 17 # bash setup.sh 18 # bash setup.sh --no-kernel # skip kernel build (use existing bzImage) 19 20 set -euo pipefail 21 22 SCRIPT_DIR="$(cd "$(dirname "${0}")" && pwd)" 23 cd "${SCRIPT_DIR}" 24 25 KERNEL_VER="${KERNEL_VER:-6.12}" 26 BUSYBOX_VER="${BUSYBOX_VER:-1.36.1}" 27 AFL_REPO="https://github.com/AFLplusplus/AFLplusplus.git" 28 29 SKIP_KERNEL=0 30 SKIP_DEPS=0 31 for arg in "$@"; do 32 [[ "$arg" == "--no-kernel" ]] && SKIP_KERNEL=1 33 [[ "$arg" == "--skip-deps" ]] && SKIP_DEPS=1 34 done 35 36 log() { echo "[*] $*"; } 37 ok() { echo "[+] $*"; } 38 warn() { echo "[!] $*"; } 39 40 # ── 1. System dependencies ─────────────────────────────────────────────────── 41 if [[ "${SKIP_DEPS}" -eq 1 ]]; then 42 ok "Skipping dependency install (--skip-deps)." 43 else 44 log "Installing system dependencies..." 45 sudo apt-get update -qq 46 sudo apt-get install -y \ 47 build-essential git curl wget python3 python3-pip tmux \ 48 libelf-dev libssl-dev flex meson bison bc cpio \ 49 libncurses-dev libncurses5-dev \ 50 libusb-1.0-0-dev libudev-dev libglib2.0-dev \ 51 pkg-config autoconf automake libtool \ 52 qemu-system-x86 \ 53 gcc-multilib \ 54 llvm clang lld \ 55 gdb 56 ok "Dependencies installed." 57 fi 58 59 # ── 2. AFL++ ───────────────────────────────────────────────────────────────── 60 if [[ ! -x "afl-build/usr/local/bin/afl-fuzz" ]]; then 61 log "Cloning AFL++..." 62 [[ -d "AFLplusplus" ]] || git clone --depth=1 "${AFL_REPO}" AFLplusplus 63 64 log "Building AFL++ (distrib target — includes QEMU mode)..." 65 cd AFLplusplus 66 67 # QEMU mode requires python3-dev 68 [[ "${SKIP_DEPS}" -eq 0 ]] && sudo apt-get install -y python3-dev python3-setuptools || true 69 70 # make distrib builds: core + LLVM instrumentation + QEMU mode 71 make distrib -j"$(nproc)" 72 73 # Install into local prefix so we don't pollute /usr/local 74 make install PREFIX="${SCRIPT_DIR}/afl-build/usr/local" 75 cd "${SCRIPT_DIR}" 76 ok "AFL++ built." 77 else 78 ok "AFL++ already built, skipping." 79 fi 80 81 # Add AFL++ to PATH for this session and child processes 82 export PATH="${SCRIPT_DIR}/afl-build/usr/local/bin:${PATH}" 83 84 # ── 3. Linux kernel ────────────────────────────────────────────────────────── 85 KERNEL_DIR="linux-${KERNEL_VER}" 86 KERNEL_TAR="linux-${KERNEL_VER}.tar.xz" 87 KERNEL_URL="https://cdn.kernel.org/pub/linux/kernel/v6.x/${KERNEL_TAR}" 88 89 if [[ "${SKIP_KERNEL}" -eq 1 ]]; then 90 warn "--no-kernel: skipping kernel build." 91 elif [[ -f "bzImage" ]]; then 92 ok "bzImage already exists, skipping kernel build." 93 else 94 if [[ ! -d "${KERNEL_DIR}" ]]; then 95 log "Downloading Linux ${KERNEL_VER}..." 96 wget -q --show-progress -c "${KERNEL_URL}" 97 log "Extracting..." 98 tar -xf "${KERNEL_TAR}" 99 rm -f "${KERNEL_TAR}" 100 fi 101 102 log "Configuring kernel (allnoconfig + USB/IP fragment)..." 103 cd "${KERNEL_DIR}" 104 105 # GCC 15+ defaults to C23, where 'bool'/'false' are keywords — the kernel's 106 # arch/x86/boot/compressed/Makefile overrides KBUILD_CFLAGS entirely and 107 # doesn't inherit the top-level -std=gnu11, so patch it directly. 108 if gcc --version 2>&1 | grep -qE "gcc \(.*\) (1[5-9]|[2-9][0-9])\."; then 109 sed -i 's/^KBUILD_CFLAGS := /KBUILD_CFLAGS := -std=gnu11 /' \ 110 arch/x86/boot/compressed/Makefile 111 warn "GCC 15+ detected: patched compressed/Makefile with -std=gnu11" 112 fi 113 114 make allnoconfig 115 116 # Merge our minimal USB/IP config on top of allnoconfig 117 scripts/kconfig/merge_config.sh -m .config "${SCRIPT_DIR}/qemu/kernel.config" 118 119 # Resolve any unresolved dependencies automatically 120 make olddefconfig 121 122 log "Building kernel (this takes ~15-20 min on first run)..." 123 make -j"$(nproc)" bzImage 2>&1 | tee /tmp/kbuild.log | grep -E "^\s*(CC|LD|AR|OBJCOPY|HOSTCC|error:|warning: )" || \ 124 { grep "error:" /tmp/kbuild.log | tail -20; exit 1; } 125 126 cp arch/x86/boot/bzImage "${SCRIPT_DIR}/bzImage" 127 cd "${SCRIPT_DIR}" 128 ok "Kernel built: bzImage" 129 fi 130 131 # ── 4. Busybox (static) ────────────────────────────────────────────────────── 132 BUSYBOX_DIR="busybox-${BUSYBOX_VER}" 133 BUSYBOX_TAR="${BUSYBOX_DIR}.tar.bz2" 134 BUSYBOX_URL="https://busybox.net/downloads/${BUSYBOX_TAR}" 135 136 if [[ -d "busybox-install" ]]; then 137 ok "Busybox already built, skipping." 138 else 139 if [[ ! -d "${BUSYBOX_DIR}" ]]; then 140 log "Downloading busybox ${BUSYBOX_VER}..." 141 wget -q --show-progress -c "${BUSYBOX_URL}" 142 tar -xjf "${BUSYBOX_TAR}" 143 rm -f "${BUSYBOX_TAR}" 144 fi 145 146 log "Building static busybox..." 147 cd "${BUSYBOX_DIR}" 148 make defconfig 149 # Enable static build 150 sed -i 's/# CONFIG_STATIC is not set/CONFIG_STATIC=y/' .config 151 sed -i 's/CONFIG_STATIC=n/CONFIG_STATIC=y/' .config 152 echo "CONFIG_STATIC=y" >> .config 153 # Disable tc applet: uses CBQ structs removed from modern kernel UAPI headers 154 sed -i 's/CONFIG_TC=y/CONFIG_TC=n/' .config 155 echo "CONFIG_TC=n" >> .config 156 make oldconfig < /dev/null || true 157 make -j"$(nproc)" 158 make install CONFIG_PREFIX="${SCRIPT_DIR}/busybox-install" 159 cd "${SCRIPT_DIR}" 160 ok "Busybox installed to busybox-install/" 161 fi 162 163 # ── 5. usbip userspace tools ───────────────────────────────────────────────── 164 USBIP_SRC="${SCRIPT_DIR}/usbip-src" 165 166 if [[ "${SKIP_KERNEL}" -eq 0 ]] && [[ -d "${SCRIPT_DIR}/${KERNEL_DIR}" ]]; then 167 KERNEL_USBIP="${SCRIPT_DIR}/${KERNEL_DIR}/tools/usb/usbip" 168 else 169 warn "No kernel source directory found. Skipping usbip build." 170 KERNEL_USBIP="" 171 fi 172 173 if [[ -n "${KERNEL_USBIP}" ]]; then 174 # 5a. Plain build (for initramfs — not AFL instrumented) 175 if [[ ! -f "usbipd-plain" ]]; then 176 log "Building usbipd (plain, for initramfs)..." 177 cd "${KERNEL_USBIP}" 178 ./autogen.sh 179 ./configure --prefix="${SCRIPT_DIR}/usbipd-plain-install" \ 180 CFLAGS="-O2 -g" 181 make -j"$(nproc)" 182 make install 183 cd "${SCRIPT_DIR}" 184 cp usbipd-plain-install/sbin/usbipd usbipd-plain || \ 185 cp usbipd-plain-install/usr/sbin/usbipd usbipd-plain 2>/dev/null || true 186 ok "Plain usbipd built." 187 fi 188 189 # 5b. AFL++-instrumented copy of the source (for in-process harnesses) 190 if [[ ! -d "${USBIP_SRC}" ]]; then 191 log "Copying usbip source for AFL++ instrumented build..." 192 cp -r "${KERNEL_USBIP}" "${USBIP_SRC}" 193 fi 194 195 ok "usbip source available at ${USBIP_SRC}/" 196 fi 197 198 # ── 6. Initramfs ────────────────────────────────────────────────────────────── 199 if [[ -f "initramfs.cpio.gz" ]]; then 200 ok "initramfs.cpio.gz already exists, skipping." 201 else 202 log "Assembling initramfs..." 203 rm -rf initramfs 204 mkdir -p initramfs/{bin,sbin,usr/bin,usr/sbin,proc,sys,dev,tmp,run,lib,lib64,etc} 205 206 # Busybox 207 cp busybox-install/bin/busybox initramfs/bin/busybox 208 # Install all busybox applets as symlinks 209 (cd initramfs && bin/busybox --list | while read applet; do 210 ln -sf /bin/busybox bin/"${applet}" 2>/dev/null || true 211 done) 212 # Ensure sh and ip are present 213 ln -sf /bin/busybox initramfs/bin/sh 214 ln -sf /bin/busybox initramfs/sbin/ip 215 ln -sf /bin/busybox initramfs/sbin/modprobe 216 217 # usbipd binary 218 if [[ -f "usbipd-plain" ]]; then 219 cp usbipd-plain initramfs/usr/sbin/usbipd 220 else 221 warn "usbipd-plain not found — initramfs will have no daemon!" 222 warn "Run setup.sh after kernel source is available." 223 # Create a placeholder so the initramfs structure is correct 224 echo '#!/bin/sh 225 echo "usbipd not found" 226 exit 1' > initramfs/usr/sbin/usbipd 227 fi 228 chmod +x initramfs/usr/sbin/usbipd 229 230 # usbip client tool (needed by /init auto-bind and /init-vhci attach loop) 231 USBIP_TOOL_SRC="" 232 for cand in \ 233 usbipd-plain-install/sbin/usbip \ 234 usbipd-plain-install/usr/sbin/usbip \ 235 usbipd-plain-install/bin/usbip \ 236 usbipd-plain-install/usr/bin/usbip; do 237 [[ -f "${cand}" ]] && USBIP_TOOL_SRC="${cand}" && break 238 done 239 if [[ -n "${USBIP_TOOL_SRC}" ]]; then 240 cp "${USBIP_TOOL_SRC}" initramfs/usr/sbin/usbip 241 chmod +x initramfs/usr/sbin/usbip 242 else 243 warn "usbip client tool not found in usbipd-plain-install (vhci mode may fail)" 244 fi 245 246 # If usbipd or usbip are dynamic, bundle their shared libraries 247 for bin in usbipd-plain "${USBIP_TOOL_SRC:-}"; do 248 [[ -n "${bin}" ]] || continue 249 [[ -f "${bin}" ]] || continue 250 if file "${bin}" | grep -q "dynamically linked"; then 251 log "Bundling shared libraries for dynamic $(basename "${bin}")..." 252 ldd "${bin}" | grep -oP '(/[^ ]+\\.so[^ ]*)' | while read lib; do 253 [[ -f "${lib}" ]] || continue 254 libdir="initramfs/$(dirname "${lib#/}")" 255 mkdir -p "${libdir}" 256 cp "${lib}" "${libdir}/" 257 done 258 fi 259 done 260 # Copy the dynamic linker 261 for ld in /lib64/ld-linux-x86-64.so.2 /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2; do 262 [[ -f "$ld" ]] && cp "$ld" initramfs/lib64/ld-linux-x86-64.so.2 && break 263 done 264 265 # /init script 266 cp qemu/init initramfs/init 267 chmod +x initramfs/init 268 269 log "Packing initramfs..." 270 (cd initramfs && find . | cpio -oH newc 2>/dev/null | gzip -9) > initramfs.cpio.gz 271 ok "initramfs.cpio.gz: $(du -sh initramfs.cpio.gz | cut -f1)" 272 fi 273 274 # Build vhci client initramfs from the base image, replacing /init. 275 if [[ -f "initramfs-vhci.cpio.gz" ]]; then 276 ok "initramfs-vhci.cpio.gz already exists, skipping." 277 else 278 [[ -d "initramfs" ]] || { warn "initramfs/ tree missing, cannot build initramfs-vhci.cpio.gz"; exit 1; } 279 log "Assembling initramfs-vhci.cpio.gz..." 280 rm -rf initramfs-vhci 281 cp -a initramfs initramfs-vhci 282 cp qemu/init-vhci initramfs-vhci/init 283 chmod +x initramfs-vhci/init 284 (cd initramfs-vhci && find . | cpio -oH newc 2>/dev/null | gzip -9) > initramfs-vhci.cpio.gz 285 ok "initramfs-vhci.cpio.gz: $(du -sh initramfs-vhci.cpio.gz | cut -f1)" 286 fi 287 288 # ── 7. net_send.c ───────────────────────────────────────────────────────────── 289 # Compile with afl-clang-fast so AFL++ can spawn it via the forkserver. 290 # The coverage of net_send.c itself is trivial (it's just a TCP relay), but 291 # building with afl-clang-fast avoids the "fork server handshake failed" error 292 # that occurs when AFL++ tries to start an uninstrumented binary as a worker. 293 if [[ ! -f "net_send" ]]; then 294 log "Compiling net_send.c (afl-clang-fast)..." 295 afl-clang-fast -O2 -Wall -o net_send net_send.c 296 ok "net_send compiled." 297 fi 298 299 # ── 8. Generate corpus (and per-harness subdirs) ───────────────────────────── 300 if [[ ! -d "corpus" ]] || [[ ! -d "corpus/protocol" ]]; then 301 log "Generating seed corpus..." 302 python3 gen_corpus.py 303 fi 304 305 # ── Done ───────────────────────────────────────────────────────────────────── 306 echo "" 307 ok "Setup complete. Next steps:" 308 echo " 1. Build harnesses: bash build_fuzzers.sh" 309 echo " 2. Start fuzzing: bash run-fuzzers.sh" 310 echo " 3. Verify QEMU: qemu-system-x86_64 -kernel bzImage -initrd initramfs.cpio.gz \\" 311 echo " -nographic -append 'console=ttyS0 quiet' -m 64M" 312 echo " Then: nc 127.0.0.1 13240 (should get a response from usbipd)"