
Outil multi-plateforme de recherche de méthodes APK/DEX avec traçage de chaînes d'appels, désobfuscation ProGuard et détection d'API cachées.
Site web : junelegency.github.io/dexfinder
Recherche de références de méthodes et de champs dans les APK/DEX multiplateforme, avec traçage des chaînes d’appels, désobfuscation ProGuard/R8 et détection des API cachées Android.
Inspiré de l’outil veridex d’Android, réimplémenté en Go avec des fonctionnalités améliorées : détection plus rapide des réflexions, traçage des chaînes d’appels (veridex n’affiche qu’un seul niveau) et formats de sortie flexibles.
--fail-on blocked se termine avec un code non nul lorsque des API restreintes sont trouvées.dexfinder.yaml pour les valeurs par défaut du projet, les options CLI prévalentHomebrew (macOS / Linux) :```bash brew install junelegency/tap/dexfinder
**Script** (détecte automatiquement OS/arch):```bash
curl -sSL https://raw.githubusercontent.com/JuneLeGency/dexfinder/main/install.sh | bash
Installation Go :```bash go install github.com/JuneLeGency/dexfinder/cmd/dexfinder@latest
**Binary**: télécharger depuis [Releases](https://github.com/JuneLeGency/dexfinder/releases).
## Démarrage rapide```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
Le flag --query accepte plusieurs formats d'entrée. dexfinder détecte et convertit automatiquement entre eux.
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"
## Contrôle de la sortie
Trois axes indépendants, librement combinables :```
--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 (utilisé avec --trace)| Valeur | Description |
|---|---|
tree | Arbre fusionné — chemins d'appel partagés réduits à un seul arbre (par défaut) |
list | Liste plate — chaque chaîne d'appel unique affichée comme pile indépendante |
--style| Valeur | Exemple | Cas d'utilisation |
|---|---|---|
java | com.example.Foo.method(Foo.java) | Lisible par l'humain (par défaut) |
dex | Foo.method(Ljava/lang/String;)V | Analyse précise des signatures |
--scope (portée de recherche)Contrôle le type de références que la requête vérifie. Ceci est crucial pour comprendre les résultats.
Comprendre la distinction entre callee et 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` (par défaut) = `callee` + `string`. La direction `caller` est délibérément exclue du comportement par défaut car elle répond à une question fondamentalement différente. Utilisez `--scope=caller` ou `--scope=everything` explicitement lorsque vous en avez besoin.
**Comprendre les étiquettes de sortie :**
| Étiquette | Signification |
|---|---|
| `[METHOD]` | Une méthode **qui est appelée** correspond à votre requête (correspondance callee). Les lignes indentées sont les appelants. |
| `[FIELD]` | Un champ **qui est accédé** correspond à votre requête. Les lignes indentées sont les accédants. |
| `[CALLER→]` | Une **méthode appelante** correspond à votre requête. La ligne indentée montre l'API qu'elle appelle. |
| `[STRING]` | Une constante de chaîne dans le code correspond à votre requête. Les lignes indentées sont les endroits où elle est utilisée. |
| `[STRING_TABLE]` | La chaîne existe dans la table de chaînes DEX mais n'a pas de référence `const-string` dans le code (peut être dans des annotations, optimisée par R8, etc.) |
## Exemples
### 1. Analyser les statistiques d'une APK```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. Traçage des chaînes d'appels — vue arborescente```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. Traçage avec le style de signature DEX```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
}
Avec --mapping, les entrées et sorties prennent en charge les noms originaux (non obscurcis).
Requête par nom original → conversion automatique en nom obscurci pour la recherche DEX :```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
**Afficher les noms désobfusqués dans la trace :**```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)
Afficher à la fois les noms obfusqués et originaux :```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]
**Toutes les combinaisons avec d'autres 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
Matrice entrée × sortie :
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. Constantes de chaîne de recherche (content:// URIs, clés API, etc.)```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. Combinez tout```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
S'ouvre dans n'importe quel navigateur — arborescences d'appels pliables, barre de recherche, thème sombre.
### 15. SARIF pour 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. Passerelle CI avec --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
Testé sur Apple M-series, mono-thread :
Comparé à veridex (C++, mode imprécis) sur le même APK de ~300 Mo :
Créez .dexfinder.yaml à la racine de votre projet pour définir les valeurs par défaut :```yaml
mapping: ./build/outputs/mapping.txt
class-filter: "Lcom/mycompany/"
api-flags: ./hiddenapi-flags.csv
style: java
depth: 8
color: auto
Les drapeaux CLI remplacent toujours les valeurs du fichier de configuration.
## Compilation à partir des sources```bash
git clone https://github.com/JuneLeGency/dexfinder.git
cd dexfinder
go build -o dexfinder ./cmd/dexfinder/
go test ./...
Apache License 2.0
Site officiel: junelegency.github.io/dexfinder
Analyseur de références de méthodes et de champs pour APK/DEX multiplateforme, prenant en charge le suivi de chaîne d'appels, la désobfuscation ProGuard/R8 et la détection des API cachées Android.
Basé sur le principe d'Android veridex, réimplémenté et amélioré en Go : détection de réflexion plus rapide, suivi multi-niveaux (veridex ne fait qu'un seul niveau), formats de sortie flexibles.
--fail-on blocked renvoie un code de sortie non nul si une API restreinte est détectée.dexfinder.yaml pour la configuration par défaut du projet, paramètres en ligne de commande prioritairesHomebrew (macOS / Linux) :```bash brew install junelegency/tap/dexfinder
**Installation du script** (détection automatique du système) :```bash
curl -sSL https://raw.githubusercontent.com/JuneLeGency/dexfinder/main/install.sh | bash
Installation de Go :```bash go install github.com/JuneLeGency/dexfinder/cmd/dexfinder@latest
**Téléchargement binaire**: [Releases](https://github.com/JuneLeGency/dexfinder/releases)
## Démarrage rapide```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)Trois dimensions indépendantes, librement combinables :``` --format (text / json / model / html / sarif) 输出什么 --layout (tree / list) 怎么排列调用链 --style (java / dex) 怎么显示名称 --color (auto / always / never) 终端着色
### `--layout` Comparaison (avec `--trace`)
**tree** — Fusionner les chemins communs, un arbre montre la vue d'ensemble :```
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 — affichage indépendant de chaque chaîne(style Java crash)``` --- 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)
### Comparaison `--style`
**java** (par défaut): `com.example.Foo.method(Foo.java)`
**dex**: `Foo.method(Ljava/lang/String;)V`
### Sortie JSON```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 Portée de rechercheContrôle quel type de référence la requête correspond. Comprendre ce paramètre est crucial pour interpréter correctement les résultats.
Différence entre callee et caller :``` scope=callee: "谁调了 finish()?" onCreate ──调用──→ finish() ← 显示这些调用者 onResume ──调用──→ finish()
scope=caller: "finish() 内部调了什么?" finish() ──调用──→ Log.i() ← 显示这些被调用者 finish() ──调用──→ super.finish()
`--scope=all` (par défaut) = `callee` + `string`. La direction `caller` est délibérément exclue du défaut car elle répond à une question complètement différente. Activez-la explicitement avec `--scope=caller` ou `--scope=everything` si nécessaire.
**Signification des étiquettes de sortie :**
| Étiquette | Signification |
|---|---|
| `[METHOD]` | L'API que vous avez recherchée est **appelée par quelqu'un d'autre**. La ligne en retrait est l'appelant. |
| `[FIELD]` | Le champ que vous avez recherché est **accessible par quelqu'un d'autre**. La ligne en retrait est l'accesseur. |
| `[CALLER→]` | Le nom de l'API que vous avez recherchée apparaît dans un **appelant**, la ligne en retrait montre quelle API il a appelée. |
| `[STRING]` | Correspondance de constante de chaîne dans le code. La ligne en retrait est la méthode qui utilise cette chaîne. |
| `[STRING_TABLE]` | La chaîne n'existe que dans la table de chaînes DEX, il n'y a pas de référence `const-string` dans le code (peut-être dans les annotations, optimisée par R8, etc.). |
## Plus d'utilisations
### Déséfuscation (--mapping)
Après avoir chargé `--mapping`, les **entrées et sorties** prennent en charge les noms originaux (non obscurcis).
**Requête avec nom original → conversion automatique en nom obscurci pour la recherche DEX :**```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
输出反混淆名称:```bash
dexfinder --dex-file app.apk --query "KotlinCases" --mapping mapping.txt --trace
**Afficher à la fois le nom obscurci et le nom d'origine :**```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]
Combinaison libre avec d'autres paramètres :```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
**Matrice entrée × sortie :**
| Entrée de requête | Sans `--mapping` | `--mapping` | `--mapping --show-obf` |
|---|---|---|---|
| Nom obscurci `LJ7;` | ✓ sortie obscurcie | ✓ sortie désormais claire | ✓ les deux côte à côte |
| Nom court original `KotlinCases` | ✗ introuvable | ✓ conversion auto + sortie clarifiée | ✓ conversion auto + les deux côte à côte |
| Nom complet original `com.example...` | ✗ introuvable | ✓ conversion auto + sortie clarifiée | ✓ conversion auto + les deux côte à côte |
### Détection d’API cachées```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
### Filtrer par nom de paquet```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/"
### Rapport HTML```bash
dexfinder --dex-file app.apk --query "getDeviceId" --trace --format html --output report.html
Utilisable directement dans le navigateur — arbre d'appels pliable, barre de recherche, thème sombre.
dexfinder --dex-file app.apk --api-flags hiddenapi-flags.csv --format sarif > results.sarif
### Comparaison des versions APK```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
## Performances
Puce Apple M series, single thread :
| Taille APK | Nb DEX | Nb classes | Réf. méthodes | Scan | API cachées |
|---|---|---|---|---|---|
| ~1 Mo | 1 | ~2K | ~18K | **24ms** | — |
| ~10 Mo | 2 | ~25K | ~100K | **335ms** | — |
| ~300 Mo | 30+ | ~180K | ~1,2M | **3,9s** | **5,4s** |
Comparaison avec veridex (C++) sur le même APK ~300 Mo :
- veridex précis : **27s** (ne peut pas tracer la réflexion Binder/AIDL)
- veridex imprécis : **>32 minutes** (explosion du produit cartésien, tué)
- **dexfinder : 5,4s** (optimisation de l'index inversé)
## Tous les paramètres
| Paramètre | Description | Valeur par défaut |
|---|---|---|
| `--dex-file` | Chemin du fichier APK/DEX/JAR **(obligatoire)** | — |
| `--query` | Mot‑clé de recherche (Java / DEX/JNI / nom simple) | — |
| `--trace` | Activer le traçage des chaînes d’appel (nécessite `--query`) | `false` |
| `--depth` | Profondeur maximale de la chaîne d’appel | `5` |
| `--layout` | Disposition du traçage : `tree` (arbre fusionné) ou `list` (liste déroulante) | `tree` |
| `--style` | Style de nommage : `java` (lisible) ou `dex` (signature JNI) | `java` |
| `--format` | Format de sortie : `text`, `json`, `model`, `html`, `sarif` | `text` |
| `--output` | Sortie vers un fichier au lieu de stdout | — |
| `--color` | Mode couleur : `auto`, `always`, `never` | `auto` |
| `--mapping` | Chemin du fichier mapping.txt ProGuard/R8 | — |
| `--show-obf` | Afficher à la fois les noms obscurs et désobfusqués | `false` |
| `--api-flags` | Chemin du fichier hiddenapi-flags.csv | — |
| `--class-filter` | Filtre de préfixe de descripteur de classe (séparé par des virgules) | — |
| `--exclude-api-lists` | Niveaux d’API exclus | — |
| `--scope` | Portée de recherche : `all`, `callee`, `caller`, `string`, `string-table`, `everything` | `all` |
| `--diff` | Comparer avec un autre APK/DEX, afficher les différences d’API | — |
| `--fail-on` | Retourner un code de sortie non nul lors de la détection d’API au niveau spécifié (point de contrôle CI) | — |
| `--stats` | Afficher uniquement le résumé statistique | `false` |
| `--version` | Afficher le numéro de version | `false` |
### Fichier de configuration
Créez un fichier `.dexfinder.yaml` à la racine du projet pour définir les valeurs par défaut :```yaml
mapping: ./build/outputs/mapping.txt
class-filter: "Lcom/mycompany/"
api-flags: ./hiddenapi-flags.csv
style: java
depth: 8
color: auto
Les arguments de la ligne de commande remplacent toujours les fichiers de configuration.
git clone https://github.com/JuneLeGency/dexfinder.git cd dexfinder go build -o dexfinder ./cmd/dexfinder/ go test ./...
## Licence
Apache License 2.0
| Format | Exemple | Comportement |
|---|
| Nom simple | getDeviceId | Correspondance floue par sous-chaîne sur toutes les API |
| Classe Java | android.telephony.TelephonyManager | Toutes les méthodes/champs de cette classe |
| Classe Java#méthode | android.telephony.TelephonyManager#getDeviceId | Toutes les surcharges de cette méthode |
| Signature Java complète | ...TelephonyManager#getDeviceId() | Exact + repli de surcharge |
| Signature DEX/JNI | Landroid/telephony/TelephonyManager;->getDeviceId()Ljava/lang/String; | Correspondance exacte uniquement |
| Valeur | Description |
|---|
text | Sortie texte brut avec balises colorées (par défaut) |
json | JSON — résultats d'analyse ou trace avec disposition arborescente/liste |
model | JSON structuré avec types complets MethodInfo/FieldInfo (pour IDE/CI) |
html | Rapport HTML autonome avec arbres repliables et recherche |
sarif | Format d'analyse statique SARIF 2.1.0 (GitHub / VS Code) |
| Valeur | Ce qu'il recherche | Question à laquelle il répond | Balise de sortie |
|---|
all | APIs cibles + champs + chaînes de code | « Qui appelle cette API ? » (par défaut) | [METHOD] [FIELD] [STRING] |
callee | Uniquement les signatures d'API cibles dans les instructions invoke-* / get/put | « Qui appelle cette méthode/champ spécifique ? » | [METHOD] [FIELD] |
caller | Uniquement la signature de la méthode appelante | « Que cette méthode appelle-t-elle en interne ? » | [CALLER→] |
string | Constantes chaîne dans les instructions const-string | « Où cette chaîne est-elle utilisée dans le code ? » | [STRING] |
string-table | Chaînes de code + table de chaînes DEX complète | « Cette chaîne existe-t-elle quelque part dans le DEX ? » (inclut annotations, code mort) | [STRING] [STRING_TABLE] |
everything | Tout ce qui précède combiné | Vue d'ensemble | toutes les balises |
| Entrée de requête | Aucun mapping | --mapping | --mapping --show-obf |
|---|
Obfusqué : LJ7; | ✓ sortie obfusquée | ✓ sortie désobfusquée | ✓ les deux noms |
Original simple : KotlinCases | ✗ non trouvé | ✓ convertit automatiquement, sortie désobfusquée | ✓ convertit automatiquement, les deux noms |
Original complet : com.example...KotlinCases | ✗ non trouvé | ✓ convertit automatiquement, sortie désobfusquée | ✓ convertit automatiquement, les deux noms |
| Taille APK | Fichiers DEX | Classes | Réf. Méthodes | Analyse | API Cachées |
|---|
| ~1 Mo | 1 | ~2K | ~18K | 24ms | — |
| ~10 Mo | 2 | ~25K | ~100K | 335ms | — |
| ~300 Mo | 30+ | ~180K | ~1.2M | 3.9s | 5.4s |
| Option | Description | Défaut |
|---|
--dex-file | Fichier APK/DEX/JAR à analyser (obligatoire) | — |
--query | Mot-clé de recherche (Java, DEX/JNI, ou nom simple) | — |
--trace | Activer le traçage des chaînes d'appels (nécessite --query) | false |
--depth | Profondeur max de la chaîne d'appels | 5 |
--layout | Disposition du tracé : tree ou list | tree |
--style | Style de nom : java ou dex | java |
--format | Format de sortie : text, json, model, html, sarif | text |
--output | Écrire la sortie dans un fichier au lieu de stdout | — |
--color | Mode couleur : auto, always, never | auto |
--mapping | Chemin du fichier ProGuard/R8 mapping.txt | — |
--show-obf | Afficher les noms obfusqués à côté des dés-obfusqués | false |
--api-flags | Chemin vers hiddenapi-flags.csv | — |
--class-filter | Préfixes de descripteurs de classe séparés par des virgules | — |
--exclude-api-lists | Listes d'API à exclure du rapport | — |
--scope | Portée de recherche : all, callee, caller, string, string-table, everything | all |
--diff | Comparer avec un autre APK/DEX et afficher les différences d'API | — |
--fail-on | Sortir avec code non nul si des API cachées à ce niveau sont trouvées (CI gate) | — |
--stats | Afficher uniquement les statistiques récapitulatives | false |
--version | Afficher la version | false |
| Format | Exemple | Comportement |
|---|
| Nom simple | getDeviceId | Correspondance floue de sous‑chaîne |
| Nom de classe Java | android.telephony.TelephonyManager | Correspond à toutes les méthodes de cette classe |
| Nom de classe Java#méthode | ...TelephonyManager#getDeviceId | Correspond à toutes les surcharges de cette méthode |
| Signature Java complète | ...#getDeviceId() | Correspondance exacte + repli sur les surcharges |
| Signature DEX/JNI | Landroid/telephony/TelephonyManager;->getDeviceId()Ljava/lang/String; | Correspondance exacte |
| Valeur | Contenu recherché | Question répondue | Étiquette de sortie |
|---|
all | API appelée + champs + chaînes de code | "Qui appelle cette méthode ?" (par défaut) | [METHOD] [FIELD] [STRING] |
callee | uniquement les signatures cibles dans les instructions invoke-* / get/put | "Qui appelle cette méthode/champ spécifique ?" | [METHOD] [FIELD] |
caller | uniquement les signatures des méthodes appelantes | "Qu'est-ce que cette méthode appelle en interne ?" | [CALLER→] |
string | chaînes constantes dans les instructions const-string | "Où cette chaîne est-elle utilisée dans le code ?" | [STRING] |
string-table | chaînes de code + table complète des chaînes DEX | "Cette chaîne existe-t-elle dans le DEX ?" (annotations, dead code inclus) | [STRING] [STRING_TABLE] |
everything | tout ce qui précède | Vue complète | toutes les étiquettes |