src/Private/Parse/Get-FotonOleCompoundStreams.ps1
|
function Get-FotonOleCompoundStreams { <# Extract named streams from an OLE compound file (CFB). Used for .automaticDestinations-ms. Caps streams and size. Returns @() on failure. #> param( [byte[]]$Bytes, [int]$MaxStreams = 64, [int]$MaxStreamBytes = 2097152 ) $out = @() if ($null -eq $Bytes -or $Bytes.Length -lt 512) { return $out } if ($Bytes[0] -ne 0xD0 -or $Bytes[1] -ne 0xCF -or $Bytes[2] -ne 0x11 -or $Bytes[3] -ne 0xE0) { return $out } try { $shift = [BitConverter]::ToUInt16($Bytes, 0x1E) if ($shift -lt 9 -or $shift -gt 12) { return $out } $sectorSize = 1 -shl $shift $miniShift = [BitConverter]::ToUInt16($Bytes, 0x20) $miniSize = 64 if ($miniShift -ge 4 -and $miniShift -le 8) { $miniSize = 1 -shl $miniShift } $dirStart = [BitConverter]::ToInt32($Bytes, 0x30) $miniCutoff = [BitConverter]::ToUInt32($Bytes, 0x38) $miniFatStart = [BitConverter]::ToInt32($Bytes, 0x3C) $difatStart = [BitConverter]::ToInt32($Bytes, 0x44) $difatCount = [BitConverter]::ToInt32($Bytes, 0x48) $fatSectors = New-Object 'System.Collections.Generic.List[int]' for ($i = 0; $i -lt 109; $i++) { $id = [BitConverter]::ToInt32($Bytes, 0x4C + (4 * $i)) if ($id -ge 0) { $null = $fatSectors.Add($id) } } $dif = $difatStart $guard = 0 $perDif = ($sectorSize / 4) - 1 while ($dif -ge 0 -and $guard -lt $difatCount -and $guard -lt 32) { $sec = Get-FotonOleSectorSlice -Bytes $Bytes -SectorSize $sectorSize -Index $dif if ($null -eq $sec) { break } $n = [int]$perDif for ($j = 0; $j -lt $n; $j++) { $id = [BitConverter]::ToInt32($sec, 4 * $j) if ($id -ge 0) { $null = $fatSectors.Add($id) } } $dif = [BitConverter]::ToInt32($sec, $sectorSize - 4) $guard++ } $fat = New-Object 'System.Collections.Generic.List[uint32]' foreach ($fs in $fatSectors) { $sec = Get-FotonOleSectorSlice -Bytes $Bytes -SectorSize $sectorSize -Index $fs if ($null -eq $sec) { continue } $ents = $sectorSize / 4 for ($k = 0; $k -lt $ents; $k++) { $null = $fat.Add([BitConverter]::ToUInt32($sec, 4 * $k)) } } if ($fat.Count -lt 1) { return $out } $dirBytes = Get-FotonOleFatChainBytes -Bytes $Bytes -SectorSize $sectorSize -Fat $fat -Start $dirStart -MaxBytes (1MB) if ($null -eq $dirBytes -or $dirBytes.Length -lt 128) { return $out } $entries = @() $off = 0 while (($off + 128) -le $dirBytes.Length) { $type = $dirBytes[$off + 0x42] $nameLen = [BitConverter]::ToUInt16($dirBytes, $off + 0x40) $name = '' if ($nameLen -ge 2) { $nb = [int]$nameLen - 2 if ($nb -gt 64) { $nb = 64 } if ($nb -gt 0) { $name = [Text.Encoding]::Unicode.GetString($dirBytes, $off, $nb).Trim([char]0) } } $start = [BitConverter]::ToInt32($dirBytes, $off + 0x74) $size = [BitConverter]::ToUInt32($dirBytes, $off + 0x78) $entries += [PSCustomObject]@{ Type = $type; Name = $name; Start = $start; Size = $size } $off += 128 } $root = $entries | Where-Object { $_.Type -eq 5 } | Select-Object -First 1 $miniData = $null $miniFat = $null if ($null -ne $root -and $root.Size -gt 0 -and $root.Start -ge 0) { $miniData = Get-FotonOleFatChainBytes -Bytes $Bytes -SectorSize $sectorSize -Fat $fat -Start $root.Start -MaxBytes ([int]$root.Size) if ($miniFatStart -ge 0) { $mf = Get-FotonOleFatChainBytes -Bytes $Bytes -SectorSize $sectorSize -Fat $fat -Start $miniFatStart -MaxBytes 256KB if ($mf) { $miniFat = New-Object 'System.Collections.Generic.List[uint32]' for ($m = 0; $m -lt ($mf.Length / 4); $m++) { $null = $miniFat.Add([BitConverter]::ToUInt32($mf, 4 * $m)) } } } } $taken = 0 foreach ($e in $entries) { if ($e.Type -ne 2) { continue } if (-not $e.Name) { continue } if ($taken -ge $MaxStreams) { break } $want = [int]$e.Size if ($want -gt $MaxStreamBytes) { $want = $MaxStreamBytes } $data = $null if ($e.Size -lt $miniCutoff -and $null -ne $miniData -and $null -ne $miniFat) { $data = Get-FotonOleMiniChainBytes -MiniData $miniData -MiniFat $miniFat -MiniSize $miniSize -Start $e.Start -Size $want } else { $data = Get-FotonOleFatChainBytes -Bytes $Bytes -SectorSize $sectorSize -Fat $fat -Start $e.Start -MaxBytes $want } if ($null -eq $data) { continue } if ($data.Length -gt $want) { $trim = New-Object byte[] $want [Array]::Copy($data, 0, $trim, 0, $want) $data = $trim } $out += [PSCustomObject]@{ Name = $e.Name; Data = $data } $taken++ } } catch { } return $out } function Get-FotonOleSectorSlice { param([byte[]]$Bytes, [int]$SectorSize, [int]$Index) if ($Index -lt 0) { return $null } $off = 512 + ($Index * $SectorSize) if (($off + $SectorSize) -gt $Bytes.Length) { return $null } $buf = New-Object byte[] $SectorSize [Array]::Copy($Bytes, $off, $buf, 0, $SectorSize) return $buf } function Get-FotonOleFatChainBytes { param( [byte[]]$Bytes, [int]$SectorSize, $Fat, [int]$Start, [int]$MaxBytes ) if ($Start -lt 0) { return $null } $chunks = New-Object System.Collections.Generic.List[byte] $s = [uint32]$Start $guard = 0 while ($guard -lt 4096 -and $chunks.Count -lt $MaxBytes) { if ($s -eq 0xFFFFFFFE -or $s -eq 0xFFFFFFFF -or $s -eq 0xFFFFFFFD -or $s -eq 0xFFFFFFFC) { break } $idx = [int]$s $sec = Get-FotonOleSectorSlice -Bytes $Bytes -SectorSize $SectorSize -Index $idx if ($null -eq $sec) { break } $take = $sec.Length $remain = $MaxBytes - $chunks.Count if ($take -gt $remain) { $take = $remain } for ($i = 0; $i -lt $take; $i++) { $null = $chunks.Add($sec[$i]) } if ($idx -ge $Fat.Count) { break } $s = [uint32]$Fat[$idx] $guard++ } if ($chunks.Count -eq 0) { return $null } return [byte[]]$chunks.ToArray() } function Get-FotonOleMiniChainBytes { param( [byte[]]$MiniData, $MiniFat, [int]$MiniSize, [int]$Start, [int]$Size ) $chunks = New-Object System.Collections.Generic.List[byte] $s = [uint32]$Start $guard = 0 while ($guard -lt 4096 -and $chunks.Count -lt $Size) { if ($s -eq 0xFFFFFFFE -or $s -eq 0xFFFFFFFF) { break } $off = [int]$s * $MiniSize if (($off + $MiniSize) -gt $MiniData.Length) { break } $take = $MiniSize $remain = $Size - $chunks.Count if ($take -gt $remain) { $take = $remain } for ($i = 0; $i -lt $take; $i++) { $null = $chunks.Add($MiniData[$off + $i]) } $idx = [int]$s if ($idx -ge $MiniFat.Count) { break } $s = [uint32]$MiniFat[$idx] $guard++ } if ($chunks.Count -eq 0) { return $null } return [byte[]]$chunks.ToArray() } |