
Um descompilador nativo de APK e DEX escrito em Rust
O parser DEX inicial em Rust em src/native_dex.rs adapta a lógica de parsing do
JADX v1.5.6, fixado no commit
28ff15e4ae69950aebea110a13e5ab895d234dfc.
O JADX é copyright de Skylot e seus contribuidores; os avisos aplicáveis do Android Open Source
Project e outros avisos upstream são preservados em NOTICE.
A implementação de referência é o
plugin de entrada DEX,
incluindo DexReader, sections/DexHeader, sections/SectionReader,
sections/DexClassData e utils/Leb128.
As modificações do RDX traduzem a lógica do parser para Rust com acesso a bytes verificado, propagação explícita de erros e representações de classes/membros próprias do RDX. O emissor de código-fonte nativo é código novo do RDX. Esta é uma implementação inicial e parcial do parser e do motor nativo, não uma porta completa do pipeline de descompilação do JADX nem uma alegação de cobertura de saída equivalente.
Os arquivos abaixo são cópias não modificadas baixadas da versão fixada:
8dada3edaf50dbc082c9a125058f25def75e625a).5c0b69a0f5298e0b329e33e860f7626f0c2c3891).O NOTICE upstream completo é mantido, incluindo avisos históricos de bibliotecas incorporadas e ícones. A manutenção não significa que o parser em Rust incorpore todas essas bibliotecas ou ativos. A implementação Java e suas dependências de runtime não são mais distribuídas.
Distribuições que contenham o parser adaptado devem incluir a licença e os avisos aplicáveis. Alterações derivadas de arquivos upstream adicionais devem manter seus avisos e estender este mapeamento de origem à medida que a porta cresce.
src/native_resources.rs mapeia o despacho de chunks, namespaces, elementos e atributos
tipados do JADX v1.5.6 fixado
jadx-core/src/main/java/jadx/core/xmlgen/BinaryXMLParser.java
para Rust nativo. Seu leitor de string-pool e emissor XML limitado são código do RDX.
O decodificador valida limites de entrada/chunk/string, suporta pools UTF-8/UTF-16
e trata valores de atributos tipados comuns do Android sem um runtime Java.
Os IDs de recursos permanecem numéricos: a busca de símbolos ARSC e os nomes de enum/flags do
manifest ainda não foram portados. Este não é o subsistema completo de recursos upstream.
src/native_java/ é a camada conservadora do RDX em Rust de rebaixamento de valores de registradores e
emissão de Java sobre o leitor DEX adaptado. Ela trata instruções tipadas suportadas, ramificações
para frente, loops simples e switches para frente com materialização explícita de efeitos e mapeamentos de código-fonte gerados.
O tratamento de opcodes de array/tipo segue a especificação de instruções DEX do AOSP.
Não é uma porta do pipeline de CFG/SSA/inferência de tipos do JADX e não reivindica
sua cobertura de reconstrução. Métodos não suportados mantêm a saída DEX nativa.
src/native_cfg.rs adapta a abordagem de divisão/conexão do JADX fixado
BlockSplitter.java.
A implementação em Rust opera diretamente sobre unidades de código DEX verificadas, mantém
instruções goto e offsets originais, exclui dados de payload e isola de forma conservadora
instruções protegidas para arestas excepcionais. Ela ainda não implementa
as transformações de blocos sintéticos, SSA ou construção de regiões do JADX. A análise de
dominadores é um estágio separado descrito abaixo.
Este estágio é exposto por meio de uma auditoria de corpus separada; o renderizador de código-fonte da GUI
ainda usa o rebaixamento de valores de registradores existente. Veja
docs/jadx-port-plan.md para as passagens restantes.
src/native_dominators.rs adapta o
DominatorTree.java fixado:
o algoritmo iterativo de dominador imediato de Cooper/Harvey/Kennedy, interseção de
predecessores e caminhadas de fronteira de dominância. As modificações em Rust preservam os IDs
originais dos blocos, usam travessia iterativa em pós-ordem reversa, limitam o armazenamento de trabalho/fronteira
e usam intervalos de árvore para consultas de dominância em vez de bitsets de dominadores por bloco.
Um predecessor de entrada virtual trata arestas de retorno para a entrada do método.
A alcançabilidade parte da entrada real do método sobre arestas normais e excepcionais
conservadoras; blocos de handler desconectados são reportados como inalcançáveis.
Esta é a análise de dominadores, não a passagem completa de transformação BlockProcessor.
src/native_ir.rs adapta o mapeamento de operandos por família de instruções do
InsnDecoder.java fixado.
Seu leitor verificado de operandos DEX brutos segue os formatos de instrução do AOSP e é código
do RDX. Ele mantém offsets/opcodes, leituras e escritas ordenadas de registradores, larguras de palavra,
literais, referências indexadas e comportamento conservador de lançamento. Diferentemente do upstream,
ele ainda não resolve entradas de pool, mescla resultados de chamadas ou realiza inferência de tipos.
Argumentos de invocação permanecem palavras de registrador brutas ordenadas até a resolução de assinatura.
src/native_calls.rs agora resolve entradas de pool de métodos e agrupa palavras de receptor/argumento
por seus protótipos efetivos, seguindo as mesmas convenções de invoke/resultado do InsnDecoder
fixado. Ele trata argumentos largos, donos de array, protótipos secundários polimórficos e arrays preenchidos,
e vincula instruções move-result tipadas adjacentes. Invokes personalizados rejeitam explicitamente
metadados de call-site ausentes.
Isto é vinculação de assinatura, não resolução de despacho virtual ou SSA.
As categorias de registrador descrevem restrições de largura de armazenamento/referência, não tipos Java inferidos. CFG e decodificação de operandos compartilham um único decodificador de largura de instrução; o antigo renderizador de código-fonte permanece separado enquanto este pipeline de análise é construído.
src/native_dex_metadata.rs decodifica valores codificados, listas de handlers try/catch e
conjuntos compartilhados de anotações de classe/campo/método/parâmetro do
formato DEX do AOSP.
Offsets verificados, orçamentos de alocação/trabalho, armazenamento compartilhado de handlers e as representações
em Rust são código do RDX. src/native_java/annotations.rs renderiza valores comuns de anotações
Java, mantendo links de tipo/enum e strings/chars escapados. A colocação de anotações segue o
AnnotationGen.java fixado.
Anotações de build/runtime são exibidas, inclusive em métodos com corpos de fallback
DEX. Anotações de sistema permanecem metadados; Throws também é renderizado como uma cláusula throws.
Valores Java não suportados são marcados explicitamente. Padrões de anotação e metadados de depuração
não são totalmente reconstruídos.
src/native_java/numeric.rs, strings.rs, liveness.rs e o renderizador de exceções
são implementações do RDX sobre a semântica das instruções DEX. Seus
limites de fallback conservadores e fixtures independentes de comportamento em Rust estão
documentados em docs/native-engine.md e docs/validation.md. Eles não
executam nem incorporam código Java upstream.
src/native_ssa.rs adapta a inserção de phi na fronteira de dominância com poda de live-in e
renomeação do JADX fixado
SSATransform.java.
As adaptações em Rust usam travessia iterativa, identidades de palavra limitadas e blocos sintéticos
de sucesso normal para preservar o estado excepcional pré-escrita. Este último substitui
o reparo de arestas try pós-renomeação do upstream. A simplificação de phi permanece não portada;
a inferência parcial de tipos é descrita abaixo. native_call_values.rs anexa
restrições de assinatura existentes a palavras SSA. Nenhum Java upstream é executado.
src/native_types.rs adapta a separação de limites de atribuição/uso e a sequência de
propagação do JADX fixado TypeInferenceVisitor.java
e TypeUpdate.java.
A worklist limitada em Rust, verificações de pares de palavras, alternativas literais e resultados
explícitos de não resolvido/conflito são adaptações do RDX. Esta é uma inferência parcial:
os listeners de elementos de array agora propagam tipos de carga e restrições de armazenamento. A inferência
geral regressiva de arrays, limites superiores mínimos de referência, generics e inserção de conversões
permanecem incompletos.
src/native_constructors.rs segue cadeias de atribuição SSA como no
ConstructorVisitor.java fixado.
O RDX verifica a dominância da alocação e mantém a origem e o dono invocado original.
Um dono diferente é marcado para redirecionamento apenas quando a hierarquia prova que
é um ancestral do tipo de alocação. O encadeamento através de um ancestral comprovado em
this segue o ConstructorInsn.java fixado. Esta análise não remove nem move
instruções, estabelece validade de estado de inicialização ou emite construtores.
O caminho limitado de alocação aninhada em native_java/allocation_lowering.rs agora
consome essas vinculações de construtor SSA. Identidades exatas de alocação/invoke e
uma verificação de evento de efeito do RDX controlam expressões Java de captura compartilhada. Esta integração
não é uma porta do pipeline completo de regiões/geração de código do JADX; a emissão de redirecionamento de dono,
regiões de exceção e verificação geral de estado de inicialização permanecem
não suportadas neste caminho.
As relações de atribuição de array em native_hierarchy.rs seguem
JLS 4.10.3:
covariância de componentes de referência, componentes primitivos invariantes e os supertipos
padrão Object/Cloneable/Serializable. O aninhamento de descritores é limitado; relações de classes
externas ausentes permanecem desconhecidas.
A pequena hierarquia de plataforma também inclui arestas de interface verificadas para Throwable / Serializable e SQLException / Iterable. Esses fatos evitam respostas falsas negativas de subtipo do grafo pai anterior, que era apenas de exceções; eles não constituem um classpath completo da plataforma Android.
O decodificador limitado de alocação reconhece resultados ignorados de StringBuilder.append(String),
guiado pelo padrão de uso de builder não encadeado no
SimplifyVisitor.convertInvoke fixado.
O RDX mantém chamadas reais de construtor/append e rastreamentos exatos de efeitos; ele não
realiza a transformação completa de concatenação de strings do upstream. Apenas a classe final exata da
plataforma e a sobrecarga com seu contrato documentado de retorno do receptor são
aceitas; métodos arbitrários que parecem fluentes permanecem não suportados. Veja
StringBuilder.append(String).
A emissão de invoke-super de classe em native_java/method.rs segue o tratamento de superclasse
no InsnGen.callSuper/getClassForSuperCall fixado.
O RDX prova ancestralidade estrita de superclasse usando sua hierarquia imutável limitada,
em vez de exigir que o dono do método DEX seja igual ao pai direto. Ele emite
super.method(...) e mantém a assinatura DEX original nos metadados de navegação.
O receptor ainda deve ser a instância atual. Padrões de interface, chamadas qualified-super de classe
envolvente e ancestralidade incompleta/ambígua permanecem não suportadas.
As distinções de despacho de classe/interface são especificadas pela
documentação de invoke-kind do AOSP.
O ConstructorVisitor.processInvoke do JADX fixado remove o NEW_INSTANCE
de origem e substitui o invoke do construtor no local; o InsnGen então emite
new Class(arguments). Fontes upstream relevantes:
O allocation.rs nativo do RDX mantém primeiro sua reconstrução de expressão mais estrita.
Apenas para janelas planas, ele pode estagiar declarações de captura ordenadas e colocar new
na posição do construtor, correspondendo à abordagem de reconstrução legível do upstream.
Diferentemente das classificações de reordenação mais amplas do upstream, o RDX ainda verifica
todos os eventos registrados de cast/chamada/leitura/string em sua ordem original. A realocação
intencional da alocação pode alterar o momento de inicialização de classe, vinculação e falha de alocação;
a cobertura de saída não estabelece equivalência semântica completa.
src/native_java/synchronized.rs adapta a abordagem de reconstrução de entrada/corpo/monitor-exit
do jadx-core/src/main/java/jadx/core/dex/visitors/regions/maker/SynchronizedRegionMaker.java fixado.
A implementação em Rust adiciona travessia limitada de CFG, verificações de cobertura de escrita em registrador
decodificada e de exceção, validação exata de limpeza e rejeição conservadora de
formas aninhadas/múltiplas liberações/exceções mistas. É uma implementação parcial,
não uma porta completa do criador de regiões upstream. Os termos originais de SPDX/licença
e avisos permanecem cobertos pelos arquivos acima.
native_java/names.rs segue a separação entre identidade original e aliases de código-fonte
válidos no RenameVisitor.java do JADX fixado,
particularmente checkClassName e checkPackage. O RDX usa seu esquema existente de alias hexadecimal
UTF-8 injetivo em vez do provedor de alias configurável e da passagem global de colisão do JADX.
Cabeçalhos, construtores, operandos de tipo e imports usam aliases;
links de código-fonte mantêm os nomes DEX originais. Esta não é uma porta completa do RenameVisitor.
O subconjunto de regiões sincronizadas também trata um latch de loop que não lança fora do intervalo protegido do DEX e emite loops inteiramente dentro de uma região de monitor comprovada. Ele mantém a separação de região de monitor do criador fixado enquanto exige cobertura para cada instrução de corpo que lança, exceto a liberação comprovada.
native_java/finally_regions.rs usa a abordagem de reconhecimento de limpeza duplicada
do MarkFinallyVisitor.java do JADX fixado.
Este é um subconjunto limitado em Rust, não uma porta completa do visitor: uma invocação de limpeza void
com entrada estável, uma cópia normal, um rethrow catch-all e um catch tipado
envolvente com caminhos terminais. Ele verifica o despacho de exceção original por instrução,
limites de fluxo de controle, operandos de limpeza e links de código-fonte antes de emitir.
Layouts aninhados não suportados ainda usam fallback.
Os metadados exatos de exceções do framework Android são baseados nas declarações da plataforma: ActivityNotFoundException, RemoteException, e IBinder.transact.
A UI de snippet de método e o formato de logging em src/frida_snippet.rs seguem
JADX FridaAction.
O gerador em Rust lê descritores exatos de símbolos DEX, sempre seleciona a sobrecarga
exata, usa nomes de argumentos posicionais e chama essa sobrecarga capturada.
Cada snippet é escopado dentro de Java.perform para que snippets colados não possam sobrescrever
os handles de método uns dos outros. Snippets de classe inteira e de campo não são implementados.