Skip to content

Instantly share code, notes, and snippets.

@daddycocoaman
Created September 14, 2019 02:34
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save daddycocoaman/e1a4f31109e17188d5ce8fd0ca15b63e to your computer and use it in GitHub Desktop.
Save daddycocoaman/e1a4f31109e17188d5ce8fd0ca15b63e to your computer and use it in GitHub Desktop.
Parses sticky note files in .snt/.sqlite formats. Sqlite files may require the WAL and SHM files of the same name as well. Once run, WAL/SHM files will be merged into .sqlite file.
import json
import sqlite3
import olefile
import argparse
def parse_snt_file(file):
# https://www.tutorialspoint.com/python_digital_forensics/python_digital_forensics_important_artifacts_in_windows
if not olefile.isOleFile(file):
return "Invalid OLE file"
ole = olefile.OleFileIO(file)
note = {}
for stream in ole.listdir():
if stream[0].count("-") == 3:
if stream[0] not in note:
note[stream[0]] = {"created": str(ole.getctime(stream[0])), "modified": str(ole.getmtime(stream[0]))}
content = None
if stream[1] == '3':
content = ole.openstream(stream).read().decode("utf-16").rstrip("\u0000")
if content:
note[stream[0]][stream[1]] = content
return json.dumps(note, indent=4, sort_keys=True)
def dict_factory(cursor, row):
d = {}
for idx, col in enumerate(cursor.description):
d[col[0]] = row[idx]
return d
def parse_plum_file(db):
con = sqlite3.connect(db)
con.row_factory = dict_factory
cur = con.cursor()
#tableNames = [v for name in cur.execute("SELECT name FROM sqlite_master WHERE type='table'") for k,v in name.items()]
tableNames = ["Note", "Stroke", "StrokeMetadata", "User", "SyncState", "UpgradedNote", "Media"]
finaloutput = {}
for name in tableNames:
#Don't do this in real life.
cur.execute(f"select * from {name}")
res = cur.fetchall()
for item in res:
if not item.get("LastServerVersion") == None:
item["LastServerVersion"] = json.loads(item.get("LastServerVersion"))
finaloutput[name] = res
return (json.dumps(finaloutput, indent=4))
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="""Parses sticky note files in .snt/.sqlite formats.
Sqlite files may require the WAL and SHM files of the same
name as well. Once run, WAL/SHM files will be merged into .sqlite file.""")
parser.add_argument('-s', metavar="<.snt file>", help='Sticky note .snt file', type=argparse.FileType('r'))
parser.add_argument('-p', metavar="<.sqlite file>", help='Sticky note plum.sqlite file', type=argparse.FileType('r'))
args = parser.parse_args()
line = "*" * 10
if args.s:
print(f"{line}\n{args.s.name}\n{line}")
print(parse_snt_file(args.s.name), end="\n\n")
if args.p:
print(f"{line}\n{args.p.name}\n{line}")
print(parse_plum_file(args.p.name), end="\n\n")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment