Like many examiners, especially those that like to do research, I have a plethora of devices that I test with, the vast majority of which are iOS devices.
I swear I don't have a problem.
I typically use my test accounts to log in to these devices to play around, but every now and then, I have to use my real account and most recently, I was using my real account on a device to access the full file system of iOS18 for testing a bunch of things in ArtEx, preparing the new build.
One of those tests happened to be the Message Intents parser of knowledgeC.
Now I’ve written about knowledgeC a LOT in the past, so I won’t beat that drum any further, but Message Intents may be unfamiliar to some people.
“Intents” are essentially a way for sandboxed applications to send data to each other via the Operating System. There can be lots of data passed as an “Intent”.
Imagine for example, clicking on a link to a location in Safari. That causes Maps to open up and load the appropriate place. That’s an “intent” passed from Safari to Maps with information about the location to open up.
And that Intent may well be logged in knowledgeC. Yay KnowledgeC :)
A long time ago, I discovered that “Message Intents” also exist, and these are Messages sent as intents for whatever reason - Notifications maybe? The reason why they exist is not too important here, but what was important was that you could recover incoming message data from knowledgeC from several different applications and ArtEx and some other tools take advantage of this which is especially useful if the original message has been deleted.
If you want to see what that looks like manually, I’ll briefly explain it here:
From knowledgeC, find a record which has the ZSTREAMNAME "/app/intents" and the ZVALUESTING "Messages".
SELECT * FROM ZOBJECT WHERE ZSTREAMNAME LIKE '/app/intents' AND ZVALUESTRING LIKE 'Messages'
This will give you the ZOBJECT record, but what we are really interested in is the data in the associated ZSTRUCTUREDMETADATA table.
The results from the query above will include a column called ZSTRUCTUREDMETADATA which is a foreign key to the ZSTRUCTUREDMETADATA table.
We can quickly join them like this:
SELECT * FROM ZOBJECT
JOIN ZSTRUCTUREDMETADATA
ON ZOBJECT.ZSTRUCTUREDMETADATA = ZSTRUCTUREDMETADATA.Z_PK
WHERE ZSTREAMNAME LIKE '/app/intents' AND ZVALUESTRING LIKE 'Messages'
Now, at this point, there will be many, many, many columns. But the one we are interested in is called Z_DKINTENTMETADATAKEY__SERIALIZEDINTERACTION.
This field is a blob; in this case, a binary plist which contains the intent data. This includes timestamps, sender, recipient and message body information, you just need to figure out which bit is which.
Even more exciting is that this Message Intent data isn’t limited to native SMS/iMessages. It can also be used to recover WhatsApp data and some limited Snapchat or Signal data too. Maybe not all applications have all message data, but some do, and that’s where this took an interesting turn.
But... What?
You see, although I was using my personal account on my test device, I had only installed a couple of apps. I was simply looking at native artefacts.
I certainly hadn’t installed WhatsApp, let alone logged into it. Which is why it caught me off guard to find over 3300 WhatsApp messages in my knowledgec database; complete with time, other party and message body data.
Now granted that some of the messages were blank; but these were messages that were either outgoing, or were attachments. But this is still important information.
It's just so bizarre that my device has messages despite that device never being used to view them.
From an evidential perspective, I figured this could work either for or against us as examiners.
It’s amazing that you have the potential to recover data from an app that isn’t, nor has ever been installed on the device!
But it’s also terrifying that the knowledgeC database we hold so dear can be contaminated by outside data.
Peer Devices
I did some more digging and found the <partial> answer to be related to the ZSOURCE column of the ZOBJECT table.
So, lets say I’m interested in this specific record.
I need to look for the ZSOURCE value, where I find a value of 3174.
I then need to go to the ZSOURCE table and find record 3174.
Here we see that the BundleID is WhatsApp and there is a value in the ZDEVICEID field.
I take that value and go to the ZSYNCPEER table where I search by ZDEVICEID.
The ZMODEL here shows as iPhone15,2 which is NOT the device I was testing but was my personal phone where I do have WhatsApp installed.
Note that there is a RapportID there too. Rapport is referenced elsewhere on iOS so I decided to see if this could be useful for finding out more information about the device and luckily, it didn’t take long.
Simply searching the device extraction, and found a file with the same name as the ZRAPPORTID value at \private\var\mobile\Library\com.apple.bluetoothuser\Production\**GUID**\CloudPairedDeviceRecords.
This file was a BPList that included a bunch of data about the sync’d device, including the device name and time of last sync.
So now I can see the messages data, the sync’d device model, name and ID. Nice.
So, what other applications may be gathering data from associated devices?
I ran a quick check against the ZSOURCE table:
SELECT DISTINCT ZBUNDLEID FROM ZSOURCE WHERE ZDEVICEID NOT LIKE ''
com.apple.InCallService
com.apple.Maps
com.apple.MobileSMS
com.apple.Music
com.apple.TVRemoteUIService
com.apple.facetimemessagestored
com.apple.mobiletimer
com.apple.weather
com.cpc.iphone
com.hammerandchisel.discord
com.linkedin.LinkedIn
com.microsoft.msedge
com.tinyspeck.chatlyio
net.whatsapp.WhatsApp
org.whispersystems.signal
us.zoom.videomeetings
Not all of these Bundles contributed something meaningful. In fact, many didn’t have anything interesting at all despite my high hopes.
Digging into this a little further, I ran the following SQL:
SELECT ZOBJECT.Z_PK, ZSTREAMNAME, Z_DKINTENTMETADATAKEY__INTENTCLASS, Z_DKINTENTMETADATAKEY__SERIALIZEDINTERACTION
FROM ZOBJECT
JOIN ZSTRUCTUREDMETADATA
ON ZOBJECT.ZSTRUCTUREDMETADATA = ZSTRUCTUREDMETADATA.Z_PK
JOIN ZSOURCE
ON ZOBJECT.ZSOURCE = ZSOURCE.Z_PK
WHERE ZDEVICEID NOT LIKE ''
Some items I did find include:
Call History between my personal device and another device.
Bluetooth Connections between my personal device and other devices (including my car and airpods).
Searches and Selections in Apple Music made from my personal device or another device associated to my account.
Alarm information from my personal device.
Apple Map Locations from my personal device.
Wrapping Up
I’m sure that there are many more apps and data types than I have listed here and it’s both good to know that this is a potential source of evidence for you, but also important to know that if you see something in knowledgeC, you may need to verify the source device.
The data you think serves as evidence on this device, may have actually occurred on a different device entirely.