[Forensics] Writing a .DS_Store file parser
Uncovering deleted filenames in the .DS_Store binary
If you delete a file on macOS and empty the Trash, your Mac still remembers exactly what it was called and where it lived.
.DS_Store files are one of my favorite forensic artifacts. They're niche, kind of weird, and pack a surprising amount of forensically useful information.
To most of my software engineer friends, ".DS_Store" is just a line you put in your .gitignore. Under the hood, there's a lot more going on if you know what you're looking for!
Quick backgrounder: the .DS_Store (Desktop Services Store) file is a hidden binary that macOS Finder creates in every directory you open. It stores display preferences, icon positions, window size, view mode, sort order and uses a proprietary format and stores records in a B-tree.
Lots of awesome reverse engineering work was done over a decade ago on the file type by Mark Mentovai and Wim Lewis and I wouldn't have understood how it works without first reading their posts.
Why should forensic investigators care?
The .DS_Store file's existence proves someone browsed that folder in Finder
Filenames persist in .DS_Store even after the files are deleted until the system reboots (I still need to do a bit more testing on how long the deleted files last - it may depend on macOS version)
Trash put-back records store where trashed files originally came from, surviving even after the Trash is emptied
Timestamps and file sizes are recorded at the time Finder indexed them
If someone deletes a sensitive file and empties their Trash, the .DS_Store in ~/.Trash can still tell you what the file was called and exactly where it lived before deletion
Writing a .DS_Store parser
Without being properly parsed, the .DS_Store file is pretty useless. I built “ds-store-parser” (with help from Claude Code, of course!) to turn the .DS_Store binary into accessible and useful forensic information.
It's a Python CLI tool using only the standard library, Python 3.10+. It reads the raw binary, decodes the record types, and outputs clean CSV or JSON. One row per file, with human-readable columns for everything (while maintaining the raw data). I validated it against 368 real .DS_Store files from my MacBook.
Next time you push to GitHub, make sure you’re not adding a .DS_Store file! It is quietly leaking your directory structure and filenames to anyone who knows how to parse it.
Check out the parser on my Github: https://github.com/jonathanlooi/ds-store-parser



