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
dizinindegradle.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ı.
Ardından Proje Yapısı > Modüller kısmından patches projesini modül olarak ekleyin.
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.
Program komutları kısmını aynı terminalde CLI kullanıyormuş gibi yazıyoruz.
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.
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
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 diziCompatiblePackage
nesnesi. Her birCompatiblePackage
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