etc.git


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)"