iOS Artifacts

MapsSync_0.0.1: Testing the Device Location Inside Apple Maps Route Requests

Validated4n6·

MapsSync_0.0.1 stores Apple Maps search and route history on iOS. Most of it tells you what a user looked up, not where the phone was. One field is different. When a user requests a route, MapsSync_0.0.1 can record the device's own position, a timestamp and an accuracy value inside a protobuf BLOB.

We tested that BLOB against a known timeline and against Cache.sqlite. The device location matched to the last decimal place.

Key findings

  • The protobuf in ZROUTEREQUESTSTORAGE holds a device latitude, longitude, timestamp and horizontal accuracy. All four matched a Cache.sqlite record exactly.
  • The same protobuf holds the destination coordinates. They matched the destination selected on the device.
  • The protobuf timestamp is not the row's ZCREATETIME. In our test it was 8 seconds earlier. Use the protobuf timestamp for the location.
  • A row with no BLOB is a search. It says nothing about where the device was.
  • Nothing in this database shows the device drove the route or reached the destination.

What was already known

This test builds on published work. Cellebrite's 2025 location booklet lists three points about the ZHISTORYITEM table:

  • ZROUTEREQUESTSTORAGE is a protobuf BLOB with the start and end of a requested route.
  • The start is the device's real location when node 1/0/3/100 holds a value of 1.
  • If ZROUTEREQUESTSTORAGE is empty, treat the row as a search and not a visit.

What was missing was a node-by-node map of the protobuf and a check of its values against a second source. That's what this test adds.

One scope note: MapsSync_0.0.1 belongs to the native Apple Maps app. It holds nothing from Google Maps, Waze or other third-party apps.

Test setup

ItemDetail
DeviceApple iPhone 16 Pro
iOS version18.3
Database/private/var/mobile/Containers/Shared/AppGroup/[GUID]/Maps/MapsSync_0.0.1
Comparison source/private/var/mobile/Library/Caches/com.apple.routined/Cache.sqlite, table ZRTCLLOCATIONMO
Test date and place13 Aug 2025, Columbia, SC, Eastern Daylight Time (UTC-4)
ExtractionFull File System, Cellebrite Inseyets UFED 10.6.0.377, SHA256 of the zip verified
Analysis tools7-Zip, DB Browser for SQLite, Mushy (protobuf viewer)

Actions on the device

Times are local (UTC-4) and read off a clock by hand, so allow 1 to 2 seconds of error.

TimeAction
13:03:07Maps opened
13:04:00Searched "Chick-fil-a"
13:04:27Selected the Harbison location (294 Harbison Blvd, Columbia, SC 29212)
13:04:48Started navigation
13:05:03Ended navigation
13:05:26Maps closed in the App Switcher
13:05:46Maps opened again. Chick-fil-a was still selected.
13:06:19Cleared the Chick-fil-a selection
13:06:36Cleared the search bar
13:07:01Searched 5623 Two Notch Rd and selected it from the list
13:07:25Cleared the search without starting navigation
13:07:30Maps closed in the App Switcher
13:08:54Maps opened
13:09:29Searched 1931 Pineview and selected it from the list
13:09:47Cleared the selection
13:10:10Started typing 1701 Main St. Never selected a result or pressed Search.
13:10:42Maps closed in the App Switcher

What MapsSync_0.0.1 recorded

Seventeen actions produced four rows in ZHISTORYITEM.

ZHISTORYITEM rows with raw Mac Absolute timestamps
Fields of interest from ZHISTORYITEM, raw values.
ZHISTORYITEM rows with timestamps converted to UTC-4
The same rows with timestamps converted to UTC-4.
Z_PKZCREATETIMEZMODIFICATIONTIMEOther dataBLOBMatches action
113:04:0413:04:04ZQUERY = Chick-fil-A
ZLOCATIONDISPLAY = Columbia
No13:04:00 search
213:04:2713:04:47NoneYes13:04:27 selection, 13:04:48 navigation start
313:07:0013:07:00ZLATITUDE1 = 34.053193
ZLONGITUDE1 = -80.9806143
ZMAPITEMLASTREFRESHED = 13:07:00
No13:07:01 Two Notch Rd search
413:09:2913:09:29NoneYes13:09:29 Pineview search and selection

Timestamps

ZCREATETIME, ZMODIFICATIONTIME and ZMAPITEMLASTREFRESHED are Mac Absolute timestamps stored in UTC. Each one landed within a few seconds of the logged action.

Row 2 is the useful one. ZCREATETIME lines up with selecting the destination. ZMODIFICATIONTIME lines up with starting navigation, one second off the logged time. In row 4 the two values are identical, and that route was requested but never started. Two records is a thin basis for a rule, so treat "modification time equals navigation start" as a lead to test, not a finding.

This query gives you the same view with UTC output:

SELECT
          Z_PK,
          datetime(ZCREATETIME + 978307200, 'unixepoch') AS create_utc,
          datetime(ZMODIFICATIONTIME + 978307200, 'unixepoch') AS modified_utc,
          datetime(ZMAPITEMLASTREFRESHED + 978307200, 'unixepoch') AS mapitem_refreshed_utc,
          ZLATITUDE1,
          ZLONGITUDE1,
          ZLOCATIONDISPLAY,
          ZQUERY,
          ZROUTEREQUESTSTORAGE
      FROM ZHISTORYITEM
      ORDER BY ZCREATETIME;

Inside the ZROUTEREQUESTSTORAGE protobuf

The BLOB from row 2 was copied out and opened in Mushy. The top level has four nodes. Everything of interest sits under node 1, which splits into two branches:

  • 1, 0 is the route start, which here was the device location.
  • 1, 1 is the destination.
Protobuf node 1 expanded to show branches 0 and 1
Node 1 expanded. Branch 0 is the start, branch 1 is the destination.

Device location nodes

Node pathContentValue in row 2
1, 0, 9, 1, 1Device latitude34.0587641220565
1, 0, 9, 1, 2Device longitude-80.9487052735735
1, 0, 9, 3Timestamp (Mac Absolute, UTC)776797459.647788
2025-08-13 17:04:19 UTC
1, 0, 9, 5Horizontal accuracy (meters)18.3073625129036

Some of these values repeat elsewhere in the protobuf. The paths above are the ones we tested.

Protobuf nodes showing device latitude, longitude, timestamp and horizontal accuracy
Device latitude, longitude, timestamp and horizontal accuracy under node 1, 0, 9.

Destination nodes

Node pathContentValue in row 2
1, 1, 9, 1Destination latitude34.0733068
1, 1, 9, 2Destination longitude-81.1562814

The destination branch carries coordinates only. No timestamp, no accuracy. In both BLOBs the coordinates matched the destination selected during the test.

Protobuf nodes showing destination latitude and longitude
Destination latitude and longitude under node 1, 1, 9.

Validating against Cache.sqlite

Cache.sqlite, and its ZRTCLLOCATIONMO table in particular, is a well-tested source of iOS device location. We looked for a record with the same timestamp as the protobuf and found one.

Matching Cache.sqlite record from ZRTCLLOCATIONMO
The matching ZRTCLLOCATIONMO record in Cache.sqlite.
SourceTimestamp (UTC-4)LatitudeLongitudeHorizontal accuracy
MapsSync_0.0.12025-08-13 13:04:1934.0587641220565-80.948705273573518.3073625129036
Cache.sqlite2025-08-13 13:04:1934.0587641220565-80.948705273573518.3073625129036

Every digit matches. We repeated the check with row 4, the only other row with a BLOB. It matched its Cache.sqlite record exactly too.

A match to 13 decimal places isn't two sensors agreeing. It points to Maps writing the same location fix that routined cached. That matters for two reasons. The MapsSync value is as good as the Cache.sqlite value. And Cache.sqlite ages out quickly, so a MapsSync_0.0.1 record may still give you a validated device position after the Cache.sqlite record is gone. How long MapsSync rows persist was not part of this test.

The timestamp gap you need to know about

The protobuf timestamp did not match either row timestamp:

ValueTime (UTC-4)What it marks
Protobuf node 1, 0, 9, 313:04:19When the location fix was taken
ZCREATETIME13:04:27Destination selected, route requested
ZMODIFICATIONTIME13:04:47Navigation started

The fix is 8 seconds older than the route request. Maps appears to use the most recent fix it already has and does not take a new one at the moment of the request. In this test the phone was stationary, so the gap changed nothing. On a moving device, or with a longer gap, the difference could matter.

Report the protobuf timestamp as the time of the location. Report ZCREATETIME as the time of the user action. Don't merge them.

What MapsSync_0.0.1 did not record

  • Typing without selecting. The 1701 Main St entry was typed but never selected or submitted. It left no row. More testing is needed before you call that a rule.
  • Travel. Navigation ran for 15 seconds and was ended. No record shows the device moved along the route or arrived. A route request is intent, not presence at the destination.
  • A BLOB for every selected address. Rows 3 and 4 came from the same kind of action: search an address, select it, clear it. Row 4 got a BLOB. Row 3 did not. We don't know why. Row 3 stored only the searched place's coordinates in ZLATITUDE1 and ZLONGITUDE1.
  • Search text for address searches. ZQUERY held "Chick-fil-A" for the business search. It was NULL for the address searches. It may hold business or place names only. That needs more testing.

How to use this in casework

  1. Query ZHISTORYITEM and separate rows with a ZROUTEREQUESTSTORAGE BLOB from rows without one.
  2. Treat rows without a BLOB as searches. ZLATITUDE1 and ZLONGITUDE1 are the searched place, not the phone.
  3. For rows with a BLOB, decode the protobuf. Pull the device location from 1, 0, 9 and the destination from 1, 1, 9.
  4. Check node 1/0/3/100 for a value of 1 before you call the start point a device location. A user can set a route start that isn't their current position.
  5. If Cache.sqlite still covers the time frame, find the matching ZRTCLLOCATIONMO record and document the match.
  6. Report the location with its own timestamp and horizontal accuracy. Report the row timestamps as user activity.

Limits of this test

  • One device, one iOS version, four database rows, two BLOBs.
  • The device was stationary the whole time.
  • Routes with a manually chosen start point were not tested, and the 1/0/3/100 flag was not examined in this round.
  • Record retention and iCloud sync behavior were not tested.

Run the same check on your own test device and iOS version before you rely on these node paths in a report.

Bottom line

When ZROUTEREQUESTSTORAGE is populated, MapsSync_0.0.1 gives you a device position, a time and an accuracy value that matched Cache.sqlite exactly in this test, plus destination coordinates that matched the route the user requested. It tells you where the phone was when the user asked for directions. It does not tell you the user went there.

Testing and original report by Michael Edwards, Digital Forensics Examiner, Richland County Sheriff's Department. Prior reference: Cellebrite, Location Booklet (2025).