Smali'de invoke-virtual için basit bir açıklama
invoke-virtual, bir örnek sanal yöntemi çağırmak için kullanılan bir DEX talimatıdır (obj.method() karşılığı). Belirli uygulama, vtable aracılığıyla nesnenin gerçek sınıfına göre çalışma zamanında seçilir; argümanlar register'larda iletilir ve sonuç move-result* aracılığıyla döndürülür.
invoke-virtual içindeki süslü parantezlerdeki ilk register genellikle nesnedir (this), geri kalanları ise argümanlardır.
Smali'de invoke-virtual nasıl okunur
Tipik bir Smali satırı: text invoke-virtual {v0, v1}, Lcom/example/User;->setName(Ljava/lang/String;)V
Açıklama:
- invoke-virtual — çağrı türü (sanal).
- {v0, v1} — register'lar: v0 — nesne referansı, v1 — argüman.
- Lcom/example/User; — yöntemin bildirildiği sınıf (imza için).
- setName — yöntem adı.
- (Ljava/lang/String;)V — imza (giriş: String, dönüş: void).
Daha basit ifadeyle: bu, user.setName(name) ifadesinin düşük seviyeli eşdeğeridir; burada belirli yöntem geçersiz kılınabilir.
"Kaputun altında" neler oluyor — vtable ve dağıtım
Çağrı algoritması (basitleştirilmiş):
- Bir register'da (örneğin v0) nesneye referans saklanır.
- Bu referans üzerinden nesnenin gerçek sınıfı belirlenir (talimatta belirtilenden farklı olabilir).
- Sınıfın sanal yöntemler tablosu (vtable) vardır.
- Yöntem indeksi (DEX meta verilerinden) kullanılarak gerekli uygulama bulunur.
- Bulunan yöntemin koduna atlama yapılır.
Önemli: Nesne bir alt sınıfın örneği ise, Smali'de temel sınıf belirtilmiş olsa bile alt sınıfın geçersiz kıldığı yöntem çağrılır.
invoke-virtual ile sonraki move-result* arasına başka talimatlar eklenemez. Bu, DEX doğrulama hatasına neden olur.
Register'lar, move-result ve /range
DEX'te JVM yığını yoktur — her şey register'lar aracılığıyla gerçekleşir. Çağrı formu, nesneyi ve argümanları süslü parantezler içinde iletir. Yöntem bir değer döndürüyorsa, sonuç hemen invoke-virtual'dan sonra gelen move-result talimatıyla alınır.
Örnek: text invoke-virtual {p0, p1, p2}, Lpkg/Class;->sum(II)I move-result v0
Argüman çok fazla ise ve art arda register'larda bulunuyorsa, register aralığını belirten invoke-virtual/range kullanılır.
invoke-virtual'ın diğer invoke-* talimatlarından farkları
Farklılıkların kısa tablosu
Smali'deki invoke türlerinin karşılaştırılması
| Talimat | Hangi yöntemler için | Nesne gerekli mi | Polimorfizm | Java karşılığı |
|---|---|---|---|---|
| invoke-virtual | Normal sanal yöntemler | Evet | Evet | obj.method() |
| invoke-direct | Yapıcılar, private, super | Evet | Hayır | new, this(), super.method() |
| invoke-static | Statik yöntemler | Hayır | Hayır | Class.method() |
| invoke-interface | Arayüz yöntemleri | Evet | Evet (arayüz tablosu üzerinden) | iface.method() |
| invoke-super | Süper sınıf uygulamasını çağırma | Evet | Hayır (sabittir) | super.method() |
| invoke-virtual/range | Aynı, çok sayıda argümanla | Evet | Evet | — |
Analiz ve yamalama sırasında bu nerede işe yarar
- İlgilenilen mantığın çağrısını bulma: Yöntem imzalarını arayıp invoke-virtual çağrılarına bakmak, yöntemin gerçekten nerede kullanıldığını anlamak için hızlı bir yoldur.
- Hook'lar ve enjeksiyonlar (Frida, Xposed): Çağrı türünü bilmek, uygulamayı doğru şekilde ele geçirmeye yardımcı olur.
- APK yamalama: invoke-virtual'ı başka bir çağrıyla değiştirebilir veya yöntemi değiştirebilirsiniz, ancak semantiği dikkate almanız gerekir (statik yöntemler mi yoksa örnek yöntemler mi).
Yama örneği (sonucun her zaman true olmasını sağlamak):
- Orijinal: text invoke-virtual {v0}, Lcom/example/Feature;->isEnabled()Z move-result v2
- Yama: text const/4 v2, 0x1 (invoke-virtual ve move-result'ı silin veya yorum satırı yapın)
Smali analiz ederken önce invoke-* türünü belirleyin, ardından çağrılan yöntemin gövdesini inceleyin — bu, anlamayı hızlandırır.
Sık yapılan hatalar
- invoke-virtual ve move-result arasına talimat eklemeye çalışmak — doğrulama hatalarına yol açar.
- Talimatta belirtilen sınıfın mutlaka nesnenin sınıfı olduğunu sanmak. Aslında uygulama, nesnenin gerçek türüne göre seçilir.
- Sonucu move-result olmadan almaya çalışmak — bu durumda değer register'a aktarılmaz.
SSS
-
null üzerinde invoke-virtual çağrılırsa ne olur?
Java'daki gibi — NullPointerException (çalışma zamanında NPE fırlatılır). -
Bazen talimatta temel sınıf görülüyor ama başka bir sınıfın yöntemi çağrılıyor, neden?
Talimatta imza için sınıf belirtilir; gerçek uygulama çalışma zamanında nesnenin sınıfına göre belirlenir. -
invoke-virtual/range ne zaman kullanılır?
Çok sayıda argüman register'larda art arda geldiğinde — /range, büyük bir register bloğunun iletimini kolaylaştırır. -
invoke-virtual'ı invoke-static ile değiştirebilir miyim?
Teorik olarak evet, ancak statik yöntemi hazırlamanız gerekir (örneğin, this için bir parametre eklemek) ve çağrıları ile imzaları düzeltmeniz gerekir — aksi takdirde uygulama bozulur.
Gerekirse, belirli bir Smali parçasını inceleyip hangi uygulamanın çağrılacağını ve nasıl doğru yamanacağını gösterebilirim.