Smali - Decompiling/[Modifying]/Compiling

Tip

AWS Hacking öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE) Değerlendirme yolları (ARTA/GRTA/AzRTA) ve Linux Hacking Expert (LHE) için tam HackTricks Training kataloğuna göz atın.

HackTricks'i Destekleyin

Bazen uygulama kodunu, sizin için gizli bilgilere erişmek amacıyla (ör. iyi obfusk edilmiş parolalar veya flags) değiştirmek ilginç olabilir. Bu durumda apk’yı decompile edip kodu değiştirip yeniden derlemek ilginç olur.

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

Fast Way

Visual Studio Code ve APKLab eklentisini kullanarak, herhangi bir komut çalıştırmadan uygulamayı otomatik olarak decompile edebilir, kodu değiştirebilir, yeniden derleyip, imzalayıp yükleyebilirsiniz.

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

Split APKs / App Bundles

Güncel hedefler genellikle tek bir monolitik APK yerine split APKs (base.apk + split_config.*.apk) şeklinde dağıtılır. Yalnızca base.apk’yi yama yaparsanız, kaynaklar veya native kütüphaneler senkron dışı kalabilir ve kurulum başarısız olabilir.

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

Hedef bir split package ise, ya tüm seti yeniden derleyin ya da önce APK’ları birleştiren araçları kullanın. apk.sh burada kullanışlıdır çünkü split APK’ları tek bir patchable APK’ye birleştirip public resource identifiers’ı düzeltebilir.
Frida/Objection-oriented repacking workflows için ayrıca Android Anti-Instrumentation & SSL Pinning Bypass sayfasına bakın.

APK’yi decompile et

APKTool kullanarak smali code and resources’a erişebilirsiniz:

apktool d APP.apk

Eğer apktool herhangi bir hata verirse, installing the latest version yüklemeyi deneyin

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 (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).

Smali kodunu değiştirin

Bazı talimatları değiştirebilir, bazı değişkenlerin değerini değiştirebilir veya yeni talimatlar ekleyebilirsiniz. Ben Smali kodunu VS Code ile değiştiriyorum, ardından smalise extension’ı kuruyorsunuz ve editör herhangi bir talimatın yanlış olduğunu söyleyecektir.
Bazı örnekler şu adreste bulunabilir:

Veya check below some Smali changes explained.

APK’ı yeniden derleyin

Kodu değiştirdikten sonra yeniden derleyebilirsiniz kullanarak:

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

Yeni APK’yı dist klasörünün içinde derleyecek.

Eğer apktool bir hata verirse, deneyin installing the latest version

Yeni APK’yı imzala

Sonra, bir anahtar oluşturmanız gerekiyor (size bir parola ve rastgele doldurabileceğiniz bazı bilgiler sorulacak):

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

Son olarak, yeni APK’yı imzala:

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

jarsigner hala bazı hızlı testler için çalışır, ancak modern Android derlemeleri için apksigner tercih edilir çünkü daha yeni APK imzalama şemalarını destekler.

Yeni uygulamayı optimize edin

zipalign bir arşiv hizalama aracıdır ve Android uygulama (APK) dosyalarına önemli optimizasyon sağlar. More information here.

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

APK, gömülü native kütüphaneler (lib/*.so) içeriyorsa, Android artık -P 16 kullanmayı önerir; böylece .so dosyaları hem 16 KiB hem de 4 KiB sayfa boyutuna sahip cihazlar için hizalanmış olur:

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

Yeni APK’yı İmzala (yine mi?)

Eğer jarsigner yerine apksigner kullanmayı tercih ediyorsanız, zipaling ile yapılan optimizasyonu uyguladıktan sonra apk’yı imzalamalısınız. ANCAK DİKKAT: UYGULAMAYI SADECE BİR KEZ İMZALAMANIZ YETERLİDİR — jarsigner ile (zipalign’dan önce) veya aspsigner ile (zipaling’den sonra).

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

Daha pratik, modern bir akış şöyledir:

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

Önemli notlar:

  • Eğer bir APK’yi apksigner ile imzaladıktan sonra değiştirirseniz, imza geçersiz hale gelir ve yeniden imzalamanız gerekir.
  • apksigner verify --print-certs yeniden oluşturulmuş APK’nin yüklenebilir olduğunu doğrulamak ve hedefin çalışma zamanında sunacağı sertifikayı incelemek için faydalıdır.

Smali’yi Değiştirme

Aşağıdaki Hello World Java kodu için:

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

Smali kodu şöyle olur:

.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 komut seti here.

Hafif Değişiklikler

Bir fonksiyon içindeki bir değişkenin başlangıç değerlerini değiştirin

Bazı değişkenler fonksiyonun başında opcode const kullanılarak tanımlanır, bu değerleri değiştirebilir veya yeni değişkenler tanımlayabilirsiniz:

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

Temel İşlemler

#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

Daha Büyük Değişiklikler

Smali’nin genellikle yeniden derlemeleri bozan tuzakları

  • Mevcut bir metodun gövdesinde yalnızca geçici register’lara ihtiyacınız olduğunda .locals’u artırmayı tercih edin. Parametre register’ları (p0, p1…) metodun en yüksek register’larına eşlenir, bu yüzden körü körüne .registers’a geçmek sıklıkla argüman düzenini bozar.
  • move-result, move-result-wide, ve move-result-object eşleşen invoke-*’ten hemen sonra görünmelidir. Aralarına loglama veya başka herhangi bir opcode eklemek metodu geçersiz kılar.
  • long ve double değerler wide değerlerdir ve bir register çifti tüketir. Bu register’ları daha sonra tekrar kullanırsanız, v10’un aynı zamanda v11’i de işgal ettiğini unutmayın.
  • Eğer çok sayıda register geçirmeniz gerekiyorsa veya çok yüksek numaralı olanları kullanmanız gerekiyorsa, invoke-virtual/range gibi /range varyantlarını kullanın.

Loglama

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

Recommendations:

  • 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.

Yaygın anti-tamper kontrollerinin düzeltilmesi

Bir uygulama yeniden paketlendiğinde, bozulabilecek ilk şeylerden biri uygulama içi signature / installer / integrity kontrolü olabilir. JADX’de veya smali ağacında aranacak iyi stringler şunlardır:

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

Modern uygulamalar genellikle PackageManager.getPackageInfo(..., GET_SIGNING_CERTIFICATES) çağırır, signer baytlarını MessageDigest ile hash’ler ve sonucu sabit bir değerle karşılaştırır. Pratikte, tüm imza işleme kodunu yeniden yazmaktansa final boolean / branch’i yamamak genellikle daha kolaydır.

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

Doğrulama kodu gereksiz yere gürültülüyse, tüm rutini değiştirmek yerine hata diyaloğu / finish() / System.exit() / telemetri çağrısından önceki son karşılaştırmayı bulun ve oraya patch uygulayın.

Toast Gösterme

Fonksiyonun başındaki .locals sayısına 3 eklemeyi unutmayın.

Bu kod, bir fonksiyonun ortasına eklenmek üzere hazırlanmıştır (gerekirse değişken sayısını değiştirin). Bu kod this.o değerini alacak, String’e dönüştürecek ve sonra değeriyle bir toast yapacaktır.

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

Başlangıçta Yerel Bir Kütüphaneyi Yükleme (System.loadLibrary)

Bazen bir native kütüphaneyi önceden yüklemeniz gerekebilir, böylece diğer JNI kütüphanelerinden önce başlatılır (ör. işlem düzeyinde telemetry/logging’i etkinleştirmek için).
System.loadLibrary() çağrısını bir static initializer içinde veya Application.onCreate()’de erken bir noktaya enjekte edebilirsiniz. Örnek smali, bir static sınıf initializer’ı () için:

.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

Alternatif olarak, kütüphanenin mümkün olduğunca erken yüklenmesini sağlamak için aynı iki talimatı Application.onCreate() metodunuzun başına yerleştirin:

.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

Notlar:

  • Doğru ABI varyantının lib// altında (ör. arm64-v8a/armeabi-v7a) bulunduğundan emin olun; aksi halde UnsatisfiedLinkError oluşabilir.
  • Çok erken yükleme (class static initializer), native logger’ın sonraki JNI etkinliğini gözlemlemesini garanti eder.

Smali Statik Analiz / Kural-Tabanlı Av

apktool ile decompile ettikten sonra, Smali’yi satır satır taramak için regex kurallarını kullanarak anti-analysis mantığını (root/emulator kontrolleri) ve muhtemel hardcoded secret’leri hızlıca tespit edebilirsiniz. Bu bir hızlı ön eleme tekniğidir: bulunan sonuçları çevresindeki Smali veya yeniden yapılandırılmış Java/Kotlin içinde doğramanız gereken ipuçları olarak ele alın.

Temel fikirler:

  • Library filtering: ortak üçüncü taraf namespace’leri altındaki bulguları bastırın veya etiketleyin, böylece uygulamaya ait kod yollarına odaklanırsınız.
  • Context hints: şüpheli dizelerin bunları kullanan API’lerin yakınında (aynı method içinde, N satır içinde) görünmesini şart koşun.
  • Confidence: bulguları sıralamak ve false positive’leri azaltmak için basit seviyeler (high/medium) kullanın.

Varsayılan olarak bastırılacak örnek kütüphane önekleri:

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

Örnek tespit kuralları (regex + bağlam heuristikleri):

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

Uygulamada iyi işleyen ek heuristikler:

  • Root package/path checks: yakınlarda PackageManager;->getPackageInfo veya File;->exists çağrıları olması gerektiğini kontrol edin; örneğin com.topjohnwu.magisk veya /data/local/tmp gibi dizeler için.
  • Emulator checks: şüpheli literal dizeleri (ör. ro.kernel.qemu, generic, goldfish) yakınlardaki Build.* getter’ları ve string karşılaştırmaları (->equals, ->contains, ->startsWith) ile eşleştirin.
  • Hardcoded secrets: const-string yalnızca yakınlarda .field veya move-result identifier’ı password, token, api_key gibi anahtar kelimeleri içeriyorsa işaretleyin. AutofillType, InputType, EditorInfo gibi yalnızca UI ile ilgili işaretleri açıkça görmezden gelin.

PulseAPK Core gibi kural tabanlı tarayıcılar bu modeli uygulayarak Smali’de analiz önleyici (anti-analysis) mantığını ve potansiyel sırları hızla ortaya çıkarır.

References

Tip

AWS Hacking öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE) Değerlendirme yolları (ARTA/GRTA/AzRTA) ve Linux Hacking Expert (LHE) için tam HackTricks Training kataloğuna göz atın.

HackTricks'i Destekleyin