Dexbgd
A native Android DEX debugger with bytecode-level stepping, live variable inspection, and runtime manipulation for reverse engineering, CTFs, and security analysis
Install / Use
/learn @arkup/DexbgdREADME
dexbgd - DEX Debugger
<p align="center"> <img src="img/dexbgd_joke.png" alt="BGD Logo" width="300"> </p>A real debugger for Android apps. Not a hooking framework, not JDWP - a native JVMTI debugger that lets you pause any Java method, step through Dalvik bytecode one instruction at a time, inspect every local variable and register, search the live heap, and force methods to return whatever you want.
Built for CTF, malware analysis and reverse engineering. Thin C++ agent inside the app, Rust TUI on the host.
<p align="center"> <img src="img/demo.gif" alt="BGD Screenshot"> </p>How It Differs from Frida?
Frida replaces functions with JavaScript hooks. dexbgd pauses execution and lets you look around. Different tools for different problems.
| | dexbgd | Frida |
|---|---|---|
| Stop at any bytecode | Yes | No (method-level only) |
| Step instruction by instruction | Yes | No |
| Read every local/register while paused | Yes | Only what your hook captures |
| Force a return value on the fly |Yes, fr false | Yes (write a hook script) |
| Built-in DEX disassembler | Yes, live PC & frames | Need external tools (jadx, baksmali) |
| Live heap search | heap SecretKey | Custom scripting required |
| Auto-intercept dynamic class loading | Built-in | Requires scripting |
| Native code | JNI monitor + redirect | Yes (multi-arch native) |
| Detection surface | Standard debug API + optional ptrace | frida-server, gadget, ptrace |
| Requires debuggable | Yes (or bypass via root + ptrace) | No (needs root or repackage) |
Use dexbgd when you need fine-grained visibility into execution - stepping through root checks, observing crypto state during execution, tracing dynamic class loading, or bypassing logic instruction-by-instruction.
What can it do / Quick Start
// Terminal 1
> adb forward tcp:12345 localabstract:dexbgd
> adb shell cmd activity attach-agent com.sketchy.apkapp libart_jit_tracer.so
// Terminal 2 (server TUI)
> launch com.sketchy.apkapp
(same as adb shell am start -n com.sketchy.apkapp/.MainActivity)
Connected (pid=12847, Android 14, arm64)
Loaded APK: 847 classes, 6203 methods
> bp-detect
Set 14 breakpoints on root/tamper detection APIs
> c
Breakpoint #3 hit: Debug.isDebuggerConnected @0
> fr false
[forced return false, paused at caller]
> c
Breakpoint #7 hit: RootBeer.isRooted @0
> fr false
[app thinks device is clean, continues]
> bp-crypto
> c
Breakpoint #31 hit: Cipher.init @0
> locals
opmode = 1 (ENCRYPT)
key = SecretKeySpec{AES, [4b 65 79 31 32 33 ...]}
> eval v2.getAlgorithm()
"AES/CBC/PKCS5Padding"
> strings http
[apk] "https://c2server.evil.com/exfil"
[apk] "http://license.check.net/verify"
> xref-bp c2server
Set bp on MainActivity.sendData (loads "https://c2server.evil.com/exfil")
Features
Breakpoint profiles
One command covers an entire attack surface:
bp-crypto Cipher, SecretKeySpec, IvParameterSpec, Mac, MessageDigest, KeyGenerator, KeyStore
bp-network URL, HttpURLConnection, HttpsURLConnection, Socket, OkHttp
bp-exec Runtime.exec, ProcessBuilder, DexClassLoader, PathClassLoader, InMemoryDexClassLoader, Method.invoke, Class.forName, ClassLoader.loadClass
bp-detect Debug.isDebuggerConnected, SafetyNet, Play Integrity, File.exists, SystemProperties, ActivityManager, Settings$Secure, signature checks
bp-exfil TelephonyManager, ContentResolver, SMS, Location, PackageManager.getInstalledPackages
bp-ssl NetworkSecurityTrustManager, SSLContext.init, HttpsURLConnection, OkHttp CertificatePinner, Conscrypt TrustManagerImpl
bp-all All of the above
Force return
Make any method return whatever you want while it's suspended. The agent figures out the return type automatically.
fr true isRooted() -> true? Not anymore.
fr false isDebuggerConnected() -> false.
fr null getSignature() -> null. Signature check bypassed.
fr 42 getRetryCount() -> 42. Why not.
After forcing the return, execution pauses at the caller's next instruction so you can see the effect before continuing.
Silent anti-tamper bypass (anti)
bypass-ssl silently intercepts SSL pinning. anti does the same for any method — it sets a ghost breakpoint that auto-ForceEarlyReturns a neutral value without pausing or showing anything to the user.
Three ways to find what to hook:
// Direct: name the method
anti com.example.Security isRooted
// xref: find methods that load a suspicious string constant
anti xref su
anti xref test-keys
anti xref Superuser
// callers: find methods that invoke a specific API
anti callers android.os.Debug isDebuggerConnected
Example against the test app on a rooted device:
> attach com.test.profiletest
[press Detect button -> "Root: DETECTED (1)"]
> anti xref su
anti xref "su": 1 unique method(s) - setting anti hooks...
MainActivity.testDetect ("/system/xbin/su")
[press Detect button -> "Root: not detected (0)"]
[anti] MainActivity.testDetect()I -> 0/false/null
> anti list
1 active anti hook(s):
#3 MainActivity.testDetect
> anti clear
Return value is auto-detected from the JNI signature (Z/B/I/... -> false/0, V -> void). Override with an explicit value:
anti com.example.License check true // always pass
anti com.example.Integrity verify void // silently swallow
Dynamic DEX interception
When bp-exec is active and the app loads code at runtime (DexClassLoader, InMemoryDexClassLoader), dexbgd automatically:
- Dumps the loaded DEX bytes
- Parses class/method/string tables
- Merges them into the searchable symbol data
So strings and xref cover both the original APK and dynamically loaded payloads - the stuff malware actually tries to hide.
Conditional breakpoints
bp Cipher init --when "v1 == 1" Break only on ENCRYPT mode
bp HttpURLConnection connect --every 5 Break every 5th call
bp LicenseCheck verify --hits 3 Break only on 3rd hit
See doc/breakpoint_cond.md for the full expression syntax and more examples.
Call recording
Record security-relevant API calls as an indented call tree:
> record
> c
[app runs for a while]
> record
Cipher.getInstance("AES/CBC/PKCS5Padding")
SecretKeySpec.<init>([...], "AES")
Cipher.init(1, SecretKeySpec)
Cipher.doFinal([...]) -> [encrypted bytes]
URL.<init>("https://c2server.evil.com/exfil")
HttpURLConnection.connect()
AI-assisted analysis (experimental)
Point an LLM at the debugger and let it drive:
> ai Find all crypto keys used by this app and trace where the ciphertext goes
> ai ask Bypass root detection (confirms each tool call)
> ai explain What is this method doing? (read-only inspection)
The AI gets access to all debugger tools - it can set breakpoints, step, inspect variables, search strings, and build a report. Supports Claude API and local Ollama models.
See doc/ai_setup.md for setup instructions.
Command reference
See doc/all_commands.md for the full, up-to-date list of commands.
Connection
procs / ps List debuggable processes
attach <pkg> Inject agent + connect + load symbols
launch <pkg> Start app + attach
connect Manual connect (127.0.0.1:12345)
disconnect / dc Disconnect
Exploration
cls [pattern] Search loaded classes
methods / m <class> List methods
fields / f <class> List fields
threads / thd List all threads
dis <class> <m> Disassemble method
u <class.method> Navigate to method (WinDbg-style unassemble)
strings <pattern> Search DEX constant pool
xref <pattern> Find code referencing a string
xref-bp <pattern> xref + set breakpoints
apk <path|pkg> Load APK symbols
Breakpoints
bp <cls> <method> [sig] [@loc] Set breakpoint
bp2 <cls> <method> [sig] [@loc] Set breakpoint + force deopt (for repacked APKs)
--hits N / --every N / --when "expr"
bc / bd <id> Clear breakpoint
bc / bd * Clear all
bl List breakpoints
bp2: On repacked APKs, ART may silently fail to deoptimize methods —bpsucceeds but never fires.bp2forces deoptimization viaRetransformClassesafter setting the breakpoint. Usebp2instead ofbpwhen debugging repacked apps.
Execution
c / g / F5 Continue
si / F7 Step into
s / n / F8 Step over
sout / finish / F9 Step out
pause / F6 Suspend thread
fr <value> Force return (true/false/null/void/0/1/<int>)
Inspection
locals / l Local variables
stack / bt Call stack
inspect / i <vN> Object fields
eval / e <expr> Evaluate (v3.getAlgorithm())
hexdump / hd <vN> Hex dump arrays/strings
hexdump / hd <vN> full Extended hex dump (32 rows)
heap <class> Search heap instances
heapstr <pattern> Search live strings
r / regs Dump registers
Watch
watch <expr> Add expression to watch list (re-evaluated on every suspension)
unwatch <n|expr> Remove watch by index or expression
unwatch * Remove all watches
watch clear Same as unwatch *
e.g.
watch key
watch v3
watch v3.getAlgorithm()
Bookmarks
Ctrl+B Toggle bookmark at bytecode cursor
bm <label> Rename selected bookmark (or press Enter in Bookmarks tab)
Session (per-app persistent state)
Aliases, comments, bookmarks, and hooks are saved per app in sessions/<package>.json next to the server binary.
Ctrl+S Save session for current app
Ctrl+L Launch session picker (select saved app, start and attach agent)
n Rename current class (IDA-style, bytecodes pane
Related Skills
next
A beautifully designed, floating Pomodoro timer that respects your workspace.
product-manager-skills
41PM skill for Claude Code, Codex, Cursor, and Windsurf: diagnose SaaS metrics, critique PRDs, plan roadmaps, run discovery, and coach PM career transitions.
devplan-mcp-server
3MCP server for generating development plans, project roadmaps, and task breakdowns for Claude Code. Turn project ideas into paint-by-numbers implementation plans.
