Public/Registry/ConvertTo-UnescapedRegistryStrings.ps1

using namespace System.Text.RegularExpressions

function ConvertTo-UnescapedRegistryStrings {

    param (
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [String[]] $String
    )

    begin {
        $ValuesList = [System.Collections.Generic.List[Object]]@()
        $RegistryHiveAbbreviations = @{
            HKEY_CLASSES_ROOT   = 'HKCR'
            HKEY_CURRENT_USER   = 'HKCU'
            HKEY_LOCAL_MACHINE  = 'HKLM'
            HKEY_USERS          = 'HKU'
            HKEY_CURRENT_CONFIG = 'HKCC'
        }
    }

    process {

        $GroupRegistryKeys = {

            param ( [string] $Key )

            $KeyName = ""
            $Values = [System.Collections.Generic.List[String]]@()

            $ValuePatternKeyName    = [regex]'(?m)^\s*(\[.*\])\s*$'
            $ValuePatternDefault    = [regex]'(?m)^\s*@="(?:.*)"\s*$'
            $ValuePatternDefaultHex = [regex]'(?m)^@=hex\((7|2)\):[0-9a-fA-F,\\\s]+'
            $ValuePatternNamed      = [regex]'(?m)^\s*"(.*)"="(.*)"\s*$|(?m)^"(.*)"=hex\((7|2)\):[0-9a-fA-F,\\\s]+'

            [MatchCollection] $MKeyName = $ValuePatternKeyName.Matches($Key)
            [MatchCollection] $MDefault = $ValuePatternDefault.Matches($Key)
            [MatchCollection] $MDefaultHex = $ValuePatternDefaultHex.Matches($Key)
            [MatchCollection] $MNamed = $ValuePatternNamed.Matches($Key)

            if ($MDefault.Success)    { $Values.Add($MDefault.Groups[0].value) }
            if ($MDefaultHex.Success) { $Values.Add($MDefaultHex.Groups[0].value) }
            if ($MKeyName.Success)    { $KeyName = $MKeyName.Groups[1].Value }

            if ($MNamed.Success) {
                foreach ($Match in $MNamed) {
                    $Values.Add($Match.Value)
                }
            }

            [PSCustomObject]@{
                KeyName = $KeyName
                Values = $Values
            }
        }

        foreach ($CurrentRegString in $String) {

            $GroupedKeys = & $GroupRegistryKeys -Key $CurrentRegString
            if($GroupedKeys.KeyName){
                $RegistryKeyHeader = $GroupedKeys.KeyName
                $RegistryHiveLong = ($RegistryKeyHeader -split '\\')[0].TrimStart('[')
                $RegistryHiveShort = $RegistryHiveAbbreviations[$RegistryHiveLong]
            }

            foreach ($CurrentValue in $GroupedKeys.Values) {

                # Begin string type detection

                if($CurrentValue -like '*hex(7)*'){ $StringType = "REG_MULTI_SZ" }
                elseif($CurrentValue -like '*hex(2)*'){ $StringType = "REG_EXPAND_SZ" }
                elseif($CurrentValue -match '^@="'){ $StringType = "REG_SZ" }
                elseif($CurrentValue -match '^"(.*)"="'){ $StringType = "REG_SZ" }

                # Handle decoding of REG_SZ (String) #########################################################
                ##############################################################################################

                if($StringType -eq 'REG_SZ'){

                    $StringIsNamedRegex = [regex]'(?m)^"(.*)"='
                    $StringIsDefaultRegex = [regex]'(?m)^@='

                    $NamedString = $StringIsNamedRegex.Match($CurrentValue)
                    $DefaultString = $StringIsDefaultRegex.Match($CurrentValue)

                    if($NamedString.Success){
                        $CurrentValue = $StringIsNamedRegex.Replace($CurrentValue, '')
                        $ValueIsDefault = $false
                        $ValueName = $NamedString.Groups[1].Value
                    }
                    elseif($DefaultString.Success){
                        $CurrentValue = $StringIsDefaultRegex.Replace($CurrentValue, '')
                        $ValueIsDefault = $true
                        $ValueName = $null
                    }

                    $UnescapedValue = ConvertTo-RegSZUnescaped -String $CurrentValue

                    $obj = [PSCustomObject]@{
                        RegistryHive        = $RegistryHiveLong
                        RegistryHiveAbbv    = $RegistryHiveShort
                        RegistryKey         = $RegistryKeyHeader
                        OriginalType        = 'REG_SZ (String)'
                        ValueIsDefault      = $true
                        ValueHasAName       = $false
                        ValueName           = $ValueName
                        ValueUnescaped      = $UnescapedValue
                        ValueOriginal       = $CurrentValue
                    }

                    $ValuesList.Add($obj)
                }

                # Handle decoding of REG_MULTI_SZ (Multi String) and REG_EXPAND_SZ (Expandable String) #######
                ##############################################################################################

                elseif($StringType -eq 'REG_EXPAND_SZ' -or $StringType -eq 'REG_MULTI_SZ'){

                    $StringIsMultiNamedRegex = [regex]'(?m)^"(.*)"=hex\(7\):'
                    $StringIsMultiDefaultRegex = [regex]'(?m)^@=hex\(7\):'
                    $StringIsExpandNamedRegex = [regex]'(?m)^"(.*)"=hex\(2\):'
                    $StringIsExpandDefaultRegex = [regex]'(?m)^@=hex\(2\):'

                    $StringMultiNamed = $StringIsMultiNamedRegex.Match($CurrentValue)
                    $StringMultiDefault = $StringIsMultiDefaultRegex.Match($CurrentValue)

                    $StringExpandNamed = $StringIsExpandNamedRegex.Match($CurrentValue)
                    $StringExpandDefault = $StringIsExpandDefaultRegex.Match($CurrentValue)

                    if($StringMultiNamed.Success){
                        $CurrentValue = $StringIsMultiNamedRegex.Replace($CurrentValue, '')
                        $ValueName = $StringMultiNamed.Groups[1].Value
                        $ValueHasAName = $true
                        $ValueIsDefault = $false
                        $OriginalType = 'REG_MULTI_SZ (Multi String)'
                    }
                    elseif($StringMultiDefault.Success){
                        $CurrentValue = $StringIsMultiDefaultRegex.Replace($CurrentValue, '')
                        $ValueName = $null
                        $ValueHasAName = $false
                        $ValueIsDefault = $true
                        $OriginalType = 'REG_MULTI_SZ (Multi String)'
                    }
                    elseif($StringExpandNamed.Success){
                        $CurrentValue = $StringIsExpandNamedRegex.Replace($CurrentValue, '')
                        $ValueName = $StringExpandNamed.Groups[1].Value
                        $ValueHasAName = $true
                        $ValueIsDefault = $false
                        $OriginalType = 'REG_EXPAND_SZ (Expandable String)'
                    }
                    elseif($StringExpandDefault.Success){
                        $CurrentValue = $StringIsExpandDefaultRegex.Replace($CurrentValue, '')
                        $ValueName = $null
                        $ValueHasAName = $false
                        $ValueIsDefault = $true
                        $OriginalType = 'REG_EXPAND_SZ (Expandable String)'
                    }

                    $CurrentValue = $CurrentValue -replace '\\', '' -replace '(\s*)', ''
                    $ValueBytes = [System.Convert]::FromHexString($CurrentValue.Replace(',', ''))
                    $ValueUnescaped = [Text.Encoding]::Unicode.GetString($ValueBytes)

                    $obj = [PSCustomObject]@{
                        RegistryHive       = $RegistryHiveLong
                        RegistryHiveAbbv   = $RegistryHiveShort
                        RegistryKey        = $RegistryKeyHeader
                        OriginalType       = $OriginalType
                        ValueIsDefault     = $ValueIsDefault
                        ValueHasAName      = $ValueHasAName
                        ValueName          = $ValueName
                        ValueUnescaped     = $ValueUnescaped
                        ValueOriginalBytes = $ValueBytes
                    }

                    $ValuesList.Add($obj)
                }
            }

            $ValuesList

        }
    }
}