Bu Vapura Binmek İçin 64-Bit Olmalısınız
Pixel 7 için güncellenmiş bir NY Waterway uygulamasında tersine mühendislik
TLDR : NY Waterway'i yüklemenize izin vermeyen daha yeni bir Android cihazınız varsa , uygulamanın değiştirilmiş sürümünü indirebilirsiniz . Rastgele uygulamaları kurarken her zaman dikkatli olmalısınız, özellikle resmi Play Store dışındaki kaynaklardan - örneğin adını hiç duymadığınız rastgele bir adamın bu Medium gönderisi gibi. Daha dikkatli olmak istiyorsanız, APK'nın nasıl değiştirildiğini görmek için ileriyi okuyabilirsiniz (hatta isterseniz adımları kendiniz tekrarlayabilirsiniz).
Google , 2019 yılında Play Store'daki tüm yeni ve güncel uygulamalar için 64 bit desteğini zorunlu hale getirdi . Ağustos 2021'den itibaren, 64 bit mimariyi desteklemeyen uygulamalar Play Store'da 64 bit özellikli cihazlar için kullanılamayacak hale geldi. Özellikle, yeni Pixel 7 ve Pixel 7 Pro, yalnızca 32 bitlik uygulamaların yüklenmesini hiçbir şekilde desteklemiyor .
Hudson Nehri feribotuna binen New Yorklular için bu oldukça elverişsiz çünkü telefonunuzda elektronik bilet sağlayan uygulama NY Waterway gerçekten eski . En son Haziran 2018'de yayınlandı ve yalnızca 32 bit mimariler için yerel kitaplıklar içeriyor… Bu nedenle, yeni Pixel cihazlarının kullanıcıları için Hudson Nehri feribotunda elektronik bilet yok !
Yıllar önce iPhone'a geçtim, ancak bir Android kullanıcısıyken, işletim sistemi ve uygulamalarla çok uğraşırdım - özel ROM'lar yükler ve uygulamaları geri derlerdim. Yakın bir arkadaşım yeni Pixel 7 Pro'yu aldı ve her zaman Hudson Nehri feribotuna biniyor, bu yüzden bu uygulamayı onun için düzeltmem için şaka yollu beni dürttü. İşte başlıyoruz!
Uygulamaya Bakmak
Yalnızca 32 bit olan ve yüklenmesini engelleyen parçaları belirlemek için NY Waterway uygulamasını inceleyerek başlayalım. kullanarak apktool
, Android uygulamasını çıkarabilir ve kodunu inceleyebiliriz.
$ apktool d ./NYWaterway.apk
I: Using Apktool 2.6.1 on NYWaterway.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
I: Loading resource table from file: /Users/joeywatts/Library/apktool/framework/1.apk
I: Regular manifest package...
I: Decoding file-resources...
I: Decoding values */* XMLs...
I: Baksmaling classes.dex...
I: Copying assets and libs...
I: Copying unknown files...
I: Copying original files...
$ cd ./NYWaterway
$ ls -l
total 72
-rw-r--r-- 1 joeywatts staff 8797 Nov 21 18:37 AndroidManifest.xml
-rw-r--r-- 1 joeywatts staff 21382 Nov 21 18:37 apktool.yml
drwxr-xr-x 14 joeywatts staff 448 Nov 21 18:37 assets
drwxr-xr-x 5 joeywatts staff 160 Nov 21 18:37 lib
drwxr-xr-x 4 joeywatts staff 128 Nov 21 18:37 original
drwxr-xr-x 178 joeywatts staff 5696 Nov 21 18:37 res
drwxr-xr-x 10 joeywatts staff 320 Nov 21 18:37 smali
drwxr-xr-x 10 joeywatts staff 320 Nov 21 18:37 unknown
64-bit Uyumluluk ve Yerel Kitaplıklar
Android uygulamaları genellikle sizi platforma özgü uyumlulukla ilgili endişelerden koruyan üst düzey bir soyutlama olan Java Sanal Makinesi'ni hedefleyen her iki dil olan Java veya Kotlin'de yazılır. Ancak yerel, platforma özgü kodu (genellikle C veya C++ gibi alt düzey dillerden derlenir) çağırmak için Java Yerel Arabirimini (JNI) kullanabilirsiniz. Dizine bakarsak libs
, NY Waterway uygulamasında yer alan yerel kütüphaneleri görebiliriz.
$ ls -lR lib/*
lib/armeabi:
total 8352
-rw-r--r-- 1 joeywatts staff 177900 Nov 21 18:37 libdatabase_sqlcipher.so
-rw-r--r-- 1 joeywatts staff 1369284 Nov 21 18:37 libsqlcipher.so
-rw-r--r-- 1 joeywatts staff 2314540 Nov 21 18:37 libsqlcipher_android.so
-rw-r--r-- 1 joeywatts staff 402604 Nov 21 18:37 libstlport_shared.so
lib/armeabi-v7a:
total 2552
-rw-r--r-- 1 joeywatts staff 1303788 Nov 21 18:37 libsqlcipher.so
lib/x86:
total 14616
-rw-r--r-- 1 joeywatts staff 1476500 Nov 21 18:37 libdatabase_sqlcipher.so
-rw-r--r-- 1 joeywatts staff 2246448 Nov 21 18:37 libsqlcipher.so
-rw-r--r-- 1 joeywatts staff 3294132 Nov 21 18:37 libsqlcipher_android.so
-rw-r--r-- 1 joeywatts staff 455740 Nov 21 18:37 libstlport_shared.so
Buradaki bir başka gözlem de armeabi
, x86
dört kütüphaneye sahipken armeabi-v7a
sadece bir kütüphaneye sahip olmasıdır. Bir kitaplığın Android uygulaması tarafından yüklenmesi için java.lang.System.loadLibrary
or öğesini çağırması gerekir java.lang.Runtime.loadLibrary
. Smali kodunda "loadLibrary" araması, yerel kitaplıkları yüklediği yalnızca bir yeri gösterir.
$ grep -r loadLibrary smali/
smali//net/sqlcipher/database/SQLiteDatabase.smali: invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
$ grep loadLibrary -A 2 -B 3 smali/net/sqlcipher/database/SQLiteDatabase.smali
:try_start_0
const-string v0, "sqlcipher"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
:try_end_0
.catchall {:try_start_0 .. :try_end_0} :catchall_0
Uygulamayı yeni Pixel cihazlarıyla uyumlu hale getirmek için libsqlcipher.so
64 bitlik bir ARM yapısına ihtiyacımız var. lib/arm64-v8a
Uygun bir şekilde, SQLCipher bir açık kaynak kitaplığıdır . Yerel sqlcipher kitaplığıyla etkileşim için üst düzey birleştirme koduna baktığımızda, kullanılan kitaplığın sürümünü görebiliriz.
$ grep -ri version smali/net/sqlcipher
smali/net/sqlcipher/database/SQLiteDatabase.smali:.field public static final SQLCIPHER_ANDROID_VERSION:Ljava/lang/String; = "3.5.4"
SQLCipher'ı v3.5.5'e yükseltme
Yükseltme işlemi, SQLCipher Smali kodunun ve yerel kitaplıkların daha yeni sürümdeki kodla değiştirilmesini içerecektir. Bu, SQLCipher'in genel API yüzeyi önemli ölçüde değişirse sorunlara neden olur (örneğin, NY Waterway tarafından kullanılan bir genel işlev imzayı değiştirirse veya kaldırılırsa, daha yeni sürümle değiştirmek sorunlara neden olur). v3.5.4'ten v3.5.5'e yapılan değişiklikleri hızlı bir şekilde taradığınızda, burada görünecek bir sorun gibi görünmüyor. SQLCipher v3.5.5 için AAR dosyasını indirdim ve sonra unzip
ayıklamak için kullandım.
$ mkdir ../sqlcipher && cd ../sqlcipher
$ unzip ~/Downloads/android-database-sqlcipher-3.5.5.aar
Archive: /Users/joeywatts/Downloads/android-database-sqlcipher-3.5.5.aar
inflating: AndroidManifest.xml
creating: res/
inflating: classes.jar
creating: jni/
creating: jni/arm64-v8a/
creating: jni/armeabi/
creating: jni/armeabi-v7a/
creating: jni/x86/
creating: jni/x86_64/
inflating: jni/arm64-v8a/libsqlcipher.so
inflating: jni/armeabi/libsqlcipher.so
inflating: jni/armeabi-v7a/libsqlcipher.so
inflating: jni/x86/libsqlcipher.so
inflating: jni/x86_64/libsqlcipher.so
Android SDK, bir dosyayı Android bayt koduna ( dosya) d8
derleyebilen adlı bir komut satırı aracı sağlar . Sonra , dosyaları . Adımları birleştirmek:jar
classes.dex
baksmali
dex
smali
$ export ANDROID_HOME=/Users/joeywatts/Library/Android/sdk
$ $ANDROID_HOME/build-tools/33.0.0/d8 classes.jar \
--lib $ANDROID_HOME/platforms/android-31/android.jar
$ java -jar ../baksmali.jar dis ./classes.dex
$ rm -r ../NYWaterway/smali/net/sqlcipher ../NYWaterway/lib
$ mv out/net/sqlcipher ../NYWaterway/smali/net/sqlcipher
$ mv jni ../NYWaterway/lib
Artık uygulamayı yeniden oluşturup imzalayabiliriz, böylece bir cihaza kurulabilir!
$ cd ../NYWaterway
$ apktool b .
$ keytool -genkey -v -keystore my-release-key.keystore -alias alias_name \
-keyalg RSA -keysize 2048 -validity 10000
$ $ANDROID_HOME/build-tools/33.0.0/apksigner sign \
--ks my-release-key.keystore ./dist/NYWaterway.apk
Hedef SDK Sürümünü Artırma
Uygulamanın Android'in eski bir sürümü için oluşturulduğunu belirten bu açılır pencereden kurtulmak için hedef SDK sürümünü apktool.yml
. <31 SDK sürümünü hedefleyen uygulamalar artık Play Store'da kabul edilmemektedir, bu yüzden bunu buna yükseltmeyi seçtim.
Kullanımdan kaldırılan API'ler daha yeni SDK sürümlerinde kullanılamadığından, Android SDK'nın daha yeni bir sürümünü hedeflemek kod değişiklikleri gerektirebilir. NY Waterway, SDK v31'i hedeflemek için çeşitli değişiklikler gerektirir.
Daha Güvenli Bileşen İhracatı
Uygulamanız Android 12 veya üstünü hedefliyorsa ve amaç filtreleri kullanan etkinlikler, hizmetler veya yayın alıcıları içeriyorsa, android:exported
bu uygulama bileşenleri için özelliği açıkça bildirmeniz gerekir.
s'ye sahip olan ve içine bir öznitelik eklenmesini <intent-filter>
gerektiren birkaç etkinlik ve bir alıcı vardır .android:exported="true"
AndroidManifest.xml
Bekleyen Niyet Değişkenliği
Uygulamanız Android 12'yi hedefliyorsa, uygulamanızın oluşturduğu her PendingIntent
nesnenin değişebilirliğini belirtmeniz gerekir. Bu ek gereksinim, uygulamanızın güvenliğini artırır.
Bu daha aldatıcı çünkü gerçek kodu değiştirmemizi gerektiriyor (proje yapılandırması veya kitaplığın yükseltilmiş bir sürümünü kopyalamanın aksine).
Bir nesne her oluşturulduğunda, açıkça veya PendingIntent
öğesini belirtmesi gerekir . Önceki SDK sürümlerinde, hiçbir bayrak belirtilmediyse varsayılan değerdi. nesneler, sınıfta bir dizi statik yöntemle oluşturulur: , , veya . Bu işlevlerin çağrılarını arayarak başlayabiliriz.FLAG_MUTABLE
FLAG_IMMUTABLE
FLAG_MUTABLE
PendingIntent
getActivity
getActivities
getBroadcast
getService
$ grep -r -E "PendingIntent;->(getActivity|getActivities|getBroadcast|getService)" smali
smali/android/support/v4/f/a/ac.smali: invoke-static {p1, v2, v0, v2}, Landroid/app/PendingIntent;->getBroadcast(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;
smali/com/google/firebase/iid/r.smali: invoke-static {p0, p1, v0, p4}, Landroid/app/PendingIntent;->getBroadcast(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;
smali/com/google/firebase/iid/m.smali: invoke-static {p0, v2, v0, v3}, Landroid/app/PendingIntent;->getBroadcast(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;
smali/com/google/firebase/messaging/c.smali: invoke-static {v0, v2, v1, v3}, Landroid/app/PendingIntent;->getActivity(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;
smali/com/google/android/gms/common/m.smali: invoke-static {p1, p3, v0, v1}, Landroid/app/PendingIntent;->getActivity(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;
smali/com/google/android/gms/common/api/GoogleApiActivity.smali: invoke-static {p0, v0, v1, v2}, Landroid/app/PendingIntent;->getActivity(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;
smali/com/google/android/gms/c/cbx.smali: invoke-static {v1, v2, v0, v3}, Landroid/app/PendingIntent;->getActivity(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;
smali/com/google/android/gms/c/cbx.smali: invoke-static {v2, v7, v1, v7}, Landroid/app/PendingIntent;->getBroadcast(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;
smali/com/google/android/gms/c/v.smali: invoke-static {v0, v1, v2, v3}, Landroid/app/PendingIntent;->getActivity(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;
smali/com/google/android/gms/c/bj.smali: invoke-static {v1, p2, v0, v2}, Landroid/app/PendingIntent;->getActivity(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;
smali/com/google/android/gms/c/byd.smali: invoke-static {v1, v4, v0, v4}, Landroid/app/PendingIntent;->getBroadcast(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;
smali/com/google/android/gms/c/mr.smali: invoke-static {v1, v3, v0, v3}, Landroid/app/PendingIntent;->getBroadcast(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;
Smali'yi Anlamak
Bayt kodu talimatı , invoke-static
statik işleve parametre olarak iletilmek üzere bir kayıt listesi alır. Landroid/app/PendingIntent;->getBroadcast(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;
Statik işlevin sembolü , tam nitelikli sınıf adından ve işlevin imzasından doğrudan bir çeviri gibi görünür . Sınıf adıyla Landroid/app/PendingIntent;
(veya android.app.PendingIntent
normal Java sözdiziminde) başlar. Ardından işlevin adı ( ->getBroadcast
), parametreler ve dönüş tipi ile birlikte. Landroid/content/Context;ILandroid/content/Intent;I
dört parametreye bölünebilen parametrelerdir: Landroid/content/Context;
( android.content.Context
), I
( int
), Landroid/content/Intent;
( android.content.Intent
) ve I
( int
). Son olarak, kapanış parantezinden sonra dönüş türü: Landroid/app/PendingIntent;
.
Bu nedenle, yukarıdaki işlevin ilk olarak , ilk olarak ve olarak invoke-static {v1, v2, v3, v4}
geçecektir . Bu API'ler için , her zaman son parametredir ( ), bu nedenle değerin her zaman ya da ayarlı olduğundan emin olmamız yeterlidir . Android SDK belgeleri , is ve değerinin olduğunu ortaya koymaktadır . Çoğu durumda, son parametre, sabit bir değerle ( veya gibi ) başlatılan yerel bir değişken kaydı ( ) olarak belirtilir. Bu durumlarda, olup olmadığını önemsiz bir şekilde kontrol edebiliriz .v1
Context
v2
int
v3
Intent
v4
int
PendingIntent
flags
int
FLAG_MUTABLE
FLAG_IMMUTABLE
FLAG_MUTABLE
0x02000000
FLAG_IMMUTABLE
0x04000000
v#
const/high16 v3, 0x8000000
const/4 v4, 0x0
FLAG_MUTABLE
FLAG_IMMUTABLE
ayarlandı ve değilse sabiti güncelleyin.
- const/high16 v3, 0x8000000
+ const/high16 v3, 0xA000000
invoke-static {v1, v2, v0, v3}, Landroid/app/PendingIntent;->getActivity(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;
# you may need to change from const/4 to const/high16 to specify the flag
# const/4 is a loading a signed 4-bit integer (seen used to load 0x0).
# const/high16 loads the high 16-bits from a value (the low 16-bits must be 0)
- const/4 v4, 0x0
+ const/high16 v4, 0x2000000
.method private static a(Landroid/content/Context;ILjava/lang/String;Landroid/content/Intent;I)Landroid/app/PendingIntent;
.locals 2
new-instance v0, Landroid/content/Intent;
const-class v1, Lcom/google/firebase/iid/FirebaseInstanceIdInternalReceiver;
invoke-direct {v0, p0, v1}, Landroid/content/Intent;-><init>(Landroid/content/Context;Ljava/lang/Class;)V
invoke-virtual {v0, p2}, Landroid/content/Intent;->setAction(Ljava/lang/String;)Landroid/content/Intent;
const-string v1, "wrapped_intent"
invoke-virtual {v0, v1, p3}, Landroid/content/Intent;->putExtra(Ljava/lang/String;Landroid/os/Parcelable;)Landroid/content/Intent;
invoke-static {p0, p1, v0, p4}, Landroid/app/PendingIntent;->getBroadcast(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;
move-result-object v0
return-object v0
.end method
if (p4 & (FLAG_IMMUTABLE | FLAG_MUTABLE) == 0) {
p4 |= FLAG_MUTABLE;
}
const/high16 v3, 0x6000000 # v3 = FLAG_IMMUTABLE | FLAG_MUTABLE
and-int v2, p4, v3 # v2 = p4 & v3
if-nez v2, :cond_0 # if (v2 != 0) { goto :cond_0; }
const/high16 v3, 0x2000000 # v3 = FLAG_MUTABLE
or-int p4, p4, v3 # p4 = p4 | v3
:cond_0
Dosya sistemi izin değişiklikleri
Özel dosyaların dosya izinleri artık dosya sahibi tarafından gevşetilmemelidir ve MODE_WORLD_READABLE
ve/veya kullanarak bunu yapmaya çalışmak MODE_WORLD_WRITEABLE
bir SecurityException
.
NET'te kullanılan bazı SharedPreferences
API kullanımları MODE_WORLD_READABLE
vardı com/google/android/gms/ads/identifier/AdvertisingIdClient.smali
. ( )' den ( ) MODE_WORLD_READABLE
' ye geçiş meselesi olduğu için bunu düzeltmesi çok basitti .0x1
MODE_PRIVATE
0x0
--- a/smali/com/google/android/gms/ads/identifier/AdvertisingIdClient.smali
+++ b/smali/com/google/android/gms/ads/identifier/AdvertisingIdClient.smali
@@ -93,7 +93,7 @@
const-string v4, "google_ads_flags"
- const/4 v5, 0x1
+ const/4 v5, 0x0
invoke-virtual {v2, v4, v5}, Landroid/content/Context;->getSharedPreferences(Ljava/lang/String;I)Landroid/content/SharedPreferences;
Android 6.0 ile Apache HTTP istemcisi desteğini kaldırdık. Android 9'dan başlayarak, bu kitaplık önyükleme sınıfı yolundan kaldırılmıştır ve varsayılan olarak uygulamalar tarafından kullanılamaz.
NY Waterway, Apache HTTP istemcisinin Android sürümünü kullanıyordu, ancak bunun düzeltmesi oldukça basit - AndroidManifest.xml
.
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 1490d73..39ccbf3 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -16,6 +16,7 @@
<permission android:name="co.bytemark.nywaterway.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
<uses-permission android:name="co.bytemark.nywaterway.permission.C2D_MESSAGE"/>
<application android:allowBackup="false" android:icon="@drawable/icon" android:label="@string/app_name" android:name="co.bytemark.nywaterway2.core.NYWWApp" android:theme="@style/AppTheme">
+ <uses-library android:name="org.apache.http.legacy" android:required="false" />
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/>
<receiver android:exported="false" android:label="NetworkConnection" android:name="co.bytemark.android.sdk.BytemarkSDK$ConnectionChangeReceiver">
<intent-filter>
Uygulamanız Android 9 veya üstünü hedefliyorsa, isCleartextTrafficPermitted()
yöntem false
varsayılan olarak geri döner. Uygulamanızın belirli alanlar için açık metni etkinleştirmesi gerekiyorsa , uygulamanızın Ağ Güvenliği Yapılandırmasında bu alanlar için açıkça ayarlamanız cleartextTrafficPermitted
gerekir .true
Bu yeni güvenlik özelliği nedeniyle ağ istekleri başarısız oluyordu. Uygulamayı uyumlu hale getirmenin en basit yolu , özniteliği AndroidManifest.xml
eklemek için yapılan başka bir değişiklikti.android:usesCleartextTraffic="true"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 39ccbf3..69b4aa7 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -15,7 +15,7 @@
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<permission android:name="co.bytemark.nywaterway.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
<uses-permission android:name="co.bytemark.nywaterway.permission.C2D_MESSAGE"/>
- <application android:allowBackup="false" android:icon="@drawable/icon" android:label="@string/app_name" android:name="co.bytemark.nywaterway2.core.NYWWApp" android:theme="@style/AppTheme">
+ <application android:allowBackup="false" android:icon="@drawable/icon" android:label="@string/app_name" android:name="co.bytemark.nywaterway2.core.NYWWApp" android:theme="@style/AppTheme" android:usesCleartextTraffic="true">
<uses-library android:name="org.apache.http.legacy" android:required="false" />
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/>
<receiver android:exported="false" android:label="NetworkConnection" android:name="co.bytemark.android.sdk.BytemarkSDK$ConnectionChangeReceiver">
Yukarıdaki tüm değişiklikleri yaptıktan sonra, uygulama, Android'in eski bir sürümü için oluşturulduğuna dair rahatsız edici bir açılır pencere olmadan başarıyla çalışır!
Biraz beklenmedik bir şekilde, daha yeni hedef SDK sürümüyle çalışmasını sağlamak, 64-bit sorununu fiilen düzeltmekten çok daha karmaşıktı, ancak günün sonunda, her şey sadece kod ve koddan korkacak bir şey yok...
Bağlanmak İster misiniz? Twitter veya LinkedIn'de bana bir mesaj gönderin !