Initial commit — FlipperDroid v0.1.0-poc
KernelSU module + Flipper Zero FAP that bridges both devices into a unified pentesting platform over USB CDC serial / BT rfcomm. Android side: bridge daemon, WebUI (:8089), bind mount namespace isolation stealth engine. Flipper side: proper FAP with 4-view GUI, GPIO/SubGHz/IR/file command handlers, async event streaming.
This commit is contained in:
365
system/bin/flipperdroid-webui
Normal file
365
system/bin/flipperdroid-webui
Normal file
@@ -0,0 +1,365 @@
|
||||
#!/system/bin/sh
|
||||
# FlipperDroid WebUI — HTTP server with bridge API endpoints
|
||||
|
||||
WEBROOT="/data/adb/modules/flipperdroid/webroot"
|
||||
CONFIG_DIR="/data/adb/flipperdroid"
|
||||
CONFIG_FILE="$CONFIG_DIR/config.sh"
|
||||
CMD_FIFO="$CONFIG_DIR/cmd_fifo"
|
||||
RESP_DIR="$CONFIG_DIR/responses"
|
||||
PORT=8089
|
||||
|
||||
mkdir -p "$CONFIG_DIR" "$RESP_DIR"
|
||||
source "$CONFIG_FILE" 2>/dev/null
|
||||
PORT="${WEBUI_PORT:-$PORT}"
|
||||
|
||||
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] [webui] $1"; }
|
||||
|
||||
# Generate unique request ID
|
||||
req_id() { echo "req_$(date +%s%N | cut -c1-16)_$$"; }
|
||||
|
||||
# Send command to bridge daemon via FIFO and wait for response
|
||||
bridge_cmd() {
|
||||
local subsystem="$1"
|
||||
local action="$2"
|
||||
local params="${3:-}"
|
||||
local rid=$(req_id)
|
||||
local timeout=10
|
||||
|
||||
echo "${rid}|${subsystem}|${action}|${params}" > "$CMD_FIFO" 2>/dev/null
|
||||
|
||||
# Wait for response file
|
||||
local waited=0
|
||||
while [ ! -f "$RESP_DIR/${rid}" ] && [ $waited -lt $timeout ]; do
|
||||
sleep 0.1
|
||||
waited=$((waited + 1))
|
||||
done
|
||||
|
||||
if [ -f "$RESP_DIR/${rid}" ]; then
|
||||
cat "$RESP_DIR/${rid}"
|
||||
rm -f "$RESP_DIR/${rid}"
|
||||
else
|
||||
echo "ERR:timeout"
|
||||
fi
|
||||
}
|
||||
|
||||
# Read config as JSON
|
||||
config_json() {
|
||||
source "$CONFIG_FILE" 2>/dev/null
|
||||
local status=$(cat "$CONFIG_DIR/status" 2>/dev/null || echo "unknown")
|
||||
local conn_type=$(cat "$CONFIG_DIR/conn_type" 2>/dev/null || echo "none")
|
||||
local flipper_dev=$(cat "$CONFIG_DIR/flipper_dev" 2>/dev/null || echo "")
|
||||
local flipper_product=$(cat "$CONFIG_DIR/flipper_product" 2>/dev/null || echo "")
|
||||
local flipper_serial=$(cat "$CONFIG_DIR/flipper_serial" 2>/dev/null || echo "")
|
||||
local flipper_version=$(cat "$CONFIG_DIR/flipper_version" 2>/dev/null || echo "")
|
||||
local flipper_caps=$(cat "$CONFIG_DIR/flipper_caps" 2>/dev/null || echo "")
|
||||
|
||||
cat << EOF
|
||||
{
|
||||
"status": "$status",
|
||||
"conn_type": "$conn_type",
|
||||
"conn_mode": "${CONN_MODE:-auto}",
|
||||
"device": "$flipper_dev",
|
||||
"product": "$flipper_product",
|
||||
"serial": "$flipper_serial",
|
||||
"firmware_version": "$flipper_version",
|
||||
"capabilities": "$flipper_caps",
|
||||
"config": {
|
||||
"auto_connect": ${AUTO_CONNECT:-1},
|
||||
"baud_rate": ${BAUD_RATE:-115200},
|
||||
"webui_port": ${WEBUI_PORT:-8089},
|
||||
"enable_gpio": ${ENABLE_GPIO:-1},
|
||||
"enable_subghz": ${ENABLE_SUBGHZ:-1},
|
||||
"enable_rfid": ${ENABLE_RFID:-1},
|
||||
"enable_nfc": ${ENABLE_NFC:-1},
|
||||
"enable_ir": ${ENABLE_IR:-1},
|
||||
"enable_ibutton": ${ENABLE_IBUTTON:-1},
|
||||
"enable_badusb": ${ENABLE_BADUSB:-0},
|
||||
"cpu_share": ${CPU_SHARE:-1},
|
||||
"cpu_share_threads": ${CPU_SHARE_THREADS:-2},
|
||||
"log_level": ${LOG_LEVEL:-2}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
# Get recent events as JSON array
|
||||
events_json() {
|
||||
local count="${1:-20}"
|
||||
local events_file="$CONFIG_DIR/events"
|
||||
[ ! -f "$events_file" ] && echo "[]" && return
|
||||
|
||||
echo -n "["
|
||||
local first=1
|
||||
tail -n "$count" "$events_file" | while IFS='|' read -r type data1 data2 ts; do
|
||||
[ $first -eq 1 ] && first=0 || echo -n ","
|
||||
echo -n "{\"type\":\"$type\",\"data\":\"$data1\",\"extra\":\"$data2\",\"timestamp\":$ts}"
|
||||
done
|
||||
echo "]"
|
||||
}
|
||||
|
||||
#############################
|
||||
# HTTP request handler
|
||||
#############################
|
||||
|
||||
handle_request() {
|
||||
local method=""
|
||||
local path=""
|
||||
local content_length=0
|
||||
local body=""
|
||||
|
||||
# Read request line
|
||||
read -r line
|
||||
method=$(echo "$line" | awk '{print $1}')
|
||||
path=$(echo "$line" | awk '{print $2}')
|
||||
|
||||
# Read headers
|
||||
while read -r header; do
|
||||
header=$(echo "$header" | tr -d '\r')
|
||||
[ -z "$header" ] && break
|
||||
case "$header" in
|
||||
Content-Length:*|content-length:*)
|
||||
content_length=$(echo "$header" | awk '{print $2}')
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Read body if POST
|
||||
if [ "$method" = "POST" ] && [ "$content_length" -gt 0 ] 2>/dev/null; then
|
||||
body=$(dd bs=1 count="$content_length" 2>/dev/null)
|
||||
fi
|
||||
|
||||
# Route
|
||||
local response=""
|
||||
local content_type="application/json"
|
||||
local status="200 OK"
|
||||
|
||||
case "$method $path" in
|
||||
# Static files
|
||||
"GET /"|"GET /index.html")
|
||||
content_type="text/html"
|
||||
response=$(cat "$WEBROOT/index.html" 2>/dev/null)
|
||||
;;
|
||||
"GET /css/style.css")
|
||||
content_type="text/css"
|
||||
response=$(cat "$WEBROOT/css/style.css" 2>/dev/null)
|
||||
;;
|
||||
"GET /js/app.js")
|
||||
content_type="application/javascript"
|
||||
response=$(cat "$WEBROOT/js/app.js" 2>/dev/null)
|
||||
;;
|
||||
|
||||
# API: Status & Config
|
||||
"GET /api/status")
|
||||
response=$(config_json)
|
||||
;;
|
||||
"GET /api/events"*)
|
||||
response=$(events_json 50)
|
||||
;;
|
||||
"GET /api/log")
|
||||
content_type="text/plain"
|
||||
response=$(tail -n 100 "$CONFIG_DIR/logs/flipperdroid.log" 2>/dev/null)
|
||||
;;
|
||||
|
||||
# API: System commands
|
||||
"POST /api/system/ping")
|
||||
response="{\"result\":\"$(bridge_cmd system ping)\"}"
|
||||
;;
|
||||
"POST /api/system/version")
|
||||
response="{\"result\":\"$(bridge_cmd system version)\"}"
|
||||
;;
|
||||
"POST /api/system/status")
|
||||
response="{\"result\":\"$(bridge_cmd system status)\"}"
|
||||
;;
|
||||
|
||||
# API: GPIO
|
||||
"POST /api/gpio/init")
|
||||
local pin=$(echo "$body" | grep -o '"pin":[0-9]*' | grep -o '[0-9]*')
|
||||
local mode=$(echo "$body" | grep -o '"mode":[0-9]*' | grep -o '[0-9]*')
|
||||
response="{\"result\":\"$(bridge_cmd gpio init "$pin,$mode")\"}"
|
||||
;;
|
||||
"POST /api/gpio/write")
|
||||
local pin=$(echo "$body" | grep -o '"pin":[0-9]*' | grep -o '[0-9]*')
|
||||
local val=$(echo "$body" | grep -o '"value":[0-9]*' | grep -o '[0-9]*')
|
||||
response="{\"result\":\"$(bridge_cmd gpio write "$pin,$val")\"}"
|
||||
;;
|
||||
"POST /api/gpio/read")
|
||||
local pin=$(echo "$body" | grep -o '"pin":[0-9]*' | grep -o '[0-9]*')
|
||||
response="{\"result\":\"$(bridge_cmd gpio read "$pin")\"}"
|
||||
;;
|
||||
"POST /api/gpio/pwm")
|
||||
local pin=$(echo "$body" | grep -o '"pin":[0-9]*' | grep -o '[0-9]*')
|
||||
local freq=$(echo "$body" | grep -o '"freq":[0-9]*' | grep -o '[0-9]*')
|
||||
local duty=$(echo "$body" | grep -o '"duty":[0-9]*' | grep -o '[0-9]*')
|
||||
response="{\"result\":\"$(bridge_cmd gpio pwm "$pin,$freq,$duty")\"}"
|
||||
;;
|
||||
"POST /api/gpio/adc")
|
||||
local pin=$(echo "$body" | grep -o '"pin":[0-9]*' | grep -o '[0-9]*')
|
||||
response="{\"result\":\"$(bridge_cmd gpio adc "$pin")\"}"
|
||||
;;
|
||||
|
||||
# API: SubGHz
|
||||
"POST /api/subghz/set_freq")
|
||||
local freq=$(echo "$body" | grep -o '"freq":[0-9]*' | grep -o '[0-9]*')
|
||||
response="{\"result\":\"$(bridge_cmd subghz set_freq "$freq")\"}"
|
||||
;;
|
||||
"POST /api/subghz/tx")
|
||||
local data=$(echo "$body" | grep -o '"data":"[^"]*"' | cut -d'"' -f4)
|
||||
response="{\"result\":\"$(bridge_cmd subghz tx "$data")\"}"
|
||||
;;
|
||||
"POST /api/subghz/rx_start")
|
||||
response="{\"result\":\"$(bridge_cmd subghz rx_start)\"}"
|
||||
;;
|
||||
"POST /api/subghz/rx_stop")
|
||||
response="{\"result\":\"$(bridge_cmd subghz rx_stop)\"}"
|
||||
;;
|
||||
"POST /api/subghz/get_rssi")
|
||||
response="{\"result\":\"$(bridge_cmd subghz get_rssi)\"}"
|
||||
;;
|
||||
"POST /api/subghz/replay")
|
||||
local slot=$(echo "$body" | grep -o '"slot":[0-9]*' | grep -o '[0-9]*')
|
||||
response="{\"result\":\"$(bridge_cmd subghz replay "$slot")\"}"
|
||||
;;
|
||||
|
||||
# API: RFID
|
||||
"POST /api/rfid/read")
|
||||
response="{\"result\":\"$(bridge_cmd rfid read)\"}"
|
||||
;;
|
||||
"POST /api/rfid/emulate")
|
||||
local data=$(echo "$body" | grep -o '"data":"[^"]*"' | cut -d'"' -f4)
|
||||
response="{\"result\":\"$(bridge_cmd rfid emulate "$data")\"}"
|
||||
;;
|
||||
|
||||
# API: NFC
|
||||
"POST /api/nfc/poll")
|
||||
response="{\"result\":\"$(bridge_cmd nfc poll)\"}"
|
||||
;;
|
||||
"POST /api/nfc/read_full")
|
||||
response="{\"result\":\"$(bridge_cmd nfc read_full)\"}"
|
||||
;;
|
||||
"POST /api/nfc/emulate")
|
||||
local data=$(echo "$body" | grep -o '"data":"[^"]*"' | cut -d'"' -f4)
|
||||
local type=$(echo "$body" | grep -o '"type":[0-9]*' | grep -o '[0-9]*')
|
||||
response="{\"result\":\"$(bridge_cmd nfc emulate "${data}${type}")\"}"
|
||||
;;
|
||||
"POST /api/nfc/relay_start")
|
||||
response="{\"result\":\"$(bridge_cmd nfc relay_start)\"}"
|
||||
;;
|
||||
"POST /api/nfc/relay_stop")
|
||||
response="{\"result\":\"$(bridge_cmd nfc relay_stop)\"}"
|
||||
;;
|
||||
|
||||
# API: IR
|
||||
"POST /api/ir/tx")
|
||||
local proto=$(echo "$body" | grep -o '"protocol":[0-9]*' | grep -o '[0-9]*')
|
||||
local addr=$(echo "$body" | grep -o '"address":[0-9]*' | grep -o '[0-9]*')
|
||||
local cmd=$(echo "$body" | grep -o '"command":[0-9]*' | grep -o '[0-9]*')
|
||||
response="{\"result\":\"$(bridge_cmd ir tx "$proto,$addr,$cmd")\"}"
|
||||
;;
|
||||
"POST /api/ir/rx_start")
|
||||
response="{\"result\":\"$(bridge_cmd ir rx_start)\"}"
|
||||
;;
|
||||
"POST /api/ir/rx_stop")
|
||||
response="{\"result\":\"$(bridge_cmd ir rx_stop)\"}"
|
||||
;;
|
||||
"POST /api/ir/replay")
|
||||
local slot=$(echo "$body" | grep -o '"slot":[0-9]*' | grep -o '[0-9]*')
|
||||
response="{\"result\":\"$(bridge_cmd ir replay "$slot")\"}"
|
||||
;;
|
||||
|
||||
# API: iButton
|
||||
"POST /api/ibutton/read")
|
||||
response="{\"result\":\"$(bridge_cmd ibutton read)\"}"
|
||||
;;
|
||||
"POST /api/ibutton/emulate")
|
||||
local data=$(echo "$body" | grep -o '"data":"[^"]*"' | cut -d'"' -f4)
|
||||
response="{\"result\":\"$(bridge_cmd ibutton emulate "$data")\"}"
|
||||
;;
|
||||
|
||||
# API: BadUSB
|
||||
"POST /api/badusb/start")
|
||||
response="{\"result\":\"$(bridge_cmd badusb start)\"}"
|
||||
;;
|
||||
"POST /api/badusb/exec")
|
||||
local script=$(echo "$body" | grep -o '"script":"[^"]*"' | cut -d'"' -f4)
|
||||
response="{\"result\":\"$(bridge_cmd badusb exec "$script")\"}"
|
||||
;;
|
||||
"POST /api/badusb/stop")
|
||||
response="{\"result\":\"$(bridge_cmd badusb stop)\"}"
|
||||
;;
|
||||
|
||||
# API: File operations on Flipper SD
|
||||
"POST /api/file/list")
|
||||
local fpath=$(echo "$body" | grep -o '"path":"[^"]*"' | cut -d'"' -f4)
|
||||
response="{\"result\":\"$(bridge_cmd file list "$fpath")\"}"
|
||||
;;
|
||||
|
||||
# API: Config update
|
||||
"POST /api/config")
|
||||
# Write config values back
|
||||
local key=$(echo "$body" | grep -o '"key":"[^"]*"' | cut -d'"' -f4)
|
||||
local value=$(echo "$body" | grep -o '"value":"[^"]*"' | cut -d'"' -f4)
|
||||
if [ -n "$key" ] && [ -n "$value" ]; then
|
||||
sed -i "s/^${key}=.*/${key}=${value}/" "$CONFIG_FILE"
|
||||
source "$CONFIG_FILE"
|
||||
response="{\"ok\":true}"
|
||||
else
|
||||
response="{\"ok\":false,\"error\":\"missing key or value\"}"
|
||||
fi
|
||||
;;
|
||||
|
||||
# API: Reconnect
|
||||
"POST /api/reconnect")
|
||||
echo "disconnected" > "$CONFIG_DIR/status"
|
||||
response="{\"ok\":true,\"message\":\"reconnecting...\"}"
|
||||
;;
|
||||
|
||||
# API: Stealth
|
||||
"GET /api/stealth/status")
|
||||
response=$(/system/bin/fd-stealth status 2>/dev/null || echo '{"error":"stealth not available"}')
|
||||
;;
|
||||
"POST /api/stealth/apply")
|
||||
/system/bin/fd-stealth apply >> "$CONFIG_DIR/logs/stealth.log" 2>&1
|
||||
response="{\"ok\":true,\"message\":\"stealth applied\"}"
|
||||
;;
|
||||
"POST /api/stealth/teardown")
|
||||
/system/bin/fd-stealth teardown >> "$CONFIG_DIR/logs/stealth.log" 2>&1
|
||||
response="{\"ok\":true,\"message\":\"stealth removed\"}"
|
||||
;;
|
||||
"POST /api/stealth/hide")
|
||||
/system/bin/fd-stealth hide-dev >> "$CONFIG_DIR/logs/stealth.log" 2>&1
|
||||
response="{\"ok\":true,\"message\":\"device hidden\"}"
|
||||
;;
|
||||
"POST /api/stealth/show")
|
||||
/system/bin/fd-stealth show-dev >> "$CONFIG_DIR/logs/stealth.log" 2>&1
|
||||
response="{\"ok\":true,\"message\":\"device visible\"}"
|
||||
;;
|
||||
|
||||
*)
|
||||
status="404 Not Found"
|
||||
response="{\"error\":\"not found\"}"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Send HTTP response
|
||||
local content_length=${#response}
|
||||
printf "HTTP/1.1 %s\r\n" "$status"
|
||||
printf "Content-Type: %s\r\n" "$content_type"
|
||||
printf "Content-Length: %d\r\n" "$content_length"
|
||||
printf "Access-Control-Allow-Origin: *\r\n"
|
||||
printf "Connection: close\r\n"
|
||||
printf "\r\n"
|
||||
printf "%s" "$response"
|
||||
}
|
||||
|
||||
#############################
|
||||
# Start HTTP server
|
||||
#############################
|
||||
|
||||
log "FlipperDroid WebUI starting on port $PORT"
|
||||
|
||||
# Use busybox nc or toybox for HTTP serving
|
||||
while true; do
|
||||
handle_request | busybox nc -l -p "$PORT" -w 30 2>/dev/null || \
|
||||
handle_request | toybox nc -l -p "$PORT" -W 30 2>/dev/null || \
|
||||
handle_request | nc -l -p "$PORT" 2>/dev/null
|
||||
done
|
||||
Reference in New Issue
Block a user