Private/ConvertFrom-CertutilRegistry.ps1

function ConvertFrom-CertutilRegistry {
    <#
    .SYNOPSIS
        Parses the text output of `certutil -getreg <value>` into a typed value.
    .DESCRIPTION
        Pure text transform (no external calls) so it is unit-testable with fixture
        strings. Handles the common REG types produced by certutil:
          - REG_DWORD / REG_QWORD -> [long] (hex form preferred)
          - REG_MULTI_SZ -> [string[]]
          - REG_SZ -> [string]
          - REG_BINARY -> [byte[]] (hex bytes reassembled)
        Returns $null when no value could be parsed.
    .PARAMETER Text
        The raw stdout captured from certutil -getreg.
    .PARAMETER ValueName
        The registry value name that was requested (used to anchor the parse).
    .OUTPUTS
        [long] | [string[]] | [string] | [byte[]] | $null
    .EXAMPLE
        ConvertFrom-CertutilRegistry -Text $out -ValueName 'EditFlags'
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [AllowNull()]
        [AllowEmptyString()]
        [string] $Text,

        [Parameter(Mandatory = $true)]
        [string] $ValueName
    )

    if ([string]::IsNullOrWhiteSpace($Text)) {
        return $null
    }

    $lines = $Text -split "`r?`n"

    $leaf = $ValueName
    if ($ValueName.Contains('\')) {
        $leaf = $ValueName.Substring($ValueName.LastIndexOf('\') + 1)
    }
    $escLeaf = [regex]::Escape($leaf)

    foreach ($line in $lines) {
        $m = [regex]::Match($line, ('^\s*' + $escLeaf + '\s+REG_(DWORD|QWORD)\s*=\s*([0-9a-fA-F]+)\b'))
        if ($m.Success) {
            $hex = $m.Groups[2].Value
            try {
                return [System.Convert]::ToInt64($hex, 16)
            }
            catch {
                $dm = [regex]::Match($line, '\((\d+)\)')
                if ($dm.Success) { return [long] $dm.Groups[1].Value }
                return $null
            }
        }
    }

    for ($i = 0; $i -lt $lines.Count; $i++) {
        $m = [regex]::Match($lines[$i], ('^\s*' + $escLeaf + '\s+REG_MULTI_SZ\s*='))
        if ($m.Success) {
            $items = New-Object System.Collections.ArrayList
            for ($j = $i + 1; $j -lt $lines.Count; $j++) {
                $ln = $lines[$j]
                if ([string]::IsNullOrWhiteSpace($ln)) { break }
                if ($ln -match 'REG_[A-Z_]+\s*=') { break }
                $im = [regex]::Match($ln, '^\s*\d+:\s*(.+?)\s*$')
                if ($im.Success) {
                    [void]$items.Add($im.Groups[1].Value.Trim())
                }
                else {
                    $trim = $ln.Trim()
                    if (-not [string]::IsNullOrWhiteSpace($trim)) { [void]$items.Add($trim) }
                }
            }
            return @($items.ToArray())
        }
    }

    for ($i = 0; $i -lt $lines.Count; $i++) {
        $m = [regex]::Match($lines[$i], ('^\s*' + $escLeaf + '\s+REG_BINARY\s*='))
        if ($m.Success) {
            $bytes = New-Object System.Collections.ArrayList
            $started = $false
            for ($j = $i + 1; $j -lt $lines.Count; $j++) {
                $ln = $lines[$j]
                if ([string]::IsNullOrWhiteSpace($ln)) { continue }
                if ($ln -match 'REG_[A-Z_]+\s*=') { break }
                $bm = [regex]::Match($ln, '^\s*[0-9a-fA-F]{4}\s+((?:[0-9a-fA-F]{2}\s*){1,16})')
                if ($bm.Success) {
                    $started = $true
                    $hexBytes = [regex]::Matches($bm.Groups[1].Value, '[0-9a-fA-F]{2}')
                    foreach ($hb in $hexBytes) {
                        [void]$bytes.Add([System.Convert]::ToByte($hb.Value, 16))
                    }
                }
                elseif ($started) {
                    break
                }
            }
            if ($bytes.Count -gt 0) {
                return [byte[]] $bytes.ToArray()
            }
            return $null
        }
    }

    foreach ($line in $lines) {
        $m = [regex]::Match($line, ('^\s*' + $escLeaf + '\s+REG_SZ\s*=\s*(.+?)\s*$'))
        if ($m.Success) {
            return [string] $m.Groups[1].Value
        }
    }

    return $null
}