How to Debug a Running Android Application with Reverse Engineering Tools
Debug any compiled Android binary by decompiling it with jadx or Fernflower, mapping the reconstructed sources to the live process via JDWP, and attaching Android Studio or jdb to intercept runtime behavior.
The android-reverse-engineering-skill repository provides a complete, script-driven workflow for turning compiled Android binaries into debuggable source code. By combining static analysis with standard Android runtime tools, you can inspect variable states, network calls, and authentication flows in apps where you lack the original source.
Prerequisites and Environment Setup
Before decompiling, verify that your environment contains Java 17+, jadx, and optional decompilers like Fernflower or Vineflower.
Run the dependency checker from the repository root:
bash plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/check-deps.sh
The script outputs machine-readable status lines such as INSTALL_REQUIRED:jadx. Install missing components automatically:
bash plugins/android-reverse-engineering/skills/android-reverse-engineering/skills/android-reverse-engineering/scripts/install-dep.sh jadx
bash plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/install-dep.sh vineflower
Why this matters: Decompiling with the --deobf flag generates source files whose line numbers map directly to the original bytecode, enabling accurate breakpoint placement in Android Studio or jdb.
Decompiling the Target Binary
First, pull the APK from the device to your workstation:
adb shell pm path com.example.app
# Output: package:/data/app/com.example.app-1/base.apk
adb pull /data/app/com.example.app-1/base.apk ./target.apk
Execute the decompilation wrapper to generate Java sources:
# Fast code-only extraction (no resources)
bash plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/decompile.sh --no-res target.apk
# For obfuscated apps, add deobfuscation
bash plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/decompile.sh --deobf target.apk
The script creates target-decompiled/ containing sources/ (Java) and resources/ (manifest, layouts). When using --deobf, it also emits deobf-mapping.txt, which you can import into Android Studio to restore original class names for debugging.
Exploring Decompiled Code
Open the APK in jadx-gui for interactive navigation:
jadx-gui target.apk
Capabilities for debugging preparation:
- Search for class/method names or string constants to locate entry points.
- Toggle Show line numbers to align with the
sources/tree produced bydecompile.sh. - Trace the call hierarchy from UI event handlers (e.g.,
LoginActivity.onCreate) down to network clients.
Locating Network Entry Points
Identify HTTP API calls using the extraction helper:
# Scan entire source tree
bash plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/find-api-calls.sh target-decompiled/sources/
# Focus on Retrofit annotations
bash plugins/android-reverse-engineering/skills/android-reverse-engineering/scripts/find-api-calls.sh target-decompiled/sources/ --retrofit
Output format shows exact file paths and line numbers:
=== Retrofit Annotations ===
src/main/java/com/example/api/ApiService.java:42:@GET("/v1/users")
src/main/java/com/example/api/ApiService.java:57:@POST("/v1/login")
Combine these results with the call-flow analysis reference documented in references/call-flow-analysis.md to map the complete path from UI components to the network layer.
Attaching a Live Debugger
With decompiled sources and identified target methods, connect to the running process.
Step 1: Launch and Prepare the Target
# Start the app if not running
adb shell am start -n com.example.app/.MainActivity
# Forward JDWP port for command-line debugging (optional)
adb forward tcp:8700 jdwp:<pid>
Step 2: Load Sources in Android Studio
- Open the generated
target-decompiled/sources/directory (File → Open). - If you used
--deobf, load the mapping: File → Settings → Build, Execution, Deployment → Debugging → ProGuard mappings, then selectdeobf-mapping.txt.
Step 3: Set Breakpoints and Attach
Place breakpoints on the methods identified earlier (e.g., ApiService.login at line 57).
Android Studio method:
- Run → Attach debugger to Android process → Select
com.example.app.
Command-line method:
jdb -attach localhost:8700
> stop in com.example.api.ApiService.login
> run
Step 4: Inspect Runtime State
Trigger the functionality in the app (e.g., tap the login button). The debugger halts at your breakpoint, allowing inspection of:
- Method parameters and local variables.
- Network request construction before it leaves the device.
- Authentication token generation logic.
You now possess both static visibility (decompiled sources) and dynamic visibility (live debugging) for an application without original source code.
Summary
- Prepare the environment using
check-deps.shandinstall-dep.shto ensure Java 17+ and decompilers are available. - Decompile the target APK with
decompile.sh, using--deobffor obfuscated apps to preserve line number mappings. - Explore the reconstructed sources in
jadx-guiand identify network entry points viafind-api-calls.sh. - Attach Android Studio or
jdbto the running process, loading deobfuscation mappings if available, to set breakpoints in decompiled methods. - Inspect runtime variables and network flows at the exact points where the app constructs HTTP requests or handles sensitive data.
Frequently Asked Questions
How do I handle obfuscated apps when debugging?
Use the --deobf flag with decompile.sh to generate a deobf-mapping.txt file. Import this mapping into Android Studio under Settings → Build, Execution, Deployment → Debugging → ProGuard mappings to restore original class and method names, enabling accurate breakpoint placement in obfuscated code.
Can I debug an app without pulling the APK first?
No, you must first extract the APK from the device using adb shell pm path and adb pull to obtain the binary. The decompilation process requires local access to the APK, XAPK, JAR, or AAR file to generate the source tree that you will attach to the debugger.
What is the difference between jadx and Fernflower for debugging?
jadx produces more readable source code and includes a GUI (jadx-gui) for interactive exploration, making it ideal for initial analysis. Fernflower (or Vineflower) often yields more accurate line number mappings to the original bytecode, which can result in more precise breakpoint alignment when attaching a debugger to the running process. The decompile.sh script supports both engines via the --engine flag.
How do I identify which methods to set breakpoints on?
Run find-api-calls.sh against the decompiled sources to locate HTTP client calls (Retrofit, OkHttp, Volley) and hard-coded endpoints. Then consult the call-flow-analysis.md reference to trace backward from these network methods to the UI entry points (Activities, Fragments, or ViewModels). Set breakpoints at the critical junctions where user input transforms into network requests.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →