This artifact maintains a local (client side) database of file
hashes. It is then possible to query this database by using the
Generic.Forensic.LocalHashes.Query artifact
name: Windows.Forensics.LocalHashes.Usn
description: |
This artifact maintains a local (client side) database of file
hashes. It is then possible to query this database by using the
`Generic.Forensic.LocalHashes.Query` artifact
type: CLIENT_EVENT
parameters:
- name: PathRegex
description: A regex to match the entire path (you can watch a directory or a file type).
default: .
type: regex
- name: Device
description: The NTFS drive to watch
default: C:\\
- name: HashDb
description: Name of the local hash database
default: hashdb.sqlite
- name: SuppressOutput
description: If this is set, the artifact does not return any rows to the server but will still update the local database.
type: bool
- name: UsnCheckPeriod
type: int
description: Dedup all file operations that occur within this period
default: "10"
precondition: SELECT OS from info() where OS = "windows"
sources:
- query: |
-- Dont be too aggressive on the USN watching to conserve CPU usage
LET NTFS_CACHE_TIME = 30
LET USN_FREQUENCY = 60
LET hash_db <= SELECT OSPath
FROM Artifact.Generic.Forensic.LocalHashes.Init(HashDb=HashDb)
LET path <= hash_db[0].OSPath
LET _ <= log(message="Will use local hash database " + path)
LET file_modifications = SELECT Device + OSPath AS OSPath
FROM watch_usn(device=Device)
WHERE OSPath =~ PathRegex
-- The USN journal may contain multiple entries for the same
-- file modification (e.g. TRUNCATE followed by APPEND and
-- CLOSE). We therefore dedup all entries that happen within the
-- period as a single modification.
LET deduped = SELECT * FROM foreach(row={
SELECT * FROM clock(period=UsnCheckPeriod, start=0)
},
query={
-- Each time the fifo is accessed we pull all the rows and
-- dedup the path, then clear the cache.
SELECT * FROM fifo(
query=file_modifications,
max_rows=5000,
max_age=6000, flush=TRUE)
GROUP BY OSPath
})
-- Stat each file that was changed to get its size and hash
LET files = SELECT * FROM foreach(row=deduped,
query={
SELECT OSPath, Size, hash(path=OSPath).MD5 AS Hash, now() AS Time
FROM stat(filename=OSPath)
WHERE Mode.IsRegular
})
-- For each file hashed, insert to the local database
LET insertion = SELECT OSPath, Hash, Size, Time, {
SELECT * FROM sqlite(file=path,
query="INSERT into hashes (path, md5, timestamp, size) values (?,?,?,?)",
args=[OSPath.String, Hash, Time, Size])
} AS Insert
FROM files
WHERE Insert OR TRUE
// If output is suppressed do not emit a row, but still update the local database.
SELECT OSPath, Hash, Size, Time
FROM insertion
WHERE NOT SuppressOutput
column_types:
- name: Time
type: timestamp
- name: Hash
type: hash
- name: ClientId
type: client_id