Smali - Decompiling/[Modifying]/Compiling

Tip

Leer & oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Leer & oefen Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Blaai deur die volledige HackTricks Training-katalogus vir die assesseringsroetes (ARTA/GRTA/AzRTA) en Linux Hacking Expert (LHE).

Ondersteun HackTricks

Sometimes it is interesting to modify the application code to access hidden information for you (maybe well obfuscated passwords or flags). Then, it could be interesting to decompile the apk, modify the code and recompile it.

Opcodes reference: http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html

Fast Way

Using Visual Studio Code and the APKLab extension, you can automatically decompile, modify, recompile, sign & install the application without executing any command.

Another script that facilitates this task a lot is https://github.com/ax/apk.sh

Split APKs / App Bundles

Modern targets are commonly delivered as split APKs (base.apk + split_config.*.apk) instead of a single monolithic APK. If you patch only base.apk, resources or native libraries can go out of sync and the installation may fail.

Quick triage from a device:

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

As die teiken ’n split-pakket is, bou óf die hele stel weer op of gebruik gereedskap wat eers die APKs bymekaar voeg. apk.sh is hier handig omdat dit split-APKs kan kombineer tot ’n enkele patchable APK en publieke hulpbron-identifiseerders kan regstel.
Vir Frida/Objection-georiënteerde herverpakkings-werkvloei, sien ook Android Anti-Instrumentation & SSL Pinning Bypass.

Dekompileer die APK

Met APKTool kan jy toegang kry tot die smali code and resources:

apktool d APP.apk

As apktool jou enige fout gee, probeer installeer die nuutste weergawe

Sommige interessante lêers waarna jy moet kyk:

  • 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 (Do not decode resources). 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).

Verander smali-kode

Jy kan change instructions, change die value van sommige veranderlikes of add nuwe instructions. Ek verander die 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.

Herkompileer die APK

Na die wysiging van die kode kan jy die kode recompile using:

apktool b . #In the folder generated when you decompiled the application

Dit sal compile die nuwe APK binne die dist gids.

Indien apktool ’n error vertoon, probeer installing the latest version

Onderteken die nuwe APK

Dan moet jy genereer ’n sleutel (daar sal vir jou ’n wagwoord en vir ’n paar besonderhede gevra word wat jy lukraak kan invul):

keytool -genkey -v -keystore key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias <your-alias>

Laastens, onderteken die nuwe APK:

jarsigner -keystore key.jks path/to/dist/* <your-alias>

jarsigner werk nog steeds vir sommige vinnige toetse, maar vir moderne Android-builds word apksigner verkies omdat dit die nuwer APK-handtekeningskemas hanteer.

Optimaliseer nuwe toepassing

zipalign is ’n argief-belyningsinstrument wat belangrike optimalisering aan Android-toepassings (APK)-lêers verskaf. Meer inligting hier.

zipalign [-f] [-v] <alignment> infile.apk outfile.apk
zipalign -v 4 infile.apk

As die APK ingeboude native biblioteke (lib/*.so) bevat, beveel Android nou aan om -P 16 te gebruik sodat die .so-lêers vir toestelle met ’n bladsyaftreegrootte van beide 16 KiB en 4 KiB uitgelyk is:

zipalign -P 16 -f -v 4 infile.apk outfile.apk

Onderteken die nuwe APK (weer?)

Indien jy verkies om apksigner te gebruik in plaas van jarsigner, moet jy die apk onderteken nadat jy die optimalisering met zipaling toegepas het. Let wel dat jy slegs een keer die toepassing onderteken met jarsigner (voor zipalign) of met aspsigner (na zipaling).

apksigner sign --ks key.jks ./dist/mycompiled.apk

’n Meer praktiese moderne vloei is:

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

Belangrike notas:

  • As jy ’n APK wysig nadat dit met apksigner onderteken is, word die handtekening ongeldig en moet jy dit weer teken.
  • apksigner verify --print-certs is nuttig om te bevestig dat die herboude APK installeerbaar is en om die sertifikaat te ondersoek wat die target tydens runtime sal blootstel.

Smali veranderings

Vir die volgende Hello World Java-kode:

public static void printHelloWorld() {
System.out.println("Hello World")
}

Die Smali-kode sou wees:

.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

Die Smali instruksieset is beskikbaar here.

Ligte Veranderinge

Wysig aanvanklike waardes van ’n veranderlike binne ’n funksie

Sommige veranderlikes word aan die begin van die funksie gedefinieer met die opcode const, jy kan die waardes wysig, of jy kan nuwe veranderlikes definieer:

#Number
const v9, 0xf4240
const/4 v8, 0x1
#Strings
const-string v5, "wins"

Basiese Operasies

#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

Groter Veranderinge

Smali-valstrikke wat gewoonlik herbouproses breek

  • Gee voorkeur aan om die .locals te verhoog wanneer jy net tydelike registers in die liggaam van ’n bestaande metode benodig. Parameter registers (p0, p1…) word gemap na die hoogste registers van die metode, so om blindelinks na .registers oor te skakel breek dikwels die argumentuitleg.
  • move-result, move-result-wide, and move-result-object moet onmiddellik na die ooreenstemmende invoke-* verskyn. Om logging of enige ander opcode tussen hulle in te voeg maak die metode ongeldig.
  • long en double waardes is wide waardes en verbruik ’n register pair. As jy daardie registers later hergebruik, onthou dat v10 ook v11 beset.
  • As jy baie registers moet deurgee, of baie hoog genommerde registers, gebruik die /range variante soos invoke-virtual/range.

Logboodskappe

#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>"

Aanbevelings:

  • As jy verklaarde veranderlikes binne die funksie gaan gebruik (verklaar v0,v1,v2…) plaas hierdie lyne tussen die .local en die deklarasies van die veranderlikes (const v0, 0x1)
  • As jy die logging code in die middel van die kode van ’n funksie wil sit:
  • Voeg 2 by die aantal verklaarde veranderlikes: Ex: van .locals 10 na .locals 12
  • Die nuwe veranderlikes moet die volgende nommers wees van die reeds verklaarde veranderlikes (in hierdie voorbeeld moet dit v10 en v11 wees, onthou dat dit in v0 begin).
  • Verander die kode van die logging-funksie en gebruik v10 en v11 in plaas van v5 en v1.

Patcheer algemene anti-tamper kontroles

Wanneer ’n app herverpak word, is een van die eerste dinge wat kan breek ’n in-app handtekening / installateur / integriteit kontrole. Goeie stringe om in JADX of in die smali-boom te soek is:

  • GET_SIGNATURES
  • GET_SIGNING_CERTIFICATES
  • apkContentsSigners
  • MessageDigest
  • SHA-256
  • Base64
  • getInstallerPackageName
  • com.android.vending

Moderne apps roep dikwels PackageManager.getPackageInfo(..., GET_SIGNING_CERTIFICATES), hash die signer-bytes met MessageDigest, en vergelyk die resultaat met ’n hardgekodeerde konstante. In die praktyk is dit gewoonlik makliker om die laaste boolean / branch te patcheer as om al die signature-handling kode te herskryf.

Voorbeeld patrone:

# 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

If die verifikasiekode rumoerig is, soek die laaste vergelyking voor die foutdialoog / finish() / System.exit() / telemetry call en plaas die patch daar in plaas van om die hele roetine te verander.

Toasting

Onthou om 3 by die aantal .locals aan die begin van die funksie te voeg.

Hierdie kode is voorberei om in die middel van ’n funksie ingevoeg te word (verander die aantal veranderlikes soos nodig). Dit sal die waarde van this.o neem, dit transformeer na String en dan ’n toast maak met sy waarde.

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

Laai ’n native-biblioteek tydens opstart (System.loadLibrary)

Soms moet jy ’n native-biblioteek vooraf laai sodat dit geïnitialiseer word voordat ander JNI libs dit doen (e.g., to enable process-local telemetry/logging). Jy kan ’n oproep na System.loadLibrary() injecteer in ’n statiese initializer of vroeg in Application.onCreate(). Voorbeeld smali vir ’n statiese klas-initializer ():

.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

Alternatiewelik plaas dieselfde twee instruksies aan die begin van jou Application.onCreate() om te verseker dat die biblioteek so vroeg as moontlik gelaai word:

.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

Aantekeninge:

  • Maak seker die korrekte ABI-variant van die library bestaan onder lib// (e.g., arm64-v8a/armeabi-v7a) om UnsatisfiedLinkError te voorkom.
  • Vroeë laai (class static initializer) verseker die native logger kan daaropvolgende JNI activity waarneem.

Smali Statiese Analise / Rule-Based Hunting

Na dekompilering met apktool, kan jy scan Smali line-by-line with regex rules om vinnig anti-analysis logika (root/emulator checks) en waarskynlike hardcoded secrets op te spoor. Dit is ’n fast triage technique: behandel treffers as leidrade wat jy in die omliggende Smali of gerekonstruerde Java/Kotlin moet verifieer.

Sleutelidees:

  • Library filtering: onderdruk of merk bevindinge binne algemene third-party namespaces sodat jy op app-beheerde code paths kan fokus.
  • Context hints: vereis dat verdagte strings naby die APIs verskyn wat dit verbruik (binne dieselfde metode, binne N lyne).
  • Confidence: gebruik eenvoudige vlakke (high/medium) om leidrade te rangskik en vals positiewe te verminder.

Example library prefixes to suppress by default:

Landroidx/
Lkotlin/
Lkotlinx/
Lcom/google/
Lcom/squareup/
Lokhttp3/
Lokio/
Lretrofit2/

Voorbeeld opsporingsreëls (regex + konteks-heuristieke):

{
"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."
}

Addisionele heuristieke wat in die praktyk goed werk:

  • Wortel pakket/pad kontroles: vereis nabygeleë PackageManager;->getPackageInfo of File;->exists oproepe vir stringe soos com.topjohnwu.magisk of /data/local/tmp.
  • Emulator-kontroles: koppel verdagte literals (bv., ro.kernel.qemu, generic, goldfish) met nabygeleë Build.* getters en stringvergelykings (->equals, ->contains, ->startsWith).
  • Hardgekodeerde geheime: merk const-string slegs wanneer ’n nabygeleë .field of move-result identifiseerder sleutelwoorde soos password, token, api_key bevat. Ignoreer uitdruklik UI-only merkers soos AutofillType, InputType, EditorInfo.

Reël-gedrewe skandeerders soos PulseAPK Core implementeer hierdie model om vinnig anti-analise logika en potensiële geheime in Smali aan die lig te bring.

Verwysings

Tip

Leer & oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Leer & oefen Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Blaai deur die volledige HackTricks Training-katalogus vir die assesseringsroetes (ARTA/GRTA/AzRTA) en Linux Hacking Expert (LHE).

Ondersteun HackTricks