Created
March 27, 2026 18:34
-
-
Save AeonDave/e9fb3b9628f833894f929a6660a5b96c to your computer and use it in GitHub Desktop.
Agent manager and builder for AdaptixC2 Extension Kit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # ============================================================================ | |
| # compile-bofs.sh — Selectively compile BOF modules | |
| # | |
| # Auto-discovers all *-BOF directories with a Makefile. | |
| # Interactive selector lets you pick which modules to build. | |
| # Optionally syncs extension-kit.axs to include newly discovered .axs files. | |
| # ============================================================================ | |
| set -euo pipefail | |
| if [[ -t 1 ]]; then | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| CYAN='\033[0;36m' | |
| BLUE='\033[0;34m' | |
| BOLD='\033[1m' | |
| DIM='\033[2m' | |
| NC='\033[0m' | |
| else | |
| RED='' GREEN='' YELLOW='' CYAN='' BLUE='' BOLD='' DIM='' NC='' | |
| fi | |
| error_exit() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; } | |
| info_msg() { echo -e "${GREEN}[+]${NC} $1"; } | |
| warn_msg() { echo -e "${YELLOW}[!]${NC} $1"; } | |
| step_msg() { echo -e "${CYAN}[*]${NC} $1"; } | |
| show_banner() { | |
| echo -e "${CYAN}" | |
| cat << 'BANNER' | |
| ░█▀█░█▀▄░█▀█░█▀█░▀█▀░▀█▀░█░█░█▀▀░▀▀▄░░░█░█░▀█▀░▀█▀ | |
| ░█▀█░█░█░█▀█░█▀▀░░█░░░█░░▄▀▄░█░░░▄▀░░░░█▀▄░░█░░░█░ | |
| ░▀░▀░▀▀░░▀░▀░▀░░░░▀░░▀▀▀░▀░▀░▀▀▀░▀▀▀░░░▀░▀░▀▀▀░░▀░ | |
| BANNER | |
| echo -e "${NC}" | |
| echo -e "${BOLD} BOF Compiler${NC}" | |
| echo "" | |
| } | |
| SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" | |
| ACTION="" | |
| SYNC_AXS=false | |
| FAILED=() | |
| SUCCEEDED=() | |
| # ── Usage ─────────────────────────────────────────────────────────────────── | |
| usage() { | |
| cat <<EOF | |
| Usage: $0 [-a <action>] [-m <module>] [--sync-axs] | |
| Optional: | |
| -a, --action <act> Action to perform (omit for interactive selector) | |
| -m, --module <name> Build a single module by name (e.g. SAL-BOF) | |
| --sync-axs Update extension-kit.axs with any missing .axs refs | |
| -h, --help Show this help | |
| Actions: | |
| all | build Build all discovered BOF modules | |
| clean Clean all discovered BOF modules | |
| (none) Interactive selector — pick which modules to build | |
| Examples: | |
| $0 # interactive selector | |
| $0 -a all # build everything | |
| $0 -m SAL-BOF # build only SAL-BOF | |
| $0 -m Creds-BOF -m AD-BOF # build Creds-BOF and AD-BOF | |
| $0 -a all --sync-axs # build all + update extension-kit.axs | |
| $0 -a clean # clean all | |
| EOF | |
| exit 0 | |
| } | |
| # ── Parse arguments ───────────────────────────────────────────────────────── | |
| EXPLICIT_MODULES=() | |
| while [[ $# -gt 0 ]]; do | |
| case $1 in | |
| -a|--action) ACTION="$2"; shift 2 ;; | |
| -m|--module) EXPLICIT_MODULES+=("$2"); shift 2 ;; | |
| --sync-axs) SYNC_AXS=true; shift ;; | |
| -h|--help) usage ;; | |
| *) error_exit "Unknown parameter: $1" ;; | |
| esac | |
| done | |
| show_banner | |
| # ── Discover BOF modules ─────────────────────────────────────────────────── | |
| MODULES=() | |
| discover_modules() { | |
| for dir in "$SCRIPT_DIR"/*/; do | |
| local name | |
| name="$(basename "$dir")" | |
| # Skip non-BOF dirs, hidden dirs, _include, _img, _bin | |
| [[ "$name" == _* ]] && continue | |
| [[ -f "$dir/Makefile" ]] || continue | |
| MODULES+=("$name") | |
| done | |
| # Sort for consistent display | |
| IFS=$'\n' MODULES=($(sort <<<"${MODULES[*]}")); unset IFS | |
| } | |
| discover_modules | |
| (( ${#MODULES[@]} == 0 )) && error_exit "No BOF modules found in working directory" | |
| # ── Interactive selector ──────────────────────────────────────────────────── | |
| interactive_select() { | |
| local count=${#MODULES[@]} | |
| local -a selected=() | |
| local -a colors=() | |
| for (( i=0; i<count; i++ )); do | |
| selected+=( 1 ) | |
| if [[ -d "${SCRIPT_DIR}/${MODULES[$i]}/_bin" ]]; then | |
| colors+=( "$BLUE" ) # already built | |
| else | |
| colors+=( "$GREEN" ) # never built | |
| fi | |
| done | |
| local cursor=0 _menu_drawn=0 | |
| local saved_stty | |
| saved_stty="$(stty -g)" | |
| _restore_term() { stty "$saved_stty" 2>/dev/null; tput cnorm 2>/dev/null; } | |
| trap _restore_term EXIT INT TERM | |
| stty -echo -icanon min 1 time 0 | |
| tput civis 2>/dev/null | |
| _render_menu() { | |
| (( _menu_drawn )) && printf '\033[%dA' "$((count + 3))" | |
| _menu_drawn=1 | |
| printf '\r\033[K' | |
| echo -e "${BOLD}Select BOF modules to build ${DIM}(Space=toggle a=all n=none Enter=confirm q=quit)${NC}" | |
| printf '\r\033[K\n' | |
| for (( i=0; i<count; i++ )); do | |
| local arrow=" " | |
| (( i == cursor )) && arrow="> " | |
| local check=" " | |
| (( selected[i] )) && check="x" | |
| local tag="" | |
| if [[ -d "${SCRIPT_DIR}/${MODULES[$i]}/_bin" ]]; then | |
| tag="${DIM}(built)${NC}" | |
| else | |
| tag="${DIM}(new)${NC}" | |
| fi | |
| echo -e "${arrow}[${check}] ${colors[$i]}${MODULES[$i]}${NC} ${tag}" | |
| done | |
| printf '\r\033[K\n' | |
| } | |
| _render_menu | |
| while true; do | |
| local key | |
| IFS= read -rsN1 key | |
| case "$key" in | |
| $'\x1b') | |
| read -rsN2 -t 0.1 seq | |
| case "$seq" in | |
| '[A') (( cursor > 0 )) && (( cursor-- )) ;; # Up | |
| '[B') (( cursor < count-1 )) && (( cursor++ )) ;; # Down | |
| esac | |
| ;; | |
| ' ') | |
| (( selected[cursor] = !selected[cursor] )) | |
| ;; | |
| 'a'|'A') | |
| for (( i=0; i<count; i++ )); do selected[$i]=1; done | |
| ;; | |
| 'n'|'N') | |
| for (( i=0; i<count; i++ )); do selected[$i]=0; done | |
| ;; | |
| ''|$'\n') | |
| break | |
| ;; | |
| 'q'|'Q') | |
| _restore_term | |
| echo -e "${YELLOW}Aborted.${NC}" | |
| exit 0 | |
| ;; | |
| esac | |
| _render_menu | |
| done | |
| _restore_term | |
| trap - EXIT INT TERM | |
| # Rebuild MODULES from selection | |
| local -a chosen=() | |
| for (( i=0; i<count; i++ )); do | |
| (( selected[i] )) && chosen+=("${MODULES[$i]}") | |
| done | |
| MODULES=("${chosen[@]}") | |
| (( ${#MODULES[@]} == 0 )) && { echo -e "${YELLOW}Nothing selected.${NC}"; exit 0; } | |
| echo -e "${GREEN}Selected ${#MODULES[@]} module(s)${NC}" | |
| } | |
| # ── Build / Clean functions ───────────────────────────────────────────────── | |
| build_module() { | |
| local name="$1" | |
| local dir="$SCRIPT_DIR/$name" | |
| [[ -d "$dir" ]] || { warn_msg "$name: directory not found (skipping)"; FAILED+=("$name"); return 1; } | |
| [[ -f "$dir/Makefile" ]] || { warn_msg "$name: no Makefile (skipping)"; FAILED+=("$name"); return 1; } | |
| step_msg "Building ${BOLD}$name${NC}..." | |
| local build_log | |
| if build_log="$(make --no-print-directory -C "$dir" 2>&1)"; then | |
| info_msg "$name ${DIM}✓${NC}" | |
| SUCCEEDED+=("$name") | |
| else | |
| echo "$build_log" | |
| echo -e "${RED}[FAIL]${NC} $name" | |
| FAILED+=("$name") | |
| return 1 | |
| fi | |
| } | |
| clean_module() { | |
| local name="$1" | |
| local dir="$SCRIPT_DIR/$name" | |
| [[ -d "$dir" ]] || return 0 | |
| [[ -f "$dir/Makefile" ]] || return 0 | |
| step_msg "Cleaning ${BOLD}$name${NC}..." | |
| make --no-print-directory -C "$dir" clean 2>/dev/null | |
| } | |
| # ── Sync extension-kit.axs ───────────────────────────────────────────────── | |
| sync_extension_kit_axs() { | |
| local axs_file="$SCRIPT_DIR/extension-kit.axs" | |
| [[ -f "$axs_file" ]] || { warn_msg "extension-kit.axs not found"; return 1; } | |
| local -a missing=() | |
| for dir in "$SCRIPT_DIR"/*/; do | |
| local name | |
| name="$(basename "$dir")" | |
| [[ "$name" == _* ]] && continue | |
| # Find top-level .axs files in this module | |
| for axs in "$dir"/*.axs; do | |
| [[ -f "$axs" ]] || continue | |
| local rel_path="${name}/$(basename "$axs")" | |
| # Check if this .axs is already loaded in extension-kit.axs | |
| if ! grep -qF "$rel_path" "$axs_file"; then | |
| missing+=("$rel_path") | |
| fi | |
| done | |
| done | |
| if (( ${#missing[@]} == 0 )); then | |
| info_msg "extension-kit.axs is up to date" | |
| return 0 | |
| fi | |
| echo "" | |
| step_msg "Found ${#missing[@]} .axs file(s) not in extension-kit.axs:" | |
| for m in "${missing[@]}"; do | |
| echo -e " ${GREEN}+${NC} $m" | |
| done | |
| # Build the new load lines | |
| local new_lines="" | |
| for m in "${missing[@]}"; do | |
| new_lines+="ax.script_load(path + \"${m}\");\n" | |
| done | |
| # Insert before the closing line (or append before last empty line) | |
| # Find the last ax.script_load line number and insert after it | |
| local last_load_line | |
| last_load_line="$(grep -n 'ax\.script_load' "$axs_file" | tail -1 | cut -d: -f1)" | |
| if [[ -n "$last_load_line" ]]; then | |
| # Insert new lines after the last ax.script_load | |
| local head_part tail_part | |
| head_part="$(head -n "$last_load_line" "$axs_file")" | |
| tail_part="$(tail -n +"$((last_load_line + 1))" "$axs_file")" | |
| { | |
| echo "$head_part" | |
| echo -e "$new_lines" | |
| echo "$tail_part" | |
| } > "${axs_file}.tmp" | |
| mv "${axs_file}.tmp" "$axs_file" | |
| info_msg "Updated extension-kit.axs with ${#missing[@]} new entry(s)" | |
| else | |
| warn_msg "Could not find insertion point in extension-kit.axs" | |
| fi | |
| } | |
| # ── Dispatch ──────────────────────────────────────────────────────────────── | |
| # If explicit modules given via -m, use those | |
| if (( ${#EXPLICIT_MODULES[@]} > 0 )); then | |
| MODULES=("${EXPLICIT_MODULES[@]}") | |
| ACTION="build" | |
| fi | |
| # Interactive selector when no action and no explicit modules | |
| if [[ -z "$ACTION" ]]; then | |
| interactive_select | |
| ACTION="build" | |
| fi | |
| case $ACTION in | |
| all|build) | |
| echo "" | |
| step_msg "${BOLD}Building ${#MODULES[@]} module(s)${NC}" | |
| echo "" | |
| for name in "${MODULES[@]}"; do | |
| build_module "$name" | |
| done | |
| ;; | |
| clean) | |
| echo "" | |
| step_msg "${BOLD}Cleaning ${#MODULES[@]} module(s)${NC}" | |
| echo "" | |
| for name in "${MODULES[@]}"; do | |
| clean_module "$name" | |
| done | |
| info_msg "Clean complete" | |
| ;; | |
| *) | |
| error_exit "Unknown action: $ACTION" | |
| ;; | |
| esac | |
| # Sync extension-kit.axs if requested | |
| if $SYNC_AXS && [[ "$ACTION" != "clean" ]]; then | |
| sync_extension_kit_axs | |
| fi | |
| # ── Summary ───────────────────────────────────────────────────────────────── | |
| if [[ "$ACTION" != "clean" ]]; then | |
| echo "" | |
| echo -e "${BOLD}━━━ Summary ━━━${NC}" | |
| (( ${#SUCCEEDED[@]} > 0 )) && echo -e " ${GREEN}OK:${NC} ${SUCCEEDED[*]}" | |
| (( ${#FAILED[@]} > 0 )) && echo -e " ${RED}FAIL:${NC} ${FAILED[*]}" | |
| echo -e " ${DIM}Total: ${#SUCCEEDED[@]}/${#MODULES[@]}${NC}" | |
| if (( ${#FAILED[@]} > 0 )); then | |
| exit 1 | |
| fi | |
| fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # manage-agents.sh — Interactively add or remove agent types from Extension Kit registrations. | |
| set -euo pipefail | |
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| # Extended regex matching all registration call types | |
| RE='(ax\.register_commands_group|menu\.add_session_access|menu\.add_processbrowser)\(' | |
| if [[ -t 1 ]]; then | |
| B='\033[1m'; D='\033[2m'; G='\033[32m'; R='\033[31m' | |
| C='\033[36m'; Y='\033[33m'; N='\033[0m' | |
| else | |
| B=''; D=''; G=''; R=''; C=''; Y=''; N='' | |
| fi | |
| show_banner() { | |
| printf "${C}" | |
| cat << 'BANNER' | |
| ░█▀█░█▀▄░█▀█░█▀█░▀█▀░▀█▀░█░█░█▀▀░▀▀▄░░░█░█░▀█▀░▀█▀ | |
| ░█▀█░█░█░█▀█░█▀▀░░█░░░█░░▄▀▄░█░░░▄▀░░░░█▀▄░░█░░░█░ | |
| ░▀░▀░▀▀░░▀░▀░▀░░░░▀░░▀▀▀░▀░▀░▀▀▀░▀▀▀░░░▀░▀░▀▀▀░░▀░ | |
| BANNER | |
| printf "${N}" | |
| printf "${B} Agent Type Manager${N}\n" | |
| } | |
| info() { printf "${G}>>>${N} %s\n" "$1"; } | |
| # --------------------------------------------------------------------------- | |
| find_files() { | |
| grep -rlE "$RE" "$SCRIPT_DIR" --include='*.axs' 2>/dev/null | sort | |
| } | |
| # Extract content of the first [...] on a line (= agent-type array) | |
| first_array() { sed 's/[^[]*\[//; s/\].*//'; } | |
| collect_types() { | |
| grep -rhE "$RE" "$SCRIPT_DIR" --include='*.axs' 2>/dev/null \ | |
| | first_array | tr ',' '\n' \ | |
| | sed 's/^[[:space:]]*//; s/[[:space:]]*$//' | tr -d '"' \ | |
| | sort -u | grep -v '^$' | |
| } | |
| show_status() { | |
| printf "\n${B}Registered agent types${N}\n" | |
| local types | |
| types=$(collect_types) | |
| if [[ -z "$types" ]]; then | |
| echo " (none found)" | |
| printf "\n" | |
| return | |
| fi | |
| while IFS= read -r t; do | |
| local cnt | |
| cnt=$(grep -rhE "$RE" "$SCRIPT_DIR" --include='*.axs' 2>/dev/null \ | |
| | grep -c "\"${t}\"" || true) | |
| printf " ${C}%-24s${N} %d line(s)\n" "$t" "$cnt" | |
| done <<< "$types" | |
| printf "\n${B}Detail by file${N}\n\n" | |
| local files | |
| files=$(find_files) | |
| while IFS= read -r f; do | |
| printf " ${C}%s${N}\n" "${f#"$SCRIPT_DIR"/}" | |
| grep -nE "$RE" "$f" | while IFS= read -r match; do | |
| local num="${match%%:*}" rest="${match#*:}" | |
| local fn agents | |
| fn=$(echo "$rest" | grep -oE 'register_commands_group|add_session_access|add_processbrowser') | |
| agents=$(echo "$rest" | first_array) | |
| printf " ${D}L%-4s${N} %-28s [%s]\n" "$num" "$fn" "$agents" | |
| done | |
| done <<< "$files" | |
| printf "\n" | |
| } | |
| # --------------------------------------------------------------------------- | |
| do_add() { | |
| local agent="$1" | |
| local files modified=0 skipped=0 | |
| files=$(find_files) | |
| [[ -z "$files" ]] && { echo "No registration files found."; return 1; } | |
| while IFS= read -r f; do | |
| local rel="${f#"$SCRIPT_DIR"/}" | |
| # Check if any registration line in this file is missing the type | |
| if grep -E "$RE" "$f" | grep -qv "\"${agent}\""; then | |
| # On matching lines that lack the type, append before closing ] | |
| sed -i -E "/$RE/{/\"${agent}\"/!s/\"]/\", \"${agent}\"]/}" "$f" | |
| printf " ${G}+${N} %s\n" "$rel" | |
| modified=$((modified + 1)) | |
| else | |
| printf " ${D}-${N} %s ${D}(already present)${N}\n" "$rel" | |
| skipped=$((skipped + 1)) | |
| fi | |
| done <<< "$files" | |
| info "Added \"${agent}\" — ${modified} file(s) modified, ${skipped} unchanged." | |
| } | |
| do_remove() { | |
| local agent="$1" | |
| local files modified=0 skipped=0 | |
| files=$(find_files) | |
| [[ -z "$files" ]] && { echo "No registration files found."; return 1; } | |
| while IFS= read -r f; do | |
| local rel="${f#"$SCRIPT_DIR"/}" | |
| if ! grep -E "$RE" "$f" | grep -q "\"${agent}\""; then | |
| printf " ${D}-${N} %s ${D}(not present)${N}\n" "$rel" | |
| skipped=$((skipped + 1)) | |
| continue | |
| fi | |
| # Remove from beginning/middle ("type", ) then from end (, "type") | |
| sed -i -E "/$RE/{s/\"${agent}\", //; s/, \"${agent}\"//}" "$f" | |
| # Warn if any lines still contain it (sole-element — can't remove without empty array) | |
| if grep -E "$RE" "$f" | grep -q "\"${agent}\""; then | |
| printf " ${Y}!${N} %s ${Y}(sole type on some lines — skipped to avoid empty array)${N}\n" "$rel" | |
| else | |
| printf " ${R}x${N} %s\n" "$rel" | |
| fi | |
| modified=$((modified + 1)) | |
| done <<< "$files" | |
| info "Removed \"${agent}\" from ${modified} file(s), ${skipped} unchanged." | |
| } | |
| # --------------------------------------------------------------------------- | |
| # Main loop | |
| # --------------------------------------------------------------------------- | |
| show_banner | |
| while true; do | |
| show_status | |
| printf " ${B}[a]${N}dd ${B}[r]${N}emove ${B}[q]${N}uit\n" | |
| read -rp "> " action | |
| case "${action,,}" in | |
| a|add) | |
| read -rp "Agent type to add: " agent | |
| [[ -z "$agent" ]] && continue | |
| if [[ ! "$agent" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]]; then | |
| printf "${R}Invalid name.${N} Use [a-zA-Z_][a-zA-Z0-9_]*\n" | |
| continue | |
| fi | |
| printf "\n" | |
| do_add "$agent" | |
| ;; | |
| r|remove|rm) | |
| read -rp "Agent type to remove: " agent | |
| [[ -z "$agent" ]] && continue | |
| printf "\n" | |
| do_remove "$agent" | |
| ;; | |
| q|quit|exit) | |
| exit 0 | |
| ;; | |
| *) | |
| printf "Unknown action. Use ${B}a${N}, ${B}r${N}, or ${B}q${N}.\n" | |
| ;; | |
| esac | |
| done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment