Smali - Απομεταγλώττιση/[Τροποποίηση]/Μεταγλώττιση

Tip

Μάθε & εξασκήσου στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθε & εξασκήσου στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθε & εξασκήσου στο Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Περιηγήσου στον πλήρη κατάλογο HackTricks Training για τα assessment tracks (ARTA/GRTA/AzRTA) και στο Linux Hacking Expert (LHE).

Υποστήριξε το HackTricks

Μερικές φορές είναι ενδιαφέρον να τροποποιήσεις τον κώδικα της εφαρμογής για να αποκτήσεις κρυφές πληροφορίες για σένα (ίσως πολύ obfuscated passwords ή flags). Τότε, μπορεί να είναι χρήσιμο να decompile το apk, να τροποποιήσεις τον κώδικα και να recompile την εφαρμογή.

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

Γρήγορος Τρόπος

Χρησιμοποιώντας Visual Studio Code και την επέκταση APKLab, μπορείς να automatically decompile, να τροποποιήσεις, recompile, να υπογράψεις & να εγκαταστήσεις την εφαρμογή χωρίς να εκτελέσεις καμία εντολή.

Ένα ακόμα script που διευκολύνει πολύ αυτήν την εργασία είναι https://github.com/ax/apk.sh

Split APKs / App Bundles

Οι σύγχρονοι στόχοι συνήθως παραδίδονται ως split APKs (base.apk + split_config.*.apk) αντί για ένα μονολιθικό APK. Αν κάνεις patch μόνο το base.apk, πόροι ή native βιβλιοθήκες μπορεί να βγουν εκτός συγχρονισμού και η εγκατάσταση να αποτύχει.

Γρήγορος έλεγχος από μια συσκευή:

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

Αν ο στόχος είναι split package, είτε να επαναχτίσεις ολόκληρο το σύνολο είτε να χρησιμοποιήσεις εργαλεία που ενώνουν πρώτα τα APKs. apk.sh είναι χρήσιμο εδώ επειδή μπορεί να συνδυάσει split APKs σε ένα ενιαίο patchable APK και να διορθώσει τους public resource identifiers.
Για Frida/Objection-oriented repacking workflows, έλεγξε επίσης Android Anti-Instrumentation & SSL Pinning Bypass.

Αποσυμπίλωση του APK

Χρησιμοποιώντας APKTool έχεις πρόσβαση στον κώδικα smali και στα resources:

apktool d APP.apk

Αν το apktool σας εμφανίσει οποιοδήποτε σφάλμα, δοκιμάστε να εγκαταστήσετε την τελευταία έκδοση

Μερικά ενδιαφέροντα αρχεία που πρέπει να κοιτάξετε είναι:

  • res/values/strings.xml (και όλα τα xml μέσα στο res/values/*)
  • AndroidManifest.xml
  • Κάθε αρχείο με κατάληξη .sqlite ή .db

Αν το apktool έχει προβλήματα αποκωδικοποίησης της εφαρμογής, ρίξτε μια ματιά στο https://ibotpeaches.github.io/Apktool/documentation/#framework-files ή δοκιμάστε να χρησιμοποιήσετε το όρισμα -r (Μην αποκωδικοποιήσετε τους πόρους). Τότε, αν το πρόβλημα ήταν σε έναν πόρο και όχι στον πηγαίο κώδικα, δεν θα έχετε το πρόβλημα (επίσης δεν θα κάνετε decompile τους πόρους).

Change smali code

Μπορείτε να αλλάξετε εντολές, να αλλάξετε την τιμή κάποιων μεταβλητών ή να προσθέσετε νέες εντολές. Εγώ αλλάζω τον κώδικα Smali χρησιμοποιώντας VS Code, στη συνέχεια εγκαθιστάτε το smalise extension και ο επεξεργαστής θα σας πει αν κάποια εντολή είναι λανθασμένη.
Κάποια παραδείγματα μπορείτε να τα βρείτε εδώ:

Ή μπορείτε check below some Smali changes explained.

Recompile the APK

Μετά την τροποποίηση του κώδικα μπορείτε να recompile τον κώδικα χρησιμοποιώντας:

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

Θα compile το νέο APK μέσα στον φάκελο dist.

Εάν το apktool εμφανίσει ένα error, δοκιμάστε installing the latest version

Υπογράψτε το νέο APK

Στη συνέχεια, πρέπει να δημιουργήσετε ένα κλειδί (θα σας ζητηθεί ένας κωδικός και κάποιες πληροφορίες που μπορείτε να συμπληρώσετε τυχαία):

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

Τέλος, υπογράψτε το νέο APK:

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

jarsigner εξακολουθεί να λειτουργεί για μερικές γρήγορες δοκιμές, αλλά για σύγχρονες Android builds προτιμάται το apksigner επειδή διαχειρίζεται τα νεότερα σχήματα υπογραφής APK.

Βελτιστοποίηση νέας εφαρμογής

zipalign είναι ένα εργαλείο ευθυγράμμισης αρχείων που παρέχει σημαντική βελτιστοποίηση στα αρχεία εφαρμογών Android (APK). More information here.

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

Εάν το APK περιέχει ενσωματωμένες native βιβλιοθήκες (lib/*.so), το Android πλέον συνιστά τη χρήση του -P 16 ώστε τα αρχεία .so να ευθυγραμμίζονται για συσκευές με μέγεθος σελίδας 16 KiB και 4 KiB:

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

Υπογράψτε το νέο APK (πάλι;)

Εάν προτιμάτε να χρησιμοποιήσετε apksigner αντί για jarsigner, θα πρέπει να υπογράψετε το apk αφού εφαρμόσετε την βελτιστοποίηση με zipaling. ΑΛΛΑ ΣΗΜΕΙΩΣΤΕ ΟΤΙ ΠΡΕΠΕΙ ΝΑ ΥΠΟΓΡΑΨΕΤΕ ΤΗΝ ΕΦΑΡΜΟΓΗ ΜΟΝΟ ΜΙΑ ΦΟΡΑ ΜΕ jarsigner (πριν zipalign) Ή ΜΕ aspsigner (μετά zipaling).

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

Μια πιο πρακτική σύγχρονη ροή είναι:

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

Σημαντικές σημειώσεις:

  • Αν τροποποιήσετε ένα APK μετά την υπογραφή του με apksigner, η υπογραφή ακυρώνεται και πρέπει να το υπογράψετε ξανά.
  • apksigner verify --print-certs είναι χρήσιμο για να επιβεβαιώσετε ότι το επαναχτισμένο APK είναι εγκαταστάσιμο και να εξετάσετε το πιστοποιητικό που ο στόχος θα εκθέσει κατά την εκτέλεση.

Τροποποίηση Smali

Για τον ακόλουθο Hello World Java κώδικα:

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

Ο κώδικας Smali θα ήταν:

.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

Το σύνολο εντολών Smali είναι διαθέσιμο here.

Ελαφρές Αλλαγές

Τροποποίηση αρχικών τιμών μεταβλητής εντός μιας συνάρτησης

Ορισμένες μεταβλητές ορίζονται στην αρχή της συνάρτησης χρησιμοποιώντας το opcode const, μπορείτε να τροποποιήσετε τις τιμές τους ή να ορίσετε νέες:

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

Βασικές λειτουργίες

#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

Μεγαλύτερες Αλλαγές

Σημεία-παγίδες του Smali που συνήθως σπάνε τις αναδημιουργίες

  • Προτιμήστε να αυξάνετε το .locals όταν χρειάζεστε μόνο προσωρινούς καταχωρητές στο σώμα μιας υπάρχουσας μεθόδου. Οι καταχωρητές παραμέτρων (p0, p1…) χαρτογραφούνται στους υψηλότερους καταχωρητές της μεθόδου, οπότε η απροβλημάτιστη αλλαγή σε .registers συχνά σπάει τη διάταξη των ορισμάτων.
  • move-result, move-result-wide, and move-result-object πρέπει να εμφανίζονται αμέσως μετά το αντίστοιχο invoke-*. Η εισαγωγή logging ή οποιουδήποτε άλλου opcode ανάμεσά τους καθιστά τη μέθοδο άκυρη.
  • long και double τιμές είναι wide τιμές και καταλαμβάνουν ένα ζευγάρι καταχωρητών. Αν ξαναχρησιμοποιήσετε αυτούς τους καταχωρητές αργότερα, θυμηθείτε ότι το v10 καταλαμβάνει επίσης το v11.
  • Αν χρειάζεται να περάσετε πολλούς καταχωρητές, ή πολύ υψηλούς σε αριθμό, χρησιμοποιήστε τις παραλλαγές /range όπως invoke-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>"

Συστάσεις:

  • Αν σκοπεύετε να χρησιμοποιήσετε δηλωμένες μεταβλητές μέσα στη συνάρτηση (δηλωμένες v0,v1,v2…) τοποθετήστε αυτές τις γραμμές ανάμεσα στο .local και τις δηλώσεις των μεταβλητών (const v0, 0x1)
  • Αν θέλετε να βάλετε τον κώδικα logging στο μέσον του κώδικα μιας συνάρτησης:
  • Προσθέστε 2 στον αριθμό των δηλωμένων μεταβλητών: Ex: from .locals 10 to .locals 12
  • Οι νέες μεταβλητές πρέπει να έχουν τους επόμενους αριθμούς των ήδη δηλωμένων μεταβλητών (σε αυτό το παράδειγμα πρέπει να είναι v10 και v11, θυμηθείτε ότι ξεκινά σε v0).
  • Αλλάξτε τον κώδικα της logging συνάρτησης και χρησιμοποιήστε v10 και v11 αντί για v5 και v1.

Επιδιόρθωση κοινών anti-tamper ελέγχων

Όταν ένα app είναι repacked, ένα από τα πρώτα πράγματα που μπορεί να σπάσουν είναι ένας in-app signature / installer / integrity έλεγχος. Καλές συμβολοσειρές για αναζήτηση στο JADX ή στο δέντρο smali είναι:

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

Τα σύγχρονα apps συχνά καλούν PackageManager.getPackageInfo(..., GET_SIGNING_CERTIFICATES), hash-άρουν τα signer bytes με MessageDigest, και συγκρίνουν το αποτέλεσμα με μια hardcoded σταθερά. Στην πράξη, συνήθως είναι πιο εύκολο να κάνετε patch το τελικό boolean / branch παρά να ξαναγράψετε όλο τον κώδικα χειρισμού signatures.

Παραδείγματα μοτίβων:

# 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

Αν ο κώδικας επαλήθευσης είναι θορυβώδης, αναζητήστε την τελευταία σύγκριση πριν από το παράθυρο σφάλματος / finish() / System.exit() / telemetry call και εφαρμόστε patch εκεί αντί να πειράξετε ολόκληρη τη ρουτίνα.

Toasting

Θυμηθείτε να προσθέσετε 3 στον αριθμό των .locals στην αρχή της συνάρτησης.

Αυτός ο κώδικας είναι έτοιμος για εισαγωγή στο μέσο μιας συνάρτησης (αλλάξτε τον αριθμό των μεταβλητών όπως απαιτείται). Θα πάρει την τιμή του this.o, θα την μετατρέψει σε String και στη συνέχεια θα δημιουργήσει ένα toast με την τιμή της.

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

Φόρτωση μιας native βιβλιοθήκης κατά την εκκίνηση (System.loadLibrary)

Μερικές φορές χρειάζεται να φορτώσετε προκαταβολικά μια native βιβλιοθήκη ώστε να αρχικοποιηθεί πριν από άλλες JNI libs (π.χ. για να ενεργοποιηθεί process-local telemetry/logging). Μπορείτε να εισάγετε μια κλήση σε System.loadLibrary() σε έναν static initializer ή νωρίς στο Application.onCreate(). Παράδειγμα smali για static class 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

Εναλλακτικά, τοποθετήστε τις ίδιες δύο εντολές στην αρχή του Application.onCreate() για να διασφαλίσετε ότι η βιβλιοθήκη φορτώνει όσο το δυνατόν νωρίτερα:

.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

Σημειώσεις:

  • Βεβαιώσου ότι η σωστή παραλλαγή ABI της βιβλιοθήκης υπάρχει κάτω από lib// (π.χ., arm64-v8a/armeabi-v7a) για να αποφευχθεί το UnsatisfiedLinkError.
  • Η φόρτωση πολύ νωρίς (class static initializer) εγγυάται ότι ο native logger μπορεί να παρατηρήσει την επακόλουθη δραστηριότητα JNI.

Smali Στατική Ανάλυση / Κυνήγι Βάσει Κανόνων

Μετά το decompiling με το apktool, μπορείς να σαρώσεις το Smali γραμμή-γραμμή με κανόνες regex για να εντοπίσεις γρήγορα anti-analysis λογική (root/emulator checks) και πιθανώς hardcoded secrets. Αυτή είναι μια τεχνική ταχείας διαλογής: αντιμετώπιζε τα ευρήματα ως ενδείξεις που πρέπει να επαληθεύσεις στο περιβάλλον του Smali ή στο ανασυντεθειμένο Java/Kotlin.

Key ideas:

  • Φιλτράρισμα βιβλιοθηκών: αποσιώπησε ή επισήμανε ευρήματα κάτω από συνήθεις ονοματοχώρους τρίτων ώστε να εστιάζεις σε διαδρομές κώδικα που ανήκουν στην εφαρμογή.
  • Υποδείξεις συμφραζομένων: απαιτήστε οι ύποπτες συμβολοσειρές να εμφανίζονται κοντά στις APIs που τις καταναλώνουν (στην ίδια μέθοδο, εντός N γραμμών).
  • Επίπεδο εμπιστοσύνης: χρησιμοποιήστε απλά επίπεδα (high/medium) για να ταξινομήσετε τις ενδείξεις και να μειώσετε τα false positives.

Παραδείγματα προθεμάτων βιβλιοθηκών που αποσιωπούνται από προεπιλογή:

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

Παραδείγματα κανόνων ανίχνευσης (regex + ευρετικές βάσει συμφραζομένων):

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

Επιπλέον ευρετικές μεθοδολογίες που λειτουργούν καλά στην πράξη:

  • Έλεγχοι root πακέτου/διαδρομής: απαιτούν κοντινές κλήσεις PackageManager;->getPackageInfo ή File;->exists για συμβολοσειρές όπως com.topjohnwu.magisk ή /data/local/tmp.
  • Έλεγχοι εξομοιωτή: ζευγαρώνουν ύποπτες literal τιμές (π.χ., ro.kernel.qemu, generic, goldfish) με κοντινούς getters Build.* και συγκρίσεις συμβολοσειρών (->equals, ->contains, ->startsWith).
  • Σκληροκωδικοποιημένα μυστικά: σημαδέψτε const-string μόνο όταν ένα κοντινό .field ή move-result αναγνωριστικό περιλαμβάνει λέξεις-κλειδιά όπως password, token, api_key. Αγνοήστε ρητά δείκτες που αφορούν μόνο UI όπως AutofillType, InputType, EditorInfo.

Αναλυτές βάσει κανόνων όπως το PulseAPK Core υλοποιούν αυτό το μοντέλο για να επισημαίνουν γρήγορα anti-analysis λογική και πιθανά μυστικά σε Smali.

Αναφορές

Tip

Μάθε & εξασκήσου στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθε & εξασκήσου στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθε & εξασκήσου στο Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Περιηγήσου στον πλήρη κατάλογο HackTricks Training για τα assessment tracks (ARTA/GRTA/AzRTA) και στο Linux Hacking Expert (LHE).

Υποστήριξε το HackTricks