Smali - Decompilare/[Modificare]/Compilare
Tip
Impara e pratica AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Sfoglia il catalogo completo di HackTricks Training per i percorsi di assessment (ARTA/GRTA/AzRTA) e Linux Hacking Expert (LHE).
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord, al gruppo telegram, segui @hacktricks_live su X/Twitter, oppure controlla la pagina LinkedIn e il canale YouTube.
- Condividi hacking tricks inviando PR ai repository github HackTricks e HackTricks Cloud.
A volte è interessante modificare il codice dell’applicazione per accedere a informazioni nascoste per te (magari password ben offuscate o flags). In questi casi, può essere utile decompilare l’apk, modificare il codice e ricompilarlo.
Riferimento opcodes: http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html
Metodo rapido
Usando Visual Studio Code e l’estensione APKLab, puoi decompilare automaticamente, modificare, ricompilare, firmare e installare l’applicazione senza eseguire alcun comando.
Un altro script che facilita molto questo compito è https://github.com/ax/apk.sh
Split APKs / App Bundles
I target moderni sono comunemente distribuiti come split APKs (base.apk + split_config.*.apk) invece di un singolo APK monolitico. Se modifichi solo base.apk, le risorse o le librerie native possono andare fuori sincronizzazione e l’installazione potrebbe fallire.
Verifica rapida da un dispositivo:
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
Se il target è un split package, ricompila l’intero set oppure usa strumenti che uniscano prima gli APK. apk.sh è utile qui perché può combinare split APK in un singolo APK patchabile e correggere gli identificatori delle risorse pubbliche.
Per workflow di repacking orientati a Frida/Objection, vedi anche Android Anti-Instrumentation & SSL Pinning Bypass.
Decompila l’APK
Usando APKTool puoi accedere al codice smali e alle risorse:
apktool d APP.apk
Se apktool ti dà un errore, prova installing the latest version
Alcuni file interessanti da controllare sono:
- res/values/strings.xml (e tutti gli xml all’interno di res/values/*)
- AndroidManifest.xml
- Any file with extension .sqlite or .db
Se apktool ha problemi nel decodificare l’applicazione dai un’occhiata a https://ibotpeaches.github.io/Apktool/documentation/#framework-files oppure prova a usare l’argomento -r (Non decodificare le risorse). Poi, se il problema era in una risorsa e non nel codice sorgente, non avrai il problema (non decompilerai nemmeno le risorse).
Cambia smali code
Puoi modificare le istruzioni, cambiare il valore di alcune variabili o aggiungere nuove istruzioni. Io modifico il codice Smali usando VS Code, poi installi la smalise extension e l’editor ti dirà se qualche istruzione è errata.
Some examples can be found here:
Oppure puoi check below some Smali changes explained.
Ricompilare l’APK
Dopo aver modificato il codice puoi ricompilare il codice usando:
apktool b . #In the folder generated when you decompiled the application
Verrà compilato il nuovo APK all’interno della cartella dist.
Se apktool restituisce un errore, prova a installare l’ultima versione
Firma il nuovo APK
Poi, devi generare una chiave (ti verrà chiesta una password e alcune informazioni che puoi compilare a caso):
keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias <your-alias>
Infine, firma il nuovo APK:
jarsigner -keystore key.jks path/to/dist/* <your-alias>
jarsigner funziona ancora per alcuni test rapidi, ma per le build Android moderne apksigner è preferito perché gestisce i più recenti schemi di firma APK.
Ottimizza la nuova applicazione
zipalign è uno strumento di allineamento degli archivi che fornisce importanti ottimizzazioni ai file delle applicazioni Android (APK). More information here.
zipalign [-f] [-v] <alignment> infile.apk outfile.apk
zipalign -v 4 infile.apk
Se l’APK contiene librerie native incluse (lib/*.so), Android ora raccomanda di usare -P 16 in modo che i file .so siano allineati sia per dispositivi con pagina da 16 KiB sia per quelli con pagina da 4 KiB:
zipalign -P 16 -f -v 4 infile.apk outfile.apk
Firma il nuovo APK (di nuovo?)
Se preferisci usare apksigner invece di jarsigner, dovresti firmare l’APK dopo aver applicato l’ottimizzazione con zipalign. MA ATTENZIONE: DEVI FIRMARE L’APPLICAZIONE UNA SOLA VOLTA CON jarsigner (prima di zipalign) O CON apksigner (dopo zipalign).
apksigner sign --ks key.jks ./dist/mycompiled.apk
Un flusso moderno più pratico è:
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
Note importanti:
- Se modifichi un APK dopo averlo firmato con
apksigner, la firma viene invalidata e devi firmarlo di nuovo. apksigner verify --print-certsè utile per confermare che l’APK ricostruito sia installabile e per ispezionare il certificato che il target esporrà durante l’esecuzione.
Modificare Smali
Per il seguente codice Java Hello World:
public static void printHelloWorld() {
System.out.println("Hello World")
}
Il codice Smali sarebbe:
.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
L’insieme di istruzioni Smali è disponibile here.
Modifiche leggere
Modificare i valori iniziali di una variabile all’interno di una funzione
Alcune variabili sono definite all’inizio della funzione usando l’opcode const, puoi modificare i loro valori, oppure puoi definirne di nuove:
#Number
const v9, 0xf4240
const/4 v8, 0x1
#Strings
const-string v5, "wins"
Operazioni di 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
Modifiche maggiori
Insidie di Smali che di solito rompono le ricompilazioni
- Preferisci aumentare
.localsquando hai bisogno solo di registri temporanei nel corpo di un metodo esistente. I registri dei parametri (p0,p1…) sono mappati sui registri più alti del metodo, quindi passare ciecamente a.registersspesso rompe il layout degli argomenti. move-result,move-result-wide, andmove-result-objectmust appear immediately after l’invoke-*corrispondente. Inserire logging o qualsiasi altro opcode tra di essi rende il metodo non valido.- I valori
longedoublesono valori wide e occupano una coppia di registri. Se riutilizzi quei registri più tardi, ricorda chev10occupa anchev11. - Se devi passare molti registri, o registri con numeri molto elevati, usa le varianti
/rangecomeinvoke-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>"
Raccomandazioni:
- Se intendi usare variabili dichiarate all’interno della funzione (declared v0,v1,v2…) metti queste righe tra .local
e le dichiarazioni delle variabili (const v0, 0x1) - Se vuoi inserire il codice di logging nel mezzo del codice di una funzione:
- Aggiungi 2 al numero di variabili dichiarate: Es: da .locals 10 a .locals 12
- Le nuove variabili devono essere i numeri successivi rispetto a quelle già dichiarate (in questo esempio dovrebbero essere v10 e v11, ricorda che si parte da v0).
- Modifica il codice della funzione di logging e usa v10 e v11 al posto di v5 e v1.
Patchare i controlli anti-tamper comuni
Quando un’app viene repacked, una delle prime cose che può rompersi è un controllo in-app di signature / installer / integrity. Buone stringhe da cercare in JADX o nell’albero smali sono:
GET_SIGNATURESGET_SIGNING_CERTIFICATESapkContentsSignersMessageDigestSHA-256Base64getInstallerPackageNamecom.android.vending
Le app moderne spesso chiamano PackageManager.getPackageInfo(..., GET_SIGNING_CERTIFICATES), hashano i byte del signer con MessageDigest, e confrontano il risultato con una costante hardcoded. In pratica, di solito è più semplice patchare il final boolean / branch che riscrivere tutto il codice di gestione delle signature.
Esempi di pattern:
# 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
Se il codice di verifica è rumoroso, cerca l’ultima comparazione prima della dialog di errore / finish() / System.exit() / chiamata di telemetry e applica la patch lì invece di toccare l’intera routine.
Mostrare un toast
Ricorda di aggiungere 3 al numero di .locals all’inizio della funzione.
Questo codice è pronto per essere inserito nel mezzo di una funzione (modifica il numero delle variabili se necessario). Prenderà il valore di this.o, lo trasformerà in String e poi mostrerà un toast con il suo valore.
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
Caricare una libreria nativa all’avvio (System.loadLibrary)
A volte è necessario caricare in anticipo una libreria nativa in modo che si inizializzi prima di altre librerie JNI (ad es., per abilitare telemetria/registrazione a livello di processo). Puoi iniettare una chiamata a System.loadLibrary() in un initializer statico o all’inizio di Application.onCreate(). Esempio smali per un inizializzatore statico di classe (
.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
In alternativa, inserisci le stesse due istruzioni all’inizio del tuo Application.onCreate() per garantire che la libreria venga caricata il prima possibile:
.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
Note:
- Assicurati che la variante ABI corretta della libreria esista sotto lib/
/ (es., arm64-v8a/armeabi-v7a) per evitare UnsatisfiedLinkError. - Il caricamento molto precoce (class static initializer) garantisce che il native logger possa osservare l’attività JNI successiva.
Smali Static Analysis / Rule-Based Hunting
Dopo il decompilamento con apktool, puoi scansionare Smali riga per riga con regole regex per individuare rapidamente la logica anti-analysis (root/emulator checks) e probabili hardcoded secrets. Questa è una tecnica di fast triage: tratta gli hits come piste che devi verificare nello Smali circostante o nel codice Java/Kotlin ricostruito.
Concetti chiave:
- Library filtering: sopprimere o taggare i riscontri sotto namespace di terze parti comuni in modo da concentrarti sui code path di proprietà dell’app.
- Context hints: richiedere che le stringhe sospette compaiano vicino alle API che le consumano (nello stesso metodo, entro N righe).
- Confidence: usare livelli semplici (high/medium) per classificare gli indizi e ridurre i falsi positivi.
Esempi di prefissi di libreria da sopprimere per impostazione predefinita:
Landroidx/
Lkotlin/
Lkotlinx/
Lcom/google/
Lcom/squareup/
Lokhttp3/
Lokio/
Lretrofit2/
Esempi di regole di rilevamento (regex + euristiche di contesto):
{
"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."
}
Altre euristiche che funzionano bene nella pratica:
- Root package/path checks: richiedere chiamate vicine a
PackageManager;->getPackageInfooFile;->existsper stringhe comecom.topjohnwu.magisko/data/local/tmp. - Emulator checks: associare letterali sospetti (es.,
ro.kernel.qemu,generic,goldfish) con getterBuild.*vicini e confronti di stringhe (->equals,->contains,->startsWith). - Hardcoded secrets: segnalare
const-stringsolo quando un identificatore.fieldomove-resultvicino include parole chiave comepassword,token,api_key. Ignorare esplicitamente i marker solo per UI comeAutofillType,InputType,EditorInfo.
Scanner basati su regole come PulseAPK Core implementano questo modello per far emergere rapidamente logiche anti-analisi e potenziali segreti in Smali.
Riferimenti
- PulseAPK Core
- PulseAPK Smali Detection Rules
- SoTap: logger leggero del comportamento JNI in-app (.so) – github.com/RezaArbabBot/SoTap
- Android Developers: apksigner e zipalign
- apk.sh: github.com/ax/apk.sh
Tip
Impara e pratica AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Sfoglia il catalogo completo di HackTricks Training per i percorsi di assessment (ARTA/GRTA/AzRTA) e Linux Hacking Expert (LHE).
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord, al gruppo telegram, segui @hacktricks_live su X/Twitter, oppure controlla la pagina LinkedIn e il canale YouTube.
- Condividi hacking tricks inviando PR ai repository github HackTricks e HackTricks Cloud.


