This change introduces the ability to patch local APK files or directories, support for separate source and target devices, and detection of common anti-tampering libraries. Key changes: - **Local APK Support**: Added `--apk <path>` flag to use local `.apk` files or split-APK directories instead of pulling from a device. - **Two-Device Workflow**: Added `--source <serial>` flag to pull an APK from one device (e.g., a Play Store emulator) and install the patched version on another (e.g., a `userdebug` emulator). - **Anti-Tampering Detection**: The patching script now scans for known integrity-protection libraries (e.g., PairIP, DexGuard, Bangcle) and issues a warning if detected. - **Improved Disassembly**: Introduced a `--no-res` optimization when user certificate trust is not required, avoiding common `apktool` resource decoding errors. - **Package Name Extraction**: Integrated `aapt2` to automatically detect package names from local APK files for cleaner uninstalls. - **Enhanced Device Selection**: Updated the interactive menu to handle source/target selection and filter unauthorized devices more effectively. - **Documentation**: Updated `README.md` and `CLAUDE.md` with new usage examples and information regarding anti-tampering limitations.
7 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
Three Bash scripts for making Android APKs debuggable and intercepting traffic:
apk-debuggable.sh— End-to-end automation that handles device selection, APK extraction, patching (vialib/make-debuggable.sh), and reinstallation. Forwards--trust-user-certstolib/make-debuggable.sh.lib/make-debuggable.sh— Core tool that converts release APKs into debuggable versions. It disassembles the APK, patchesAndroidManifest.xmlto setandroid:debuggable="true", reassembles, and re-signs with a debug keystore. Optionally injectsnetwork_security_config.xmlto trust user CA certs (--trust-user-certs).lib/proxy-setup.sh— Starts mitmproxy in Docker, restarts a running Android emulator with HTTP proxy enabled, and installs the mitmproxy CA certificate.
Usage
# Automated end-to-end (device → extract → patch → reinstall)
./apk-debuggable.sh <app-name> [--device <serial>] [--keep] [--trust-user-certs] [--proxy]
# Use a local APK file or split-APK directory
./apk-debuggable.sh --apk <path> [--device <serial>] [--keep] [--trust-user-certs] [--proxy]
# Single APK
./lib/make-debuggable.sh <path-to-apk> [output-apk] [--trust-user-certs]
# Split APK directory (contains base.apk + split APKs)
./lib/make-debuggable.sh <directory> [output-directory] [--trust-user-certs]
# Start mitmproxy and restart emulator with proxy
./lib/proxy-setup.sh
# Stop mitmproxy
./lib/proxy-setup.sh --stop
There are no build, test, or lint commands.
Script Architecture
lib/make-debuggable.sh is organized into these key functions:
find_android_tools()— Auto-discovers Android SDK, apksigner, Java/JDK, keytool, and apktool from standard macOS paths and environment variables (ANDROID_HOME,ANDROID_SDK_ROOT,JAVA_HOME)ensure_keystore()— Generates a debug keystore (debug-resign.keystore) if one doesn't existsign_apk()— Signs an APK using apksigner with the debug keystoreinject_network_security_config()— Createsres/xml/network_security_config.xmltrusting system + user CAs, patches manifest to reference it. Called when--trust-user-certsis set.process_single_apk()— Disassembles APK via apktool, patches the manifest withsed, optionally injects network security config, reassembles, signs, and verifiesprocess_split_apks()— Handles split APK bundles by processingbase.apkthen signing all splitsmain()— Entry point that parses--trust-user-certsflag, detects input type (file vs directory), and routes accordingly
apk-debuggable.sh Architecture
Automation wrapper that orchestrates the full device-to-device workflow. Each function sets globals consumed by subsequent steps:
parse_args()— Parses positionalAPP_NAME+ optional--apk,--device,--keep,--trust-user-certs,--proxyflags (--proxyimplies--trust-user-certs). Validates that exactly one ofAPP_NAMEor--apkis provided.find_adb()— Discoversadbfrom SDK locations or PATH (same pattern asfind_android_tools())find_aapt2()— Discoversaapt2from latestbuild-tools/*/aapt2in SDK locations, fallback tocommand -v aapt2. Used only in--apkmode for package name extraction.select_device()— Parsesadb devices, skips unauthorized; auto-selects if one device, interactive numbered menu if multiple. Fetchesro.product.modelfor display.prepare_local_apk()— (when--apk) For directory input, uses the path as-is asPULL_DIR. For single APK, copies to temp dirapks_local_<basename>. ExtractsPACKAGE_NAMEviaaapt2 dump badging(soft failure if aapt2 unavailable).select_package()— Runsadb shell pm list packages | grep -i <name>; auto-selects if one match, interactive menu if multiplepull_apks()— Gets paths viaadb shell pm path, pulls each toapks_<package>/directorymake_debuggable()— Delegates to./lib/make-debuggable.sh <pull-dir>(directory mode), forwarding--trust-user-certsif set. Output lands in<pull-dir>_debuggable/.install_apks()— Uninstalls existing package ifPACKAGE_NAMEis known (non-fatal), thenadb installoradb install-multipledepending on APK countcleanup()— Removes temp directories unless--keepflag was set. Never deletes a user-provided--apkdirectory.start_proxy()— (when--proxy) Starts mitmproxy Docker container with--set web_password, pushes CA cert to device, waits for web UI. Always restarts the container fresh to avoid stale state.
Proxy globals: CONTAINER_NAME="mitmproxy-android", PROXY_PORT=8080, WEB_PORT=8081, PROXY_PASSWORD="proxy", MITMPROXY_DIR="$HOME/.mitmproxy".
Main flow: find_adb → select_device → [prepare_local_apk | select_package + pull_apks] → make_debuggable → install_apks → cleanup
Key conventions: all adb commands use -s "$DEVICE_SERIAL", all adb shell output stripped of \r with tr -d '\r', grep calls that may match zero use || true to avoid set -e abort.
lib/proxy-setup.sh Architecture
Sets up mitmproxy in Docker and configures an Android emulator to route traffic through it:
parse_args()— Parses--stopand--port <port>flagsfind_tools()— Discoversadb,emulatorbinary, and checks Docker availabilitystop_proxy()— Stops themitmproxy-androidDocker container (used with--stop)start_mitmproxy()— Runs mitmproxy Docker container (detached,--rm, namedmitmproxy-android), volume-mounts~/.mitmproxyfor cert persistence. Polls for CA cert file to appear.find_emulator()— Finds running emulators fromadb devices(entries matchingemulator-*). Gets AVD name viaadb emu avd name. Interactive menu if multiple.restart_emulator()— Kills running emulator, waits for it to disappear, relaunches with-http-proxy http://127.0.0.1:$PROXY_PORT, waits for boot viasys.boot_completed.install_cert()— Pushes~/.mitmproxy/mitmproxy-ca-cert.certo emulator, attempts automated cert install via intent, prints manual fallback instructions.print_summary()— Displays proxy URL, web UI URL, device info, and usage tips.
Key details: Docker container uses --rm for auto-cleanup, ~/.mitmproxy volume persists certs across runs, emulator launched in background with &.
Platform Notes
- macOS-specific: Uses
sed -i ''(BSD sed syntax) and searches macOS paths like/Applications/Android Studio.appfor bundled JDK - Requires: Android SDK (build-tools), apktool (
brew install apktoolor jar), Java/JDK lib/proxy-setup.shadditionally requires Docker and an Android emulator
Hardcoded Configuration
Keystore credentials are intentionally hardcoded for debug use:
- Keystore:
debug-resign.keystore, alias:debug_key, password:debugpass123 - Work directory:
apk-disassembled(cleaned up after processing)