• Deutsch
  • English
  • Deutsch
  • English
  • Home
  • Blog
  • UFADE

    • What is UFADE?
    • Installation
    • Connect devices
    • Navigation
    • Reporting
    • Extraction
    • Logging
    • Developer options
    • Advanced Options
    • Data Operations
  • Testpoints
  • Legal

Shall I describe it to you ...? ALEAPP Live Data Parsing

Andro-las


In the age of file-based encryption, complete file system extractions are the gold standard for analyzing mobile devices.

App and system data, logs, media, and ideally the keychain allow for a comprehensive analysis of an Android smartphone (we will exclude multi-user scenarios from this discussion for now).

But what if, despite known login credentials, no file system backup is currently available for a recent device? Android/iOS developers and forensic software providers have long been engaged in a cat-and-mouse game. It often takes some time before a “FFS” is made available for a new model.

Many commercial tools offer an “advanced logical backup” for these cases - that is, a collection of the data that the device voluntarily discloses. This backup is usually far less comprehensive and contains, for example, only a fraction of the available app data. Nevertheless, depending on the objectives of the investigation, this type of extraction can also be justified. Incidentally, I have named my own modification of an extended logical backup for UFADE and ALEX PRFS (“Partially Restored File System”).

Beyond the scope of actually available files (as copies from the device file system), Android offers several built-in tools that allow relevant information to be retrieved from the system without the source files being directly accessible. The Bash tool Android Triage by Mattia Epifani automates some of these queries and provides a dialog-based interface for the command line. This script also served as the inspiration for some of the features I integrated into ALEX.

For example, a PRFS backup of an Android device (in the default configuration) includes not only files accessible to the user (the “Dump” folder) but also live-queried content (the “Extra” folder), which cannot be found in the file system in this form. Currently, the following ADB commands are used:

CommandPath in Backup
adb shell logcat -d -b all -v epochextra\logcat.txt
adb shell dumpsysextra\dumpsys_{unix_timestamp}.txt
adb shell appops get {app}extra\app_ops.json
adb shell content query --uri content://{key}extra\content_provider\*

The intention, of course, is for these commands to be used as debugging aids at runtime. However, as part of the PRFS backup, a subsequent analysis is planned. One obstacle here: relative timestamps.

Time is relative

When querying App Ops (more on this later), ALEX already records absolute timestamps. This is done by querying the device time (adb shell date +%s) and therefore provides “only” a representation accurate to the second. With the “-v epoch” option, the Logcat entries also include UNIX timestamps. The situation is different for Dumpsys entries. Here, the display also varies across different Android versions. To establish a time reference for later analysis, the (device) timestamp of the query is included in the filename of the Dumpsys output.

Data Processing

Since commercial analytics tools currently have limited capabilities when it comes to processing the raw data from an ALEX extraction, I have created a parser for what is likely the most comprehensive open-source Android analytics framework, ALEAPP, and plan to update it regularly.

ALEAPP ALEX Options

In some cases, the live information is already structured in a way that makes it easy to read and is simply made searchable via ALEAPP. Other content, however, required a bit of research. The formats currently available are briefly described below:

App Ops

This log records which app or system component was granted or denied which permission, and when. In a file system backup, this information can be found in /system/appops.xml.

ALEAPP App Ops

Logcat

The log entries displayed by Logcat only go back as far as the device's last reboot and should therefore be extracted immediately if relevant information is suspected to be present. These entries may include notifications displayed to the user or general information about the duration of app usage.

ALEAPP Logcat

Dumpsys

Dumpsys allows you to retrieve a wide range of relevant information that would otherwise require a file system backup. The parser functions created so far are by no means exhaustive. This could be an excellent topic for a research paper, in case anyone is still looking for one.

Usagestats (Events)

Usagestats Events

Whether an app was brought to the foreground or closed is tracked under the Usagestats service.

Usagestats (Yearly)

Usagestats Yearly

The annual usage times for apps allow conclusions to be drawn about particularly relevant applications.

Role (default apps)

default apps

“Role” includes standard applications, e.g., for email, phone calls, or browsing.

Configured Networks

Configured Networks

“Configured Networks” are listed with at least the SSID. What additional information is available depends heavily on the Android version.

Accounts

Accounts

In some cases, this section includes not only the Google account but also user IDs for chat and social media accounts.

BTM Bonded Devices

Bluetooth

This information is only available if Bluetooth was active on the device when the service was queried. Whether MAC addresses are displayed in full depends on the version and manufacturer.

Companiondevice

Watch

The Companiondevice service appears to be available only on newer versions of Android. Here, for example, you can find a paired smartwatch and its corresponding app.

Dumpsys - Batterystats

The battery stats were added most recently and took a little more getting used to. That’s why I’d like to go into a bit more detail about them here.

Batterystats

Initially, two different time formats were observed. Older versions of Android (4–14) displayed a relative time format:

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

The TIME: parameter specifies a reference point from which time is counted relative to that point (specified in days, hours, minutes, seconds, and milliseconds)

In addition, a new time format using absolute values (but without the year) was used on an Android 16 device:

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

Since the capture time was recorded in the filename and entries are no older than one year (ranging from a few days to weeks, or in exceptional cases months, depending on usage), it was simply determined that entries predating the capture time should be assigned to the current year, and entries that appear to be in the future must originate from the previous year.

Furthermore, inconsistencies were found in the old format. On some devices, the last entries appeared to be months old, even though the backup had been performed only a few minutes earlier. It turned out that after each device restart, a new TIME: reference time was created, but the relative time continued to count up as if no time had passed between the shutdown and the restart of the device. After adding an offset for such reboot events, the times leading up to the backup could be traced seamlessly.

The time value is followed by the battery charge level. Thus, 076 represents a battery charge of 76%.

The following hexadecimal value is the actual core of the battery stats, as various device states can be derived from it. This is the hexadecimal representation of a bitmask.

hex80001a18
bin1 0 0 0 0 0 0 00 0 0 0 0 0 0 00 0 0 1 1 0 1 00 0 0 1 1 0 0 0

The first 16 bits of the mask are decisive for determining the STATE entries:

31302928272625242322212019181716
bin1000000000000000

The following 16 bits relate to status changes.

The mapping of the STATE entries to the individual status bits can be derived directly from the Android source code (BatteryStats.java):

		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;

The example 80001a18 shortly after the device starts up therefore simply indicates that the CPU is currently active.

Of course, this mapping has evolved over time, and different Android versions use a slightly modified mapping of the battery stats. Therefore, as part of my research, I used a script to extract the STATE flags from the various Android versions, compared them, and created the following dictionary, which can be used for mapping:

BitAndroid 4Android 5Android 6 - 8Android 9 and up
31CPU_RUNNINGCPU_RUNNINGCPU_RUNNING
30WAKE_LOCKWAKE_LOCKWAKE_LOCKWAKE_LOCK
29SENSOR_ONGPS_ONGPS_ONGPS_ON
28GPS_ONWIFI_FULL_LOCKWIFI_FULL_LOCKWIFI_FULL_LOCK
27PHONE_SCANNINGWIFI_SCANWIFI_SCANWIFI_SCAN
26WIFI_RUNNINGWIFI_MULTICAST_ONWIFI_RADIO_ACTIVEWIFI_RADIO_ACTIVE
25WIFI_FULL_LOCKMOBILE_RADIO_ACTIVEMOBILE_RADIO_ACTIVEMOBILE_RADIO_ACTIVE
24WIFI_SCAN_LOCK
23WIFI_MULTICAST_ONSENSOR_ONSENSOR_ONSENSOR_ON
22AUDIO_ONAUDIO_ONAUDIO_ONAUDIO_ON
21VIDEO_ONVIDEO_ONVIDEO_ONVIDEO_ON
20SCREEN_ONSCREEN_ONSCREEN_ONSCREEN_ON
19BATTERY_PLUGGEDBATTERY_PLUGGEDBATTERY_PLUGGEDBATTERY_PLUGGED
18PHONE_IN_CALLPHONE_IN_CALLSCREEN_DOZE
17WIFI_ON
16BLUETOOTH_ONBLUETOOTH_ONWIFI_MULTICAST_ONWIFI_MULTICAST_ON

In summary: Up until Android 3, the batterystats STATE entries were not available in this format. From version 4 through 5 and 6, changes were made to the mapping that remained in place until Android 9. Currently, the schema introduced with Android 9 is still in use. Incidentally, the battery stats themselves do not include any indication of the Android version being used. Conveniently, however, this information is also stored by ALEX in a PRFS backup.

Marco Neumann recently built an ALEAPP parser for the batterystats-daily.xml file included in a full filesystem backup. In his code, I found a reference to the definitions in the Android source code.

I’ll likely be busy for quite a while making ALEAPP compatible with ALEX’s output. I haven’t even started on the content provider data yet, and Dumpsys also contains information that hasn’t been taken into account so far.

I welcome any feedback or suggestions regarding the parsers via the relevant channels.