plist

Just Call Me Buffy the Proto Slayer – An Initial Look into Protobuf Data in Mac and iOS Forensics

I was first introduced to the protobuf data format years ago accidentally when I was doing some MITM network analysis from an Android device. The data I was looking at was being transferred in this odd format, I could tell there were some known strings and some patterns to it - but I did not recognize the format. It had no magic number or file header. I started looking at it more in depth, it did have a structure. I spent an embarrassingly amount of time trying to reverse engineer this structure before I realized it was in fact a protobuf data blob. I think it was a moment of frustration in reversing this structure that lead me to searching something like “weird data format Android network traffic” that finally lead me to protobufs. 

 Ok, so what the heck is a protobuf? It actually stands for Protocol Buffer, but everyone calls them protobufs. It is a “language-neutral, platform-neutral extensible mechanism for serializing structured data” created by Google. It is a super-efficient way of storing and transferring data.

Since I was looking at an Android device, a protobuf made perfect sense. This was a Google thing afterall. I started noticing them more and more on Android devices, not just in the network traffic but also storing data on disk as well. It took me a long time to also notice that they were being stored on Apple devices! Native applications, 3rdparty applications, they are used EVERYWHERE! A great example was found by my friend Phill Moore in the iOS Spotify application to keep track of items listened to

In this article I’ll introduce you to some of the Apple-specific protobufs that I’ve come across. Some are fairly straight forward, others are less so. The kicker with protobufs is that there is an accompanying *.proto file that contains the definition to what is contained in these buffers. Unfortunately, we likely do not have this file as it is most likely server-side or inaccessible therefore we need to reverse engineer the contents and meaning of the items stored in this blob.

To parse these protobufs, I use protoc from Google to get a raw output. If you have the .proto file you can use this as well, but I have yet to give that a go. On a Mac, I would do a ‘brew install protobuf’ to get protoc installed. To parse a given buffer I will use the following command:

protoc --decode_raw < [protobuf_blob_file]

I will parse out some protobufs from different applications to give you an idea of what is stored in them - Maps, Locations, Health, and Notes.

Maps Data

The Maps application on both macOS and iOS use many protobufs to store location data. These can be found in quite a few different Maps related plist files. I will focus on GeoHistory.mapsdata plist file from iOS which stores historical locations that were mapped. This plist has GUID keys that contain a “contents” subkey. This “contents” blob contains the protobuf of the mapped location. I chose a small example to begin with as some of these can be very large

I’ve extracted this blob and put into a hex editor. You can see some visible strings in this blob but really no context. This is where parsing with protoc can be helpful.

I saved this protobuf to a file I named geohistory.pb. Using protoc I decoded it to the output below. I can see the same GUID and location name but now I get some other hex based values. This is where you will have to determine what these values mean and what they are used for, it may not be obvious initially.

At this time, I believe the highlighted pairs of hex in the screenshot above are coordinates. To read these, I copy them into a hex editor to read their values highlighted below. On the left is latitude, on the right is longitude. Plotting these two pairs above I get one location in Manassas, VA and another in Bethesda, MD. Clearly, neither of these are in Fairfax, VA. This is the tricky part, what do these values mean? More testing needs to be done here.

Location Data

The next example shows a protobuf blob being stored inside of a SQLite database. The example below is from the Local.sqlite routine locations database from iOS 11. (In iOS 12, the same data exists but has been placed into separate columns – which IMHO, is far easier to interpret.) The ZRTLEARNEDLOCATIONOFINTERESTMO table contains “learned” locations of interest or significant locations. This example is Heathrow Airport. Two columns contain protobuf data -  ZPLACEMAPITEMGEOMAPITEMHANDLE (highlighted), and ZPLACEMAPITEMGEOMAPITEM. To decode these with protoc, I will need to extract them to separate files. In DB Browser for SQLite, I can use the export feature.

The ZPLACEMAPITEMGEOMAPITEMHANDLE protobuf blob parsed with protoc contains much of the same location data as seen before. You will find that most of the location blobs will look similar. 

Taking the highlighted hex coordinates and plotting them using Google Maps, they make more sense than the ones highlighted above. These coordinates are right at Heathrow Airport in London.

Health Data 

Protobufs are not just used for location data, but also for other data storage such as split times when I have a running or walking workout. The healthdb_secure.sqlite database on iOS contains this information. Shown below is the “workout_events” table. I have selected the BLOB data for one “event” or one split time (a workout may consist of multiple split times for multiple associated “events”).

I exported and decoded with protoc using the same methods described above. The example below is from a walk I took in Monaco. Presumably the labels “s” is for seconds and “m” is for meters, however I’m still verifying this assumption.

Notes Data

One last example brings us to the Notes application. Anyone who has looked into this database likely knows that it is a complicated one. I created a sample note to show how it may look in the protobuf produced. This note has some text, a link, and a cat meme that I’ve been enjoying recently.

This example comes from MacOS in the NotesStore.sqlite database (iOS is similar). The “ZICNOTEDATA” table contains the contents of each note. Some eagle-eyed readers may notice the highlighted bytes in the binary output. It stores note contents in gzip archives embedded in the SQLite database.

I exported this gzip archive and used gzcat and xxd to preview the contents of it. I can see the text and link in the note along with some media information. What we have here is another protobuf!

The protoc output is below, many of the same strings are visible but there are some odd ones in there.

One in strange one in particular is the line "\033\'w\213\256?@Q\251IHEn\221\242\260". This is escaped octal. You will find this being used in a variety of different protobuf data. Some are smaller, some are much larger. I recall the one that Heather Mahalik and I looked at for Android Auto. That one was just full of octal nastiness, it was awful.

This one is small and converts well using echo. What does it represent? I have no idea…yet.

The media itself is stored in the file system (you can use information in the database to find the proper path). We can use the GUID in the protoc output to find the preview thumbnail.

Summing Up 

I will be very honest, I have been looking at these weird data blobs for years without knowing what they were. I am at the point now where I’m a little obsessed with protobufs. I know…its weird – but now every time I see rando-blob-data with a bunch of 0x0A hex bytes – I think protobuf, and nine out of ten times, I am correct! 

If you happen to know where I can find more information on the .proto files, please let me know!

Do it Live! Dynamic iOS Forensic Testing

fuck-it-bill-fubar-well-do-it-live.jpg

Testing and forensics go hand in hand. You cannot be sure about a certain artifact on what it contains or what certain pieces mean without testing (and not just once, but over and over and on multiple devices and operating systems!) I probably do this more than most forensic investigators but it is something I obsess about. If I’m not absolutely sure, I test – always. Even then I will caveat it to very specific instances of my testing platform. I hope this article will help most investigators and researches up and running with dynamic iOS testing.

Requirements & Prerequisites:

  • Jailbroken iOS Test Device (the newer iOS the better, generally!)

    • iPad, iPhone, iPod Touch – doesn’t matter.

  • Familiarity with your jailbreak, every jailbreak is different and may provide different sets of utilities.

  • Patience – Testing is neither quick nor easy and can be downright infuriating sometimes.

Connecting to Your iOS Device

Most newer jailbreaks will come with some sort of SSH server on them, determine how to connect to it via SSH. All examples in this article will be performed on MacOS (big surprise) however it shouldn’t be impossible to do this on other platforms. I’ve written a couple of articles on accessing jailbroken devices:

To connect to your iOS device, you can use normal access via Wi-Fi, but this may lead to stability issues when copying off large amounts of data or general connection issues. You may want to access the device off the network instead. I like to use a utility from the libimobiledevice suite of tools called iproxy. This allows me to access it via a USB lightning cable and no network. This uses usbmuxd to create a TCP tunnel over USB. More info/similar tools here if you need it.

Run iproxy in a separate terminal window or set it up as a Launch Agent.

Usage: iproxy 4242 22 [UDID]

  • Local Port 4242 – You can use whatever port you like, I like 4242 (It’s the answer to everything.)

  • Device Port 22 – Default SSH port for most jailbreaks, except for Meridian that likes to be fancy and run on 2222 (customize as required).

  • Device ID (UDID) is optional but useful if you are connected to multiple devices at a time.

Nothing will be displayed apart from “waiting for connection” until you connect a device. Once you do, you’ll see the devices UDID and port number in the iproxy output.

In another terminal window, SSH into it using the local port you setup with iproxy. You can use localhost or 127.0.0.1, whatever your personal preference is. As for the username you have two choices – root or mobile, root is obviously root and mobile is a limited user account. I always choose root but worth knowing about both. Also FWIW, make sure you change the default iOS password of these accounts from ‘alpine’ with passwd if it’s connecting to any network at all.

Executing Non-Native Binaries

Depending on which jailbreak you are using, certain utilities may not be made available to you. With any luck the basics will be provided by the jailbreak using Jonathan Levin’s binpack. If you want to upload your own, you’ll have to sign them and provide entitlements to run them on the device.

One tool that I really like for file monitoring (discussed later) is fsmon by NowSecure. I’ve attempted to build this from source for iOS but once uploaded I get an error that I don’t know how to fix (granted I didn’t research it much). Instead I pulled the binary from the DEB package provided here https://github.com/nowsecure/fsmon/releases.

I can unarchive the DEB archive using The Unarchiver (My favorite unarchive on macOS!). I then unarchive the data.tar.gz using The Unarchiver or native utilities to get the ‘/usr/bin/fsmon’ binary.

The fsmon binary is Fat or Universal binary meaning it can have multiple architectures stuffed into a single file. This particular binary has arm_v7 (32-bit) and arm64 (64-bit) Mach-O binaries.

We will need to thin this binary to a single architecture to get to run on our device. I chose arm64 for this since I have a 64-bit device (iPhone 7). You can thin/sign/entitle on the Mac too using jtool but just in the event you are not working from one (and why not!? I’m sitting here judging you right now.) I’ll upload the binary to the device and run the same commands.

Using scp I can upload this binary. For my particular jailbreak I needed to create a symlink (ln -s) to the scp binary provided by Jonathan’s binpack before I could use it.

Using scp with the CAPITAL P argument (why the port flags are not consistent between SCP and SSH is beyond me) and our iproxy port of 4242 to copy fsmon to root’s home directory, /var/root.

Now if we try to run it from root’s home directory, it will fail (“Operation no permitted”) since binaries on newer iOS’s can only run from certain directories. That’s not the only problem, it also needs to be signed and entitled.

First let’s deal with only one binary, in order to sign and entitle we need to extract the 64-bit binary from Fat binary. We can do with a couple tools. If you are on macOS and have lipo (get it? fat binary…lipo…thin…sorry, this makes me giggle every time.) I will output to a file named fsmon64 so I know it is the 64-bit binary and upload it to the device.

lipo fsmon -thin arm64 -output fsmon64

Since I’ve uploaded mine to the device already, I don’t have lipo on the device I will use Jonathan’s jtool instead.

jtool -arch arm64 -e arch fsmon

This will extract the 64-bit binary to a file named fsmon.arch_arm64 into the current directory. You will have to change the permissions to execute it (chmod 700), but we still need to deal with signature and entitlements. As shown below it has an adhoc signature and no entitlements.

To get the signature and entitlements from any binary you can run the following jtool command, example below is from the dd binary. Notice it has a Platform Binary signature and the “platform-applications” entitlement.

jtool --sig --ent <binary>

If I tried to execute the fsmon binary in /var/root, I’ll still get the “Operation not permitted” error. If I move it to a directory, I should be able to execute from I’ll get a different error, “Killed: 9”, next step is fixing the signature and entitlements.

Extract the entitlements from another (working) binary on the device using jtool and save this to a file named ent.xml for use later.

jtool --ent /jb/bin/dd > ~/ent.xml

Using jtool again, lets sign fsmon.arch_arm64 as a platform application and provide the binary the entitlements we just extracted. Verify it worked with (--sig/--ent) and execute it. Yay, working binary! (Feel free to rename as necessary, again I use fsmon64 or just fsmon.) On newer versions of jtool, ‘platform’ is no longer required.

jtool --sign platform --inplace --ent ~/ent.xml /jb/bin/fsmon.arch_arm64

Reviewing Directories and Files

Quick iOS partition review - take a look at the /etc/fstab file. There are two primary partitions (and a baseband one if you’re into that kinda thing.) The first mounted on / is the system partition where the operating system files are contained. It is theoretically read only as noted by the ‘ro’, however recall that we just put the fsmon binary in /jb/bin. When it comes to jailbreaks that ‘ro’ is more of a reminder of what it is on stock devices. The data partition mounted on /private/var is where all the user data is, this is the primary partition that you’ll be using for your forensic analysis. All native and 3rd party application plists, databases, and other files are located there.

In order to find data associated with a particular application I can use the find command and the bundle ID associated with an app.

find /private/var -ipath *net.whatsapp.Whatsapp*

In the example below, I started looking for WhatsApp data. This is a good initial triage of the applications data, this doesn’t get me all the files but it helps direct me to the related directories. You’ll notice that GUID in the file path, this will be different for all applications across all devices and will change.

Going to the following directories and perusing the data will help me determine what type of data a certain application stores and how it stores it.

  • iCloud Artifacts:

    • /private/var/mobile/Library/Caches/CloudKit/com.apple.bird/57T9237FN3.net.whatsapp.WhatsApp

  • /private/var/mobile/Library/Application Support/CloudDocs/session/containers/57T9237FN3.net.whatsapp.WhatsApp

  • The combination of the ‘Shared App Group’ and ‘Data Application’ directories will generally hold most of the user data associated with an application.

    • /private/var/mobile/Containers/Shared/AppGroup/133904F3-EAA0-48E9-905C-90BB93A7DDA2/

    • /private/var/mobile/Containers/Data/Application/97AD4FDE-2089-455A-8B21-06E4E2225626/

  • It is worth mentioning that if you are looking for the Applications bundle the Bundle ID will not get you there, instead look for the name of the App.

    • /private/var/containers/Bundle/Application/6B6D4621-845A-4EE7-AECF-D68CC00E5C4E/WhatsApp.app/

Finding the right directory/directories for the app in question can be time consuming. One of my favorite tools that solves this issue is cda. Using the same method above, I’ll upload this binary to my device.

As shown below, all you need to provide cda is a search term. I provided it the term ‘whatsapp’ and it provided me the same directories (more even) as above. If you’re not quite sure what you are looking for yet, provide it a single period ‘.’ and get a listing by Bundle ID. It is worth noting that this will only provide app data, if you are doing research on native iOS data you won’t get it using this – time to dig in and find it the hard way.

Looking into one of the WhatsApp directories, I start to get a feeling for what data an application is storing. You will normally find SQLite databases, plist files, media, log files and other files related to an application. ~90% of what you’ll be looking at are SQLite databases and plist files, so I’ll focus on those for this part of the article.

Starting with SQLite databases, we can use sqlite3 (provided by the binpack) on the device to triage the database. My process is to look at the database names first and see if anything is obvious. If I’m looking at a chat app, I’ll look for keywords like chat, message, etc. I’ll focus on WhatsApp’s ChatStorage.sqlite database for this example – seems like a reasonable choice for chat messages. Using sqlite3, I’ll dump the table listing and peruse it for anything interesting. Seems to me that ZWAMESSAGE would contain the chat messages!

Using a SQL SELECT statement I can dump the contents of this table; however this is where sqlite3 may not provide the best analytical view. In this case I might use scp to copy it off the device and use a GUI based SQL viewer to create queries and join multiple tables. (FWIW, I like DB Browser for SQLite). Need a SQLite reference guide, check out the one Heather Mahalik and I created for our classes (FOR585 – Advanced Smartphone Forensics, FOR518 – Mac Forensic Analysis and Incident Response).

I will still use sqlite3 for per-message testing to answer different questions.

  • What does xyz flag in this column mean? Are 0 and 1 the only values?

  • If I send a piece of media, what does it look like in the database? Is it stored in a different table?

  • Message direction or contact information which column stores that?

I will run the same query over and over testing different flags in different columns as I populate data manually on the device. Does this take time? Sure does! But it’s the only way to be sure. Applications are known to change database schemas and values as their application updates.

sqlite3 ProTip: Ctrl+D to quit out of the sqlite3 shell

Next let’s dive into those pesky plist files. For third party applications I will usually start in the applications Preferences directory. The Preferences directory will usually contain a configuration plist file containing some useful bits like usernames, servers, usage timestamps, keys, and passwords. Yes, plaintext, non-obfuscated passwords! 🤷🏻‍♀️

The example below is one for Whatsapp (specifically the one located in the Shared App Group directory. There may be multiple for each application.) I’m using jlutil (Jonathan’s interpretation of plutil, the native macOS utility, to view this plist.)

Another option is plconvert which will convert it to a text file representation and show a less-than-helpful representation on standard out. It will also output to an XML file, here named tmpfile by myself. I consider this slightly less “clean” as it will leave these converted files everywhere on the system, but it’s all personal preference. I will say the plconvert is more useful from a timestamp interpretation and data BLOB stance.

File Monitoring

Say you’re testing a application that has the ability to take photos using the iPhone camera. Where are those photos stored? Or how about if you toggle one of the switches in the Settings application, where is that stored? In a database or a plist file?

An easy way to figure this out is to use a file system monitor, it should at least point you in the right direction. As shown above I prefer to use the fsmon utility from NowSecure, however Jonathan’s binpack includes fs_usage which provides the same data. I prefer the layout of fsmon’s output.

In the example below, I took a picture of my sidekick Elwood with the Camera app on the iPhone while running fsmon. (I’d like to say he helped with this article, but he just slept all day next to me. Cats don’t make the best research assistants.) In the fsmon output you can see that when I took a picture, the “Camera” process saves the picture in a few temp files before it finally saves it in the DCIM directory as an HEIC file (the newer iOS image format) and creates a thumbnail image for the Camera app, PreviewWellImage.tiff.

I hope this helps everyone getting started doing their own iOS forensic research!

(In the rare event anyone wants to see my sidekick Elwood…here is the pic I took. Super Lazy.)

New Script! - MacMRU (Most Recently Used) Plist Parser

I have been studying the new SFL-based MRU plist files found in OS X 10.11. They make analysis hard because they are binary plist files using the NSKeyedArchiver format – see here for my manual analysis of these files. I’ve also included the ‘older’ format plist files used in OS X 10.10 and older.

In order to analyze them better (and student requests) I wrote a Python script to output the contents of these files in an easier to read format. Nothing fancy, just text printed to standard output.

Get the script here from my Github page. I hope you find the script useful! 

The script is meant to be run on a directory; this can be a directory of extracted plist files from an image, a directory on your own system (ie: ~/Library), or from a mounted image (ie: /Volumes/mounted_image_file/Users/<username>/), you get the idea.

This script parses the following plist files:

  • /Users/<username>/Library/Preferences/<bundle_id>.LSShardFileList.plist   
  • /Users/<username>/Library/Preferences/com.apple.finder.plist   
  •  [10.10-] /Users/<username>/Library/Preferences/com.apple.recentitems.plist   
  • [10.11+] /Users/<username>/Library/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/<bundle_id>.sfl   
  • [10.11+] /Users/<username>/Library/Library/Application Support/com.apple.sharedfilelist/RecentApplications.sfl   
  •  [10.11+] /Users/<username>/Library/Library/Application Support/com.apple.sharedfilelist/RecentDocuments.sfl   
  •  [10.11+] /Users/<username>/Library/Library/Application Support/com.apple.sharedfilelist/RecentServers.sfl   
  •  [10.11+] /Users/<username>/Library/Library/Application Support/com.apple.sharedfilelist/RecentHosts.sfl 

The script usage is below. The only required argument is the directory, but the output can include binary BLOB hex dump of the Bookmark data (--blob). Most of the Mac MRUs contain a binary Bookmark BLOB of data that can be useful to determine where a certain file was located or where an application was run from. I’ve included it as an option as it can get very, very verbose.

The script also has two dependencies, hexdump.py and ccl_bplist.py. These files can be installed or just simply placed in the same directory you are running the macMRU.py script from. (Installation on OS X 10.11 systems are limited thanks to SIP.)

A few screenshots of example script output:

This example shows the output without the BLOB data of the newer SFL-based MRU files:

This example shows the same output with a sample of the hexdump BLOB data, you can see where this can get quite verbose.

The last example shows the ‘older’ MRU plist files found on 10.10 and older systems. (The com.apple.finder.plist files is the same on 10.11.)

Manual Analysis of ‘NSKeyedArchiver’ Formatted Plist Files - A Review of the NEW OS X 10.11 “Recent Items”

In my iOS Frequent Locations blog post, I mentioned that the locations are stored in a ‘less than analyst friendly’ format. These plist files are in a binary plist format – no different than other binary plists except it is hard to put context to their structure. Rather than directly associated key/value pairs, the keys and values are stored in a seemingly random way.

I noticed that this format is being used more often, especially in iOS device files. The binary plist format is not going away anytime soon! Expect to see them on iOS and OS X systems.

About the NSKeyedArchiver Format

Generally speaking, this format is used to allow developers easier access to their stored data. They can also be used for backward/forward compatibility with their applications. As far as forensic analysis is concerned, think of them as data stored in a serialized tree-based format. We need to start at the base of the tree and work from there to manually put context to these values. The following links describe the NSKeyedArchiver format in greater detail.

The “Ugh Why!?” Moment

As an analyst if you ever open a plist file and are presented the following plist structure, you can be sure there will be groans, expletives, and general signs of discontent coming from the analyst. Analyzing this file will not be as easy as just popping it into your favorite plist viewer and reading keys and values like a “normal” binary plist file.

I was recently reviewing new and changed artifacts on an El Capitan (10.11) system when I came across a drastic change in the “Recent Items” for each user. The file com.apple.recentitems.plist no longer existed in the user’s /Preferences directory. In previous versions, the plist file looked like the example below. Keys and values have context and I can quickly see the last ten applications the user accessed.

In 10.11 this data now uses the NSKeyedArchiver format. The location of this artifact has also changed. These files are now located in ~/Library/Application Support/com.apple.sharedfilelist/ directory.

The file extension has also changed from ‘.plist’ to ‘.sfl’. However, they are still binary plists as shown in the screenshot below - note the file header ‘bplist00’.

Because of the file extension change, just trying to open them in the Xcode plist viewer is challenging. We end up with the following view. (Note: Not all NSKeyedArchiver binary plists will have a custom file extension, some do have the expected ‘.plist’ extension. These should open up as expected in Xcode.)

We could rename all these ‘.sfl’ files as ‘.plist’, open them in Xcode, and review. You will see something like the following screenshot. Notice the keys named $version, $objects, $archiver (with the value of NSKeyedArchiver), and $top. These are the telltale signs you got yourself an NSKeyedArchiver binary plist on your hands. Lucky you!

If we expand the plist a bit more, we can see items of potential interest. We can see application names, file paths, binary blobs - all sorts of good stuff! Unfortunately, there is no context to any of this. It is not guaranteed that keys in sequential order are related in any way.

Xcode isn’t even showing you everything here, a better tool to review these types of files is the command line utility plutil. (Note: Other plist viewers may show you this information without the need to use other utilities, I’m looking at you Blacklight – excellent work!)

In the terminal we will use the command below to dump these files making them easier to analyze. The ‘-p’ flag to plutil prints to standard output in a JSON format. I have also redirected them to an output file rather than standard output so I can review the file easier.

plutil –p <plist_file> > <output_file_here>

The partial output below shows what was missing in the Xcode viewer. Notice the additional information in the ‘1’ key from the Xcode example above. We now can view the ‘NS.keys’ and ‘NS.objects’ along with their values and class types. This is exactly what we need to put context to this data.

Here is a copy of the example output I am using in this article if you would like to follow along.

Analysis Time! Starting at the $top:

The root of these files is the ‘root’ key in $top. Always go here first to find the start of the tree. In the example below the ‘root’ key has a value of ‘1’. This means we start at key ‘1’. Time to scroll to the top of the output file!

Looking at key ‘1’ under ‘$objects’, we see it has two sub keys, ‘NS.keys’ and ‘NS.objects’.

The Keys and Objects are directly related, in the example below, key with value 2 is associated with object value 5, key 3 with object 6, and key 4 with object 10.

Each ‘value’ number is a key within the plist, so for this section we will be looking at keys 2, 3, 4, 5, 6, and 10.

There is also a $class (value ‘9’) listed here – these values describe the type of data contained. In this case we are looking at a ‘NSDictionary’ data type. In many instances, depending on the plist file, you will find proprietary data types. The StateModel files used in the iOS Frequent Locations makes heavy use of these.

Lets look at keys 2, 3, 4. ‘NS.keys’ usually tell us what the key name is:

  • 2 – “version”
  • 3 – “properties”
  • 4 – “items”

Now let’s look at their associated objects, in the 5, 6, and 10 keys. This is usually where the data is stored for each key name above.

  • 2 – “version”
    • 5 – “1”
  • 3 – “properties”
    • 6 – Another NS.key/NS.object data field. We need to dig further.
  • 4 – “items”
    • 10 – A NS.object data field. We need to dig further.

Ok, so we are looking at version 1 of something. We need to keep digging into these nested values to figure what what that something is!

Let’s look into key 3, object 6. We see this has a $class value of 9 (NSDictionary). Enumerating this data gives us a key of 7, with an associated object of 8.

  • “com.apple.LSSharedFileList.MaxAmount” = “10”

Looks like we have a max file count of 10 for LSSharedFileList recents. This is expected, 10 recent items have been the max amount for years!

Let’s go back and look at key 4, value 10. This is listed as “items”. The 10 key holds ten objects (0-9) with the following values 11, 21, 27, 33, 39, 45, 51, 57, 63, 69. All these have to be enumerated themselves. Let look at the first “item” key 11.

“Item” key 11 has the following data:

  • "$class” – (Value 20) – “SFListItem”
    • One of those “proprietary” data types.
  • order” – “-146”
  • bookmark” – (Value 18) – A binary BLOB containing the “bookmark” information for the Recent Application.
    • Copy this hex out and view it in a hex editor.
  • uniqueIdentifier” – (Value 12) – A UUID
  • properties” – (Value 19) – An empty NS.key/NS.object dictionary.
    • Not populated in this instance.
  •  URL” – (Value 15) – An “NSURL” data type ($class with value 17).
    • If we follow this down using the key 16, we get “file:///Applications/System%20Preferences.app/”.
  •  name” – (Value 14) – “System Preferences”

We could now go through and enumerate the rest of the “items”, however, I will leave that task to the reader for practice!

You can see that these files can be highly nested with many different data types. Going through these files can be a very manual process which is why I expect to write many scripts parsing these files out when I get the time (or am frustrated enough) to do so.

Each one of these NSKeyedArchiver files is its own special file, no two will be alike which makes parsing these even more difficult. For the scripters out there – I have become very fond of ccl_bplist.py to help in this process.

UPDATE (1/2/15) - Additional blog article and python implementation for SFL files by @mikeymikey