#!/usr/bin/env bash set -Eeuo pipefail # Usage: # ./build.sh [--debug] # # Examples: # ./build.sh /path/to/agent.dll /tmp/output # ./build.sh --debug /path/to/agent.dll /tmp/output MODE="release" DLL_PATH="" OUTPUT_PATH="" readonly LINKER="./dist/link" readonly SPEC_FILE="spec/loader.spec" readonly STUB_HEADER="stub/pic.h" readonly RELEASE_BIN_FILENAME="out.x64.bin" readonly DEBUG_BIN_FILENAME="out.x64.debug.bin" readonly RULES_FILENAME="rules.yar" readonly STUB_FILENAME="stub.exe" usage() { cat <<'EOF' Usage: ./build.sh [--debug] Arguments: dll_path Path to the DLL to link output_path Directory where output files will be written Options: --debug Build the debug version instead of the release version -h, --help Show this help message EOF } log() { printf '[*] %s\n' "$*" } error() { printf '[!] %s\n' "$*" >&2 } require_command() { local cmd="$1" if ! command -v "$cmd" >/dev/null 2>&1; then error "Command not found: $cmd" exit 1 fi } require_file() { local file_path="$1" if [[ ! -f "$file_path" ]]; then error "File not found: $file_path" exit 1 fi } ensure_directory() { local dir_path="$1" mkdir -p "$dir_path" } parse_args() { while [[ $# -gt 0 ]]; do case "$1" in --debug) MODE="debug" shift ;; -h|--help) usage exit 0 ;; --) shift break ;; -*) error "Unknown option: $1" usage exit 1 ;; *) if [[ -z "$DLL_PATH" ]]; then DLL_PATH="$1" elif [[ -z "$OUTPUT_PATH" ]]; then OUTPUT_PATH="$1" else error "Unexpected argument: $1" usage exit 1 fi shift ;; esac done if [[ -z "$DLL_PATH" || -z "$OUTPUT_PATH" ]]; then usage exit 1 fi } build_release() { local release_bin_output local rules_output local stub_output release_bin_output="$OUTPUT_PATH/$RELEASE_BIN_FILENAME" rules_output="$OUTPUT_PATH/$RULES_FILENAME" log "Building release version" ensure_directory "$OUTPUT_PATH" make -s clean make -s release "$LINKER" "$SPEC_FILE" "$DLL_PATH" "$release_bin_output" -g "$rules_output" -r %root=./ %MODE=release xxd -i -n crystal_loader "$release_bin_output" > "$STUB_HEADER" make -s stub "STUB_OUTPUT=$OUTPUT_PATH/$STUB_FILENAME" log "Release build completed successfully" } build_debug() { local debug_bin_output debug_bin_output="$OUTPUT_PATH/$DEBUG_BIN_FILENAME" log "Building debug version" ensure_directory "$OUTPUT_PATH" make -s clean make -s debug "$LINKER" "$SPEC_FILE" "$DLL_PATH" "$debug_bin_output" -r %root=./ %MODE=debug log "Debug build completed successfully" } main() { parse_args "$@" require_command make require_command xxd require_file "$DLL_PATH" require_file "$LINKER" require_file "$SPEC_FILE" case "$MODE" in release) build_release ;; debug) build_debug ;; *) error "Invalid mode: $MODE" exit 1 ;; esac } main "$@"