Initial release: automated APK debuggable patching for Android
This commit is contained in:
commit
18f5562aca
9 changed files with 1625 additions and 0 deletions
376
lib/proxy-setup.sh
Executable file
376
lib/proxy-setup.sh
Executable 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 "$@"
|
||||
Loading…
Add table
Add a link
Reference in a new issue