src/Public/Parse/ConvertFrom-FotonAmcache.ps1
|
function ConvertFrom-FotonAmcache { <# .SYNOPSIS Parse an Amcache.hve already copied off the host (offline hive bytes). .DESCRIPTION Pure function over a file path. Does not load the hive into the live registry and does not read %SystemRoot%\AppCompat\Programs. Win10+ InventoryApplicationFile keys: named values only (path, FileId, Name, Publisher, Version, Size, LinkDate as stored). KeyLastWriteUtc is the nk FILETIME (key last-write), not proof of execution. LinkDate is left as the hive string; it is not converted to UTC. Legacy Root\File numeric value IDs are recorded as unsupported rather than guessed. Use a dedicated hive tool (for example AmcacheParser) for those layouts. .PARAMETER Path Copied Amcache.hve (or a fixture hive). .PARAMETER MaxEntries Cap on InventoryApplicationFile rows (default 8000). .EXAMPLE ConvertFrom-FotonAmcache -Path D:\Cases\CASE-0001\08_Filesystem\amcache\Amcache.hve .OUTPUTS One or more parse rows. #> [CmdletBinding()] param( [Parameter(Mandatory)][string]$Path, [int]$MaxEntries = 8000 ) if (-not (Test-Path -LiteralPath $Path)) { throw "Amcache hive not found: $Path" } $item = Get-Item -LiteralPath $Path $bytes = [IO.File]::ReadAllBytes($item.FullName) $sha = Get-FotonSha256Hex -LiteralPath $item.FullName $row = { param($Format, $Supported, $Identity, $Note, $FileId, $Name, $Publisher, $Version, $Size, $LinkDateRaw, $KeyLastWriteUtc, $LowerCaseLongPath) [PSCustomObject]@{ SourcePath = $item.FullName SizeBytes = $bytes.Length SHA256 = $sha Format = $Format Supported = $Supported Identity = $Identity FileId = $FileId Name = $Name Publisher = $Publisher Version = $Version Size = $Size LinkDateRaw = $LinkDateRaw KeyLastWriteUtc = $KeyLastWriteUtc LowerCaseLongPath = $LowerCaseLongPath Path = $LowerCaseLongPath Note = $Note } } if ($bytes.Length -lt 0x1000) { return @( & $row 'Unsupported-TooSmall' $false '' 'Shorter than a registry hive header. Paths and times were not guessed.' '' '' '' '' '' '' '' '' ) } $magic = [Text.Encoding]::ASCII.GetString($bytes, 0, 4) if ($magic -ne 'regf') { return @( & $row 'Unsupported-NotHive' $false '' 'Header is not regf. Wrong Amcache fields would be worse than this record.' '' '' '' '' '' '' '' '' ) } $root = Get-FotonRegfRootNk -Bytes $bytes if ($null -eq $root) { return @( & $row 'Unsupported-HiveWalk' $false '' 'regf header present but the root nk was not read. Use a hive tool; times were not guessed.' '' '' '' '' '' '' '' '' ) } $inv = Get-FotonRegfFindSubkey -Bytes $bytes -StartNk $root -Name 'InventoryApplicationFile' -MaxDepth 4 if ($null -eq $inv) { $legacy = Get-FotonRegfFindSubkey -Bytes $bytes -StartNk $root -Name 'File' -MaxDepth 3 if ($null -ne $legacy) { $note = 'Amcache Root\File (Win7/8-style) numeric value IDs are not mapped here. Use an Amcache/hive tool. FILETIME fields were not guessed.' return @( & $row 'Amcache-LegacyFile' $false '' $note '' '' '' '' '' '' '' '' ) } $note = 'regf hive has no InventoryApplicationFile key. Absence of parsed rows is not evidence the host never executed software. Use a hive tool if this is a non-Amcache hive.' return @( & $row 'AmcacheHive' $false '' $note '' '' '' '' '' '' '' '' ) } $noteOk = 'InventoryApplicationFile named values. KeyLastWriteUtc is the registry key last-write, not proof of execution. LinkDateRaw is the stored string (often PE compile time), not converted to UTC.' $children = @(Get-FotonRegfSubkeys -Bytes $bytes -Nk $inv) if ($children.Count -eq 0) { return @( & $row 'AmcacheHive' $true '' 'InventoryApplicationFile is present but has no file subkeys. That is an empty key, not proof of a clean host.' '' '' '' '' '' '' $inv.KeyLastWriteUtc '' ) } $out = @() $n = 0 foreach ($ch in $children) { if ($n -ge $MaxEntries) { break } $vals = Get-FotonRegfValues -Bytes $bytes -Nk $ch $path = '' $fileId = '' $nm = '' $pub = '' $ver = '' $size = '' $link = '' if ($vals.ContainsKey('LowerCaseLongPath')) { $path = [string]$vals['LowerCaseLongPath'] } if ($vals.ContainsKey('FileId')) { $fileId = [string]$vals['FileId'] } if ($vals.ContainsKey('Name')) { $nm = [string]$vals['Name'] } if ($vals.ContainsKey('Publisher')) { $pub = [string]$vals['Publisher'] } if ($vals.ContainsKey('Version')) { $ver = [string]$vals['Version'] } if ($vals.ContainsKey('Size')) { $size = [string]$vals['Size'] } if ($vals.ContainsKey('LinkDate')) { $link = [string]$vals['LinkDate'] } $id = $path if (-not $id) { $id = $nm } if (-not $id) { $id = $fileId } if (-not $id) { $id = [string]$ch.Name } $out += & $row 'AmcacheFile' $true $id $noteOk $fileId $nm $pub $ver $size $link $ch.KeyLastWriteUtc $path $n++ } if ($out.Count -eq 0) { return @( & $row 'AmcacheHive' $false '' 'InventoryApplicationFile children were present but no values were decoded. Use a hive tool; times were not guessed.' '' '' '' '' '' '' '' '' ) } return $out } |