Awalnya saya cuma ingin memahami kenapa tahun lalu gagal melakukan akuisisi data WhatsApp di Android yang memakai Android 12 (non-root), tapi ternyata eksplorasinya malah mendalam ke bagaimana sistem backup di Android bekerja. Saya melakukan berbagai eksperimen dan juga langsung membaca source code Android untuk memahami berbagai detail mengenai backup data di Android.
Dalam tulisan ini saya akan menuliskan nomor versi Android yang dikenal user (misalnya Android 6.0) dan kadang mencantumkan API Levelnya jika memang relevan (misalnya: Android 6.0 memiliki API Level 23). Menurut saya versi Android ini lebih mudah dipahami dibandingkan API Level yang biasanya hanya dikenal oleh developer.
Contents
Latar Belakang
Untuk keperluan tertentu kadang kita ingin mengekstrak data dari sebuah aplikasi Android. Beberapa contoh: untuk keperluan forensik, untuk recovery data, atau untuk riset security.
Sebuah aplikasi bisa menyimpan data di lokasi yang dengan mudah diakses karena berada di lokasi publik. Contoh lokasinya: galeri foto, direktori Downloads, dan SD Card. Informasi yang disimpan di sini sifatnya publik dan untuk bisa dikonsumsi aplikasi lain. Contoh: media (foto, video, file) dari aplikasi chat seperti WhatsApp bisa disimpan di direktori publik.
Sebuah aplikasi juga bisa menyimpan data di lokasi yang sifatnya privat. Hal ini tentunya masuk akal: banyak aplikasi tidak ingin datanya mudah diekstrak dan atau dimodifikasi. Isi (teks) chat WhatsApp disimpan di direktori privat ini.
Beberapa aplikasi juga menyimpan data di cloud yang kadang bisa mudah diakses (misalnya di Google Drive) dan kadang tidak (misalnya di server khusus milik pembuat aplikasi). Tulisan ini tidak membahas lokasi di cloud.
Baik data yang sifatnya publik ataupun privat, keduanya perlu dipastikan tidak hilang, jadi biasany perlu bisa dibackup. Sebuah aplikasi bisa membuat sistem backup sendiri, atau memakai fasilitas dari sistem. Android sudah menyediakan sistem backup otomatis yang terhubung ke akun user. Kebanyakan aplikasi memakai sistem backup dari Android karena tidak perlu coding ekstra.
Sistem backup bawaan Android sudah berkembang banyak dari sejak versi Android pertama hingga versi terbaru. Perkembangan ini yang akan dibahas di tulisan ini.
Perkembangan terbaru adalah adanya fasilitas Device to Device transfer, yang memungkinkan data ditransfer ke ponsel baru, tanpa melewati komputer ataupun layanan cloud. Karena sistem ini tidak melewati sistem komputer ataupun cloud, data yang ditransfer bisa lebih banyak.
Sebuah sistem backup membuat salinan data, termasuk dari direktori privat, maka ini bisa dimanfaatkan untuk akuisisi data dari ponsel. Tentunya tidak semua data akan bisa disalin, ini akan dijelaskan lebih lanjut.
Rooting
Sebuah perangkat Android bisa diroot untuk mendapatkan akses ke semua data baik publik maupun privat yang ada di device tersebut. Sebagai catatan: untuk mengakses data sebenarnya akses root ini tidak selalu dibutuhkan, misalnya sebuah bug di boot loader juga bisa dipakai untuk ektraksi data. Bug-bug lain kadang juga bisa dipakai mengakses sembarang data meski tidak memiliki akses root.
Jika ponsel bisa diroot (atau ada bug yang bisa dieksploitasi) maka ini cara terbaik untuk untuk forensic data acquisition, karena isi keseluruhan ponsel bisa dicopy.
Untuk pembahasan berikutnya diasumsikan bahwa ponsel Android tidak diroot. Jadi untuk berbagai teks, misalnya dijelaskan “hanya bisa diakses aplikasi itu sendiri”, artinya: bisa diakses aplikasi itu sendiri, atau oleh user/aplikasi yang memiliki akses root.
Dengan fakta bahwa semua data bisa diakses oleh root, beberapa aplikasi menambahkan enkripsi ekstra untuk mempersulit membaca data privat yang diekstrak dengan akses root. Beberapa enkripsi ini sangat aman dan kadang sifatnya hanya sekedar menyulitkan saya karena keynya ada di aplikasi jika kita reverse engineer.
Sebelum ada yang bertanya: kenapa repot melakukan adb backup
atau teknik lain jika ekstraksi bisa dilakukan dengan rooting? jawabanya: tidak semua device bisa diroot, tidak semua device memiliki bug (misalnya baru diupdate dengan OS terbaru). Mungkin jika ditunggu sekian hari, bulan atau sekian tahun baru akan ada bug di versi Android tersebut untuk rooting atau ekstraksi data, tapi kadang ekstraksi data dibutuhkan segera.
Software forensik semacam Cellebrite, Oxygen, dsb memiliki koleksi eksploit yang meliputi banyak ponsel terbaru, tapi tetap tidak semua ponsel bisa dieksploitasi. Ponsel keluaran terbaru dengan software update terbaru biasanya tidak didukung sampai update berikutnya.
Beberapa device mudah diroot dengan mengunlock bootloadernya, tapi ketika bootloader diunlock, biasanya semua data saat ini akan diwipe. Kadang untuk device tertentu ada bug sehingga bisa diunlock tanpa menghapus data. Tapi sekali lagi: tidak semua device ada bugnya di waktu proses ekstraksi dilakukan.
Akuisisi data ini kadang dilakukan secara sukarela, misalnya karena masalah hardware (contoh: screen pecah) dan ingin datanya ditransfer ke device baru. Dalam kasus ini user bisa dengan mudah diminta user/password untuk proses backup/transfer data. Di kasus lain (misalnya kasus kriminal), hal ini kadang tidak bisa dilakukan, dan proses backup/transfer tidak bisa dilakukan.
AndroidManifest.xml
File AndroidManifest.xml adalah file XML yang berisi informasi aplikasi Android dan ada pada setiap APK. Ketika membuat aplikasi Android, file ini adalah file XML teks biasa, ketika dipackage ke dalam APK, XML ini diubah menjadi binary XML. Isi file ini berisi berbagai informasi mengenai aplikasi, seperti: nama aplikasi, nama package, versi aplikasi, lokasi file ikon, dsb. AndroidManifest.xml juga berisi informasi yang dibutuhkan sistem untuk melakukan backup aplikasi.
File manifest ini bisa dilihat dengan menggunakan jadx
, apktool
ataupun tool lain. Di tulisan ini, fokus pembahasan adalah pada elemen <application>
. Di dalam bagian application ini ada beberapa atribut yang berhubungan dengan backup yang akan dibahas lebih detail di bagian berikutnya.
Attribut backup yang akan dibahas meliputi:
- android:allowBackup
- android:fullBackupContent
- android:fullBackupOnly
- android:debuggable
- android:backupAgent
- android:backupInForeground
- android:dataExtractionRules
- android:hasFragileUserData
- android:killAfterRestore
- android:restoreAnyVersion
Contoh tag application
seperti ini:
<application
android:theme="@style/Theme.App"
android:label="@string/app_name"
android:icon="@drawable/icon"
android:debuggable="true"
android:name="com.whatsapp.App">
ROM Custom
Android sifatnya open source, ini berarti kita bisa membaca langsung kode berbagai komponennya. Tapi karena sifatnya yang open source ini, banyak pihak yang mengcustomize sistemnya atau membuat sendiri sistem pesaing. Misalnya untuk transfer dari device ke device, Samsung memiliki SmartSwitch dan Mi memiliki Mi Mover yang mekanismenya berbeda dari Android standar.
Di tulisan ini saya mengasumsikan Android yang standard yang dipakai oleh berbagai vendor mainstream dan juga di emulator Android. Saya mengasumsikan tidak ada kustomisasi oleh vendor yang menyimpang dari source code Android Open Source Project (AOSP).
Lokasi data aplikasi Android
Secara praktis, saat ini lokasi file data private Android ada di /data/data/nama.package
dan hanya bisa diakses oleh aplikasi itu sendiri. Aplikasi juga bisa menshare file privatnya dengan mekanisme spesifik Android (Content Provider) ke proses lain. Lokasi default in bisa berganti jika fitur Adoptable Storage digunakan (ini ada sejak Android 6.0), atau lokasinya bisa diubah oleh pembuat custom ROM.
Supaya aplikasi portabel, jangan mengasumsikan nama direktori tersebut. Aplikasi seharusnya memakai nama direktori yang dikembalikan oleh Context.getFilesDir()
. Aplikasi juga bisa langsung menggunakan Context.openFileOutput()
yang akan menyimpan file di tempat yang sama. Pada Android yang umum, saat ini lokasi file ada di subdirektori files
Aplikasi juga bisa membuat database SQLite dengan Context.openOrCreateDatabase()
dan lokasinya dikembalikan oleh Context.getDatabasepath()
. Secara praktis, saat ini lokasi database ada di subdirektori databases
.
Aplikasi bisa saja menyimpan file konfigurasi sendiri dengan API java.io.File
, tapi Android sudah menyediakan SharedPreferences
yang memudahkan menyimpan key dan value pada sebuah XML. SharedPreferences
ini defaultnya akan disimpan dalam XML yang sifatnya plaintext (tidak dienkrip), di subdirektori shared_prefs
Direktori files
, databases
dan shared_prefs
biasanya adalah yang penting dalam akuisisi data. Ada juga direktori lain seperti cache
(Context.getCacheDir()
) dan code_cache
(Context.GetCodeCacheDir())
. Ada juga file-file yang tidak untuk dibackup: no_backup
(Context.getNoBackupFilesDir()
) karena biasanya memang tidak penting.
Seperti dibahas sebelumnya: lokasi file juga bisa ada di direktori publik. Ini di luar scope pembahasan karena mudah diakses dengan berbagai cara.
Backup Agent
Sebuah aplikasi bisa mendefinisikan sebuah kelas yang menjadi Backup Agent dengan meng-extend kelas BackupAgent dan menuliskan nama kelasnya di AndroidManifest.xml
. BackupAgent ini akan diinstansiasi dan dipanggil ketika Android melakukan backup. Jenis backup yang dilakukan ini dinamakan Key-Value Backup.
Sebuah aplikasi yang mengimplementasikan Backup Agent bisa secara selektif memilih file apa atau preferences apa yang ingin dibackup. Aplikasi juga bisa melakukan enkripsi ketika membackup dan mendekrip ketika restore. kelas BackupAgent ini sudah ada sejak Android 2.2.
Karena tidak semua pembuat aplikasi mau repot mengimplementasikan BackupAgent, banyak aplikasi lama yang tidak bisa dengan mudah dibackup dan dipindah ke Android lain. Sejak Android 6.0 ada fitur Auto Backup. Fitur ini akan mengupload data aplikasi ke Google Drive. Semua data umum akan dibackup dengan fitur ini.
Sejak versi Android versi 6.0 ada tambahan attribut: android:fullBackupOnly
. Defaultnya adalah false
. Jika android:fullBackupOnly
diset menjadi true
maka BackupAgent tidak dipanggil dan aplikasi akan memakai Auto Backup.
Limit penyimpanan auto backup hanya 25 Mb. Agar fleksibel dan tidak makan banyak space, fitur auto backup memiliki atribut fullBackupContent
yang berisi lokasi XML yang berisi informasi file apa yang disertakan atau diabaikan dalam backup. Jika diisi dengan false
, maka semua diabaikan. Ini berlaku untuk Android 11 atau sebelumnya.
Sebagai catatan: limit 25 Mb hanya untuk backup otomatis. Aplikasi bisa meminta koneksi langsung ke Google Drive untuk backup data yang lebih besar. Akan ada dialog ekstra untuk melakukan koneksi ini (contohnya adalah pada fitur backup WhatsApp ke Google Drive).
sedangkan di Android 12 (dan setelahnya) fullBackupContent
diganti dengan atribut android:dataExtractionRules
, yang sekarang memiliki dua jenis rule: untuk backup ke cloud dan device to device transfer.
Metode Eksplorasi
Saya menggunakan berbagai device yang saya miliki plus emulator untuk memastikan bahwa behavior yang saya lihat di source code sesuai dengan kenyataan. Untuk pengujian, saya membuat aplikasi kecil Android dengan MainActivity yang singkat (kurang dari 100 baris), hanya seperti ini:
package com.tinyhack.backuptest;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SharedPreferences sharedPref = this.getSharedPreferences(
"MyPref", Context.MODE_PRIVATE);
int count = sharedPref.getInt("Count", 0);
TextView count_text = (TextView) findViewById(R.id.count_text);
count_text.setText("Count: " + count);
Button b = (Button)findViewById(R.id.button_add);
b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SharedPreferences sharedPref = getSharedPreferences(
"MyPref", Context.MODE_PRIVATE);
int count = sharedPref.getInt("Count", 0) + 1;
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt("Count", count);
editor.apply();
TextView count_text = (TextView) findViewById(R.id.count_text);
count_text.setText("Count: " + count);
try {
FileWriter writer = new FileWriter(getFilesDir() + "/myfile.txt", false);
writer.write("The count is " + count);
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
FileWriter writer = new FileWriter(getFilesDir() + "/anotherfile.txt", false);
writer.write("The count is also " + count);
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
Source code di atas melakukan beberapa hal:
- Menyimpan ke SharedPreferences
- Menyimpan 2 file (ini untuk sekedar eksplorasi, misalnya ingin membackup satu file, tapi mengabaikan file yang lain).
Lalu saya juga membuat backup agent sederhana. Ini bisa diaktifkan dan dinonaktifkan dari AndroidManifest.xml
package com.tinyhack.backuptest;
import android.app.backup.BackupAgentHelper;
import android.app.backup.FileBackupHelper;
import android.util.Log;
public class MyBackupAgent extends BackupAgentHelper {
@Override // android.app.backup.BackupAgent
public void onCreate() {
Log.d("TinyhackBackupTokenAgentHelper", "onCreate");
addHelper("file_to_backup", new FileBackupHelper(this, "anotherfile.txt"));
}
}
Lalu berikutnya saya mengubah-ubah file AndroidManifest.xml
dimulai dari tanpa atribut backup apapun (tanpa allowBackup
tanpa backupAgent
) dsb. Lalu mulai ditambah satu per satu, dan setiap kali saya membuat APK debug dan release. Selain itu saya juga mengubah versi aplikasi untuk menguji apakah akan ada komplain downgrade dari Android. Saya juga mengubah-ubah targetSDK karena sifat backup berubah dengan perubahan targetSDK.
Karena masih punya disk space yang cukup besar dan punya internet cepat, saya mendownload berbagai versi Android dengan petunjuk dari sini:
https://source.android.com/docs/setup/download/downloading
Untuk menghemat disk space mendownload berbagai versi Android, saya memakai filesystem btrfs dan memakai fitur subvolumenya.
adb backup
Perintah adb backup
ini sudah tersedia sejak lama untuk membackup data aplikasi Android ke komputer. Sebagai informasi: adb backup sekarang sudah deprecated meski masih disupport di versi terbaru Android 13.
Jika sebuah aplikasi memiliki attribut allowBackup=false
maka datanya tidak akan dibackup. Defaultnya jika aplikasi tidak memiliki atribut allowBackup
maka dianggap true
. Banyak aplikasi Android lama yang tidak memiliki atribut ini dan dianggap sebagai boleh dibackup.
Trik yang biasa digunakan untuk mengambil data dari aplikasi dengan allowBackup=false
adalah dengan menginstall aplikasi versi lama — yang ditandatangi dengan signature yang sama — yang masih memiliki allowBackup=true
atau tidak memiliki allowBackup
sama sekali.
Setelah downgrade selesai, segera lakukan adb backup
tanpa menjalankan aplikasi versi lama tersebut. Jika aplikasi lama dijalankan mungkin bingung dengan data yang ada dan mungkin merusak data yang ada. Lebih bijaksana juga jika notifikasi dibersihkan dan sementara masuk ke Airplane Mode agar aplikasi tidak otomatis jalan karena kita mengklik notifikasi.
Ada beberapa situs yang mengarsipkan versi lama APK berbagai aplikasi yang pernah terbit di Play store (walau sering kurang lengkap untuk aplikasi yang kurang populer). Harap berhati-hati karena APK dari situs tidak terpercaya bisa disisipi malware.
Mulai Android 11, allowBackup=true
akan diabaikan untuk backup Device to Device jika target SDKnya adalah Android 11 ke atas. APK lama yang menargetkan API level lama masih dihormati allowBackup
-nya, jadi selama ini belum diubah, teknik downgrade masih bisa dipakai.
Android 14 (saat ini belum dirilis, masih beta) akan mulai memblok APK lama agar tidak bisa diinstall via device Android langsung, tapi tetap bisa diinstall via adb dengan flag tertentu. Android mendatang mungkin akan memblok sepenuhnya APK lama.
Sebagai catatan, untuk mendowngrade aplikasi ada beberapa langkah: kadang kita bisa langsung melakukan adb install -r app.apk
(flag -r artinya . Tapi ini bisa gagal tergantung Aplikasi dan OS yang dipakai (misalnya bisa ada error gagal downgrade).
Langkah yang selalu berhasil adalah seperti ini:
- Uninstall versi lama, tapi tetap pertahankan datanya dengan command line:
adb shell pm uninstall -k namapackage
(jangan sampai terlewat parameter-k
untuk keep data) - Restart (kadang ini tidak dibutuhkan, tapi di Android yang baru, kita akan mendapatkan pesan bahwa kita mendowngrade aplikasi jika tidak restart dulu)
- Install versi baru dengan
adb install -r app.apk
(pastikan data tidak terhapus dengan-r
)
Restart dibutuhkan jika mengalami error ini ketika menginstall versi lama.
Cara kerja adb backup
Mari kita lihat bagaimana adb backup
ini bekerja. Pertama kita menjalankan adb backup <namapackage>
di komputer, perintah ini akan dikirimkan ke adbd
(adb daemon) di Android oleh adb client. Jadi kita lihat dulu source adb client ini di:
packages/modules/adb/client/commandline.cpp
Terlihat bahwa perintah yang dikirimkan ke adbd
adalah backup:
diikuti oleh parameter yang diescape.
Di sisi Android ini akan diproses oleh daemon, ini bisa dilihat di: packages/modules/adb/daemon/services.cpp
Terlihat bahwa ini akan menjalankan program /system/bin/bu backup
. Jika kita lihat file bu
di frameworks/base/cmds/bu
, terlihat bahwa ini hanyalah shell script. Isinya memanggil program Java.
Isi file ini tidak singkat, jadi tidak akan ditampilkan di sini. File Backup.java
ini ada di:
frameworks/base/cmds/busrc/com/android/commands/bu/Backup.java
Ada method doBackup
yang memproses segala parameter yang diberikan dari adb client. Untuk saat ini perhatikan parameter -all
yang akan mengaktifkan semua opsi dan -keyvalue
yang akan mengaktifkan Key Value Backup (memanggil BackupAgent).
Tapi inti backupnya tidak dilakukan di sini. Yang dilakukan Backup.java hanya melakukan koneksi ke service backup. Pertama koneksi ke service backup dilakukan
mBackupManager
= IBackupManager.Stub.asInterface(ServiceManager.getService("backup"));
Lalu method adbBackup
dipanggil dengan semua parameter hasil parsing command line:
mBackupManager.adbBackup(userId, fd, saveApks, saveObbs, saveShared, doWidgets, doEverything,
155 allIncludesSystem, doCompress, doKeyValue, packages.toArray(packArray));
Servicenya diimplementasikan di frameworks/base/services/backup/
, Source code utama ada di direktori:
frameworks/base/services/backup/java/com/android/server/backup
Isi filenya ada banyak, jika kita berfokus pada adbBackup
ini ada di BackupManagerService.java
dan ini akan memanggil adbBackup
di UserBackupManagerService.java
yang akan meminta password dan konfirmasi dari user, lalu flownya diteruskan di java/com/android/server/backup/internal/BackupHandler.java
Bisa dilihat bahwa ini akan menginstansiasi thread PerformAdbBackupTask
. Lokasi file ini di:
frameworks/base/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
Struktur header backup dijelaskan di sini dan juga bagaimana enkripsi backup dilakukan jika kita mengeset password backup.
Jika -keyvalue
atau -all
disertakan, maka proses backup akan memanggil BackupAgent
pada sebuah APK, jika tidak maka BackupAgent akan diabaikan.
Setelah itu backup dilakukan oleh kelas FullBackupEngine
. Isinya
Perhatikan salah satu field memakai kelas BackupEligibilityRules
, ini baru diperkenalkan di 2020. Aturan mengenai apakah backup diperkenankan atau tidak untuk sebuah package dikumpulkan di kelas tersebut:
frameworks/base/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
Di Android 12 ada dua sifat baru yang dikendalikan oleh flag berikut di awal file. Ini hanya dienable jika targetSdkVersion
sebuah package sesuai dengan yang dituliskan (Version Code S adalah Android 12).
Perhatikan bagian ini (Android 12+) yang menyatakan bahwa: khusus untuk D2D transfer, maka allowBackup akan diabaikan, semua aplikasi dipaksa pindah jika menargetkan SDK Android 12.
Nanti bagian ini akan dibahas lagi ketika membahas bmgr
, sekarang fokusnya adalah di bagian ADB_BACKUP
. Untuk versi lama, maka allowBackup
ini yang akan dihormati nilainya. Untuk versi baru, hanya aplikasi tertentu (core app atau privileged) yang akan dibackup. Selain itu hanya aplikasi yang bisa didebug yang akan dibackup.
bmgr
Tool bmgr
sudah ada sejak android 2.2, dan gunanya adalah untuk melakukan backup dan restore secara manual. Kenapa saya mencoba memahami lebih lanjut tool ini? adb backup
sudah deprecated sejak 2019, walau belum ada tanda-tanda akan dihapus dari Android 14, tapi jika adb backup
dihapus, kita perlu memikirkan alternatifnya. Flow backup bmgr
ini berbeda dari adb backup
.
Tool bmgr
ini jarang dipakai selain oleh developer karena hasil backupnya tidak bisa kita lihat/akses langsung (ad adi direktori private). Perintah ini berguna untuk eksperimen fitur backup tanpa harus bersusah payah memakai dua ponsel atau mengupload data ke cloud. Contoh penggunaan bmgr bisa dilihat di dokumentasi resmi Android di sini.
Sama seperti bu
isi bmgr
hanyalah skrip yang menjalankan kode Java. Kali ini kode utamanya ada di com.android.commands.bmgr.Bmgr
ini terletak di:
base/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
Perintah yang berguna adalah bmgr backupnow <namapackage>
yang akan melakukan backup terhadap sebuah package. Tapi sebelumnya kita perlu mengeset dulu backup ini mau dikemanakan (menggunakan Transport apa).
Pertama kita bisa melihat daftar transport yang tersedia dengan:
bmgr list transports
Catatan: asumsinya perintah di atas dilakukan dalam adb shell
bisa juga kita ketikkan adb shell bmgr list transports
dari command line di komputer. Saya hanya ingin mempersingkat tidak menambahkan adb shell
di depan setiap perintah.
Transport yang aktif saat ini diberi tanda asterisk (*) di depannya.
crownlte:/ $ bmgr list transports
* com.android.localtransport/.LocalTransport
com.google.android.gms/.backup.migrate.service.D2dTransport
com.google.android.gms/.backup.BackupTransportService
Untuk memilih transport, gunakan bmgr transport <namatransport>
Gunakan bmgr init <namatransport>
untuk menginisialiasi transport.
Perhatikan bahwa source code untuk com.google.android.gms
tidak tersedia, karena merupakan komponen komersial milik Google. Jika ingin melihat isi file ini, kita bisa melakukan reverse engineering dengan berbagai tool yang ada.
Sifat backup akan bergantung pada jenis transport yang dipakai:
LocalTransport
akan membackup ke dalam Android itu sendiri (hanya untuk testing), hasilnya bisa dilihat di/data/data/com.android.localtransport
jika kita punya akses root, kita bisa memeriksa isi direktori.- D2D transport digunakan untuk transfer ke device lain (tidak dibatasi ukurannya)
- BackupTransportService digunakan untuk backup ke Google Drive (max hanya 25Mb)
Di emulator, dan di device android yang sudah diroot, kita bisa melakukan testing D2D tanpa perlu device lain. Dengan mengikuti petunjuk dari halaman resmi, kita bisa melakukan testing ini:
- Dengan LocalTransport: Seperti apa data aplikasi yang akan diupload ke Google
- Dengan D2D plus setting
backup_enable_d2d_test_mode
: Seperti apa data aplikasi yang akan ditransfer dengan D2D transfer
Flow bmgr
berbeda dengan adb backup
yang memanggil fungsi adbBackup
dari BackupManager
, bmgr
memanggil requestBackupForUser
. Tapi akhirnya keduanya akan sampai ke handler di BackupHander.java
hanya di bagian yang berbeda:
Di sini KeyValueBackup task akan selalu dilakukan (BackupAgent akan selalu dipanggil). Perbedaan dengan adb backup
ada di transport yang digunakan, adb backup
selalu mengoutputkan hasil backup ke file, sedangkan bmgr
akan mengirimkan hasil backup sesuai Transport yang terpilih.
Device to Device Transfer
Seperti dibahas sebelumnya: device to device transfer akan mengabaikan allowBackup untuk aplikasi yang menargetkan Android 12, artinya aplikasi yang sudah menggunakan allowBackup=false
masih bisa ditransfer ke device lain. Tapi aplikasi yang menggunakan BackupAgent
, full-backup-content
, dan data-extraction-rules
akan tetap bisa membatasi data apa yang ditransfer.
Tidak mudah untuk mencoba device to device transfer untuk melihat data apa yang akan tertranser. Kita butuh 2 ponsel, dan ponsel yang menjadi target harus diwipe dulu datanya.
Dengan mode test D2D (secure backup_enable_d2d_test_mode
), kita bisa pura-pura melakukan transfer antar device. Data tidak ditransfer ke device lain tapi sekedar disimpan di file. Jika device tidak diroot, kita tidak bisa melihat datanya, hanya bisa melihat apakah datanya kembali atau tidak ketika kita melakukan proses restore.
Percobaan ini akan berguna jika dilakukan di device yang sudah diroot. Jadi intinya: lakukan percobaan dengan aplikasi yang menjadi target, lalu lihat hasilnya (dengan akses root), apakah data yang kita inginkan ada di dalam backupnya. Data ini seharusnya ada di:
/data/data/com.google.android.gms/files/d2d_restore_data
Jika data tidak ditemukan, maka percuma melakukan D2D transfer. Jika ternyata data ada, maka kemungkinan bisa diekstrak, langkah berikutnya adalah melakukan D2D yang sesungguhnya. Langkahnya:
- Siapkan ponsel yang ingin diakses datanya (tidak diroot)
- Siapkan Ponsel target (yang sudah diroot atau sudah diunlock bootloadernya untuk diroot kemudian)
- Transfer data dari device yang tidak diroot ke device yang diroot/bisa diroot.
- Ekstraksi data dari ponsel target sebagai root
Saya berharap di masa depan ada yang membuat device yang secara otomatis menjadi target D2D dengan berpura-pura menjadi ponsel lain.
Studi kasus: WhatsApp
Bagian ini tidak berisi detail ekstraksi data WhatsApp sampai bisa dibaca isi chatnya. Ini mungkin akan saya bahas di artikel terpisah jika banyak yang tertarik. Bagian ini hanya berisi ekstraksi data mentah saja.
Selain menggunakan akses root, ekstraksi WhatsApp bisa dilakukan dengan mendownload backup di cloud. Jika root tidak tersedia dan chat tidak dibackup ke cloud, maka biasanya alternatifnya adalah melakukan downgrade. WhatsApp versi 2.11.431
memiliki tag application yang “kosong”, yang artinya: allowBackup
dianggap true
, tidak ada backupAgent
, dan tidak ada rule apapun yang membatasi backup.
<application android:theme="@style/Theme.App" android:label="@string/app_name" android:icon="@drawable/icon" android:name="com.whatsapp.App">
Bandingkan dengan WhatsApp terbaru:
<application android:theme="@style/(name removed)_res_0x7f1403f3" android:label="@string/res_0x7f122252_name_removed" android:icon="@mipmap/icon" android:name="com.whatsapp.AppShell" android:backupAgent="com.whatsapp.registration.backuptoken.BackupTokenAgentHelper" android:allowBackup="true" android:vmSafeMode="@bool/res_0x7f050004_name_removed" android:restoreAnyVersion="true" android:hardwareAccelerated="@bool/res_0x7f050009_name_removed" android:supportsRtl="true" android:fullBackupOnly="false" android:extractNativeLibs="true" android:fullBackupContent="false" android:networkSecurityConfig="@xml/res_0x7f160006_name_removed" android:appComponentFactory="androidx.core.app.CoreComponentFactory" android:hasFragileUserData="true" android:requestLegacyExternalStorage="true" android:preserveLegacyExternalStorage="true">
fullBackupContent
diset falsebackupAgent
diset, danfullBackupOnly
disetfalse
jadi agent akan selalu dipanggilallowBackup
diset true, tapi percuma karena atribut yang lain akan membatasi backuprestoreAnyVersion
diset true, bisa direstore dari versi manapunhasFragileUserData
: artinya ketika diuninstall, data akan ditanya apakah dihapus atau tidak. Defaultnya adalah false (akan dihapus ketika diuninstall secara manual, bukan ketika uninstall dengan adb)
Sekarang mari kita lihat Backup Agent di WhatsApp terbaru. Agent ini mengoverride onBackup
dan onRestore
, tapi isinya dikosongkan dan tidak memanggil parent method. Jadi operasi backup dan restore menjadi no-op
alias tidak melakukan apapun.
Sebagai catatan: jika ingin bereksperimen, silakan edit implementasi BackupAgent yang saya berikan di awal tulisan ini untuk melihat efek dari melakukan override terhadap onBackup
dan onRestore
.
WhatsApp juga memiliki enkripsi ekstra pada databasenya. Sudah ada yang melakukan reverse engineering terhadap enkripsi ini, namun formatnya sering berubah. Jadi setelah file diekstrak, masih ada perlu langkah lain untuk mendekrip filenya.
Peringatan allowBackup=false untuk developer
Developer yang kurang mengikuti perkembangan Android mungkin masih akan memakai allowBackup=false
saja di AndroidManifest, dan sudah mengupdate target SDK ke 31 atau lebih baru.
Ini berbahaya karena konten backup bisa diakses dengan D2D transfer. Sebaiknya gunakan dataExtractionRules
untuk membatasi data mana yang boleh dan tidak boleh dibackup. Ini penting terutama untuk aplikasi sensitif seperti banking. Biasanya aplikasi banking tidak ingin ada data apapun yang ditransfer, tapi harus aktifasi ulang di ponsel baru menggunakan SMS, call atau bahkan harus ke ATM untuk mendapatkan kode aktivasi.
Sebagai catatan: mengubah target SDK ke versi lebih baru/lebih tinggi akan membuat aplikasi tetap jalan di Android lama asalkan minSDKVersion
tetap diset ke versi yang rendah. Jika ada library yang tidak mau digunakan, kadang Android Studio akan menyarankan untuk mengupdate/menaikkan target SDKnya.
Untuk para pentester, silakan dicek lagi targetSDKVersion dan apakaah atribut dataExtractionRules
sudah digunakan. Atribut allowBackup
tetap terpakai untuk mencegah backup di versi Android sebelum 12.
Atribut yang lain
Dari teks di atas, atribut yang belum dibahas ada 2 yaitu android:backupInForeground
yang mengijinkan backup tanpa meng-kill aplikasi dan android:killAfterRestore
yang menyatakan apakah setelah proses restore aplikasi perlu dikill dulu (defaultnya: “true”). Dari sudut pandang security, kedua atribut ini tidak terlalu penting.
Penutup
Awalnya saya hanya penasaran tentang sistem backup Android, tapi ternyata topik ini menarik dan bisa dieksplorasi sampai dalam. Masih banyak detail yang tidak dibahas di sini, tapi jika tertarik, ya silakan eksplorasi sendiri.
Sampai sekarang saya masih tidak tahu penyebab kegagalan ekstraksi data dengan tool professional tahun lalu, karena saya sudah melakukan semua aksi sesuai seharusnya. Kemungkinan ada bug di tool yang saya pakai.