WDACSimulation/Get-SignerInfo.psm1

Function Get-SignerInfo {
    <#
    .SYNOPSIS
        Function that takes an XML policy content as input and returns an array of Signer objects
        The output contains as much info as possible about each signer
    .INPUTS
        System.Xml.XmlDocument
    .OUTPUTS
        WDACConfig.Signer[]
    .PARAMETER XML
        The the WDAC policy XML content
    #>

    [CmdletBinding()]
    [OutputType([WDACConfig.Signer[]])]
    param(
        [System.Xml.XmlDocument]$XML
    )
    begin {
        [System.Boolean]$Verbose = $PSBoundParameters.Verbose.IsPresent ? $true : $false

        # Get User Mode Signers IDs
        $AllowedUMCISigners = [System.Collections.Generic.HashSet[System.String]]@(($XML.SiPolicy.SigningScenarios.SigningScenario.Where({ $_.value -eq '12' })).ProductSigners.AllowedSigners.AllowedSigner.SignerId)
        $DeniedUMCISigners = [System.Collections.Generic.HashSet[System.String]]@(($XML.SiPolicy.SigningScenarios.SigningScenario.Where({ $_.value -eq '12' })).ProductSigners.DeniedSigners.DeniedSigner.SignerId)

        # Get Kernel Mode Signers IDs
        $AllowedKMCISigners = [System.Collections.Generic.HashSet[System.String]]@(($XML.SiPolicy.SigningScenarios.SigningScenario.Where({ $_.value -eq '131' })).ProductSigners.AllowedSigners.AllowedSigner.SignerId)
        $DeniedKMCISigners = [System.Collections.Generic.HashSet[System.String]]@(($XML.SiPolicy.SigningScenarios.SigningScenario.Where({ $_.value -eq '131' })).ProductSigners.DeniedSigners.DeniedSigner.SignerId)

        # Unique IDs of all Allowed Signers
        $AllAllowedSigners = [System.Collections.Generic.HashSet[System.String]]@($AllowedUMCISigners.Clone())
        if ($null -ne $AllAllowedSigners -and $AllAllowedSigners.count -gt 0) {
            $AllAllowedSigners.UnionWith($AllowedKMCISigners)
        }

        # Unique IDs of all Denied Signers
        $AllDeniedSigners = [System.Collections.Generic.HashSet[System.String]]@($DeniedUMCISigners.Clone())
        if ($null -ne $AllDeniedSigners -and $AllDeniedSigners.count -gt 0) {
            $AllDeniedSigners.UnionWith($DeniedKMCISigners)
        }

        $WellKnownIDs = [System.Collections.Generic.HashSet[System.String]]::new(
            [System.String[]]@('03', '04', '05', '06', '07', '09', '0A', '0E', '0G', '0H', '0I'),
            # Make it case-insensitive
            [System.StringComparer]::InvariantCultureIgnoreCase
        )

        # WHQL EKU Hex value
        [System.String]$WHQLEKUHex = '010A2B0601040182370A0305'

        # an empty list to store the output
        $Output = New-Object -TypeName 'System.Collections.Generic.List[WDACConfig.Signer]'
    }
    process {

        # Loop through each Signer node and extract all of their information
        foreach ($Signer in $XML.SiPolicy.Signers.Signer) {

            if ($AllAllowedSigners.Contains($Signer.ID)) {
                [System.Boolean]$IsAllowed = $true
            }
            elseif ($AllDeniedSigners.Contains($Signer.ID)) {
                [System.Boolean]$IsAllowed = $false
            }
            else {
                # Skip if the current signer is neither an allowed nor a denied signer, meaning it can either be UpdatePolicySigner or SupplementalPolicySigner which we don't need for simulation
                continue
            }

            # Replacing Wellknown root IDs with their corresponding TBS values and Names (Common Names)
            # These are all root certificates, they have no leaf or intermediate certificates in their chains, that's why they're called Trusted Roots
            if ($WellKnownIDs.Contains($Signer.CertRoot.Value)) {
                switch ($Signer.CertRoot.Value) {
                    '03' {
                        $Signer.CertRoot.Value = 'D67576F5521D1CCAB52E9215E0F9F743'
                        $Signer.Name = 'Microsoft Authenticode(tm) Root Authority'
                        break
                    }
                    '04' {
                        $Signer.CertRoot.Value = '8B3C3087B7056F5EC5DDBA91A1B901F0'
                        $Signer.Name = 'Microsoft Root Authority'
                        break
                    }
                    '05' {
                        $Signer.CertRoot.Value = '391BE92883D52509155BFEAE27B9BD340170B76B'
                        $Signer.Name = 'Microsoft Root Certificate Authority'
                        break
                    }
                    '06' {
                        $Signer.CertRoot.Value = '08FBA831C08544208F5208686B991CA1B2CFC510E7301784DDF1EB5BF0393239'
                        $Signer.Name = 'Microsoft Root Certificate Authority 2010'
                        break
                    }
                    '07' {
                        $Signer.CertRoot.Value = '279CD652C4E252BFBE5217AC722205D7729BA409148CFA9E6D9E5B1CB94EAFF1'
                        $Signer.Name = 'Microsoft Root Certificate Authority 2011'
                        break
                    }
                    '09' {
                        $Signer.CertRoot.Value = '09CBAFBD98E81B4D6BAAAB32B8B2F5D7'
                        $Signer.Name = 'Microsoft Test Root Authority'
                        break
                    }
                    '0A' {
                        $Signer.CertRoot.Value = '7A4D9890B0F9006A6F77472D50D83CA54975FCC2B7EA0563490134E19B78782A'
                        $Signer.Name = 'Microsoft Testing Root Certificate Authority 2010'
                        break
                    }
                    '0E' {
                        $Signer.CertRoot.Value = 'ED55F82E1444F79CA9DCE826846FDC4E0EA3859E3D26EFEF412D2FFF0C7C8E6C'
                        $Signer.Name = 'Microsoft Development Root Certificate Authority 2014'
                        break
                    }
                    '0G' {
                        $Signer.CertRoot.Value = '68D221D720E975DB5CD14B24F2970F86A5B8605A2A1BC784A17B83F7CF500A70EB177CE228273B8540A800178F23EAC8'
                        $Signer.Name = 'Microsoft ECC Testing Root Certificate Authority 2017'
                        break
                    }
                    '0H' {
                        $Signer.CertRoot.Value = '214592CB01B59104195F80AF2886DBF85771AF42A3821D104BF18F415158C49CBC233511672CD6C432351AC9228E3E75'
                        $Signer.Name = 'Microsoft ECC Development Root Certificate Authority 2018'
                        break
                    }
                    '0I' {
                        $Signer.CertRoot.Value = '32991981BF1575A1A5303BB93A381723EA346B9EC130FDB596A75BA1D7CE0B0A06570BB985D25841E23BE944E8FF118F'
                        $Signer.Name = 'Microsoft ECC Product Root Certificate Authority 2018'
                        break
                    }
                }
            }

            #Region Scope Determinations
            # Determine the scope of the signer
            [System.String]$SignerScope = $AllowedUMCISigners.Contains($Signer.ID) ? 'UserMode' : 'KernelMode'
            #Endregion Scope Determination

            #Region File Attributes Processing
            # Determine whether the signer has a FileAttribRef, if it points to a file
            if ($Signer.FileAttribRef.RuleID) {

                # Get all the FileAttribs associated with the signer
                $FileAttribsAssociatedWithTheSigner = foreach ($ID in $Signer.FileAttribRef.RuleID) {
                    $XML.SiPolicy.FileRules.FileAttrib.Where({ $_.ID -eq $ID })
                }

                # The File Attributes property that will be added to the Signer object
                # It contains details of all File Attributes associated with the Signer
                $SignerFileAttributesProperty = New-Object -TypeName 'System.Collections.Generic.Dictionary[[System.String], [System.Collections.Generic.Dictionary[[System.String], [System.String]]]]'

                # Loop over each FileAttribute associated with the Signer
                foreach ($FileAttrib in $FileAttribsAssociatedWithTheSigner) {

                    # a temp dictionary to store the current FileAttribute details
                    $Temp = New-Object -TypeName 'System.Collections.Generic.Dictionary[[System.String], [System.String]]'

                    if ($null -ne $FileAttrib.FileName) {
                        $Temp.Add('OriginalFileName', $FileAttrib.FileName)
                        $Temp.Add('SpecificFileNameLevel', 'OriginalFileName')
                    }
                    if ($null -ne $FileAttrib.FileDescription) {
                        $Temp.Add('FileDescription', $FileAttrib.FileDescription)
                        $Temp.Add('SpecificFileNameLevel', 'FileDescription')
                    }
                    if ($null -ne $FileAttrib.InternalName) {
                        $Temp.Add('InternalName', $FileAttrib.InternalName)
                        $Temp.Add('SpecificFileNameLevel', 'InternalName')
                    }
                    if ($null -ne $FileAttrib.ProductName) {
                        $Temp.Add('ProductName', $FileAttrib.ProductName)
                        $Temp.Add('SpecificFileNameLevel', 'ProductName')
                    }
                    if ($null -ne $FileAttrib.MinimumFileVersion) {
                        $Temp.Add('MinimumFileVersion', $FileAttrib.MinimumFileVersion)
                    }
                    if ($null -ne $FileAttrib.MaximumFileVersion) {
                        $Temp.Add('MaximumFileVersion', $FileAttrib.MaximumFileVersion)
                    }

                    $SignerFileAttributesProperty.Add($FileAttrib.ID, $Temp)
                }
            }
            #Endregion File Attributes Processing

            #Region EKU Processing
            # Select the EKU nodes if they exist
            if ($XML.SiPolicy.EKUs.EKU) {

                # Create a hashtable to store the correlation between the EKU IDs and their values
                [System.Collections.Hashtable]$EKUAndValuesCorrelation = @{}

                # Add the EKU IDs and their values to the hashtable
                foreach ($EKUItem in $XML.SiPolicy.EKUs.EKU) {
                    $EKUAndValuesCorrelation.Add($EKUItem.ID, $EKUItem.Value)
                }
            }

            [System.Boolean]$HasEKU = $false
            [System.Boolean]$IsWHQL = $false

            # Convert all of the EKUs that apply to the signer to their OID values and store them with the Signer info
            [System.String[]]$CertEKUs = foreach ($EKU in $Signer.CertEKU.ID) {

                if ($EKUAndValuesCorrelation[$EKU] -eq $WHQLEKUHex) {
                    $IsWHQL = $true
                }
                $HasEKU = $true
                [WDACConfig.CertificateHelper]::ConvertHexToOID($EKUAndValuesCorrelation[$EKU])
            }
            #Endregion EKU Processing

            # Create a new instance of the Signer class in the WDACConfig Namespace And add it to the output
            $Output.Add([WDACConfig.Signer]::New(
                    $Signer.ID,
                    $Signer.Name,
                    $Signer.CertRoot.Value,
                    $Signer.CertPublisher.Value,
                    $Signer.CertIssuer.Value,
                    $CertEKUs,
                    $Signer.CertOemID.Value,
                    $Signer.FileAttribRef.RuleID,
                    $SignerFileAttributesProperty,
                    $SignerScope,
                    $IsWHQL,
                    $IsAllowed,
                    $HasEKU
                ))
        }
    }
    end {
        # Return the output array
        return $Output
    }
}
Export-ModuleMember -Function 'Get-SignerInfo'