Gönderi

ReVanced Bytecode ve Resource Patch Yazımı

Peki, uygulamaların üzerinde değişiklik yapabiliyoruz. Bunlar sadece Android paketi halinde elimizde duran şeyler. Fakat yapılan değişikliklerin, yani yemeğin tarifini kimse göremiyor. İşte bir ReVanced yaması tam olarak bu. Tarifteki bazı kısımlar mutfağa girmeyenler için pek anlamlı olmayabilir, amaç tarifin açık bir şekilde mevcut olması ve aşçılar tarafından okunabilmesidir. Öncelikle, geliştirme ortamını hazırlayalım.

Araçlar

Geliştirme Ortamı

Bir dizin oluşturup revanced-cli ve revanced-patches depolarını klonlayın.

1
2
3
4
5
6
7
8
9
10
11
12
mkdir revanced && cd revanced

repositories=(
    "revanced-cli"
    "revanced-patches"
    "revanced-integrations" (isteğe bağlı)
    "revanced-patcher" (isteğe bağlı)
)

for repository in "${repositories[@]}" ; do
    git clone -b dev --single-branch --depth 1 https://github.com/revanced/$repository
done

Patches projesinin dizininde ./gradlew build komutunu çalıştırın.

Authentication hatası alıyorsanız bağlantıdan read:packages iznine sahip bir token oluşturun. Verilen tokeni kullanıcı dosyasında .gradle dizininde gradle.properties içerisine (yoksa oluşturarak) ekleyin.

1
2
gpr.user = <github kullanıcı adı>
gpr.key = <token>

API check failed for project revanced-patches hatası alırsanız ./gradlew apidump komutunu çalıştırıp tekrar build edin.

BUILD SUCCESSFUL yazısını gördüyseniz kurulum tamamlandı.

IntelliJ’de Kurulum

IDEA’da CLI projesi açılıp Gradle senkronizasyon işlemi tamamlandıktan sonra MainCommand.kt dosyasında üçgen sembolü gözüküyor olmalı.

idea64 idea64

Ardından Proje Yapısı > Modüller kısmından patches projesini modül olarak ekleyin.

idea64 idea64

Konfigürasyon

Pencereyi kapattıktan sonra Çalıştırma/Ayıklama Konfigürasyonları ayarlarından yeni bir Kotlin konfigürasyonu oluşturun. Ana sınıfı app.revanced.cli.command.MainCommandKt olarak ayarlayın.

idea64 idea64

Program komutları kısmını aynı terminalde CLI kullanıyormuş gibi yazıyoruz. ibaresinin değişken olabileceğine dikkat edin.

1
2
3
patch
-b ..\revanced-patches\build\libs\revanced-patches-<version>.jar
..\some.apk

Gradle türünde bir Before launch komutu ekleyin. Proje olarak patches projesini seçin ve komutlar kısmına build yazın.

idea64 idea64

Debug Süreci

Projedeki patchlerden birine debug noktası koyarak düzgün çalışmakta olduğunu kontrol edin. Konfigürasyonda some.apk yerine patchlemek istediğiniz Android paketi ismi ve patches projesinin versiyonu doğru şekilde yazılmış olmalı. Ben revanced.app sitesindeki önerilen versiyonu kullanacağım, dolayısıyla konfigürasyon şu şekilde.

1
2
3
patch
-b ..\revanced-patches\build\libs\revanced-patches-4.5.0-dev.1.jar
..\com.google.android.youtube_19.09.37.apk

Patchlemekte olduğunuz uygulamaya uygun olanlardan birine debug noktası koyup başarılı bir şekilde durduğunu gördüyseniz başka bir şeye ihtiyaç kalmadı.

Patch Anatomisi

Patch Shot

Patchler çoğunlukla Bytecode ve Resource olarak ikiye ayrılır. Bytecode, uygulamanın smali kodunda; Resource, Android’in uygulama kaynakları (strings, values, styles) kısmında değişiklik yapmaya yarar. Çoğu patch ikisini birlikte kullanır. Sonradan eklenen Hex patch desteğiyse byte değerleri üzerinde değişikliği mümkün kılmakta. Byte seviyesinde inceleme yapılması, uygulamanın farklı dillerle yazılmış decode edilemeyen kütüphaneler (native library) kullanması durumunda gerekiyor.

RawResourcePatch benzer isimdeki yöntemle aynı işe yarıyor, sadece uygulamanın kaynaklarını decode etmeden daha hızlı bir şekilde patchlemek için mevcut.

Bu rehberde sadece basit düzeyde Bytecode ve Resource patch işlemini anlatacağım.

Parmak İzi

Hedeflediğiniz fonksiyonun adı şifrelenmiş ya da birkaç sürüm sonra değişikliğe uğramış olabilir. Bu noktada parmak izi yöntemi devreye giriyor. Bir fonksiyonun dönüş değeri (void, int) erişim belirteçleri (PUBLIC, STATIC) ve aldığı parametreler gibi değerleri belirterek tespit etmeye yarıyor.

Örneğin şu şekilde değerler belirttim diyelim:

1
2
3
returnType = "V",
access = AccessFlags.PUBLIC,
parameters = listOf("Z"),

Bu durumda void değer dönüşlü, PUBLIC erişimli ve BOOLEAN (smali kodunda Z) türünde parametre alan bir fonksiyonu hedefliyor olduğum anlaşılır. Sonradan bunu BytecodePatch constructor fonksiyonuna sunacağız.

1
2
3
4
5
object SomePatch : BytecodePatch(
    setOf(SomeFingerprint)
) {
    // ...
 }

Patch İskeleti

Bir patch öncelikle @Patch() içerisinde yazılan metadata bilgileri içermelidir.

1
2
3
4
5
6
@Patch(
    name = "Disable ads",
    description = "Disable ads in the app.",
    dependencies = [DisableAdsResourcePatch::class],
    compatiblePackages = [CompatiblePackage("com.some.app", ["1.3.0"])]
)
  • name: Patch ismi. Belirteç olarak kullanılır. İsimsiz olursa PatchBundleLoader tarafından tanınmaz ama diğer patchler bağlılık olarak kullanabilir.
  • description: Patch açıklaması. İsim yeterince açıklayıcıysa gerekli olmayabilir.
  • dependencies: Patchin bağımlı olduğu diğer patchler. Öncelikle bunlar çalıştırılacaktır, eğer bağımlı olunan patchler başarısız olursa bu patch de uygulanmaz.
  • compatiblePackages: Bir dizi CompatiblePackage nesnesi. Her bir CompatiblePackage nesnesi, paket adı ve uyumlu versiyonlar dizisi içerir. Bu parametre paket adı ve versiyonları tanımlar. Ad verilmezse tüm paketler ile uyumlu olduğu var sayılır. Versiyon dizisi boş bırakılırsa, pakete ait tüm versiyonlarla uyumlu olduğu kabul edilir.

Dizin Yapısı

Patchler, revanced-patches/src/main/kotlin/app/revanced/patches/<uygulama-adı> şeklinde düzene koyulur. Patch ismi işlevinden gelmektedir. Objektif bir dilde açıklama yazılır (örn. ‘Shorts butonunu gizler’). Parmak izi, olabildiğince az ve birçok sürümde (tabii şart değil, bazı patchler tek bir sürüme özel) geçerli olabilecek şekilde belirteçler kullanarak yazılmalı. Pek anlaşılır olmayan kısımlar için yorum satırları da eklenebilir.

Patch Yazımı

Rehberde kullanılmak üzere yazdığım ufak bir patchme uygulaması bulunmakta. Bir metin kutusu ve butondan oluşuyor. İstenilen parolayı girdiğinizde doğru olduğunu belirten bir toast mesajı çıkarıyor. Bu uygulamayı patchleyerek her parolayı doğru kabul etmesini sağlayacağız.

JADX ile İnceleme

Bağlantıdan patchme.apk dosyasını indirin ve JADX ile açın.

dev.seaque.patchme paketinin MainActivity dosyasında uygulamanın mantık kısmı bulunmakta. Buradan göze çarpan, boolean türünde değer döndüren isUnlocked fonksiyonu. Parmak izinde bunu hedef alacağız. Parmak izi yazarken smali koduna göre yazdığımıza dikkat edin.

.method isUnlocked(Ljava/lang/String;)Z
    .registers 3
    .param p1, "input"    # Ljava/lang/String;

    .line 37
    const-string v0, "VWF!ac3Gq&"

    invoke-virtual {v0, p1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

    move-result v0

    return v0
.end method
Bu gönderi CC BY 4.0 lisansı altındadır.