Digging Deeper: Extracting Hidden GPS Data from iOS Live Location Messages
- David Tull
- Jan 27
- 2 min read
I recently encountered a fascinating artifact while working on a case. Based on the details provided by the detective, I was able to narrow my timeline to a very specific window. However, the device I was examining didn’t have much geolocation data, and Cellebrite didn’t auto-populate any results for me.
While digging through the text messages, I noticed something interesting within the timeline: a message indicating that the user had shared their location. The message read, “You sent a live location.” This caught my attention because while the location was shared, it hadn’t been parsed by the tools I was using. Naturally, I had to dig deeper. Eventually, I was able to locate the full GPS data.
In this post, I’ll walk you through the process, using Josh Hickman’s public iOS 17 image as an example.
Locating the Message: sms.db
To start, I identified the message in question within the sms.db database, specifically in the message table. One important note: the timestamps in the date column of this table are in Apple Cocoa time, but in nanoseconds.

In my case, the message was at ROWID 88. To keep things simple, I used the following SQL query to pull the specific data:
SELECT
ROWID,
text,
attributedBody,
account,
datetime('2001-01-01', (date/1000000000) || ' seconds') AS 'Date',
payload_data,
destination_caller_id
FROM
message
WHERE
ROWID IS 88
Here’s what I found:

The text in the message was just a black diamond with a question mark symbol (a placeholder for unrendered content). The real treasure was in the payload_data column, which contained a BLOB — a BPList to be specific.


Digging Into the BPList
At first glance, the BPList contained several large data fields that seemed promising, but they turned out to be generic icons with no useful information. However, near the bottom of the BPList, I found what I was looking for:
<string>?FindMyMessagePayloadVersionKey=v0&FindMyMessagePayloadZippedDataKey=
Following the second = sign was a Base64-encoded string. This was the key piece of data. In Josh Hickman’s iOS image, the string looked like this:
VZHNTsMwEITfxefEsr3+WefWUkUcOPEGVmqoRRKXxEGCqu/Opq1KuY2s9cw3uyf2kcY9a05sPoQprmK/TKGkPK46j/E5LxPJ87liaW6nPLzGzyXOhTVvoZ/j5Z0MmBLaolW6ds5vay13bY1KbuuNETuN6qltjWAVK2mgz2E4ssZpQKe9klwJ4awlozGVFPqX3N0JOoqf46briKr7Zo2oWOhLKsueYMFzShVKSYdWGFWx+Rgj0YDkAsBrA0IggnGuYoc8pZ88ltD/uWluLGqQwqGXVmhdsa84ldQ9DgFHC85JsLAK5W8x/6AeeklHQNRdWu49Sm2o2bXGZbLP4/uNn3bFpbIIGo2X6JHy+3Bvh5xcvAABUmsKfzjBHNerXS/wCw%3D%3D
If the string ends with %3D, you’ll want to delete it, as it’s sometimes added as padding during Base64 encoding.
Decoding the Base64 String
For decoding, I like using RabbitHole or CyberChef. Both tools work well, though CyberChef is especially handy since it’s free and available offline, ensuring evidence isn’t uploaded to the internet.
Once you decode the Base64 string, you might not immediately recognize the output. That’s because the data is compressed.

In RabbitHole: Select the Deflate Compression Algorithm.
In CyberChef: Use the Raw Inflate operation.
After decompressing the data, you’ll see it’s in JSON format.

In RabbitHole: You can reparse it directly as JSON.
In CyberChef: Add the JSON Beautify step to clean it up.
Here’s what the JSON looks like:


Final Thoughts
This Base64 → BPList → Compressed → JSON structure isn’t unique to this artifact. I’ve seen similar patterns across other parts of the iOS ecosystem. While this post focuses on this specific artifact, the bigger takeaway is understanding how to navigate these layers of data within iOS.
Hopefully, this walk-through helps you when you come across something similar in your investigations!
Kommentare