Backup Android dan Akuisisi Forensik Android

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.

Dialog Backup Android

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.

Data yang dibackup oleh fitur AutoBackup

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.

commandline.cpp

Di sisi Android ini akan diproses oleh daemon, ini bisa dilihat di: packages/modules/adb/daemon/services.cpp

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).

Backup.java

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

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.

PerformAdbBackupTask.java

Jika -keyvalue atau -all disertakan, maka proses backup akan memanggil BackupAgent pada sebuah APK, jika tidak maka BackupAgent akan diabaikan.

PerformAdbBackupTask.java

Setelah itu backup dilakukan oleh kelas FullBackupEngine. Isinya

FullbBackupEngine.java

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).

BackupEligibilityRules.java

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.

BackupEligibilityRules.java

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, bmgrmemanggil requestBackupForUser. Tapi akhirnya keduanya akan sampai ke handler di BackupHander.java hanya di bagian yang berbeda:

BackupHandler.java

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:

  1. Siapkan ponsel yang ingin diakses datanya (tidak diroot)
  2. Siapkan Ponsel target (yang sudah diroot atau sudah diunlock bootloadernya untuk diroot kemudian)
  3. Transfer data dari device yang tidak diroot ke device yang diroot/bisa diroot.
  4. 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 false
  • backupAgent diset, dan fullBackupOnly diset false jadi agent akan selalu dipanggil
  • allowBackup diset true, tapi percuma karena atribut yang lain akan membatasi backup
  • restoreAnyVersion diset true, bisa direstore dari versi manapun
  • hasFragileUserData: artinya ketika diuninstall, data akan ditanya apakah dihapus atau tidak. Defaultnya adalah false (akan dihapus ketika diuninstall secara manual, bukan ketika uninstall dengan adb)
Uninstall aplikasi dengan hasFragileUserData

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.

Tinggalkan Balasan

Situs ini menggunakan Akismet untuk mengurangi spam. Pelajari bagaimana data komentar Anda diproses.