Smali - Décompilation/[Modification]/Compilation
Tip
Apprenez et pratiquez AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Parcourez le catalogue complet de HackTricks Training pour les parcours d’évaluation (ARTA/GRTA/AzRTA) et Linux Hacking Expert (LHE).
Support HackTricks
- Consultez les subscription plans!
- Rejoignez 💬 le groupe Discord, le groupe telegram, suivez @hacktricks_live sur X/Twitter, ou consultez la page LinkedIn et la chaîne YouTube.
- Partagez des hacking tricks en soumettant des PRs aux dépôts github HackTricks et HackTricks Cloud.
Parfois il est intéressant de modifier le code de l’application pour accéder à des informations cachées pour vous (peut‑être des mots de passe fortement obfusqués ou des flags). Dans ce cas, il peut être intéressant de décompiler l’apk, modifier le code et le recompiler.
Référence des opcodes : http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html
Méthode rapide
En utilisant Visual Studio Code et l’extension APKLab, vous pouvez automatiquement décompiler, modifier, recompiler, signer et installer l’application sans exécuter la moindre commande.
Un autre script qui facilite grandement cette tâche est https://github.com/ax/apk.sh
Split APKs / App Bundles
Les cibles modernes sont souvent distribuées sous forme de split APKs (base.apk + split_config.*.apk) plutôt que comme un unique APK monolithique. Si vous ne modifiez que base.apk, les ressources ou les bibliothèques natives peuvent se désynchroniser et l’installation peut échouer.
Triage rapide depuis un appareil :
adb shell pm path com.example.app
adb pull /data/app/.../base.apk
adb pull /data/app/.../split_config.arm64_v8a.apk
adb pull /data/app/.../split_config.en.apk
Si la cible est un split package, soit reconstruisez l’ensemble, soit utilisez un outil qui fusionne d’abord les APKs. apk.sh est pratique ici car il peut combiner des split APKs en un seul APK patchable et corriger les identifiants de ressources publiques.
Pour les workflows de repacking orientés Frida/Objection, consultez aussi Android Anti-Instrumentation & SSL Pinning Bypass.
Décompiler l’APK
Avec APKTool, vous pouvez accéder au smali code and resources:
apktool d APP.apk
If apktool gives you any error, try d’installer la dernière version
Some interesting files you should look are:
- res/values/strings.xml (and all xmls inside res/values/*)
- AndroidManifest.xml
- Any file with extension .sqlite or .db
If apktool has problems decoding the application take a look to https://ibotpeaches.github.io/Apktool/documentation/#framework-files or try using the argument -r (Ne pas décoder les ressources). Then, if the problem was in a resource and not in the source code, you won’t have the problem (you won’t also decompile the resources).
Modifier le code smali
You can change instructions, change the value of some variables or add new instructions. I change the Smali code using VS Code, you then install the smalise extension and the editor will tell you if any instruction is incorrect.
Some examples can be found here:
Or you can check below some Smali changes explained.
Recompiler l’APK
After modifying the code you can recompile the code using:
apktool b . #In the folder generated when you decompiled the application
Cela va compiler le nouvel APK dans le dossier dist.
Si apktool renvoie une erreur, essayez d’installer la dernière version
Signer le nouvel APK
Ensuite, vous devez générer une clé (il vous sera demandé un mot de passe et certaines informations que vous pouvez remplir aléatoirement):
keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias <your-alias>
Enfin, signez le nouvel APK:
jarsigner -keystore key.jks path/to/dist/* <your-alias>
jarsigner fonctionne encore pour quelques tests rapides, mais pour les builds Android modernes apksigner est préféré car il gère les nouveaux schémas de signature APK.
Optimiser la nouvelle application
zipalign est un outil d’alignement d’archives qui apporte une optimisation importante aux fichiers d’application Android (APK). More information here.
zipalign [-f] [-v] <alignment> infile.apk outfile.apk
zipalign -v 4 infile.apk
Si l’APK contient des bibliothèques natives incluses (lib/*.so), Android recommande désormais d’utiliser -P 16 afin que les fichiers .so soient alignés pour les appareils ayant une taille de page de 16 KiB et 4 KiB :
zipalign -P 16 -f -v 4 infile.apk outfile.apk
Signer le nouvel APK (encore ?)
Si vous préférez utiliser apksigner au lieu de jarsigner, vous devez signer l’APK après avoir appliqué l’optimisation avec zipaling. MAIS ATTENTION : VOUS N’AVEZ QU’À SIGNER L’APPLICATION UNE SEULE FOIS AVEC jarsigner (avant zipalign) OU AVEC aspsigner (après zipaling).
apksigner sign --ks key.jks ./dist/mycompiled.apk
Un flux moderne plus pratique est :
apktool b . -o dist/app-unsigned.apk
zipalign -P 16 -f -v 4 dist/app-unsigned.apk dist/app-aligned.apk
apksigner sign --ks key.jks --out dist/app-signed.apk dist/app-aligned.apk
apksigner verify --verbose --print-certs dist/app-signed.apk
Notes importantes :
- Si vous modifiez un APK après l’avoir signé avec
apksigner, la signature est invalidée et vous devez le signer à nouveau. apksigner verify --print-certsest utile pour confirmer que l’APK reconstruit est installable et pour inspecter le certificat que la cible exposera à l’exécution.
Modification de Smali
Pour le code Java Hello World suivant :
public static void printHelloWorld() {
System.out.println("Hello World")
}
Le code Smali serait :
.method public static printHelloWorld()V
.registers 2
sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
const-string v1, "Hello World"
invoke-virtual {v0,v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
return-void
.end method
Le jeu d’instructions Smali est disponible here.
Modifications légères
Modifier les valeurs initiales d’une variable à l’intérieur d’une fonction
Certaines variables sont définies au début de la fonction en utilisant l’opcode const, vous pouvez modifier leurs valeurs, ou en définir de nouvelles :
#Number
const v9, 0xf4240
const/4 v8, 0x1
#Strings
const-string v5, "wins"
Opérations de base
#Math
add-int/lit8 v0, v2, 0x1 #v2 + 0x1 and save it in v0
mul-int v0,v2,0x2 #v2*0x2 and save in v0
#Move the value of one object into another
move v1,v2
#Condtions
if-ge #Greater or equals
if-le #Less or equals
if-eq #Equals
#Get/Save attributes of an object
iget v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I #Save this.o inside v0
iput v0, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I #Save v0 inside this.o
#goto
:goto_6 #Declare this where you want to start a loop
if-ne v0, v9, :goto_6 #If not equals, go to: :goto_6
goto :goto_6 #Always go to: :goto_6
Changements majeurs
Pièges Smali qui cassent généralement les reconstructions
- Préférez augmenter
.localslorsque vous avez seulement besoin de registres temporaires dans le corps d’une méthode existante. Les registres de paramètres (p0,p1…) sont mappés sur les registres les plus élevés de la méthode, donc passer aveuglément à.registerscasse souvent l’agencement des arguments. move-result,move-result-wide, etmove-result-objectdoivent apparaître immédiatement après l’invoke-*correspondant. Insérer du logging ou n’importe quel autre opcode entre eux rend la méthode invalide.- Les valeurs
longetdoublesont des valeurs wide et consomment une paire de registres. Si vous réutilisez ces registres plus tard, souvenez-vous quev10occupe aussiv11. - Si vous devez passer beaucoup de registres, ou des registres de numéro très élevé, utilisez les variantes
/rangetelles queinvoke-virtual/range.
Logging
#Log win: <number>
iget v5, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I #Get this.o inside v5
invoke-static {v5}, Ljava/lang/String;->valueOf(I)Ljava/lang/String; #Transform number to String
move-result-object v1 #Move to v1
const-string v5, "wins" #Save "win" inside v5
invoke-static {v5, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I #Logging "Wins: <num>"
Recommandations :
- If you are going to use declared variables inside the function (declared v0,v1,v2…) put these lines between the .local
and the declarations of the variables (const v0, 0x1) - If you want to put the logging code in the middle of the code of a function:
- Add 2 to the number of declared variables: Ex: from .locals 10 to .locals 12
- The new variables should be the next numbers of the already declared variables (in this example should be v10 and v11, remember that it starts in v0).
- Change the code of the logging function and use v10 and v11 instead of v5 and v1.
Patch des vérifications anti-tamper courantes
Lorsqu’une application est repackagée, une des premières choses qui peut se casser est une vérification dans l’application de signature / installateur / intégrité. Les bonnes chaînes à rechercher dans JADX ou dans l’arborescence smali sont :
GET_SIGNATURESGET_SIGNING_CERTIFICATESapkContentsSignersMessageDigestSHA-256Base64getInstallerPackageNamecom.android.vending
Les applications modernes appellent souvent PackageManager.getPackageInfo(..., GET_SIGNING_CERTIFICATES), hachent les octets du signataire avec MessageDigest, et comparent le résultat à une constante codée en dur. En pratique, il est généralement plus simple de patcher le booléen final / branche que de réécrire tout le code de gestion des signatures.
Example patterns:
# Force a boolean result to "valid"
const/4 v0, 0x1
# Or invert the branch that sends execution to the tamper handler
if-eqz v0, :tamper_detected # original
if-nez v0, :tamper_detected # patched
Si le code de vérification est bruyant, cherchez la dernière comparaison avant la boîte de dialogue d’erreur / finish() / System.exit() / telemetry call et patchez là au lieu de modifier toute la routine.
Toasting
N’oubliez pas d’ajouter 3 au nombre de .locals au début de la fonction.
Ce code est prêt à être inséré au milieu d’une fonction (modifiez le nombre des variables si nécessaire). Il prendra la valeur de this.o, la transformera en String puis créera un toast avec sa valeur.
const/4 v10, 0x1
const/4 v11, 0x1
const/4 v12, 0x1
iget v10, p0, Lcom/google/ctf/shallweplayagame/GameActivity;->o:I
invoke-static {v10}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;
move-result-object v11
invoke-static {p0, v11, v12}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
move-result-object v12
invoke-virtual {v12}, Landroid/widget/Toast;->show()V
Chargement d’une bibliothèque native au démarrage (System.loadLibrary)
Parfois, il est nécessaire de précharger une bibliothèque native afin qu’elle s’initialise avant les autres libs JNI (p. ex., pour activer la télémétrie/la journalisation locale au processus). Vous pouvez injecter un appel à System.loadLibrary() dans un initialiseur statique ou tôt dans Application.onCreate(). Exemple smali pour un initialiseur de classe statique (
.class public Lcom/example/App;
.super Landroid/app/Application;
.method static constructor <clinit>()V
.registers 1
const-string v0, "sotap" # library name without lib...so prefix
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
return-void
.end method
Alternativement, placez les mêmes deux instructions au début de votre Application.onCreate() pour garantir que la bibliothèque se charge le plus tôt possible :
.method public onCreate()V
.locals 1
const-string v0, "sotap"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
invoke-super {p0}, Landroid/app/Application;->onCreate()V
return-void
.end method
Remarques:
- Assurez-vous que la bonne variante ABI de la bibliothèque existe sous lib/
/ (p.ex., arm64-v8a/armeabi-v7a) pour éviter UnsatisfiedLinkError. - Le chargement très tôt (class static initializer) garantit que le native logger peut observer l’activité JNI ultérieure.
Smali Static Analysis / Rule-Based Hunting
Après avoir décompilé avec apktool, vous pouvez parcourir Smali ligne par ligne avec des règles regex pour repérer rapidement la logique anti-analysis (root/emulator checks) et les secrets codés en dur probables. C’est une technique de triage rapide : considérez les détections comme des pistes qu’il faut vérifier dans le Smali environnant ou dans le Java/Kotlin reconstruit.
Idées clés:
- Library filtering: supprimer ou marquer les résultats sous des espaces de noms tiers courants afin de vous concentrer sur les chemins de code appartenant à l’app.
- Context hints: exiger que les chaînes suspectes apparaissent près des API qui les consomment (dans la même méthode, dans un rayon de N lignes).
- Confidence: utilisez des niveaux simples (high/medium) pour classer les pistes et réduire les faux positifs.
Example library prefixes to suppress by default:
Landroidx/
Lkotlin/
Lkotlinx/
Lcom/google/
Lcom/squareup/
Lokhttp3/
Lokio/
Lretrofit2/
Exemples de règles de détection (regex + heuristiques contextuelles) :
{
"category": "root_check",
"regex_patterns": [
"(?i)invoke-static .*Runtime;->getRuntime\\(\\).*->exec\\(.*\\"(su|magisk|busybox)\\"",
"(?i)const-string [vp0-9, ]+\\"(/system/xbin/su|/system/bin/su|/sbin/su)\\""
],
"context_hint": "Only report when the same method also calls File;->exists/canExecute or Runtime;->exec."
}
Heuristiques supplémentaires efficaces en pratique :
- Root package/path checks : exiger des appels
PackageManager;->getPackageInfoouFile;->existsà proximité pour des chaînes telles quecom.topjohnwu.magiskou/data/local/tmp. - Emulator checks : associer les littéraux suspects (p. ex.,
ro.kernel.qemu,generic,goldfish) avec des gettersBuild.*à proximité et des comparaisons de chaînes (->equals,->contains,->startsWith). - Hardcoded secrets : marquer
const-stringuniquement lorsqu’un identifiant.fieldoumove-resultà proximité contient des mots-clés commepassword,token,api_key. Ignorer explicitement les marqueurs réservés à l’UI tels queAutofillType,InputType,EditorInfo.
Les scanners basés sur des règles comme PulseAPK Core implémentent ce modèle pour faire rapidement remonter la logique anti-analyse et les secrets potentiels dans Smali.
Références
- PulseAPK Core
- PulseAPK Smali Detection Rules
- SoTap: Lightweight in-app JNI (.so) behavior logger – github.com/RezaArbabBot/SoTap
- Android Developers: apksigner and zipalign
- apk.sh: github.com/ax/apk.sh
Tip
Apprenez et pratiquez AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Parcourez le catalogue complet de HackTricks Training pour les parcours d’évaluation (ARTA/GRTA/AzRTA) et Linux Hacking Expert (LHE).
Support HackTricks
- Consultez les subscription plans!
- Rejoignez 💬 le groupe Discord, le groupe telegram, suivez @hacktricks_live sur X/Twitter, ou consultez la page LinkedIn et la chaîne YouTube.
- Partagez des hacking tricks en soumettant des PRs aux dépôts github HackTricks et HackTricks Cloud.


