Pages

Wednesday, January 8, 2020

Usagestats on Android 10 (Q)

UsageStats

If you are unfamiliar with this artifact, Alex Brignoni explains the UserStats artifact in the blog post here. Located at /data/system/usagestats/ this information can be useful in cases. Up until Android 9 (Pie), this was in XML format, however since Android 10(Q), it is now in a different format. So the tool written by Alex didn't work out for me or my students investigating this artifact a couple of months back.

The file name has the same format (unix millisecond time as integer) and below you can see what the new data looks like.

Figure 1 - File 1572840777639 - raw hex view (complete file not shown)
It appeared to be some sort of binary format, but without a standard consistent header (after I compared a few files). Taking a cue from fellow DFIR researchers (Sarah Edwards and Phill Moore), I tested if this was a protocol buffer. If you aren't familiar with a Protocol Buffer, read these posts from Sarah and Phill. This is a google creation, and as they describe it - ...a language-neutral, platform-neutral extensible mechanism for serializing structured data.

To test for protocol buffer presence (on windows), you will need to download protoc.exe from here. Run protoc.exe as shown below. Here 1572840777639 is the filename. If you got output, its a protobuf.

W:\usagestats\0\daily>protoc --decode_raw < 1572840777639
1: 1862148
3: 1
4: 1
2 {
  1: 74
  2: "com.google.android.youtube"
  2: "com.google.android.ext.services"
  2: "com.android.providers.telephony"
  2: "com.android.dynsystem"
  2: "com.android.settings.CryptKeeper"
...
...output snipped...
...
22 {
  2: 23
  4: 60
  5: 1249881
  7: 23
  14: 92830887
  15: 23
  16: 60
}

OK, so we got some decoded json data back. But it still did not look like anything we are used to seeing (see XML below).
Figure 2 - XML usagestats snippet
The way protocol buffers work, you need a .proto file that defines the structure and data types of the data contained in the buffer. So to decode this, we need the .proto file!

Since Android is open source, so why not peek at the source code of AOSP? To avoid downloading the entire source code, just browse the aosp-mirror on github.
Figure 3 - aosp source code on github

After a bit of searching, we find the file we are looking for at:
platform_frameworks_base/core/proto/android/server/usagestatsservice.proto

Figure 4 - usagestatsservice.proto file snippet
As seen above, the file references other .proto files too. So we must get those too, and any dependencies in those as well (recursively). We eventually end up with 7 files:

  • usagestatsservice.proto
  • configuration.proto
  • privacy.proto
  • locale.proto
  • rect.proto
  • protobuf_descriptor.proto
  • window_configuration.proto

Next, we need to transform (google says compile) our .proto files into python libraries. Use protoc.exe to do so. The syntax is :

protoc -I=$SRC_DIR --python_out=$DST_DIR $SRC_DIR/your_proto_file.proto

Do this for every .proto file. It will generate a .py file for each one. For example, the usagestatsservice.proto compiles to usagestatsservice_pb2.py. Now all that remains is to use these generated python files to read our raw protocol buffer from file. We will need to write some code to do so.

Peeking into the usagestatsservice.proto file, you get some idea of how this might work. I constructed a basic python script to read this (below).
import usagestatsservice_pb2
input_path = "W:\\usagestats\\0\\daily\\1572840777639" stats = usagestatsservice_pb2.IntervalStatsProto() with open (input_path, 'rb'as f:     stats.ParseFromString(f.read())     # GET PACKAGES     for usagestat in stats.packages:         print('package = '+ stats.stringpool.strings[usagestat.package_index - 1])         print(usagestat)
# GET CONFIGURATIONS for conf in stats.configurations:         print(conf)
# GET EVENT LOGS for event in stats.event_log:         print(event)

You can check for the existence of a field using the HasField() function. So here is what a package object consists of:
package = com.android.settings
package_index: 58
last_time_active_ms: 663647
total_time_active_ms: 4897
app_launch_count: 3
last_time_service_used_ms: -1572840673324
last_time_visible_ms: 673237
total_time_visible_ms: 25221
A configuration object consists of:
config {
  font_scale: 1.0
  locales {
    language: "en"
    country: "US"
  }
  screen_layout: 268435794
  color_mode: 5
  touchscreen: 3
  keyboard: 2
  keyboard_hidden: 1
  hard_keyboard_hidden: 1
  navigation: 1
  navigation_hidden: 2
  orientation: 1
  screen_width_dp: 411
  screen_height_dp: 659
  smallest_screen_width_dp: 411
  density_dpi: 560
  window_configuration {
    app_bounds {
      right: 1440
      bottom: 2392
    }
    windowing_mode: 1
    bounds {
      right: 1440
      bottom: 2560
    }
  }
}
last_time_active_ms: 662163
total_time_active_ms: 37
count: 1

An event log object contains:
package = com.google.android.apps.nexuslauncher
class = com.google.android.apps.nexuslauncher.NexusLauncherActivity
task root package = com.google.android.apps.nexuslauncher
task root class = com.google.android.apps.nexuslauncher.NexusLauncherActivity
type = MOVE_TO_FOREGROUND
time_ms: 34440
So now, our protobuf parsed and file read and interpreted successfully! That's it for now. On to the next artifact..