
Note: This article assumes you have read a previous article about AirTags, found here, as it references items associated with iOS searchpartyd with little to no explanation. It is highly recommend you read the previous article before proceeding.
Over the past several months I have been exploring iOS’s Search Party. It is that thing that makes Apple’s FindMy network tick, and it has taken some time to understand what is happening under the hood, and what data could be forensically available. The main obstacle has been the encryption, on which I am conflicted. As a user, I appreciate the data protection. As a practitioner, I do NOT appreciate the data protection. Last year while working on a SANS presentation, I wrestled with it for a while before I eventually figured out how to get information on unwanted AirTags (you can read about that endeavor here ).
As it turns out, there is other data available from searchpartyd, which aligns it with how Android handles Bluetooth tracker detections. For testing, I used an iPhone 14 on iOS 17.5.1, and an iPhone 11 on iOS 18.3, the latter being an update to the research last year. Note that the files described in this post were only seen in iOS 16.x and above.
Encryption. Again.
While I was working on the Wild AirTags article, I found myself wondering how iOS was keeping tabs on what FindMy-compatible devices it was seeing in the wild. After all, Apple likes to tout the number of compatible devices that are on the network when talking about the feature. Obviously, iOS had to be storing the data somewhere, and I thought it might be similar to how Android handles rogue devices . There were several databases in the same parent directory (/private/var/mobile/Library/com.apple.icloud.searchpartyd) that looked like viable candidates, but I found those files were, of course, encrypted. One of those candidates was a SQLite database named Observations.db. See Figure 1.
Looking at this file in hex revealed some interesting information. See Figure 2.
At first glance this file looks encrypted, and it is; however, there is a very subtle clue here, which I missed at first until I started looking at the other databases in the parent searchpartyd directory. The clue, highlighted in the red box in Figure 2, is that bytes 16-23 are not encrypted. These bytes are part of the SQLite header, and provide some information about a database. They represent the following (read big endian):
| Bytes | Value |
| 16 – 17 | Page Size (in bytes) |
| 18 | Format write version (1 = legacy, 2 = WAL) |
| 19 | Format read version (1 = legacy, 2 = WAL) |
| 20 | Reserved bytes at end of page (padding) |
| 21 | Max embedded payload (must be 64) |
| 22 | Min embedded payload (must be 32) |
| 23 | Leaf payload fraction (must be 32) |
Examining the other databases in the same directory path found that this behavior (and byte values) was (were) the same across all of them. I observed the following values for each database:
| Bytes | Value |
| 16 – 17 | 4096 bytes per page (x1000) |
| 18 | WAL write version (x02) |
| 19 | WAL read version (x02) |
| 20 | 12 bytes padding (x0C) |
| 21 | 64 bytes max payload (x40) |
| 22 | 32 bytes minimum payload (x20) |
| 23 | 32 bytes for Leaf payload fraction (x20) |
The bytes being the same across multiple files ruled out SQL Cipher as an encryption method. As it turns out, Observations.db (and the others) were encrypted using SQLite Encryption Extension (SEE) . The SEE spec states these bytes are not encrypted as part of the method. Further, the method operates in RC4, AES-128 (OFB or CCM mode), or AES-256 in OFB mode, and the padding is 12 bytes; in this case the padding is the initialization vector (IV or nonce) for each page in the database. Knowing this information I headed to the keychain to see what other credentials might be associated with searchpartyd. See Figure 3.
Figure 3 shows the keychain from the iPhone 14 filtered to items associated with searchpartyd (highlighted in the red box). Surprisingly, each database I saw in the searchpartyd directory (seen in Figure 1) had their own key chain entry that corresponded to their exact name. The key for Observations.db was 32 bytes in length, which meant the AES-256 OFB flavor of SEE was likely in use. Armed with the key, page size and padding, I set out to try to decrypt the data. While I am not its biggest fan, I will admit ChatGPT actually came in handy here with attempting different things before finally rendering a working database. I did not (and I still do not) trust it, so I enlisted the assistance of Ian Whiffin who found some additional shenanigans with the encryption of the -WAL files which took a while to sort out. The end result, though, was that there were decrypted databases and their associated -WAL files.
Observe
Observations.db actually behaved as I suspected: it is keeping track of all of the FindMy-compatible devices it sees (more on how this looks later). Unlike its Android equivalent, the data is scattered across three tables: ObservedAdvertisement, ObservedAdvertisementBeaconInfo, and ObservedAdvertisementLocation. To help, I created a quick SQLite query to pull the data together
SELECT
ObservedAdvertisement.scanDate AS "Seen_Time",
ObservedAdvertisementLocation.latitude AS "Latitude",
ObservedAdvertisementLocation.longitude AS "Longitude",
ObservedAdvertisementBeaconInfo.beaconIdentifier AS "Identifier",
ObservedAdvertisement.macAddress AS "MAC_Address",
ObservedAdvertisement.rssi AS "Signal_Strength",
ObservedAdvertisement.advertisementData AS "Advertised_Data"
FROM
ObservedAdvertisementLocation
LEFT JOIN ObservedAdvertisement ON ObservedAdvertisement.advId = ObservedAdvertisementLocation.advId
LEFT JOIN ObservedAdvertisementBeaconInfo ON ObservedAdvertisementBeaconInfo.advId = ObservedAdvertisementLocation.advId
ORDER BY "Seen_Time"
See Figure 4 for the completed query.

Just like Android, iOS is logging what time it sees the FindMy-compatible devices along with the location of the phone when it detected them. iOS is not lacking when it comes to sources of location data, but this is one more reliable source (these latitude and longitude values were very accurate). There is also a signal strength (RSSI) value present, which can help with understanding proximity to the device being examined. The closer the value is to 0, the stronger the signal, thus, in theory, the closer the device is to the phone; however, it is important to remember that environmental conditions such as structures, topography, people, and electromagnetic interference can all play a role in an RSSI value.
The query also displays the beacon identifier. Beacons (read as anything that participates in the FindMy network) that are owned by the iCloud account signed in on the phone or one that is shared with the account will have an identifier. Information about these UUIDs can be found in the plist files in the ~/BeaconNamingRecords/ or ~/SharedBeacons, respectively. Those are covered in my previous AirTag article . Each of the entries seen in Figure 4 have an UUID value, which is indicative of the fact that the seen beacons are associated with the signed in iCloud account on the phone, both owned and shared.
The last two columns returned by the query relate to the observed devices’ MAC address and the respective advertisement payload. Since devices operating on the FindMy network use a rotating Bluetooth MAC address (per the specification), these MAC addresses are essentially useless in cases where a beacon is not associated with the account (i.e., no UUID in the database record). There could be applications, however, in instances of unwanted tracking. For example, if the same MAC address continues to appear in Observations.db records during a period of time (likely 24 hours or so) but the phone has not yet identified it as a WildMode item.
The advertisement information is interesting. To start, see Figure 5.
The data in Advertised_Data column is supposed to contain certain information, but I found, for AirTags and AirPods at least, had only some of the data. The public specification that was written by both Apple and Google state items that advertise their data should provide the following information in their advertisement payloads:
| Bytes: | Value |
| 0 – 5 | MAC address |
| 6 – 8 | Flags TLV (optional) |
| 9 – 12 | Service Data TLV (optional) |
| 13 | Network ID |
| 14 | Near owner bit (LSB + 7 reserved bits (1=yes, 0=no) |
| 15 – 36 | Proprietary payload (optional) |
Three things to note about this specification and the payloads I observed. First, this specification is still in DRAFT, so it is likely incomplete. For example, the Network IDs have not yet been registered with IANA as far as I can tell, so, at the moment, it is hard to tell what network (Google or Apple) a device belongs to. I did not find any consistency in byte 13 described above. Second, I also found the near owner bit in the advertisement payloads to be inconsistent across the devices. Sometimes the least significant bit in byte 14 was set to “1,” but it was not a beacon that was owned by the test accounts and the test phones would alert that it was unwanted. This raised questions about whether or not either company is following the specification at this time. I fully expect this to change, but just know that, for now, things do not seem to align consistently, which makes them unreliable.
Third, there is a difference between the MAC address in the broadcasted payload data in the “Advertised_Data” column and the MAC address seen in the “MAC_Address” columns. See Figure 6.
Notice that the first nibble of the first byte in Figure 6 differs from that seen in Figure 5 (87 versus C7). This is due to how the randomization that devices operating on the network use. The Bluetooth specification goes into detail (see Section 1.3.2 of the spec), but just know that the two most significant bits should switch in such a way as to become a private, non-resolvable MAC address according to the Apple-Google specification; however, that was not what I observed (again, the specification is a DRAFT). In the example above, the second most significant bit shifts from a 1 to a 0, causing the first octet in the MAC address to change from C7 (0b11 – static address) to 87 (0b10 – reserved for private use).
Figure 7-1 and 7-2 show how the bit change affects the first octet.
To provide an example of this, I took an AirTag out with the iPhone 11 and a Xiaomi Poco X7 Pro (Android 15). Both devices saw the tag. Figure 8 shows the MAC address from Observations.db, and Figure 9 shows the MAC address from the Xiaomi from Android’s tracker detection database, personalsafety_db .
Note that the Xiaomi device appears to have done the conversion from the “reserved for future use” address to the static address as there was no reference to the address starting with 8F, which is seen in the payload the iPhone observed (seen in Figure 10).
To again illustrate the bit change, see Figures 11-1 and 11-2.
Three notes about Observations.db. First, I found that entries are written to this file more frequently when the device is stationary or is not moving very much. During periods while I was driving, it seemed to observe less frequently compared to when the phone was at my house or some other place where I or the phone did not move around as much. While stationary, the phone was seeing beacons often. One beacon, for example, was observed in intervals ranging from two to four seconds. Others were seen more frequently! The frequency likely is a product of the environment in which the phone is observing, but just know that it can happen very often.
The second note, and this is a HUGE one: this database discards and vacuums records very, very quickly. As an example, one of the testing environments was a local restaurant five minutes from my home. I took one of my “rogue” AirTags with me and sat in the restaurant for over an hour. I drove directly home and ran the extraction. Observations.db was completely devoid of any information about the AirTag at the restaurant; however, it did see it ten times between when I pulled into my garage and when I placed the phone into Airplane Mode to run the extraction (at my home). Another test involved the AirTag and the iPhone 14 while I was getting some tattoo work done (approximately three hours worth). Before I left the tattoo shop, I placed the phone in to Airplane Mode, made sure the Bluetooth radio was off, and drove home. This time, Observations.db had a substantial amount of record entries for what appeared to be the AirTag (based on the RSSI value) at the tattoo shop, which is almost 40 minutes away from my home by car, in addition to an occasional recorded location along my route to the tattoo shop from my home.
I mention these two scenarios to say this: if you want location data out of this database that does not involve your lab/workplace, it is imperative you ensure the bluetooth radio is cut off wherever the device is seized (i.e., the crime scene). Otherwise, Observations.db will discard and vacuum records that may be relevant to your examination objectives. The exception here are records related to owned/shared beacons. Those records have a tendency to stick around much longer.
Finally, it is also extremely important that you examine this file with the associated -WAL file. To illustrate why, see Figures 12 & 13.
The amount of records pulled from the -WAL can be substantial and will likely contain data that is not contained in the main database, so make sure whatever tool(s) you are using can account for the -WAL entries. Because of the speed at which records are written and removed, examining the file with the -WAL is a necessity.
An Example
To illustrate how Observations.db works with unknown beacons I took the iPhone 11 and an AirTag into a population-dense location: JFK airport in New York City. With so many people (and i-Devices) the iPhone should have plenty to observe, which should have made for a busy Observations.db. The AirTag and the iPhone 11 were in the pocket of my backpack and were literally touching, so, in theory, the RSSI signal should be relatively high, which could assist with identifying the AirTag. As it turns out, these ideas were correct. See Figure 14.
The values highlighted in the red boxes in Figure 14 were outliers compared to the rest of the data in Observations.db due to their RSSI signal. RSSI values of -37 and -38 are both good indicators of close proximity. The broadcasted payloads associated with both of these entries are the same. Figure 15 highlights the advertisement payload of the -38 RSSI record entry in Figure 14. Also highlighted in Figure 15 is the advertised MAC address (bytes 0-5 – blue box).
There were multiple records in Observations.db with this payload, which was indicative of the AirTag “following” me around the airport. Eventually, the AirTag was recognized as unwanted, which triggered a plist creation in ~/WildModeAssociationRecord in the searchpartyd directory, but no notification. The AirTag was assigned the identifier 13BB48EF-F050-4013-A36B-309821C999ED by the iPhone 11. Decrypting the plist found the following information. See Figure 16.
The advertisement data in the encrypted plist file (red box in Figure 16) matches what was seen in the advertisement payload in Observations.db. Further, the MAC address in the payload gets its own key/value pair in the plist (highlighted in the blue box in Figure 16) and matches what was in the advertisement payload. Further, the first location entry (not highlighted) put the phone in Terminal 4 at JFK, which is where I was located. See Figure 17.
The bottom of the .plist file contains a few bits of information. Namely, the status of the (likely eventual) notification to the user (me), and the last location it was seen prior to the .plist file being created. See Figure 18.
Above, the notification status “staged” is seen in the red box along with the last seen location, which is not highlighted. The mapped location is seen in Figure 19. I was stationary at Gate B48 in Terminal 4 waiting to board my flight.
The location from Observations.db is slightly different as the associated timestamp was after the one found in the .plist file; the Observations.db record occurs approximately 1 hour and 20 minutes after the last location in the .plist file (21:29:21 versus 20:08:22). Figure 20 shows the mapped location. Note that the location appears outside of Terminal 4, which makes sense because I turned on Airplane Mode on the iPhone 11 while on the plane (just prior to take off).
One More Observation
While I was able to view the other databases in the parent searchpartyd directory, none of them held any data of interest, and most were empty, save one: ItemSharingKeys.db. This database is interesting. It contains the advertisement data of beacons that have been shared with the iCloud account owner on the device being examined, but that’s not what makes it interesting. It is interesting in that it appears to contain present and future advertisement payload information for a shared beacon, including a set private keys AND the Bluetooth MAC address. To pull the necessary information, I modified the SQL query slightly to add the Advertisement Identifier from Observations.db.
SELECT
ObservedAdvertisement.scanDate AS "Seen_Time",
ObservedAdvertisementLocation.latitude AS "Latitude",
ObservedAdvertisementLocation.longitude AS "Longitude",
ObservedAdvertisementLocation.advId AS "Advertisment_Identifier",
ObservedAdvertisementBeaconInfo.beaconIdentifier AS "Identifier",
ObservedAdvertisement.macAddress AS "MAC_Address",
ObservedAdvertisement.rssi AS "Signal_Strength",
ObservedAdvertisement.advertisementData AS "Advertised_Data",
ObservedAdvertisementBeaconInfo.sequence AS "Sequence_Number"
FROM
ObservedAdvertisementLocation
LEFT JOIN ObservedAdvertisement ON ObservedAdvertisement.advId = ObservedAdvertisementLocation.advId
LEFT JOIN ObservedAdvertisementBeaconInfo ON ObservedAdvertisementBeaconInfo.advId = ObservedAdvertisementLocation.advId
ORDER BY "Seen_Time"
This query pulls back the Advertisement Identifier from the table ObservedAdvertisementLocation. The value in this column relates to the column index in the table ObservedAdvertisementBeaconInfo, which is the column of interest. To follow, see Figures 21 and 22. Note the highlighted MAC address payload for the shared beacon in right pane of Figure 21.


The column index in ObservedAdvertisementBeaconInfo (Figure 22) relates to a column by the same name in the table NearOwnerKeys in ItemSharingKeys.db. See Figure 23.
The interesting find in Figure 23 is that the advertised MAC address of the AirTag is listed in the column nearOwnerAdvertisement. There are also additional index numbers that increase numerically, each with their own nearOwnerAdvertisment value, all of which appear to be MAC addresses. To further investigate this, I waited for a few hours, took the same “Shared” AirTag with me, traveled around in car for a bit, and re-extracted the device. Figure 24 shows Observations.db from the later extraction.
Looking in the table ObservedAdvertisementBeaconInfo finds a value in the index column of 126603. See Figure 25.
Jumping back to ItemSharingKeys.db from the first extraction, and looking for 126603 finds what is seen below in Figure 26.
The advertised MAC addresses match. In essence, the phone with which the AirTag was shared is keeping a batch of advertisement MAC addresses for the shared AirTag, likely for future use in the event it “sees” the AirTag. The NearOwnerKeys table had 230 records in it, so, in theory, it may be possible to predict the advertised MAC address of a shared AirTag, within reason (i.e., as far out as there are records in the NearOwnerKeys table). Obviously, this would require additional research beyond the scope of this article, but the specification requirement of being able to predict the sequence of addresses certainly does lend credibility to this theory.
That Is All. For Now.
This was a nice find in that it confirmed, to some degree, my thought that iOS devices are keeping track of all of the FindMy-compatible devices they see, much like Android does. Observations.db gives examiners one more source of location data on iOS, though the retention time leaves a lot to be desired. Still, it is another source, and one that can be leveraged if handled properly.
While things seem pretty consistent between iOS 17 and 18, I fully expect things to change once the public specification is released. Of course, that does not rule out things changing when iOS 126 arrives later this year.

























Yes, in the searchpartyd & findmylocated directories they do. I updated the Lost Apples utility to decrypt the databases I have seen.
Great write up! Thank you for sharing, I am curious if other sqlite databases encrypt with the same SEE format as mentioned with the Observations.db.