This artifact parses notepad TabState files in available in Windows 11.
In Windows 11, notepad has implemented a feature to repopulate previously open notepad tabs - both saved and unsaved. This data is stored on disk and provides an interesting opportunity for DFIR practitioners.
name: Windows.Forensics.TabState
author: Matt Green - @mgreen27
description: |
This artifact parses notepad TabState files in available in Windows 11.
In Windows 11, notepad has implemented a feature to repopulate previously
open notepad tabs - both saved and unsaved. This data is stored on disk and
provides an interesting opportunity for DFIR practitioners.
reference:
- https://medium.com/@mahmoudsoheem/new-digital-forensics-artifact-from-windows-notepad-527645906b7b
- https://www.youtube.com/watch?v=zSSBbv2fc2s
type: CLIENT
parameters:
- name: TargetGlob
description: Target glob for notepad TabState bin files.
default: C:\Users\*\AppData\Local\Packages\Microsoft.WindowsNotepad_8wekyb3d8bbwe\LocalState\TabState\*.bin
- name: ContentRegex
description: Content filter regex to select which TabState files return a row.
type: regex
default: .
- name: FilenameRegex
description: Filter regex to select Saved filename path. ```^$``` returns only unsaved files.
type: regex
default: .
- name: UploadFile
description: If selected will upload TabState file.
type: bool
export: |
LET TSProfile = '''[
["TabState", 0, [
["__Magic", 0, "String", {"length": 3, "term_hex" : "FFFFFF" }],
["__Saved", 3, "char"],
["__FileNameSize", 4, "int8"],
["__Filename", 5, "String", {
encoding: "utf8",
length: "x=>x.__FileNameSize * 2",
term_hex : "000000",
}],
["Filename",0,"Value",{"value": "x=>if(condition= x.__Saved > 0, then=utf16(string=x.__Filename),else='')"}],
["__HeaderPrefix", "x=>5 + len(list=x.__Filename)", "String",{"term_hex": "0100", length: 1000, max_length: 1000}],
["__DataOffset",0,"Value",{ "value": "x=>5 + len(list=x.__Filename) + len(list=x.__HeaderPrefix)"}],
["__Data", "x=> x.__DataOffset + 5", "String", {
encoding: "utf8",
length: 100000,
max_length: 100000,
"term_hex" : "000000000000000000000000"
}],
["StateData",0,"Value",{ "value": "x=>utf16(string=x.__Data[:(len(list=x.__Data) - 5)])"}],
]]]'''
sources:
- precondition:
SELECT OS From info() where OS = 'windows'
query: |
LET results = SELECT OSPath, Name,Mtime,Atime,Ctime,Btime,
parse_binary(filename=OSPath,profile=TSProfile,struct='TabState') as Parsed
FROM glob(globs=expand(path=TargetGlob))
WHERE NOT IsDir
AND NOT OSPath =~'''\.(0|1)\.bin$'''
AND Parsed.StateData =~ ContentRegex
AND Parsed.Filename =~ FilenameRegex
SELECT
Name,Mtime,Atime,Ctime,Btime,
Parsed.Filename as SavedFilename,
Parsed.StateData as StateData,
OSPath
FROM if(condition= UploadFile,
then={
SELECT *, upload(file=OSPath) as Upload
FROM results
},
else= results )