
Décompilateur APK et DEX natif écrit en Rust
Le parseur DEX Rust initial dans src/native_dex.rs adapte la logique d'analyse de
JADX v1.5.6, épinglé au commit
28ff15e4ae69950aebea110a13e5ab895d234dfc.
JADX est copyright Skylot et ses contributeurs ; les avis applicables de l'Android Open Source
Project et d'autres amonts sont conservés dans NOTICE.
L'implémentation de référence est le
plugin d'entrée DEX,
incluant DexReader, sections/DexHeader, sections/SectionReader,
sections/DexClassData et utils/Leb128.
Les modifications RDX traduisent la logique du parseur en Rust avec accès aux octets vérifié, propagation explicite des erreurs et représentations de classes/membres propres à RDX. L'émetteur de source natif est du nouveau code RDX. Il s'agit d'une implémentation initiale et partielle du parseur et du moteur natif, et non d'un portage complet du pipeline de décompilation de JADX ni d'une prétention à une couverture de sortie équivalente.
Les fichiers ci-dessous sont des copies non modifiées téléchargées depuis la release épinglée :
8dada3edaf50dbc082c9a125058f25def75e625a).5c0b69a0f5298e0b329e33e860f7626f0c2c3891).Le NOTICE amont complet est conservé, y compris les avis historiques sur les bibliothèques intégrées et les icônes. Cette conservation ne signifie pas que le parseur Rust intègre l'ensemble de ces bibliothèques ou ressources. L'implémentation Java et ses dépendances d'exécution ne sont plus distribuées.
Les distributions contenant le parseur adapté doivent inclure la licence et les avis applicables. Les modifications dérivées de fichiers amont supplémentaires doivent conserver leurs avis et étendre cette correspondance de sources à mesure que le portage progresse.
src/native_resources.rs transpose en Rust natif la répartition des chunks, la gestion des
namespaces, des éléments et l'analyse des attributs typés de JADX v1.5.6 épinglé
jadx-core/src/main/java/jadx/core/xmlgen/BinaryXMLParser.java.
Son lecteur de pool de chaînes et son émetteur XML borné sont du code RDX.
Le décodeur valide les frontières entrée/chunk/chaîne, prend en charge les pools UTF-8/UTF-16
et gère les valeurs d'attributs typés Android courants sans runtime Java.
Les identifiants de ressources restent numériques : la résolution des symboles ARSC et les noms
d'énumérations/drapeaux du manifeste ne sont pas encore portés. Ce n'est pas le sous-système
complet de ressources amont.
src/native_java/ est la couche Rust conservatrice de RDX pour l'abaissement des valeurs de
registres et l'émission Java au-dessus du lecteur DEX adapté. Elle gère les instructions typées
prises en charge, les branches avant, les boucles simples et les switchs avant avec
matérialisation explicite des effets et correspondances de sources générées.
La gestion des opcodes de tableaux/types suit la spécification des instructions DEX AOSP.
Ce n'est pas un portage du pipeline CFG/SSA/inférence de types de JADX et cela ne prétend pas
à sa couverture de reconstruction. Les méthodes non prises en charge conservent la sortie DEX native.
src/native_cfg.rs adapte l'approche split/connect de JADX épinglé
BlockSplitter.java.
L'implémentation Rust opère directement sur les unités de code DEX vérifiées, conserve
les instructions goto et les offsets d'origine, exclut les données de payload et isole de
manière conservatrice les instructions protégées pour les arêtes exceptionnelles. Elle
n'implémente pas encore les transformations de blocs synthétiques, le SSA ou la construction
de régions de JADX. L'analyse de dominance est une étape distincte décrite ci-dessous.
Cette étape est exposée via un audit de corpus séparé ; le moteur de rendu de source de l'interface
graphique utilise toujours l'abaissement existant des valeurs de registres. Voir
docs/jadx-port-plan.md pour les passes restantes.
src/native_dominators.rs adapte
DominatorTree.java
épinglé : l'algorithme itératif de dominator immédiat de Cooper/Harvey/Kennedy, l'intersection
des prédécesseurs et les parcours de frontière de dominance. Les modifications Rust préservent
les identifiants de blocs d'origine, utilisent un parcours itératif en reverse-postorder, bornent
le stockage du travail et des frontières, et utilisent des intervalles d'arbre pour les requêtes
de dominance au lieu de bitsets de dominators par bloc. Un prédécesseur d'entrée virtuel gère les
arêtes arrière vers l'entrée de méthode. L'accessibilité part de l'entrée réelle de méthode sur
les arêtes normales et exceptionnelles conservatrices ; les blocs gestionnaires déconnectés sont
signalés comme inaccessibles. Il s'agit de l'analyse de dominance, pas de la passe complète de
transformation BlockProcessor.
src/native_ir.rs adapte la correspondance des opérandes par famille d'instructions de
InsnDecoder.java
épinglé. Son lecteur d'opérandes DEX bruts vérifié suit les formats d'instructions AOSP et est du
code RDX. Il conserve les offsets/opcodes, les lectures et écritures ordonnées de registres, les
largeurs de mots, les littéraux, les références indexées et le comportement conservateur de
levée d'exception. Contrairement à l'amont, il ne résout pas encore les entrées de pool, ne
fusionne pas les résultats d'appels et n'effectue pas d'inférence de types.
Les arguments d'invocation restent des mots de registres bruts ordonnés jusqu'à la résolution
des signatures. src/native_calls.rs résout désormais les entrées de pool de méthodes et
regroupe les mots de récepteur/argument selon leurs prototypes effectifs, en suivant les mêmes
conventions invoke/result de InsnDecoder épinglé. Il gère les arguments larges, les
propriétaires de tableaux, les prototypes secondaires polymorphes et les tableaux remplis, et
relie les instructions move-result typées adjacentes. Les invokes personnalisés rejettent
explicitement les métadonnées de site d'appel manquantes.
Il s'agit de liaison de signatures, pas de résolution de dispatch virtuel ni de SSA.
Les catégories de registres décrivent les contraintes de largeur de stockage/référence, pas les types Java inférés. Le CFG et le décodage des opérandes partagent un même décodeur de largeur d'instruction ; l'ancien moteur de rendu de source reste séparé pendant la construction de ce pipeline d'analyse.
src/native_dex_metadata.rs décode les valeurs encodées, les listes de gestionnaires try/catch
et les ensembles partagés d'annotations de classe/champ/méthode/paramètre depuis le
format DEX AOSP.
Les offsets vérifiés, les budgets d'allocation/travail, le stockage partagé des gestionnaires et
les représentations Rust sont du code RDX. src/native_java/annotations.rs rend les valeurs
d'annotations Java courantes, en conservant les liens de type/énumération et les chaînes/caractères
échappés. Le placement des annotations suit
AnnotationGen.java
épinglé. Les annotations de build/runtime sont affichées, y compris sur les méthodes avec des
corps de repli DEX. Les annotations système restent des métadonnées ; Throws est également rendu
sous forme de clause throws. Les valeurs Java non prises en charge sont marquées explicitement.
Les valeurs par défaut des annotations et les métadonnées de débogage ne sont pas entièrement
reconstruites.
src/native_java/numeric.rs, strings.rs, liveness.rs et le moteur de rendu d'exceptions sont
des implémentations RDX au-dessus de la sémantique des instructions DEX. Leurs frontières de
repli conservatrices et leurs fixtures de comportement Rust indépendantes sont documentées dans
docs/native-engine.md et docs/validation.md. Elles n'exécutent ni n'intègrent de code Java amont.
src/native_ssa.rs adapte l'insertion de phi sur la frontière de dominance avec élagage des
live-in et le renommage de JADX épinglé
SSATransform.java.
Les adaptations Rust utilisent un parcours itératif, des identités de mots bornées et des blocs
synthétiques de succès normal pour préserver l'état exceptionnel avant écriture. Ces derniers
remplacent la réparation post-renommage des arêtes try de l'amont. La simplification des phi
reste non portée ; l'inférence de types partielle est décrite ci-dessous. native_call_values.rs
attache les contraintes de signature existantes aux mots SSA. Aucun Java amont n'est exécuté.
src/native_types.rs adapte la séparation affectation/borne d'utilisation et la séquence de
propagation de JADX épinglé
TypeInferenceVisitor.java
et TypeUpdate.java.
La worklist Rust bornée, les vérifications de paires de mots, les alternatives littérales et les
résultats explicites non résolus/conflits sont des adaptations RDX. Il s'agit d'une inférence
partielle : les écouteurs d'éléments de tableau propagent désormais les types de chargement et
les contraintes de stockage. L'inférence générale rétrograde des tableaux, les plus petites
bornes supérieures de références, les génériques et l'insertion de conversions restent incomplets.
src/native_constructors.rs suit les chaînes d'affectation SSA comme dans
ConstructorVisitor.java
épinglé. RDX vérifie la dominance de l'allocation et conserve l'origine et le propriétaire invoqué
d'origine. Un propriétaire différent n'est marqué pour reciblage que lorsque la hiérarchie prouve
qu'il est un ancêtre du type d'allocation. Le chaînage via un ancêtre prouvé sur this suit
ConstructorInsn.java
épinglé. Cette analyse ne supprime ni ne déplace d'instructions, n'établit pas la validité de
l'état d'initialisation et n'émet pas de constructeurs.
Le chemin borné d'allocation imbriquée dans native_java/allocation_lowering.rs consomme
désormais ces liaisons de constructeurs SSA. Les identités exactes d'allocation/invocation et une
vérification d'événements d'effets RDX conditionnent les expressions Java à capture partagée.
Cette intégration n'est pas un portage du pipeline complet de régions/génération de code de JADX ;
l'émission de reciblage de propriétaire, les régions d'exception et la vérification générale de
l'état d'initialisation restent non prises en charge dans ce chemin.
Les relations d'affectation de tableaux dans native_hierarchy.rs suivent
JLS 4.10.3 :
covariance des composants de référence, invariance des composants primitifs et les supertypes
standard Object/Cloneable/Serializable. L'imbrication des descripteurs est bornée ; les relations
de classes externes manquantes restent inconnues.
La petite hiérarchie de plateforme inclut également des arêtes d'interface vérifiées pour Throwable / Serializable et SQLException / Iterable. Ces faits évitent les réponses faussement négatives de sous-type du graphe parent précédent limité aux exceptions ; ils ne constituent pas un classpath complet de la plateforme Android.
Le décodeur d'allocation borné reconnaît les résultats ignorés de
StringBuilder.append(String), guidé par le motif d'utilisation de builder non chaîné de
SimplifyVisitor.convertInvoke
épinglé. RDX conserve les appels réels de constructeur/append et les traces d'effets exactes ; il
n'effectue pas la transformation complète de concaténation de chaînes de l'amont. Seule la classe
de plateforme finale exacte et la surcharge avec son contrat documenté de retour du récepteur sont
acceptées ; les méthodes arbitraires d'apparence fluide restent non prises en charge. Voir
StringBuilder.append(String).
L'émission de invoke-super de classe dans native_java/method.rs suit la gestion de superclasse
de InsnGen.callSuper/getClassForSuperCall
épinglé. RDX prouve l'ascendance stricte de superclasse à l'aide de sa hiérarchie immuable bornée,
plutôt que d'exiger que le propriétaire de méthode DEX soit égal au parent direct. Il émet
super.method(...) et conserve la signature DEX d'origine dans les métadonnées de navigation.
Le récepteur doit toujours être l'instance courante. Les méthodes par défaut d'interface, les
appels qualified-super de classe englobante et l'ascendance incomplète/ambiguë restent non pris
en charge. Les distinctions de dispatch classe/interface sont spécifiées par la
documentation invoke-kind d'AOSP.
ConstructorVisitor.processInvoke de JADX épinglé supprime le NEW_INSTANCE d'origine et
remplace l'invocation de constructeur sur place ; InsnGen émet ensuite
new Class(arguments). Sources amont pertinentes :
Le allocation.rs natif de RDX conserve d'abord sa reconstruction d'expression plus stricte.
Pour les fenêtres plates uniquement, il peut mettre en scène des déclarations de capture ordonnées
et placer new à la position du constructeur, correspondant à l'approche de reconstruction lisible
de l'amont. Contrairement aux classifications de réordonnancement plus larges de l'amont, RDX
vérifie toujours tous les événements enregistrés de cast/appel/lecture/chaîne dans leur ordre
d'origine. Le déplacement intentionnel de l'allocation peut modifier le moment de
l'initialisation de classe, du linkage et de l'échec d'allocation ; la couverture de sortie
n'établit pas une équivalence sémantique complète.
src/native_java/synchronized.rs adapte l'approche de reconstruction entrée/corps/monitor-exit de
jadx-core/src/main/java/jadx/core/dex/visitors/regions/maker/SynchronizedRegionMaker.java
épinglé. L'implémentation Rust ajoute un parcours CFG borné, des vérifications de couverture des
écritures de registres décodées et des exceptions, une validation exacte du nettoyage, et un rejet
conservateur des formes d'exceptions imbriquées/à libérations multiples/mixtes. C'est une
implémentation partielle, pas un portage complet du créateur de régions amont. Les termes
SPDX/licence d'origine et les avis restent couverts par les fichiers ci-dessus.
native_java/names.rs suit la séparation entre identité d'origine et alias de source valides dans
RenameVisitor.java
de JADX épinglé, en particulier checkClassName et checkPackage. RDX utilise son schéma d'alias
hexadécimal UTF-8 injectif existant plutôt que le fournisseur d'alias configurable et la passe
globale de collision de JADX. Les en-têtes, constructeurs, opérandes de type et imports utilisent
les alias ; les liens de source conservent les noms DEX d'origine. Ce n'est pas un portage complet
de RenameVisitor.
Le sous-ensemble des régions synchronisées gère en outre un verrou de boucle non levant en dehors de l'intervalle protégé DEX et émet des boucles entièrement à l'intérieur d'une région de moniteur prouvée. Il conserve la séparation des régions de moniteur du créateur épinglé tout en exigeant une couverture pour chaque instruction de corps levant une exception autre que la libération prouvée.
native_java/finally_regions.rs utilise l'approche de reconnaissance des nettoyages dupliqués de
MarkFinallyVisitor.java
de JADX épinglé. C'est un sous-ensemble Rust borné, pas un portage complet du visiteur : une
invocation de nettoyage void à entrée stable, une copie normale, un rethrow catch-all, et un catch
typé englobant avec chemins terminaux. Il vérifie la répartition d'exceptions par instruction
d'origine, les frontières de flux de contrôle, les opérandes de nettoyage et les liens de source
avant l'émission. Les dispositions imbriquées non prises en charge retombent toujours en repli.
Les métadonnées exactes d'exceptions du framework Android sont basées sur les déclarations de la plateforme : ActivityNotFoundException, RemoteException, et IBinder.transact.
L'interface utilisateur des extraits de méthode et le format de journalisation dans
src/frida_snippet.rs suivent
JADX FridaAction.
Le générateur Rust lit les descripteurs de symboles DEX exacts, sélectionne toujours la surcharge
exacte, utilise des noms d'arguments positionnels et appelle cette surcharge capturée.
Chaque extrait est délimité à l'intérieur de Java.perform afin que les extraits collés ne
puissent pas écraser les handles de méthode les uns des autres. Les extraits à l'échelle de la
classe et de champ ne sont pas implémentés.