MCPcopy Index your code
hub / github.com/JuneLeGency/dexfinder

github.com/JuneLeGency/dexfinder @v0.4.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.4.0 ↗ · + Follow
713 symbols 2,179 edges 62 files 381 documented · 53% updated 2mo agov0.4.0 · 2026-04-16★ 88
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

dexfinder

English | 中文 image


Cross-platform APK/DEX method & field reference finder with call chain tracing, ProGuard/R8 deobfuscation, and Android hidden API detection.

Inspired by Android's veridex tool, reimplemented in Go with enhanced capabilities: faster reflection detection, call chain tracing (veridex only shows one level), and flexible output formats.

Features

  • APK/DEX/JAR scanning — Parse DEX bytecode, extract all method/field/string references
  • Multi-format query — Search by Java name, DEX/JNI signature, or simple keyword
  • Call chain tracing — Trace callers up to N levels deep, merged tree or flat list, with cycle detection
  • ProGuard/R8 deobfuscation — Load mapping.txt, display original names alongside obfuscated
  • Hidden API detection — Load hiddenapi-flags.csv, detect blocked/unsupported APIs
  • Reflection detection — Cross-match classes × strings to find reflection-based hidden API usage
  • Flexible output — text / json / model / html / sarif, tree / list layout, java / dex name style — all orthogonal
  • Color terminal output — Auto-detected ANSI colors for tags, tree connectors, and API levels
  • APK diff — Compare two APK/DEX versions, detect added/removed/changed API references
  • HTML reports — Self-contained interactive HTML with collapsible trees, search, and dark theme
  • SARIF output — SARIF 2.1.0 for GitHub Code Scanning, VS Code, and CI pipelines
  • CI integration--fail-on blocked exits non-zero when restricted APIs are found
  • Config file.dexfinder.yaml for project defaults, CLI flags override
  • Zero external dependencies — Pure Go, self-contained DEX parser
  • Cross-platform — macOS (Intel / Apple Silicon), Linux (amd64 / arm64), Windows

Install

Homebrew (macOS / Linux):

brew tap JuneLeGency/tap
brew install dexfinder

Script (auto-detects OS/arch):

curl -sSL https://raw.githubusercontent.com/JuneLeGency/dexfinder/main/install.sh | bash

Go install:

go install github.com/JuneLeGency/dexfinder/cmd/dexfinder@latest

Binary: download from Releases.

Quick Start

# Show APK overview
dexfinder --dex-file app.apk --stats

# Find all calls to getDeviceId (IMEI)
dexfinder --dex-file app.apk --query "getDeviceId"

# Trace call chains as merged tree
dexfinder --dex-file app.apk --query "getDeviceId" --trace

# Trace as flat call stacks (Java crash style)
dexfinder --dex-file app.apk --query "getDeviceId" --trace --layout list

# Exact JNI signature query
dexfinder --dex-file app.apk \
  --query "Landroid/telephony/TelephonyManager;->getDeviceId()Ljava/lang/String;" \
  --trace --depth 8

# Hidden API detection
dexfinder --dex-file app.apk --api-flags hiddenapi-flags.csv

Query Formats

The --query flag accepts multiple input styles. dexfinder auto-detects and converts between them.

Format Example Behavior
Simple name getDeviceId Fuzzy substring match across all APIs
Java class android.telephony.TelephonyManager All methods/fields of that class
Java class#method android.telephony.TelephonyManager#getDeviceId All overloads of that method
Java full signature ...TelephonyManager#getDeviceId() Exact + overload fallback
DEX/JNI signature Landroid/telephony/TelephonyManager;->getDeviceId()Ljava/lang/String; Exact match only
# All equivalent — find requestLocationUpdates in LocationManager:
dexfinder --dex-file app.apk --query "requestLocationUpdates"
dexfinder --dex-file app.apk --query "android.location.LocationManager#requestLocationUpdates"
dexfinder --dex-file app.apk --query "Landroid/location/LocationManager;->requestLocationUpdates(Ljava/lang/String;JFLandroid/location/LocationListener;)V"

Output Control

Three independent axes, freely combinable:

--format  (text / json / model / html / sarif)    what to output
--layout  (tree / list)                           how to arrange traces
--style   (java / dex)                            how to display names
--color   (auto / always / never)                 terminal colors

--format

Value Description
text Plain text output with colored tags (default)
json JSON — scan results or trace with tree/list layout
model Structured JSON with full MethodInfo/FieldInfo types (for IDE/CI)
html Self-contained HTML report with collapsible trees and search
sarif SARIF 2.1.0 static analysis format (GitHub / VS Code)

--layout (used with --trace)

Value Description
tree Merged tree — shared call paths collapsed into one tree (default)
list Flat list — each unique call chain shown as independent stack

--style

Value Example Use case
java com.example.Foo.method(Foo.java) Human-readable (default)
dex Foo.method(Ljava/lang/String;)V Precise signature analysis

--scope (search scope)

Controls what kind of references the query matches against. This is critical for understanding results.

Value What it searches Question it answers Output tag
all Callee APIs + fields + code strings "Who calls this API?" (default) [METHOD] [FIELD] [STRING]
callee Only target API signatures in invoke-* / get/put instructions "Who calls this specific method/field?" [METHOD] [FIELD]
caller Only the calling method's signature "What does this method call internally?" [CALLER→]
string String constants in const-string instructions "Where is this string used in code?" [STRING]
string-table Code strings + full DEX string table "Does this string exist anywhere in DEX?" (includes annotations, dead code) [STRING] [STRING_TABLE]
everything All of the above combined Full picture all tags

Understanding callee vs caller:

scope=callee: "Who calls finish()?"
    onCreate ──calls──→ finish()     ← these callers are shown
    onResume ──calls──→ finish()

scope=caller: "What does finish() call internally?"
    finish() ──calls──→ Log.i()      ← these callees are shown
    finish() ──calls──→ super.finish()

--scope=all (default) = callee + string. The caller direction is intentionally excluded from default because it answers a fundamentally different question. Use --scope=caller or --scope=everything explicitly when you need it.

Understanding output tags:

Tag Meaning
[METHOD] A method being called matches your query (callee match). Indented lines are the callers.
[FIELD] A field being accessed matches your query. Indented lines are the accessors.
[CALLER→] A calling method matches your query. The indented line shows what API it's calling.
[STRING] A string constant in code matches your query. Indented lines are where it's used.
[STRING_TABLE] String exists in DEX string table but has no const-string reference in code (may be in annotations, optimized out by R8, etc.)

Examples

1. Scan APK statistics

dexfinder --dex-file app.apk --stats
Loaded 31 DEX file(s): 183913 classes, 1250566 method refs
Method references: 680610
Field references:  625572
String constants:  654353
Referenced types:  192586
Time: 3.9s

2. Find all location tracking calls

dexfinder --dex-file app.apk --query "requestLocationUpdates"
[METHOD] Landroid/location/LocationManager;->requestLocationUpdates(Ljava/lang/String;JFLandroid/location/LocationListener;)V (3 ref)
       Lcom/example/TestEntry;->init(Landroid/content/Context;)V (2 occurrences)
       Lcom/example/service/LocationService;->onStartCommand(Landroid/content/Intent;II)I

3. Trace call chains — tree view

dexfinder --dex-file app.apk \
  --query "Landroid/telephony/TelephonyManager;->getDeviceId()Ljava/lang/String;" \
  --trace --depth 5
android.telephony.TelephonyManager.getDeviceId()
└── com.example.aopsdk.TelephonyManager.getDeviceId(TelephonyManager.java)
    ├── com.example.session.PhoneInfo.getImei(PhoneInfo.java)
    ├── com.example.logging.ClientIdHelper.initClientId(ClientIdHelper.java)
    │   └── com.example.logging.ContextInfo.<init>(ContextInfo.java)
    │       ├── com.example.logging.LogStrategyManager.getInstance(LogStrategyManager.java)
    │       └── com.example.logging.LogContextImpl.<init>(LogContextImpl.java)
    ├── com.example.msp.DeviceInfo.k(DeviceInfo.java)
    │   └── com.example.msp.DeviceInfo.<init>(DeviceInfo.java)
    │       └── com.example.msp.DeviceInfo.getInstance(DeviceInfo.java)
    │           ├── com.example.msp.TidHelper.getIMEI(TidHelper.java)
    │           ├── com.example.msp.TidHelper.getIMSI(TidHelper.java)
    │           └── com.example.msp.DeviceCollector.collectData(DeviceCollector.java)
    └── com.example.weex.WXEnvironment.getDevId(WXEnvironment.java)
        └── com.example.weex.WXEnvironment.<clinit>(WXEnvironment.java)

4. Trace call chains — list view (Java crash style)

dexfinder --dex-file app.apk \
  --query "Landroid/telephony/TelephonyManager;->getDeviceId()Ljava/lang/String;" \
  --trace --depth 5 --layout list
--- Call chain #1 for android.telephony.TelephonyManager.getDeviceId() ---
    at com.example.session.PhoneInfo.getImei(PhoneInfo.java)
    at com.example.aopsdk.TelephonyManager.getDeviceId(TelephonyManager.java)
    at android.telephony.TelephonyManager.getDeviceId(TelephonyManager.java)

--- Call chain #2 for android.telephony.TelephonyManager.getDeviceId() ---
    at com.example.logging.LogStrategyManager.getInstance(LogStrategyManager.java)
    at com.example.logging.ContextInfo.<init>(ContextInfo.java)
    at com.example.logging.ClientIdHelper.initClientId(ClientIdHelper.java)
    at com.example.aopsdk.TelephonyManager.getDeviceId(TelephonyManager.java)
    at android.telephony.TelephonyManager.getDeviceId(TelephonyManager.java)

5. Trace with DEX signature style

dexfinder --dex-file app.apk --query "getDeviceId" --trace --depth 3 --style dex
Landroid/telephony/TelephonyManager;->getDeviceId()Ljava/lang/String;
└── TelephonyManager.getDeviceId(Landroid/telephony/TelephonyManager;)Ljava/lang/String;
    ├── PhoneInfo.getImei(Landroid/content/Context;)Ljava/lang/String;
    ├── ClientIdHelper.initClientId(Landroid/content/Context;)Ljava/lang/String;
    └── DeviceInfo.k(Landroid/content/Context;)V

6. JSON output — tree

dexfinder --dex-file app.apk --query "getDeviceId" --trace --depth 2 --format json
{
  "targets": [{
    "api": "android.telephony.TelephonyManager.getDeviceId()",
    "tree": {
      "method": "android.telephony.TelephonyManager.getDeviceId(TelephonyManager.java)",
      "callers": [
        { "method": "com.example.aopsdk.TelephonyManager.getDeviceId(TelephonyManager.java)",
          "callers": [
            { "method": "com.example.session.PhoneInfo.getImei(PhoneInfo.java)" },
            { "method": "com.example.logging.ClientIdHelper.initClientId(ClientIdHelper.java)" }
          ]}
      ]
    }
  }]
}

7. JSON output — list

dexfinder --dex-file app.apk --query "getDeviceId" --trace --depth 2 --format json --layout list
{
  "targets": [{
    "api": "android.telephony.TelephonyManager.getDeviceId()",
    "chains": [
      ["com.example.session.PhoneInfo.getImei(PhoneInfo.java)",
       "com.example.aopsdk.TelephonyManager.getDeviceId(TelephonyManager.java)",
       "android.telephony.TelephonyManager.getDeviceId(TelephonyManager.java)"],
      ["com.example.logging.ClientIdHelper.initClientId(ClientIdHelper.java)",
       "com.example.aopsdk.TelephonyManager.getDeviceId(TelephonyManager.java)",
       "android.telephony.TelephonyManager.getDeviceId(TelephonyManager.java)"]
    ]
  }]
}

8. Structured model output (for CI/IDE)

dexfinder --dex-file app.apk --query "getDeviceId" --trace --format model | jq '.call_chains[0]'
{
  "target": "Landroid/telephony/TelephonyManager;->getDeviceId()Ljava/lang/String;",
  "chain": [
    { "method": { "dex_signature": "...", "class": "...", "name": "getImei",
                   "param_types": ["Landroid/content/Context;"], "return_type": "Ljava/lang/String;",
                   "java_readable": "com.example.session.PhoneInfo.getImei(...)" }},
    { "method": { "dex_signature": "...", "java_readable": "...TelephonyManager.getDeviceId(...)" }},
    { "method": { "dex_signature": "...", "java_readable": "...TelephonyManager.getDeviceId(...)" }}
  ],
  "depth": 2
}

9. ProGuard/R8 mapping — query and display

With --mapping, both input and output support original (unobfuscated) names.

Query by original name → auto-converts to obfuscated name for DEX search:

# Query with original simple class name (mapping converts "KotlinCases" → "LJ7;" internally)
dexfinder --dex-file app.apk --query "KotlinCases" --mapping mapping.txt

# Query with original Java full name
dexfinder --dex-file app.apk --query "com.example.app.utils.Helper" --mapping mapping.txt

# Query with obfuscated name still works
dexfinder --dex-file app.apk --query "LJ7;" --mapping mapping.txt

Output deobfuscated names in trace:

# Tree trace with deobfuscated names
dexfinder --dex-file app.apk --query "KotlinCases" --mapping mapping.txt --trace --depth 3
com.example.kotlin.KotlinCases$$ExternalSyntheticLambda1.<init>(int)
└── com.example.TestEntry.runAllTests(TestEntry.java)
    └── com.example.MainActivity.onCreate(MainActivity.java)

**

Extension points exported contracts — how you extend this code

Transformer (Interface)
Generic interface
test-apk-project/app/src/main/java/com/test/dexfinder/cases/GenericCases.java
AsyncCallback (Interface)
Callback interface for async results
test-apk-project/app/src/main/java/com/test/dexfinder/cases/ThreadingCases.java
OnItemClickListener (Interface)
(no doc)
test-apk-project/app/src/main/java/com/test/dexfinder/MainActivity.java

Core symbols most depended-on inside this repo

String
called by 72
pkg/hiddenapi/apilist.go
le32
called by 41
pkg/dex/dexfile.go
Query
called by 34
pkg/finder/query.go
decodeULEB128
called by 20
pkg/dex/dexfile.go
GetApiList
called by 20
pkg/hiddenapi/database.go
Enabled
called by 18
pkg/report/color.go
DiffScans
called by 17
pkg/finder/diff.go
TraceCallers
called by 16
pkg/finder/callgraph.go

Shape

Function 308
Method 283
Struct 77
Class 32
TypeAlias 9
Interface 3
Enum 1

Languages

Go77%
Java18%
Kotlin4%

Modules by API surface

pkg/dex/instruction.go70 symbols
pkg/dex/dexfile.go38 symbols
test-apk-project/app/src/main/java/com/test/dexfinder/kotlin/KotlinCases.kt31 symbols
pkg/finder/query_test.go26 symbols
pkg/report/text.go25 symbols
pkg/mapping/proguard.go24 symbols
pkg/report/display_test.go19 symbols
pkg/finder/integration_test.go19 symbols
test-apk-project/app/src/main/java/com/test/dexfinder/cases/GenericCases.java18 symbols
pkg/report/sarif.go18 symbols
test-apk-project/app/src/main/java/com/test/dexfinder/cases/DeepCallChain.java16 symbols
pkg/report/display.go16 symbols

For agents

$ claude mcp add dexfinder \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page