Ich könnte es dir beschreiben ... ALEAPP Live Data Aufbereitung

In den Zeiten der dateibasierten Verschlüsselungen sind vollständige Dateisystemextraktionen der Goldstandard einer Datenbasis für die Analyse von mobilen Endgeräten.
App- und System-Daten, Logs, Medien und idealerweise noch die Keychain erlauben eine vollumfängliche Analyse eines Android-Smartphones (Multi-User Szenarien klammern wir bei dieser Betrachtung einmal aus).
Was aber, wenn für ein aktuelles Gerät trotz bekannter Zugangsdaten derzeit keine Dateisystemsicherung verfügbar ist? Seit jeher liefern sich die Android/iOS-Entwickler und Anbieter von Forensik-Software ein Katz-und-Maus-Spiel. Häufig vergeht eine gewisse Zeit, bis für ein neues Modell ein "FFS" angeboten wird.
Viele kommerzielle Tools bieten für diese Fälle eine "erweiterte logische Sicherung" an. Also eine Sammlung der Daten, die das Gerät freiwillig preisgibt. Diese Sicherung ist meist weit weniger umfangreich und beinhaltet z.B. nur einen Bruchteil der verfügbaren App-Daten. Dennoch kann je nach Zielsetzung der Untersuchung auch diese Art der Extraktion ihre Berechtigung haben. Meine eigene Abwandlung einer erweiterten logischen Sicherung für UFADE und ALEX habe ich übrigens PRFS ("Partially Restored File System") getauft.
Über den Umfang tatsächlich verfügbarer Dateien (als Kopie aus dem Gerätedateisystem) hinaus, bietet Android einige Boardmittel, über die sich relevante Informationen vom System abfragen lassen, ohne dass die Quelldateien direkt abrufbar sind. Das Bash-Tool Android Triage von Mattia Epifani automatisiert einige dieser Abfragen und bietet dabei eine Dialog-basierte Oberfläche für die Kommandozeile. Dieses Script stand auch Pate für einige der Funktionen, die ich in ALEX integriert habe.
So beinhaltet ein PRFS Backup eines Android-Geräts (in der Default Einstellung) neben dem Nutzer zugänglichen Dateien (Ordner "Dump") auch live abgefragte Inhalte (Ordner "Extra"), die so nicht im Dateisystem zu finden sind. Aktuell werden dabei die folgenden ADB Befehle genutzt:
| Befehl | Datei in der Sicherung |
|---|---|
adb shell logcat -d -b all -v epoch | extra\logcat.txt |
adb shell dumpsys | extra\dumpsys_{unix_timestamp}.txt |
adb shell appops get {app} | extra\app_ops.json |
adb shell content query --uri content://{key} | extra\content_provider\* |
Beabsichtigt ist natürlich, dass diese Befehle als Debugging-Hilfen zur Laufzeit genutzt werden. Im Rahmen der PRFS-Sicherung ist aber eine spätere Analyse vorgesehen. Eine Hürde dabei: relative Zeitstempel.
Zeit ist relativ
Über ALEX werden bei der Abfrage der App Ops (später mehr) bereits absolute Zeitstempel erfasst. Dies geschieht unter Abfrage der Gerätezeit (adb shell date +%s) und liefert entsprechend "nur" eine sekundengenaue Darstellung. Über den Zusatz "-v epoch" beinhalten die Logcat-Einträge ebenfalls bereits UNIX-Timestamps. Anders sieht es bei den Dumpsys Einträgen aus. Hier unterscheidet sich die Darstellung zudem bei verschiedenen Android-Versionen. Um auch hier eine Zeitbasis für eine spätere Analyse zu schaffen, wird der (Geräte-)Zeitstempel der Abfrage mit im Dateinamen des Dumpsys-Outputs festgehalten.
Aufbereitung
Da kommerzielle Analysetools aktuell nur wenig mit den Live-Daten einer ALEX-Extraktion anfangen können, habe ich einen Parser für das wahrscheinlich umfangreichste Open Source Android-Analyseframework ALEAPP erstellt und plane diesen regelmäßig zu aktualisieren.

Zum Teil sind die Live-Informationen bereits gut lesbar strukturiert und werden über ALEAPP nur noch durchsuchbar dargestellt. Andere Inhalte benötigten jedoch ein wenig Recherchearbeit. Die bisher verfügbaren Aufbereitungen sollen folgend kurz vorgestellt werden:
App Ops
Hier wird festgehalten, welche App oder Systemkomponente welche Berechtigung wann erhalten hat oder wann ihr diese verweigert wurde. In einer Dateisystemsicherung wären diese Informationen unter /system/appops.xml zu finden.

Logcat
Die Log-Einträge, die mit Logcat ausgegeben werden reichen maximal bis zum letzten Neustart des Geräts zurück und sollten entsprechend unmittelbar extrahiert werden, wenn hier relevante Informationen vermutet werden. Hier sind z.T. Benachrichtigungen zu finen, die dem Nutzer angezeigt werden oder allgemein Hinweise über die Dauer der Nutzung von Anwendungen.

Dumpsys
Über Dumpsys lassen sich zahlreiche relevante Informationen abrufen, für die sonst eine Dateisystemsicherung erforderlich wäre. Die bisher erstellten Parser-Funktionen erheben keinen Anspruch auf Vollständigkeit. Dies könnte ein ausgezeichnetes Thema für eine wissenschaftliche Ausarbeitung sein, falls hier noch jemand auf der Suche ist.

Ob eine App in den Vordergrund geholt oder beendet wurde, wird unter dem Service Usagestats erfasst.

Die Jahreswerte der Nutzungszeiten von Apps lassen Rückschlüsse auf besonders relevante Anwendungen zu.

Unter "Role" werden Standardanwendungen z.B. für Mail, Telefonie oder Browsing erfasst.

"Configured Networks" werden wenigstens mit der SSID angegeben. Welche Informationen darüe hinaus verfügbar sind, hängt stark von der Android-Version ab.

Teilweise finden sich hier neben dem Google-Konto auch User-IDs zu Chat und Social-Media Accounts.

Diese Informationen sind nur verfügbar, wenn bei der Abfrage des Service Bluetooth am Gerät aktiv war. Ob MAC-Adressen vollständig ausgegeben werden ist wieder Versions-/Hersteller-abhängig.

Der Companiondevice Service scheint nur bei aktuelleren Android-Versionen verfügbar zu sein. Hier findet sich z.B. eine gekoppelte Smartwatch und die dazugehörige App.
Dumpsys - Batterystats
Die Batterystats sind zuletzt hinzugekommen und erforderten ein wenig mehr an Einarbeitung. Daher möchte ich hierauf ein wenig genauer eingehen.

Zunächst konnten zwei verschiedene Zeitdarstellungen beobachtet werden. Ältere Android Versionen (4-14) zeigten ein relatives Zeitformat:
DUMP OF SERVICE batterystats:
Battery History (98% used, 4039KB used of 4096KB, 320 strings using 32KB):
0 (14) TIME: 2025-12-16-15-26-57
0 (2) 070 c0200040 status=discharging health=good ...
+212ms (1) 070 80200040 -wake_lock
...
+3d01h15m11s323ms (2) 043 80000020 +running wake_reason=0:"179 SPM"
...
Mit TIME: wird ein Referenzpunkt angegeben, von dem aus dann relativ weitergezählt wird (Angabe in Tagen, Stunden, Minuten, Sekunden und Millisekunden)
Daneben wurde auf einem Android 16 Gerät ein neues Zeitformat mit absoluten Angaben (jedoch ohne Jahreszahl) verwendet:
DUMP OF SERVICE batterystats:
Battery History [Format: 2] (101% used, 4156KB used of 4096KB, 11476 strings using 689KB):
03-17 09:59:37.666 076 80001a18 status=discharging health=good ...
03-17 09:59:37.711 076 02001a18 -running +mobile_radio -cellular_high_tx_power
03-17 09:59:38.536 076 82001a18 +running wake_reason=0:"335 cp2ap_wakeup"
...
Da die Erfassungszeit im Dateinamen festgehalten wurde und Einträge nicht älter als ein Jahr sind (je nach Nutzung wenige Tage bis Wochen, in Ausnahmen Monate). Konnte schlicht festgehalten werden, dass Einträge, die vor dem Erfassungszeitpunkt liegen dem aktuellen Jahr zuzuordnen sind und Einträge, die schinbar in der Zukunft liegen aus dem vorangegangenen Jahr stammen müssen.
Darüber hinaus fanden sich Untimmigkeiten im alten Format. Bei einigen Geräten lagen die letzten Einträge scheinbar Monate zurück, obwohl die Sicherung erst wenige Minuten zuvor erfolgt war. Es stellte sich heraus, dass nach jedem Neustart des Gerätes ein neuer TIME: Referenzzeitpunkt erstellt wurde, die relative Zeitangabe aber unbeirrt weiterzählte, als sei zwischen Herunterfahren und Neustart des Geräts keine Zeit vergangen. Nach der Ergänzung eines Offsets für solche Neustart-Events ließen sich die Zeiten bis zur Sicherung lückenlos nachvollziehen.
Der Zeitangabe folgt der Ladezustand des Akkus. So steht 076 für eine Akkuladung von 76%.
Die folgende Hexadezimal-Angabe ist das eigentliche Kernstück der Batterystats, da sich hieraus diverse Gerätezustände ableiten lassen. Hierbei handelt es sich um die Hexadezimaldarstellung einer Bitmaske.
| 80 | 00 | 1a | 18 | |
|---|---|---|---|---|
| binär | 1 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 | 0 0 0 1 1 0 1 0 | 0 0 0 1 1 0 0 0 |
Für die Bestimmung der STATE Einträge sind die vorderen 16 Bit der Maske entscheidend:
| 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| binär | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Die folgenden 16 Bit betreffen Statusänderungen.
Die Zuordnung der STATE-Einträge zu den einzelnen Statusbits kann direkt aus dem Android-Sourcecode (BatteryStats.java) abgeleitet werden:
public static final int STATE_CPU_RUNNING_FLAG = 1<<31;
public static final int STATE_WAKE_LOCK_FLAG = 1<<30;
public static final int STATE_GPS_ON_FLAG = 1<<29;
public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<28;
public static final int STATE_WIFI_SCAN_FLAG = 1<<27;
public static final int STATE_WIFI_RADIO_ACTIVE_FLAG = 1<<26;
public static final int STATE_MOBILE_RADIO_ACTIVE_FLAG = 1<<25;
private static final int STATE_RESERVED_0 = 1<<24;
public static final int STATE_SENSOR_ON_FLAG = 1<<23;
public static final int STATE_AUDIO_ON_FLAG = 1<<22;
public static final int STATE_PHONE_SCANNING_FLAG = 1<<21;
public static final int STATE_SCREEN_ON_FLAG = 1<<20; // consider moving to states2
public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<19; // consider moving to states2
public static final int STATE_SCREEN_DOZE_FLAG = 1 << 18;
// empty slot
public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<16;
Das Beispiel 80001a18 kurz nach dem Gerätestart zeigt demnach nur auf, dass die CPU aktuell aktiv ist.
Natürlich hat sich diese Zuordnung im Laufe der Zeit entwickelt und verschiedene Android-Versionen nutzen eine leicht abgewandelte Zuordnung der Batterystats. Daher habe ich im Rahmen der Recherche über ein Skript die STATE-Flags der verschiedenen Android-Haptversionen extrahiert, gegenübergestellt und das folgende Dictionary angelegt, über das ein Mapping erfolgen kann:
| Bit | Android 4 | Android 5 | Android 6 - 8 | Android 9 und darüber |
|---|---|---|---|---|
| 31 | CPU_RUNNING | CPU_RUNNING | CPU_RUNNING | |
| 30 | WAKE_LOCK | WAKE_LOCK | WAKE_LOCK | WAKE_LOCK |
| 29 | SENSOR_ON | GPS_ON | GPS_ON | GPS_ON |
| 28 | GPS_ON | WIFI_FULL_LOCK | WIFI_FULL_LOCK | WIFI_FULL_LOCK |
| 27 | PHONE_SCANNING | WIFI_SCAN | WIFI_SCAN | WIFI_SCAN |
| 26 | WIFI_RUNNING | WIFI_MULTICAST_ON | WIFI_RADIO_ACTIVE | WIFI_RADIO_ACTIVE |
| 25 | WIFI_FULL_LOCK | MOBILE_RADIO_ACTIVE | MOBILE_RADIO_ACTIVE | MOBILE_RADIO_ACTIVE |
| 24 | WIFI_SCAN_LOCK | |||
| 23 | WIFI_MULTICAST_ON | SENSOR_ON | SENSOR_ON | SENSOR_ON |
| 22 | AUDIO_ON | AUDIO_ON | AUDIO_ON | AUDIO_ON |
| 21 | VIDEO_ON | VIDEO_ON | VIDEO_ON | VIDEO_ON |
| 20 | SCREEN_ON | SCREEN_ON | SCREEN_ON | SCREEN_ON |
| 19 | BATTERY_PLUGGED | BATTERY_PLUGGED | BATTERY_PLUGGED | BATTERY_PLUGGED |
| 18 | PHONE_IN_CALL | PHONE_IN_CALL | SCREEN_DOZE | |
| 17 | WIFI_ON | |||
| 16 | BLUETOOTH_ON | BLUETOOTH_ON | WIFI_MULTICAST_ON | WIFI_MULTICAST_ON |
Zusammengefasst: Bis Android 3 konnten die Batterystats STATE Einträge in dieser Form nicht vorgefunden werden. Von Version 4 über 5 und 6 fanden Änderungen an der Zuordnung statt, die bis Android 9 Bestand hatten. Aktuell findet noch immer das Schema Anwendung, welches mit Android 9 eingeführt wurde. In den Batterystats selbst findet sich der Hinweis auf die verwendete Android-Version übrigens nicht. Passenderweise wird diese Information aber ebenfalls von ALEX in einem PRFS Backup vorgehalten.
Für die batterystats-daily.xml einer Full Filesystem Sicherung hat kürzlich Marco Neumann einen ALEAPP-Parser gebaut. In seinem Code fand ich den Hinweis auf die Definitionen im Android Sourcecode.
Ich werde wohl noch eine ganze Weile beschäftigt sein, ALEAPP den Output von ALEX schmackhaft zu machen. Mit den Contentprovider-Daten habe ich noch gar nicht angefangen und auch Dumpsys beinhaltet noch Informationen, die bisher keine Berücksichtigung fanden.
Über Hinweise oder Anregungen zu den Parsern freue ich mich auf den einschlägigen Kanälen.
