Forensics Tools: Stop Miscalculating iOS Usage Analytics!

There are some great forensics tools out there… and also some really crummy ones. I’ve found an incredible amount of wrong information in the often 500+ page reports some of these tools crank out, and often times the accuracy of the data is critical to one of the cases I’m assisting with at the time. Tools validation is critical to the healthy development cycle of a forensics tool, and unfortunately many companies don’t do enough of it. If investigators aren’t doing their homework to validate the information (and subsequently provide feedback to the software manufacturer), the consequences could mean an innocent person goes to jail, or a guilty one goes free. No joke. This could have happened a number of times had I not caught certain details.

Today’s reporting fail is with regards to the application “usage” information stored in iOS in the ADDataStore.sqlitedb file. At least a couple forensics tools are misreporting this data so as to be up to 26 hours or more off.

The database stored at /private/var/mobile/Library/Logs/ADDataStore.sqlitedb contains analytics on a per-24 hour period basis of application activity. The database stores a date stamp calculated as days since the Apple epoch (January 1, 1970 UTC). A disassembly of Apple’s AggregateDictionary framework shows the formula used to calculate the date stamp as the current device’s UNIX timestamp divided by 86400 (the number of seconds in a day). The decimal portion is simply truncated in the integer conversion, leaving you with a date stamp.

The time values on the usage side (to calculate state change durations) are passed in after a query to the CACurrentMediaTime function; the measurement recorded is the measurement between application state changes. Apple’s SpringBoard program, which acts much like an application launcher, tracks application state changes and records the delta between these state changes. Unfortunately, the resulting data may record multiple days of activity (especially background activity) into one single date stamp, which would be recorded for the day that the application left that state.

The ADDaysSince1970 function gets a standard time_t in UTC, not the current localized time of day. Depending on this time, the value reported to the daysSince1970 field in the database should have had a decimal value indicating the time AFTER that day started, however since the decimal portion is truncated, all that can be presumed is it started AFTER the date stamp (not the 24-hour period before).

If you load up AggregateDictionary in Hopper (a cheap and effective disassembly tool for OSX), you can see the assembly for the ADDaysSince1970 function:

_ADDaysSince1970:

3281406c 80B5 push {r7, lr}
; XREF=0x32813a64, 0x32813aac, 0x32813b98, 0x32813bec, 0x32813cb6,
; 0x32813e1e, ...
3281406e 00AF add r7, sp, #0x0
32814070 0020 movs r0, #0x0
32814072 01F0C2EF blx imp___picsymbolstub4__time
32814076 0028 cmp r0, #0x0
32814078 06DB blt 0x32814088
; Basic Block Input Regs: r0 pc - Killed Regs: r0 r2 r3
3281407a 044B ldr r3, = 0xc22e4507
3281407c 80FB0323 smull r2, r3, r0, r3
32814080 1B18 adds r3, r3, r0
32814082 C017 asrs r0, r0, #0x1f
32814084 C0EB2340 rsb r0, r0, r3, asr #16
; Basic Block Input Regs: r7 pc - Killed Regs:
32814088 80BD pop {r7, pc}
; XREF=0x32814078
; endp
3281408a 00BF nop
3281408c dd 0xc22e4507
; XREF=0x3281407a

This assembly effectively translates to the following C code:

{
  int result;
  result = time(0);
  if ( result >= 0 ) {
    result /= 86400;
  }
  return result;
}

Ironically, Apple doesn’t do any error checking themselves to see if time() failed, at which point it returns the value -1.

By reversing the calculation so as to multiply 86400 (60 seconds X 60 minutes X 24 hours), the date stamp (in UTC) can be estimated. Given that the decimal portion of the initial calculation is discarded, there is no way to determine a time of day from the dates in this file. Additionally, adjusting for local time zone, it is entirely possible that analytics were collected for 24-hour periods, each overlapping two calendar dates. The date stamp in the database is ambiguous at best, and should be assumed to have a margin of error of +/- 26 hours (the span of all time zones from UTC). But in addition to that, take into account that the forensics tool itself could also be spitting out timestamps corrected for its own time zone (instead of the device’s), or both, so it could potentially be off even more. So be aware of this if your case involves nailing down application activity to a specific date or time. The logs just aren’t accurate enough for tight tolerances. To determine an actual date and time, you need to know the exact time zone that the device was in when all activity over that time period occurred. If the subject was traveling, this makes it even more difficult to place the events within the correct context of time.

If you’re working on a case where the date of usage is critical, I would strongly recommend validating the time stamps your reporting tool is spitting out with respect to these.