Initial release: automated APK debuggable patching for Android

This commit is contained in:
benjamin-luescher 2026-02-02 16:57:46 +01:00
commit 18f5562aca
9 changed files with 1625 additions and 0 deletions

376
lib/proxy-setup.sh Executable file
View file

@ -0,0 +1,376 @@
#!/bin/bash
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
CONTAINER_NAME="mitmproxy-android"
PROXY_PORT=8080
WEB_PORT=8081
PROXY_PASSWORD="proxy"
MITMPROXY_DIR="$HOME/.mitmproxy"
ADB=""
EMULATOR_BIN=""
DEVICE_SERIAL=""
AVD_NAME=""
STOP_MODE=false
print_step() {
echo -e "${GREEN}==>${NC} $1"
}
print_warning() {
echo -e "${YELLOW}Warning:${NC} $1"
}
print_error() {
echo -e "${RED}Error:${NC} $1"
}
print_info() {
echo -e "${BLUE}::${NC} $1"
}
usage() {
echo "Usage: $0 [--stop] [--port <port>]"
echo ""
echo "Starts mitmproxy in Docker, restarts a running Android emulator with"
echo "HTTP proxy enabled, and installs the mitmproxy CA certificate."
echo ""
echo "Options:"
echo " --stop Stop the mitmproxy container and exit"
echo " --port <port> Proxy port (default: 8080)"
echo " --help Show this help message"
echo ""
echo "Prerequisites:"
echo " - Docker running"
echo " - An Android emulator currently running"
echo ""
echo "Examples:"
echo " $0 # Start proxy and restart emulator"
echo " $0 --port 9090 # Use a custom proxy port"
echo " $0 --stop # Stop the proxy container"
exit 0
}
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
--help|-h)
usage
;;
--stop)
STOP_MODE=true
shift
;;
--port)
if [[ -z "$2" || "$2" == --* ]]; then
print_error "--port requires a port number argument"
exit 1
fi
PROXY_PORT="$2"
shift 2
;;
-*)
print_error "Unknown option: $1"
exit 1
;;
*)
print_error "Unexpected argument: $1"
exit 1
;;
esac
done
}
find_tools() {
print_step "Searching for required tools..."
# Find adb
local sdk_locations=(
"$HOME/Library/Android/sdk"
"/Users/$USER/Library/Android/sdk"
"$ANDROID_HOME"
"$ANDROID_SDK_ROOT"
)
for loc in "${sdk_locations[@]}"; do
if [[ -n "$loc" && -x "$loc/platform-tools/adb" ]]; then
ADB="$loc/platform-tools/adb"
break
fi
done
if [[ -z "$ADB" ]]; then
if command -v adb &> /dev/null; then
ADB="$(command -v adb)"
else
print_error "Could not find adb. Please ensure Android SDK is installed and ANDROID_HOME is set."
exit 1
fi
fi
print_step "Found adb: $ADB"
# Find emulator binary
for loc in "${sdk_locations[@]}"; do
if [[ -n "$loc" && -x "$loc/emulator/emulator" ]]; then
EMULATOR_BIN="$loc/emulator/emulator"
break
fi
done
if [[ -z "$EMULATOR_BIN" ]]; then
if command -v emulator &> /dev/null; then
EMULATOR_BIN="$(command -v emulator)"
else
print_error "Could not find Android emulator binary. Please ensure Android SDK is installed."
exit 1
fi
fi
print_step "Found emulator: $EMULATOR_BIN"
# Check Docker
if ! command -v docker &> /dev/null; then
print_error "Docker is not installed or not in PATH."
exit 1
fi
if ! docker info &> /dev/null; then
print_error "Docker is not running. Please start Docker and try again."
exit 1
fi
print_step "Docker is available"
}
stop_proxy() {
print_step "Stopping mitmproxy container..."
if docker ps -q --filter "name=$CONTAINER_NAME" | grep -q .; then
docker stop "$CONTAINER_NAME"
print_step "mitmproxy container stopped"
else
print_warning "Container '$CONTAINER_NAME' is not running"
fi
}
start_mitmproxy() {
print_step "Starting mitmproxy..."
# Check if already running
if docker ps -q --filter "name=$CONTAINER_NAME" | grep -q .; then
print_step "mitmproxy container is already running"
return
fi
# Remove stopped container with same name if exists
if docker ps -aq --filter "name=$CONTAINER_NAME" | grep -q .; then
docker rm "$CONTAINER_NAME" &> /dev/null || true
fi
mkdir -p "$MITMPROXY_DIR"
docker run --rm -d \
--name "$CONTAINER_NAME" \
-p "$PROXY_PORT:8080" \
-p "127.0.0.1:$WEB_PORT:8081" \
-v "$MITMPROXY_DIR:/home/mitmproxy/.mitmproxy" \
mitmproxy/mitmproxy \
mitmweb --web-host 0.0.0.0 --set web_password="$PROXY_PASSWORD"
print_step "mitmproxy container started"
# Wait for cert file to be generated
print_step "Waiting for mitmproxy CA certificate..."
local cert_file="$MITMPROXY_DIR/mitmproxy-ca-cert.cer"
local waited=0
while [[ ! -f "$cert_file" ]]; do
if [[ "$waited" -ge 10 ]]; then
print_error "Timed out waiting for mitmproxy certificate at $cert_file"
exit 1
fi
sleep 1
waited=$((waited + 1))
done
print_step "CA certificate ready: $cert_file"
}
find_emulator() {
print_step "Looking for running emulators..."
local devices_output
devices_output=$("$ADB" devices 2>&1)
local serials=()
while IFS= read -r line; do
line=$(echo "$line" | tr -d '\r')
if [[ "$line" == "List of devices attached" ]] || [[ -z "$line" ]]; then
continue
fi
local serial state
serial=$(echo "$line" | awk '{print $1}')
state=$(echo "$line" | awk '{print $2}')
if [[ "$state" == "device" ]] && [[ "$serial" == emulator-* ]]; then
serials+=("$serial")
fi
done <<< "$devices_output"
if [[ ${#serials[@]} -eq 0 ]]; then
print_error "No running emulator found."
echo "Please start an emulator first:"
echo " emulator -list-avds # list available AVDs"
echo " emulator -avd <avd-name> & # start an emulator"
exit 1
fi
# Auto-select if only one emulator
if [[ ${#serials[@]} -eq 1 ]]; then
DEVICE_SERIAL="${serials[0]}"
else
echo ""
echo "Multiple emulators found:"
for i in "${!serials[@]}"; do
echo " $((i + 1))) ${serials[$i]}"
done
echo ""
while true; do
read -rp "Select emulator [1-${#serials[@]}]: " choice
if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#serials[@]} ]]; then
DEVICE_SERIAL="${serials[$((choice - 1))]}"
break
fi
echo "Invalid selection. Enter a number between 1 and ${#serials[@]}."
done
fi
print_step "Using emulator: $DEVICE_SERIAL"
# Get AVD name
AVD_NAME=$("$ADB" -s "$DEVICE_SERIAL" emu avd name 2>/dev/null | head -n 1 | tr -d '\r' || true)
if [[ -z "$AVD_NAME" ]]; then
print_error "Could not determine AVD name for $DEVICE_SERIAL"
exit 1
fi
print_step "AVD name: $AVD_NAME"
}
restart_emulator() {
print_step "Restarting emulator with HTTP proxy..."
# Kill running emulator
print_info "Shutting down emulator $DEVICE_SERIAL..."
"$ADB" -s "$DEVICE_SERIAL" emu kill 2>/dev/null || true
# Wait for emulator to disappear from adb devices
local waited=0
while "$ADB" devices 2>&1 | grep -q "$DEVICE_SERIAL"; do
if [[ "$waited" -ge 30 ]]; then
print_error "Timed out waiting for emulator to shut down"
exit 1
fi
sleep 1
waited=$((waited + 1))
done
print_step "Emulator stopped"
# Launch emulator with proxy
print_info "Starting emulator '$AVD_NAME' with -http-proxy http://127.0.0.1:$PROXY_PORT..."
"$EMULATOR_BIN" -avd "$AVD_NAME" -http-proxy "http://127.0.0.1:$PROXY_PORT" &
# Wait for device to come online
print_info "Waiting for emulator to boot..."
"$ADB" wait-for-device
# Poll for boot completion
waited=0
while true; do
local boot_completed
boot_completed=$("$ADB" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r' || true)
if [[ "$boot_completed" == "1" ]]; then
break
fi
if [[ "$waited" -ge 120 ]]; then
print_error "Timed out waiting for emulator to boot"
exit 1
fi
sleep 2
waited=$((waited + 2))
done
# Update DEVICE_SERIAL to the new emulator instance
DEVICE_SERIAL=$("$ADB" devices 2>&1 | grep 'emulator-' | grep 'device' | awk '{print $1}' | head -n 1 | tr -d '\r')
print_step "Emulator booted: $DEVICE_SERIAL"
}
install_cert() {
print_step "Installing mitmproxy CA certificate..."
local cert_file="$MITMPROXY_DIR/mitmproxy-ca-cert.cer"
if [[ ! -f "$cert_file" ]]; then
print_error "Certificate file not found: $cert_file"
exit 1
fi
"$ADB" -s "$DEVICE_SERIAL" push "$cert_file" /sdcard/mitmproxy-ca-cert.cer
print_step "Certificate pushed to /sdcard/mitmproxy-ca-cert.cer"
# Attempt automated install via cert installer intent
"$ADB" -s "$DEVICE_SERIAL" shell am start \
-n com.android.certinstaller/.CertInstallerMain \
-a android.intent.action.VIEW \
-t application/x-x509-ca-cert \
-d file:///sdcard/mitmproxy-ca-cert.cer 2>/dev/null || true
echo ""
print_warning "If the certificate installer did not open automatically:"
echo " 1. Open Settings → search \"certificate\""
echo " 2. Tap \"Install a certificate\" → \"CA certificate\""
echo " 3. Tap \"Install anyway\""
echo " 4. Select \"mitmproxy-ca-cert.cer\" from internal storage"
}
print_summary() {
echo ""
echo -e "${GREEN}=== Proxy Setup Complete ===${NC}"
echo ""
echo " Proxy: http://127.0.0.1:$PROXY_PORT"
echo " Web UI: http://localhost:$WEB_PORT"
echo " Password: $PROXY_PASSWORD"
echo " Emulator: $DEVICE_SERIAL ($AVD_NAME)"
echo ""
echo " The emulator is configured to route traffic through mitmproxy."
echo " Open the Web UI to inspect HTTP/HTTPS traffic."
echo ""
echo -e " ${YELLOW}Tip:${NC} To intercept HTTPS from apps targeting API 24+, use:"
echo " ./lib/make-debuggable.sh <apk-or-dir> --trust-user-certs"
echo " ./apk-debuggable.sh <app-name> --trust-user-certs"
echo ""
echo " To stop the proxy:"
echo " ./lib/proxy-setup.sh --stop"
}
main() {
parse_args "$@"
if [[ "$STOP_MODE" == true ]]; then
stop_proxy
exit 0
fi
echo ""
echo -e "${GREEN}=== Proxy Setup ===${NC}"
echo ""
find_tools
start_mitmproxy
find_emulator
restart_emulator
install_cert
print_summary
}
main "$@"