
Plattformübergreifender APK/DEX-Methodenfinder mit Aufrufkettenverfolgung, ProGuard-Entschleierung und Erkennung versteckter APIs
Englisch | Chinesisch | Webseite

Webseite: junelegency.github.io/dexfinder
Plattformübergreifender APK/DEX-Methoden- und Feldreferenz-Finder mit Aufrufkettenverfolgung, ProGuard/R8-Entobfuskierung und Erkennung verborgener Android-APIs.
Inspiriert von Androids veridex-Tool, neu implementiert in Go mit erweiterten Fähigkeiten: schnellere Reflexionserkennung, Aufrufkettenverfolgung (veridex zeigt nur eine Ebene) und flexible Ausgabeformate.
--fail-on blocked beendet mit Fehlercode, wenn eingeschränkte APIs gefunden werden.dexfinder.yaml für Projektvorgaben, CLI-Flags überschreibenHomebrew (macOS / Linux):```bash brew install junelegency/tap/dexfinder
**Skript** (erkennt automatisch OS/Architektur):```bash
curl -sSL https://raw.githubusercontent.com/JuneLeGency/dexfinder/main/install.sh | bash
Go installieren:```bash go install github.com/JuneLeGency/dexfinder/cmd/dexfinder@latest
**Binärdatei**: herunterladen von [Releases](https://github.com/JuneLeGency/dexfinder/releases).
## Schnellstart```bash
# 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
Das --query-Flag akzeptiert mehrere Eingabestile. dexfinder erkennt sie automatisch und konvertiert zwischen ihnen.
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
Drei unabhängige Achsen, frei kombinierbar:```
--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--layout (verwendet mit --trace)| Wert | Beschreibung |
|---|---|
tree | Zusammengeführter Baum — gemeinsame Aufrufpfade zu einem Baum zusammengefasst (Standard) |
list | Flache Liste — jeder eindeutige Aufrufpfad als unabhängiger Stack dargestellt |
--style| Wert | Beispiel | Anwendungsfall |
|---|---|---|
java | com.example.Foo.method(Foo.java) | Menschenlesbar (Standard) |
dex | Foo.method(Ljava/lang/String;)V | Präzise Signaturanalyse |
--scope (Suchbereich)Steuert, welche Art von Referenzen die Abfrage abgleicht. Dies ist entscheidend für das Verständnis der Ergebnisse.
Callee vs. Caller verstehen:``` 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` (Standard) = `callee` + `string`. Die `caller`-Richtung ist bewusst vom Standard ausgeschlossen, da sie eine grundlegend andere Frage beantwortet. Verwenden Sie `--scope=caller` oder `--scope=everything` explizit, wenn Sie dies benötigen.
**Ausgabe-Tags verstehen:**
| Tag | Bedeutung |
|---|---|
| `[METHOD]` | Eine Methode, die **aufgerufen wird**, stimmt mit Ihrer Abfrage überein (Callee-Übereinstimmung). Eingerückte Zeilen sind die Aufrufer. |
| `[FIELD]` | Ein Feld, auf das **zugegriffen wird**, stimmt mit Ihrer Abfrage überein. Eingerückte Zeilen sind die Zugreifenden. |
| `[CALLER→]` | Eine **aufrufende Methode** stimmt mit Ihrer Abfrage überein. Die eingerückte Zeile zeigt, welche API aufgerufen wird. |
| `[STRING]` | Eine Zeichenfolgenkonstante im Code stimmt mit Ihrer Abfrage überein. Eingerückte Zeilen zeigen, wo sie verwendet wird. |
| `[STRING_TABLE]` | Die Zeichenfolge existiert in der DEX-String-Tabelle, hat aber keinen `const-string`-Verweis im Code (möglicherweise in Annotationen, durch R8 optimiert usw.) |
## Beispiele
### 1. APK-Statistiken scannen```bash
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
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. Ablaufverfolgung von Aufrufketten — Baumansicht```bash
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)
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.(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 mit DEX-Signaturstil```bash
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
dexfinder --dex-file app.apk --query "getDeviceId" --trace --depth 2 --format json
```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)" }
]}
]
}
}]
}
dexfinder --dex-file app.apk --query "getDeviceId" --trace --depth 2 --format json --layout list
```json
{
"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)"]
]
}]
}
dexfinder --dex-file app.apk --query "getDeviceId" --trace --format model | jq '.call_chains[0]'
```json
{
"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
}
Mit --mapping unterstützen sowohl Eingabe als auch Ausgabe originale (nicht verschleierte) Namen.
Abfrage nach originalem Namen → automatische Umwandlung in verschleierten Namen für DEX-Suche:```bash
dexfinder --dex-file app.apk --query "KotlinCases" --mapping mapping.txt
dexfinder --dex-file app.apk --query "com.example.app.utils.Helper" --mapping mapping.txt
dexfinder --dex-file app.apk --query "LJ7;" --mapping mapping.txt
**Ausgabe deobfuszierten Namen in der Ablaufverfolgung:**```bash
# 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)
Zeige sowohl obfuskatierte als auch ursprüngliche Namen:```bash dexfinder --dex-file app.apk --query "KotlinCases" --mapping mapping.txt --show-obf --trace
com.example.kotlin.KotlinCases.fetchLocationAsync(KotlinCases.java) └── com.example.kotlin.KotlinCases$testCoroutines$3.invokeSuspend(KotlinCases.java) [obf: G7.e] └── com.example.kotlin.KotlinCases$testCoroutines$3.create(KotlinCases.java) [obf: G7.b]
**Alle Kombinationen mit anderen Flags:**```bash
# Original name + trace as flat list
dexfinder --dex-file app.apk --query "KotlinCases" --mapping mapping.txt --trace --layout list
# Original name + DEX signature style
dexfinder --dex-file app.apk --query "KotlinCases" --mapping mapping.txt --trace --style dex
# Original name + JSON tree + show-obf
dexfinder --dex-file app.apk --query "KotlinCases" --mapping mapping.txt --show-obf --trace --format json
# Original name + reverse direction (what does this class call?)
dexfinder --dex-file app.apk --query "com.example.kotlin.KotlinCases" --mapping mapping.txt --scope caller
Input × Output matrix:
curl -o hiddenapi-flags.csv
https://dl.google.com/developers/android/baklava/non-sdk/hiddenapi-flags.csv
dexfinder --dex-file app.apk --api-flags hiddenapi-flags.csv
#1: Linking unsupported Lsun/misc/Unsafe;->allocateInstance(Ljava/lang/Class;)Ljava/lang/Object; use(s): Lcom/google/gson/internal/UnsafeAllocator;->create()Lcom/google/gson/internal/UnsafeAllocator;
#2: Reflection blocked Landroid/location/ILocationManager;->getCurrentLocation potential use(s): Lcom/example/monitor/LocationMonitor;->hookSystemLocationManager(Landroid/content/Context;)V
### 11. Suchstring-Konstanten (content:// URIs, API-Schlüssel usw.)```bash
# Find content:// URIs in code
dexfinder --dex-file app.apk --query "content://com.android.contacts" --scope string
# Include strings only in DEX table (optimized out by R8, annotations, etc.)
dexfinder --dex-file app.apk --query "content://com.android.contacts" --scope everything
[STRING] "content://com.android.contacts/" (1 ref)
Lcom/example/imageloader/BaseImageDownloader;->getStreamFromContent(Ljava/lang/String;)Ljava/io/InputStream;
[STRING_TABLE] "content://com.android.contacts" (in DEX string table, no code reference found)
dexfinder --dex-file app.apk --query "getDeviceId" --class-filter "Lcom/mycompany/"
dexfinder --dex-file app.apk --query "getDeviceId" --class-filter "Lcom/mycompany/,Lcom/mylib/"
### 13. Alles kombinieren```bash
# Deobfuscated JSON tree of location API usage, filtered to your code
dexfinder --dex-file app.apk \
--query "android.location.LocationManager#requestLocationUpdates" \
--trace --depth 8 \
--format json --layout tree --style java \
--mapping mapping.txt --show-obf \
--class-filter "Lcom/mycompany/"
dexfinder --dex-file app.apk --query "getDeviceId" --trace --format html --output report.html
Öffnet in jedem Browser – einklappbare Aufrufbäume, Suchleiste, dunkles Thema.
### 15. SARIF für GitHub Code Scanning```bash
dexfinder --dex-file app.apk --api-flags hiddenapi-flags.csv --format sarif > results.sarif
# Upload to GitHub:
# gh api repos/OWNER/REPO/code-scanning/sarifs -f "[email protected]"
dexfinder --dex-file new.apk --diff old.apk --query "getDeviceId"
Summary: +1 added, -1 removed, ~0 changed
### 17. CI-Gate mit --fail-on```bash
# Fail CI if any blocked hidden APIs are used
dexfinder --dex-file app.apk --api-flags hiddenapi-flags.csv --fail-on blocked
# Exit code: 0 = clean, 2 = violations found
Gemessen auf Apple M-Serie, Einzelthread:
Verglichen mit veridex (C++, ungenauer Modus) auf derselben ~300MB APK:
Erstelle .dexfinder.yaml im Projektstammverzeichnis, um Standardwerte festzulegen:```yaml
mapping: ./build/outputs/mapping.txt
class-filter: "Lcom/mycompany/"
api-flags: ./hiddenapi-flags.csv
style: java
depth: 8
color: auto
CLI flags überschreiben immer Konfigurationsdateiwerte.
## Erstellen aus dem Quellcode```bash
git clone https://github.com/JuneLeGency/dexfinder.git
cd dexfinder
go build -o dexfinder ./cmd/dexfinder/
go test ./...
Apache Lizenz 2.0
Offizielle Website: junelegency.github.io/dexfinder
Plattformübergreifender APK/DEX Methoden- und Feldreferenz-Finder mit Unterstützung für Aufrufkettenverfolgung, ProGuard/R8-Entschleierung und Android Hidden API Erkennung.
Basiert auf dem Android veridex-Prinzip, neu implementiert und erweitert in Go: schnellere Reflexionserkennung, mehrschichtige Aufrufkettenverfolgung (veridex nur eine Schicht), flexible Ausgabeformate.
--fail-on blocked gibt bei Erkennung eingeschränkter APIs einen Nicht-Null-Exit-Code zurück.dexfinder.yaml Projektstandardkonfiguration, überschreibbar durch BefehlszeilenparameterHomebrew (macOS / Linux):```bash brew install junelegency/tap/dexfinder
**Skriptinstallation** (automatisches Erkennungssystem):```bash
curl -sSL https://raw.githubusercontent.com/JuneLeGency/dexfinder/main/install.sh | bash
Go Installation:```bash go install github.com/JuneLeGency/dexfinder/cmd/dexfinder@latest
**Binärer Download**: [Releases](https://github.com/JuneLeGency/dexfinder/releases)
## Schnellstart```bash
# 查看 APK 概况
dexfinder --dex-file app.apk --stats
# 查找所有 getDeviceId 调用(获取 IMEI)
dexfinder --dex-file app.apk --query "getDeviceId"
# 追踪调用链(合并树形视图)
dexfinder --dex-file app.apk --query "getDeviceId" --trace
# 追踪调用链(展开为独立调用栈)
dexfinder --dex-file app.apk --query "getDeviceId" --trace --layout list
# 用精确 JNI 签名查询
dexfinder --dex-file app.apk \
--query "Landroid/telephony/TelephonyManager;->getDeviceId()Ljava/lang/String;" \
--trace --depth 8
--query)Drei unabhängige Dimensionen, frei kombinierbar:``` --format (text / json / model / html / sarif) 输出什么 --layout (tree / list) 怎么排列调用链 --style (java / dex) 怎么显示名称 --color (auto / always / never) 终端着色
### `--layout` Vergleich (mit `--trace`)
**tree** — gemeinsame Pfade zusammenführen, ein Baum zeigt die Übersicht:```
android.telephony.TelephonyManager.getDeviceId()
└── ...aopsdk...TelephonyManager.getDeviceId(TelephonyManager.java)
├── PhoneInfo.getImei(PhoneInfo.java)
├── ClientIdHelper.initClientId(ClientIdHelper.java)
│ └── ContextInfo.<init>(ContextInfo.java)
└── DeviceInfo.k(DeviceInfo.java)
└── DeviceInfo.getInstance(DeviceInfo.java)
├── TidHelper.getIMEI(TidHelper.java)
└── DeviceCollector.collectData(DeviceCollector.java)
list — jede Kette unabhängig anzeigen (Java-Crash-Stil):``` --- Call chain #1 --- at PhoneInfo.getImei(PhoneInfo.java) at ...aopsdk...TelephonyManager.getDeviceId(TelephonyManager.java) at android.telephony.TelephonyManager.getDeviceId(TelephonyManager.java)
--- Call chain #2 --- at ContextInfo.(ContextInfo.java) at ClientIdHelper.initClientId(ClientIdHelper.java) at ...aopsdk...TelephonyManager.getDeviceId(TelephonyManager.java) at android.telephony.TelephonyManager.getDeviceId(TelephonyManager.java)
### `--style` Vergleich
**java** (Standard): `com.example.Foo.method(Foo.java)`
**dex**: `Foo.method(Ljava/lang/String;)V`
### JSON Ausgabe```bash
# JSON 树
dexfinder --dex-file app.apk --query "getDeviceId" --trace --format json
# JSON 列表
dexfinder --dex-file app.apk --query "getDeviceId" --trace --format json --layout list
--scope SuchbereichSteuert, welcher Referenztyp bei der Abfrage übereinstimmen soll. Das Verständnis dieses Parameters ist entscheidend für die korrekte Interpretation der Ergebnisse.
Unterschied zwischen callee und caller:``` scope=callee: "谁调了 finish()?" onCreate ──调用──→ finish() ← 显示这些调用者 onResume ──调用──→ finish()
scope=caller: "finish() 内部调了什么?" finish() ──调用──→ Log.i() ← 显示这些被调用者 finish() ──调用──→ super.finish()
`--scope=all`(Standard)= `callee` + `string`. Die `caller`-Richtung wurde bewusst vom Standard ausgeschlossen, da sie eine ganz andere Frage beantwortet. Aktivieren Sie sie explizit mit `--scope=caller` oder `--scope=everything`, wenn nötig.
**Bedeutung der Ausgabekennzeichnungen:**
| Kennzeichnung | Bedeutung |
|---|---|
| `[METHOD]` | Die gesuchte Methode **wird von jemand anderem aufgerufen**. Die eingerückte Zeile ist der Aufrufer. |
| `[FIELD]` | Das gesuchte Feld **wird von jemand anderem zugegriffen**. Die eingerückte Zeile ist der Zugreifende. |
| `[CALLER→]` | Der gesuchte Methodenname erscheint in einem **Aufrufer**, die eingerückte Zeile zeigt, welche API aufgerufen wird. |
| `[STRING]` | Ein String-Literal im Code stimmt überein. Die eingerückte Zeile ist die Methode, die diesen String verwendet. |
| `[STRING_TABLE]` | Der String existiert nur in der DEX-String-Tabelle, es gibt keine `const-string`-Referenz im Code (möglicherweise in Annotationen, von R8 optimiert usw.). |
## Weitere Verwendung
### Deobfuskierung (--mapping)
Nach dem Laden von `--mapping` unterstützen **Eingabe und Ausgabe** die ursprünglichen (nicht obfuskierten) Namen.
**Abfrage mit ursprünglichem Namen → automatische Umwandlung in obfuskierten Namen für die DEX-Suche:**```bash
# 用原始简短类名查(mapping 内部将 "KotlinCases" 转为 "LJ7;")
dexfinder --dex-file app.apk --query "KotlinCases" --mapping mapping.txt
# 用原始 Java 全名查
dexfinder --dex-file app.apk --query "com.example.app.utils.Helper" --mapping mapping.txt
# 用混淆名查也正常工作
dexfinder --dex-file app.apk --query "LJ7;" --mapping mapping.txt
Ausgabe-Entschleierungsname:```bash
dexfinder --dex-file app.apk --query "KotlinCases" --mapping mapping.txt --trace
**Gleichzeitig den obfuskierten Namen und den ursprünglichen Namen anzeigen:**```bash
dexfinder --dex-file app.apk --query "KotlinCases" --mapping mapping.txt --show-obf --trace
com.example.KotlinCases.fetchLocationAsync(KotlinCases.java)
└── com.example.KotlinCases$testCoroutines$3.invokeSuspend(KotlinCases.java) [obf: G7.e]
Frei kombinierbar mit anderen Parametern:```bash
dexfinder --dex-file app.apk --query "KotlinCases" --mapping mapping.txt --trace --layout list
dexfinder --dex-file app.apk --query "KotlinCases" --mapping mapping.txt --trace --style dex
dexfinder --dex-file app.apk --query "KotlinCases" --mapping mapping.txt --show-obf --trace --format json
dexfinder --dex-file app.apk --query "com.example.KotlinCases" --mapping mapping.txt --scope caller
**Eingabe×Ausgabe-Matrix:**
| Abfrageeingabe | Kein Mapping | `--mapping` | `--mapping --show-obf` |
|---|---|---|---|
| Verschleierter Name `LJ7;` | ✓ Verschleierte Ausgabe | ✓ Entschleierte Ausgabe | ✓ Beide nebeneinander |
| Originaler Kurzname `KotlinCases` | ✗ Nicht gefunden | ✓ Automatische Konvertierung + entschleierte Ausgabe | ✓ Automatische Konvertierung + beide nebeneinander |
| Originaler vollständiger Name `com.example...` | ✗ Nicht gefunden | ✓ Automatische Konvertierung + entschleierte Ausgabe | ✓ Automatische Konvertierung + beide nebeneinander |
### Hidden-API-Erkennung```bash
# 下载 CSV(一次性)
curl -o hiddenapi-flags.csv \
https://dl.google.com/developers/android/baklava/non-sdk/hiddenapi-flags.csv
# 全量检测(直接链接 + 反射检测)
dexfinder --dex-file app.apk --api-flags hiddenapi-flags.csv
dexfinder --dex-file app.apk --query "content://com.android.contacts" --scope string
dexfinder --dex-file app.apk --query "content://com.android.contacts" --scope everything
### Nach Paketnamen filtern```bash
# 只扫描自己的代码
dexfinder --dex-file app.apk --query "getDeviceId" --class-filter "Lcom/mycompany/"
dexfinder --dex-file app.apk
--query "android.location.LocationManager#requestLocationUpdates"
--trace --depth 8
--format json --layout tree --style java
--mapping mapping.txt --show-obf
--class-filter "Lcom/mycompany/"
### HTML-Bericht```bash
dexfinder --dex-file app.apk --query "getDeviceId" --trace --format html --output report.html
Direkt im Browser nutzbar – zusammenklappbarer Aufrufbaum, Suchleiste, dunkles Thema.
dexfinder --dex-file app.apk --api-flags hiddenapi-flags.csv --format sarif > results.sarif
### APK Versionsvergleich```bash
dexfinder --dex-file new.apk --diff old.apk --query "getDeviceId"
+ 1 added method(s)
+ Lcom/new/Feature;->trackDevice()V
- 1 removed method(s)
- Lcom/old/Legacy;->getIMEI()V
Summary: +1 added, -1 removed, ~0 changed
dexfinder --dex-file app.apk --api-flags hiddenapi-flags.csv --fail-on blocked
## Leistung
Apple M-Serie Chips, Single-Thread:
| APK Größe | DEX Anzahl | Klassenanzahl | Methodenreferenzen | Scan | Hidden API |
|---|---|---|---|---|---|
| ~1 MB | 1 | ~2K | ~18K | **24ms** | — |
| ~10 MB | 2 | ~25K | ~100K | **335ms** | — |
| ~300 MB | 30+ | ~180K | ~1.2M | **3.9s** | **5.4s** |
Vergleich mit veridex (C++) auf demselben ~300MB APK:
- veridex precise: **27s** (kann Binder/AIDL-Reflexion nicht verfolgen)
- veridex imprecise: **>32 Minuten** (kartesisches Produkt explodiert, gekillt)
- **dexfinder: 5.4s** (optimiert mit invertiertem Index)
## Alle Parameter
| Parameter | Beschreibung | Standardwert |
|---|---|---|
| `--dex-file` | APK/DEX/JAR Dateipfad **(erforderlich)** | — |
| `--query` | Suchschlüsselwort (Java / DEX/JNI / einfacher Name) | — |
| `--trace` | Aufrufkettenverfolgung aktivieren (erfordert `--query`) | `false` |
| `--depth` | Maximale Tiefe der Aufrufkette | `5` |
| `--layout` | Verfolgungslayout: `tree` (zusammengeführter Baum) oder `list` (erweiterte Liste) | `tree` |
| `--style` | Namensstil: `java` (lesbar) oder `dex` (JNI-Signatur) | `java` |
| `--format` | Ausgabeformat: `text`, `json`, `model`, `html`, `sarif` | `text` |
| `--output` | Ausgabe in Datei statt stdout | — |
| `--color` | Farbmodus: `auto`, `always`, `never` | `auto` |
| `--mapping` | ProGuard/R8 mapping.txt Pfad | — |
| `--show-obf` | Gleichzeitig verschleierte und entverschleierte Namen anzeigen | `false` |
| `--api-flags` | hiddenapi-flags.csv Pfad | — |
| `--class-filter` | Klassendeskriptor-Präfixfilter (kommagetrennt) | — |
| `--exclude-api-lists` | Ausgeschlossene API-Stufen | — |
| `--scope` | Suchbereich: `all`, `callee`, `caller`, `string`, `string-table`, `everything` | `all` |
| `--diff` | Vergleich mit einem anderen APK/DEX, zeigt API-Unterschiede | — |
| `--fail-on` | Gibt einen Exit-Code ungleich Null zurück, wenn API der angegebenen Stufe erkannt wird (CI-Gate) | — |
| `--stats` | Nur statistische Zusammenfassung anzeigen | `false` |
| `--version` | Versionsnummer anzeigen | `false` |
### Konfigurationsdatei
Erstelle `.dexfinder.yaml` im Projektstammverzeichnis zum Festlegen von Standardwerten:```yaml
mapping: ./build/outputs/mapping.txt
class-filter: "Lcom/mycompany/"
api-flags: ./hiddenapi-flags.csv
style: java
depth: 8
color: auto
Befehlszeilenargumente überschreiben immer die Konfigurationsdatei.
git clone https://github.com/JuneLeGency/dexfinder.git cd dexfinder go build -o dexfinder ./cmd/dexfinder/ go test ./...
## Lizenz
Apache License 2.0
| Format | Beispiel | Verhalten |
|---|
| Einfacher Name | getDeviceId | Ungefähre Teilzeichenfolgen-Übereinstimmung über alle APIs hinweg |
| Java-Klasse | android.telephony.TelephonyManager | Alle Methoden/Felder dieser Klasse |
| Java-Klasse#Methode | android.telephony.TelephonyManager#getDeviceId | Alle Überladungen dieser Methode |
| Vollständige Java-Signatur | ...TelephonyManager#getDeviceId() | Exakt + Überladungs-Fallback |
| DEX/JNI-Signatur | Landroid/telephony/TelephonyManager;->getDeviceId()Ljava/lang/String; | Nur exakte Übereinstimmung |
| Wert | Beschreibung |
|---|
text | Klartext-Ausgabe mit farbigen Tags (Standard) |
json | JSON — Scan-Ergebnisse oder Trace mit Baum/Liste-Layout |
model | Strukturiertes JSON mit vollständigen MethodInfo/FieldInfo-Typen (für IDE/CI) |
html | In sich geschlossener HTML-Bericht mit zusammenklappbaren Bäumen und Suche |
sarif | SARIF 2.1.0 Format für statische Analyse (GitHub / VS Code) |
| Wert | Was wird gesucht | Beantwortete Frage | Ausgabe-Tag |
|---|
all | Callee-APIs + Felder + Code-Strings | "Wer ruft diese API auf?" (Standard) | [METHOD] [FIELD] [STRING] |
callee | Nur Ziel-API-Signaturen in invoke-* / get/put-Anweisungen | "Wer ruft diese spezifische Methode/dieses spezifische Feld auf?" | [METHOD] [FIELD] |
caller | Nur die Signatur der aufrufenden Methode | "Was ruft diese Methode intern auf?" | [CALLER→] |
string | String-Konstanten in const-string-Anweisungen | "Wo wird dieser String im Code verwendet?" | [STRING] |
string-table | Code-Strings + vollständige DEX-String-Tabelle | "Existiert dieser String irgendwo im DEX?" (beinhaltet Annotationen, toten Code) | [STRING] [STRING_TABLE] |
everything | Alle oben genannten kombiniert | Gesamtbild | alle Tags |
| Query input | No mapping | --mapping | --mapping --show-obf |
|---|
Verschleiert: LJ7; | ✓ verschleierte Ausgabe | ✓ entschleierte Ausgabe | ✓ beide Namen |
Original einfach: KotlinCases | ✗ nicht gefunden | ✓ automatische Konvertierung, entschleierte Ausgabe | ✓ automatische Konvertierung, beide Namen |
Original vollständig: com.example...KotlinCases | ✗ nicht gefunden | ✓ automatische Konvertierung, entschleierte Ausgabe | ✓ automatische Konvertierung, beide Namen |
| APK-Größe | DEX-Dateien | Klassen | Methodenreferenzen | Scan | Versteckte API |
|---|
| ~1 MB | 1 | ~2K | ~18K | 24ms | — |
| ~10 MB | 2 | ~25K | ~100K | 335ms | — |
| ~300 MB | 30+ | ~180K | ~1.2M | 3.9s | 5.4s |
| Flag | Beschreibung | Standard |
|---|
--dex-file | APK/DEX/JAR-Datei zur Analyse (erforderlich) | — |
--query | Suchbegriff (Java, DEX/JNI oder einfacher Name) | — |
--trace | Aktiviert Aufrufketten-Tracing (erfordert --query) | false |
--depth | Maximale Tiefe der Aufrufkette | 5 |
--layout | Trace-Layout: tree oder list | tree |
--style | Namensstil: java oder dex | java |
--format | Ausgabeformat: text, json, model, html, sarif | text |
--output | Ausgabe in Datei statt stdout schreiben | — |
--color | Farbmodus: auto, always, never | auto |
--mapping | Pfad zu ProGuard/R8 mapping.txt | — |
--show-obf | Zeigt verschleierte Namen neben entschleierten an | false |
--api-flags | Pfad zu hiddenapi-flags.csv | — |
--class-filter | Kommagetrennte Präfixe von Klassen-Deskriptoren | — |
--exclude-api-lists | Von der Berichterstattung auszuschließende API-Listen | — |
--scope | Suchumfang: all, callee, caller, string, string-table, everything | all |
--diff | Vergleiche mit einer anderen APK/DEX und zeige API-Unterschiede | — |
--fail-on | Gebe einen Nicht-Null-Exitcode aus, wenn versteckte APIs auf dieser Ebene gefunden werden (CI-Gate) | — |
--stats | Nur zusammenfassende Statistiken anzeigen | false |
--version | Version anzeigen | false |
| Format | Beispiel | Verhalten |
|---|
| Einfacher Name | getDeviceId | unscharfe Teilstring-Übereinstimmung |
| Java-Klassenname | android.telephony.TelephonyManager | Übereinstimmung aller Methoden dieser Klasse |
| Java-Klassenname#Methode | ...TelephonyManager#getDeviceId | Übereinstimmung aller Überladungen dieser Methode |
| Vollständige Java-Signatur | ...#getDeviceId() | exakte Übereinstimmung + Rückfall auf Überladungen |
| DEX/JNI-Signatur | Landroid/telephony/TelephonyManager;->getDeviceId()Ljava/lang/String; | exakte Übereinstimmung |
| Wert | Suchinhalt | Beantwortete Frage | Ausgabe-Tag |
|---|
all | Aufgerufene APIs + Felder + Code-Strings | "Wer hat diese Methode aufgerufen?" (Standard) | [METHOD] [FIELD] [STRING] |
callee | Nur Ziel-Signaturen in invoke-* / get/put-Anweisungen | "Wer hat diese spezifische Methode/dieses Feld aufgerufen?" | [METHOD] [FIELD] |
caller | Nur Signaturen von aufrufenden Methoden | "Was wurde innerhalb dieser Methode aufgerufen?" | [CALLER→] |
string | String-Konstanten in const-string-Anweisungen | "Wo wird dieser String im Code verwendet?" | [STRING] |
string-table | Code-Strings + vollständige DEX-String-Tabelle | "Existiert dieser String im DEX?" (einschließlich Annotationen, totem Code) | [STRING] [STRING_TABLE] |
everything | Alles oben Genannte | Vollständige Ansicht | Alle Tags |