
Ferramenta multiplataforma para localização de métodos APK/DEX com rastreamento de cadeia de chamadas, desofuscação ProGuard e deteção de APIs ocultas
Localizador de referências de métodos e campos APK/DEX multiplataforma com rastreamento de cadeia de chamadas, desofuscação ProGuard/R8 e detecção de APIs ocultas do Android.
Inspirado pela ferramenta veridex do Android, reimplementada em Go com capacidades aprimoradas: detecção de reflexão mais rápida, rastreamento de cadeia de chamadas (o veridex mostra apenas um nível) e formatos de saída flexíveis.
--fail-on blocked sair com código diferente de zero quando APIs restritas são encontradas.dexfinder.yaml para padrões do projeto, flags de CLI sobrescrevemHomebrew (macOS / Linux):```bash brew install junelegency/tap/dexfinder
**Script** (detecta automaticamente SO/arquitetura):```bash
curl -sSL https://raw.githubusercontent.com/JuneLeGency/dexfinder/main/install.sh | bash
Instalação com Go:```bash go install github.com/JuneLeGency/dexfinder/cmd/dexfinder@latest
**Binário**: baixe dos [Lançamentos](https://github.com/JuneLeGency/dexfinder/releases).
## Início Rápido```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
A flag --query aceita múltiplos estilos de entrada. O dexfinder detecta e converte automaticamente entre eles.
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"
## Controle de Saída
Três eixos independentes, livremente combináveis:```
--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 (usado com --trace)| Valor | Descrição |
|---|---|
tree | Árvore mesclada — caminhos de chamada compartilhados colapsados em uma árvore (padrão) |
list | Lista plana — cada cadeia de chamada única mostrada como pilha independente |
--style| Valor | Exemplo | Caso de uso |
|---|---|---|
java | com.example.Foo.method(Foo.java) | Legível para humanos (padrão) |
dex | Foo.method(Ljava/lang/String;)V | Análise precisa de assinatura |
--scope (escopo de pesquisa)Controla que tipo de referências a consulta corresponde. Isto é crítico para compreender os resultados.
Compreendendo 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` (padrão) = `callee` + `string`. A direção `caller` é intencionalmente excluída do padrão porque responde a uma pergunta fundamentalmente diferente. Use `--scope=caller` ou `--scope=everything` explicitamente quando precisar.
**Entendendo as tags de saída:**
| Tag | Significado |
|---|---|
| `[METHOD]` | Um método **sendo chamado** corresponde à sua consulta (correspondência callee). Linhas indentadas são os chamadores. |
| `[FIELD]` | Um campo **sendo acessado** corresponde à sua consulta. Linhas indentadas são os acessadores. |
| `[CALLER→]` | Um **método chamador** corresponde à sua consulta. A linha indentada mostra qual API ele está chamando. |
| `[STRING]` | Uma constante de string no código corresponde à sua consulta. Linhas indentadas são onde ela é usada. |
| `[STRING_TABLE]` | String existe na tabela de strings DEX, mas não tem referência `const-string` no código (pode estar em anotações, otimizada pelo R8, etc.) |
## Exemplos
### 1. Escanear estatísticas do 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. Rastrear cadeias de chamadas — visualização em árvore```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. Rastrear com estilo de assinatura 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
}
Com --mapping, tanto a entrada quanto a saída suportam nomes originais (não ofuscados).
Consulta por nome original → converte automaticamente para nome ofuscado para pesquisa 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
**Saída dos nomes desofuscados no rastreio:**```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)
Mostrar nomes ofuscados e originais:```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]
**Todas as combinações com outras 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
Matriz Entrada × Saída:
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 string de pesquisa (content:// URIs, API keys, 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. Combine tudo```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
Abre em qualquer navegador — árvores de chamadas colapsáveis, barra de pesquisa, tema escuro.
### 15. SARIF para Varredura de Código no GitHub```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. Gate de CI com --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
Benchmarked on Apple M-series, single thread:
Comparado ao veridex (C++, modo impreciso) no mesmo APK de ~300MB:
Crie .dexfinder.yaml na raiz do seu projeto para definir padrões:```yaml
mapping: ./build/outputs/mapping.txt
class-filter: "Lcom/mycompany/"
api-flags: ./hiddenapi-flags.csv
style: java
depth: 8
color: auto
As flags de CLI sempre sobrescrevem os valores do arquivo de configuração.
## Construindo a partir do Código Fonte```bash
git clone https://github.com/JuneLeGency/dexfinder.git
cd dexfinder
go build -o dexfinder ./cmd/dexfinder/
go test ./...
Apache License 2.0
Site oficial: junelegency.github.io/dexfinder
Localizador de métodos e campos APK/DEX multiplataforma, com suporte a rastreamento de cadeias de chamadas, desofuscação ProGuard/R8 e detecção de API oculta do Android.
Baseado no princípio do Android veridex, reimplementado e aprimorado em Go: detecção de reflexão mais rápida, rastreamento de cadeias de chamadas em várias camadas (veridex apenas uma camada) e formatos de saída flexíveis.
--fail-on blocked retorna código de saída diferente de zero ao detectar API restrita.dexfinder.yaml para configuração padrão do projeto, parâmetros de linha de comando sobrepõemHomebrew (macOS / Linux):```bash brew install junelegency/tap/dexfinder
**Instalação do script** (Sistema de detecção automática):```bash
curl -sSL https://raw.githubusercontent.com/JuneLeGency/dexfinder/main/install.sh | bash
Go Instalação:```bash go install github.com/JuneLeGency/dexfinder/cmd/dexfinder@latest
**Download binário**: [Releases](https://github.com/JuneLeGency/dexfinder/releases)
## Início rápido```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)Três dimensões independentes, combinação livre:``` --format (text / json / model / html / sarif) 输出什么 --layout (tree / list) 怎么排列调用链 --style (java / dex) 怎么显示名称 --color (auto / always / never) 终端着色
### `--layout` comparação (com `--trace`)
**tree** — mescla caminhos comuns, uma árvore mostra a visão geral:```
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 — Cada cadeia exibida independentemente (estilo 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)
### `--style` comparação
**java** (padrão): `com.example.Foo.method(Foo.java)`
**dex**: `Foo.method(Ljava/lang/String;)V`
### Saída 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 Escopo de pesquisaControla qual tipo de referência a consulta corresponde. Entender este parâmetro é crucial para interpretar corretamente os resultados.
Diferença entre callee vs caller:``` scope=callee: "谁调了 finish()?" onCreate ──调用──→ finish() ← 显示这些调用者 onResume ──调用──→ finish()
scope=caller: "finish() 内部调了什么?" finish() ──调用──→ Log.i() ← 显示这些被调用者 finish() ──调用──→ super.finish()
`--scope=all` (padrão) = `callee` + `string`. A direção `caller` foi intencionalmente excluída do padrão porque responde a uma pergunta completamente diferente. Ative explicitamente com `--scope=caller` ou `--scope=everything` quando necessário.
**Significado das tags de saída:**
| Tag | Significado |
|---|---|
| `[METHOD]` | O método que você pesquisou **foi chamado por outro**. A linha indentada é o chamador. |
| `[FIELD]` | O campo que você pesquisou **foi acessado por outro**. A linha indentada é o acessador. |
| `[CALLER→]` | O nome do método que você pesquisou aparece em algum **chamador**, a linha indentada mostra qual API ele chamou. |
| `[STRING]` | Constante de string no código corresponde. A linha indentada é o método que usa a string. |
| `[STRING_TABLE]` | A string existe apenas na tabela de strings DEX, não há referência `const-string` no código (pode estar em anotações, otimizada pelo R8, etc.). |
## Mais usos
### Ofuscação reversa (--mapping)
Após carregar `--mapping`, tanto a **entrada quanto a saída** suportam nomes originais (não ofuscados).
**Consultar pelo nome original → convertido automaticamente para o nome ofuscado para pesquisar no 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
Nome desobfuscado de saída:```bash
dexfinder --dex-file app.apk --query "KotlinCases" --mapping mapping.txt --trace
**Exibir simultaneamente nome ofuscado e nome original:**```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]
Combine livremente com outros parâmetros:```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
**Matriz de entrada × saída:**
| Consulta de entrada | Sem `--mapping` | Com `--mapping` | Com `--mapping --show-obf` |
|---|---|---|---|
| Nome ofuscado `LJ7;` | ✓ Saída ofuscada | ✓ Saída desofuscada | ✓ Ambos lado a lado |
| Nome curto original `KotlinCases` | ✗ Não encontrado | ✓ Conversão automática + saída desofuscada | ✓ Conversão automática + ambos lado a lado |
| Nome completo original `com.example...` | ✗ Não encontrado | ✓ Conversão automática + saída desofuscada | ✓ Conversão automática + ambos lado a lado |
### Detecção de Hidden API```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
### Filtrar por nome do pacote```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/"
### Relatório HTML```bash
dexfinder --dex-file app.apk --query "getDeviceId" --trace --format html --output report.html
Aberto e utilizável no navegador — árvore de chamadas recolhível, barra de pesquisa, tema escuro.
dexfinder --dex-file app.apk --api-flags hiddenapi-flags.csv --format sarif > results.sarif
### Comparação de versões 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
## Desempenho
Apple M-series, single-thread:
| Tamanho do APK | Nº de DEX | Nº de classes | Referências de métodos | 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** |
Comparação com veridex (C++) no mesmo APK de ~300MB:
- veridex precise: **27s** (não pode rastrear reflexão Binder/AIDL)
- veridex imprecise: **>32 minutos** (explosão de produto cartesiano, morto)
- **dexfinder: 5.4s** (otimização de índice invertido)
## Todos os parâmetros
| Parâmetro | Descrição | Valor padrão |
|---|---|---|
| `--dex-file` | Caminho do arquivo APK/DEX/JAR ** (obrigatório) ** | — |
| `--query` | Palavra-chave de pesquisa (Java / DEX/JNI / nome simples) | — |
| `--trace` | Ativar rastreamento de cadeia de chamadas (requer `--query`) | `false` |
| `--depth` | Profundidade máxima da cadeia de chamadas | `5` |
| `--layout` | Layout de rastreamento: `tree` (árvore mesclada) ou `list` (lista expandida) | `tree` |
| `--style` | Estilo de nomeação: `java` (legível) ou `dex` (assinatura JNI) | `java` |
| `--format` | Formato de saída: `text`, `json`, `model`, `html`, `sarif` | `text` |
| `--output` | Saída para arquivo em vez de stdout | — |
| `--color` | Modo de cor: `auto`, `always`, `never` | `auto` |
| `--mapping` | Caminho do mapping.txt do ProGuard/R8 | — |
| `--show-obf` | Mostrar nomes ofuscados e desofuscados simultaneamente | `false` |
| `--api-flags` | Caminho do hiddenapi-flags.csv | — |
| `--class-filter` | Filtro de prefixo de descritor de classe (separado por vírgulas) | — |
| `--exclude-api-lists` | Níveis de API excluídos | — |
| `--scope` | Escopo de pesquisa: `all`, `callee`, `caller`, `string`, `string-table`, `everything` | `all` |
| `--diff` | Comparar com outro APK/DEX, mostrar diferenças de API | — |
| `--fail-on` | Retornar código de saída não zero ao detectar API de nível especificado (ponto de bloqueio CI) | — |
| `--stats` | Mostrar apenas resumo estatístico | `false` |
| `--version` | Mostrar número da versão | `false` |
### Arquivo de configuração
Crie `.dexfinder.yaml` no diretório raiz do projeto para definir valores padrão:```yaml
mapping: ./build/outputs/mapping.txt
class-filter: "Lcom/mycompany/"
api-flags: ./hiddenapi-flags.csv
style: java
depth: 8
color: auto
Os argumentos da linha de comando sempre sobrescrevem o arquivo de configuração.
git clone https://github.com/JuneLeGency/dexfinder.git cd dexfinder go build -o dexfinder ./cmd/dexfinder/ go test ./...
## Licença
Apache License 2.0
| Formato | Exemplo | Comportamento |
|---|
| Nome simples | getDeviceId | Correspondência difusa por substring em todas as APIs |
| Classe Java | android.telephony.TelephonyManager | Todos os métodos/campos dessa classe |
| Classe Java#método | android.telephony.TelephonyManager#getDeviceId | Todas as sobrecargas desse método |
| Assinatura completa Java | ...TelephonyManager#getDeviceId() | Correspondência exata + fallback de sobrecarga |
| Assinatura DEX/JNI | Landroid/telephony/TelephonyManager;->getDeviceId()Ljava/lang/String; | Correspondência exata apenas |
| Valor | Descrição |
|---|
text | Saída de texto simples com tags coloridas (padrão) |
json | JSON — resultados de varredura ou rastreamento com layout em árvore/lista |
model | JSON estruturado com tipos completos MethodInfo/FieldInfo (para IDE/CI) |
html | Relatório HTML autossuficiente com árvores recolhíveis e pesquisa |
sarif | Formato de análise estática SARIF 2.1.0 (GitHub / VS Code) |
| Valor | O que pesquisa | Pergunta que responde | Tag de saída |
|---|
all | APIs de destino + campos + strings de código | "Quem chama esta API?" (padrão) | [METHOD] [FIELD] [STRING] |
callee | Apenas assinaturas de API de destino nas instruções invoke-* / get/put | "Quem chama este método/campo específico?" | [METHOD] [FIELD] |
caller | Apenas a assinatura do método chamador | "O que este método chama internamente?" | [CALLER→] |
string | Constantes de string em instruções const-string | "Onde esta string é usada no código?" | [STRING] |
string-table | Strings de código + tabela completa de strings DEX | "Esta string existe em algum lugar no DEX?" (inclui anotações, código morto) | [STRING] [STRING_TABLE] |
everything | Tudo acima combinado | Quadro completo | todas as tags |
| Consulta de entrada | Sem mapeamento | --mapping | --mapping --show-obf |
|---|
Ofuscado: LJ7; | ✓ saída ofuscada | ✓ saída desofuscada | ✓ ambos os nomes |
Original simples: KotlinCases | ✗ não encontrado | ✓ converte automaticamente, saída desof. | ✓ converte automaticamente, ambos os nomes |
Original completo: com.example...KotlinCases | ✗ não encontrado | ✓ converte automaticamente, saída desof. | ✓ converte automaticamente, ambos os nomes |
| Tamanho do APK | Arquivos DEX | Classes | Refs de Métodos | Scan | API Oculta |
|---|
| ~1 MB | 1 | ~2K | ~18K | 24ms | — |
| ~10 MB | 2 | ~25K | ~100K | 335ms | — |
| ~300 MB | 30+ | ~180K | ~1.2M | 3.9s | 5.4s |
| Flag | Descrição | Padrão |
|---|
--dex-file | Arquivo APK/DEX/JAR para analisar (obrigatório) | — |
--query | Palavra-chave de busca (Java, DEX/JNI ou nome simples) | — |
--trace | Ativar rastreamento de cadeia de chamadas (requer --query) | false |
--depth | Profundidade máxima da cadeia de chamadas | 5 |
--layout | Layout do rastreamento: tree ou list | tree |
--style | Estilo de nome: java ou dex | java |
--format | Formato de saída: text, json, model, html, sarif | text |
--output | Escrever saída em arquivo em vez de stdout | — |
--color | Modo de cor: auto, always, never | auto |
--mapping | Caminho do mapping.txt do ProGuard/R8 | — |
--show-obf | Mostrar nomes ofuscados junto com os desofuscados | false |
--api-flags | Caminho para hiddenapi-flags.csv | — |
--class-filter | Prefixos de descritor de classe separados por vírgula | — |
--exclude-api-lists | Listas de API a excluir do relatório | — |
--scope | Escopo de busca: all, callee, caller, string, string-table, everything | all |
--diff | Comparar com outro APK/DEX e mostrar diferenças de API | — |
--fail-on | Sair com código diferente de zero se APIs ocultas neste nível forem encontradas (portão de CI) | — |
--stats | Mostrar apenas estatísticas resumidas | false |
--version | Mostrar versão | false |
| Formato | Exemplo | Comportamento |
|---|
| Nome simples | getDeviceId | Correspondência de substring difusa |
| Nome de classe Java | android.telephony.TelephonyManager | Corresponder todos os métodos da classe |
| Nome de classe Java#método | ...TelephonyManager#getDeviceId | Corresponder todas as sobrecargas do método |
| Assinatura completa Java | ...#getDeviceId() | Correspondência exata + fallback de sobrecarga |
| Assinatura DEX/JNI | Landroid/telephony/TelephonyManager;->getDeviceId()Ljava/lang/String; | Correspondência exata |
| Valor | O que pesquisa | Pergunta respondida | Rótulo de saída |
|---|
all | toda API chamada + campos + strings no código | "Quem chama este método?" (padrão) | [METHOD] [FIELD] [STRING] |
callee | apenas assinaturas alvo nas instruções invoke-* / get/put | "Quem chama este método/campo específico?" | [METHOD] [FIELD] |
caller | apenas assinaturas dos métodos chamadores | "O que este método chama internamente?" | [CALLER→] |
string | constantes string nas instruções const-string | "Onde esta string é usada no código?" | [STRING] |
string-table | strings do código + tabela completa de strings do DEX | "Esta string existe no DEX?" (inclui anotações, código morto) | [STRING] [STRING_TABLE] |
everything | tudo acima | Visão completa | todos os rótulos |