VirusTotalAnalyzer.psm1

function ConvertTo-VTBody {
    <#
    .SYNOPSIS
    Converts file to memory stream to create body for Invoke-RestMethod and send it to Virus Total.
 
    .DESCRIPTION
    Converts file to memory stream to create body for Invoke-RestMethod and send it to Virus Total.
 
    .PARAMETER FileInformation
    Path to a file to send to Virus Total
 
    .PARAMETER Boundary
    Boundary information to say where the file starts and ends.
 
    .EXAMPLE
    $Boundary = [Guid]::NewGuid().ToString().Replace('-', '')
    ConvertTo-VTBody -File $File -Boundary $Boundary
 
    .NOTES
    Notes
 
    #>

    [cmdletBinding()]
    param(
        [parameter(Mandatory)][System.IO.FileInfo] $FileInformation,
        [string] $Boundary
    )
    [byte[]] $CRLF = 13, 10 # ASCII code for CRLF

    $MemoryStream = [System.IO.MemoryStream]::new()

    $BoundaryInformation = [System.Text.Encoding]::ASCII.GetBytes("--$Boundary")
    $MemoryStream.Write($BoundaryInformation, 0, $BoundaryInformation.Length)
    $MemoryStream.Write($CRLF, 0, $CRLF.Length)

    $FileData = [System.Text.Encoding]::ASCII.GetBytes("Content-Disposition: form-data; name=`"file`"; filename=$($FileInformation.Name);")
    $MemoryStream.Write($FileData, 0, $FileData.Length)
    $MemoryStream.Write($CRLF, 0, $CRLF.Length)

    $ContentType = [System.Text.Encoding]::ASCII.GetBytes('Content-Type:application/octet-stream')
    $MemoryStream.Write($ContentType, 0, $ContentType.Length)

    $MemoryStream.Write($CRLF, 0, $CRLF.Length)
    $MemoryStream.Write($CRLF, 0, $CRLF.Length)

    $FileContent = [System.IO.File]::ReadAllBytes($FileInformation.FullName)
    $MemoryStream.Write($FileContent, 0, $FileContent.Length)

    $MemoryStream.Write($CRLF, 0, $CRLF.Length)
    $MemoryStream.Write($BoundaryInformation, 0, $BoundaryInformation.Length)

    $Closure = [System.Text.Encoding]::ASCII.GetBytes('--')
    $MemoryStream.Write($Closure, 0, $Closure.Length)
    $MemoryStream.Write($CRLF, 0, $CRLF.Length)

    , $MemoryStream.ToArray()
}
function Get-VirusReport {
    <#
    .SYNOPSIS
    Get the report from Virus Total about file, hash, url, ip address or domain.
 
    .DESCRIPTION
    Get the report from Virus Total about file, hash, url, ip address or domain.
 
    .PARAMETER ApiKey
    Provide ApiKey from Virus Total.
 
    .PARAMETER FileHash
    Provide FileHash to check. You can do this with Get-FileHash.
 
    .PARAMETER File
    Provide FilePath to a file to check.
 
    .PARAMETER Url
    Provide Url to check on Virus Total
 
    .PARAMETER IPAddress
    Provide IPAddress to check on Virus Total
 
    .PARAMETER DomainName
    Provide DomainName to check on Virus Total
 
    .PARAMETER Search
    Search for file hash, URL, domain, IP address or Tag comments.
 
    .EXAMPLE
    $VTApi = 'ApiKey from VirusTotal'
 
    Get-VirusReport -ApiKey $VTApi -FileHash 'BFF77EECBB2F7DA25ECBC9D9673E5DC1DB68DCC68FD76D006E836F9AC61C547E'
    Get-VirusReport -ApiKey $VTApi -File 'C:\Support\GitHub\PSPublishModule\Releases\v0.9.47\PSPublishModule.psm1'
    Get-VirusReport -ApiKey $VTApi -DomainName 'evotec.xyz'
    Get-VirusReport -ApiKey $VTApi -IPAddress '1.1.1.1'
 
    .NOTES
    General notes
    #>

    [CmdletBinding(DefaultParameterSetName = 'FileInformation')]
    Param(
        [Parameter(Mandatory)][string] $ApiKey,
        [Parameter(ParameterSetName = "Analysis", ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [string] $AnalysisId,
        [Parameter(ParameterSetName = "Hash", ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [string] $Hash,
        [alias('FileHash')][Parameter(ParameterSetName = "FileInformation", ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [System.IO.FileInfo] $File,
        [alias('Uri')][Parameter(ParameterSetName = "Url", ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Uri] $Url,
        [Parameter(ParameterSetName = "IPAddress", ValueFromPipeline , ValueFromPipelineByPropertyName)]
        [string] $IPAddress,
        [Parameter(ParameterSetName = "DomainName", ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [string] $DomainName,
        [Parameter(ParameterSetName = "Search", ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [string] $Search
    )
    Process {
        $RestMethod = @{}
        if ($PSCmdlet.ParameterSetName -eq 'FileInformation') {
            $VTFileHash = Get-FileHash -LiteralPath $File -Algorithm SHA256
            $RestMethod = @{
                Method  = 'GET'
                Uri     = "https://www.virustotal.com/api/v3/files/$($VTFileHash.Hash)"
                Headers = @{
                    "Accept"   = "application/json"
                    'X-Apikey' = $ApiKey
                }
            }
        } elseif ($PSCmdlet.ParameterSetName -eq "Analysis") {
            $SearchQueryEscaped = [uri]::EscapeUriString($AnalysisId)
            $RestMethod = @{
                Method  = 'GET'
                Uri     = "https://www.virustotal.com/api/v3/analyses/$SearchQueryEscaped"
                Headers = @{
                    "Accept"   = "application/json"
                    'X-Apikey' = $ApiKey
                }
            }

        } elseif ($PSCmdlet.ParameterSetName -eq "Hash") {
            $RestMethod = @{
                Method  = 'GET'
                Uri     = "https://www.virustotal.com/api/v3/files/$Hash"
                Headers = @{
                    "Accept"   = "application/json"
                    'X-Apikey' = $ApiKey
                }
            }
        } elseif ($PSCmdlet.ParameterSetName -eq "Url") {
            $RestMethod = @{
                Method  = 'POST'
                Uri     = "https://www.virustotal.com/api/v3/urls/$Url"
                Headers = @{
                    "Accept"   = "application/json"
                    'X-Apikey' = $ApiKey
                }
            }
        } elseif ($PSCmdlet.ParameterSetName -eq "IPAddress") {
            $RestMethod = @{
                Method  = 'GET'
                Uri     = "http://www.virustotal.com/api/v3/ip_addresses/$IPAddress"
                Headers = @{
                    "Accept"   = "application/json"
                    'X-Apikey' = $ApiKey
                }
            }
        } elseif ($PSCmdlet.ParameterSetName -eq "DomainName") {
            $RestMethod = @{
                Method  = 'GET'
                Uri     = "http://www.virustotal.com/api/v3/domains/$DomainName"
                Headers = @{
                    "Accept"   = "application/json"
                    'X-Apikey' = $ApiKey
                }
            }
        } elseif ($PSCmdlet.ParameterSetName -eq 'Search') {
            #$SearchQueryEscaped = [System.Web.HttpUtility]::UrlEncode(($Search)
            #$SearchQueryEscaped = [uri]::EscapeDataString($Search)
            $SearchQueryEscaped = [uri]::EscapeUriString($Search)
            $RestMethod = @{
                Method  = 'GET'
                Uri     = "http://www.virustotal.com/api/v3/search?query=$SearchQueryEscaped"
                Headers = @{
                    "Accept"   = "application/json"
                    'X-Apikey' = $ApiKey
                }
            }
        }
        Try {
            $InvokeApiOutput = Invoke-RestMethod @RestMethod -ErrorAction Stop
            $InvokeApiOutput
        } catch {
            if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                throw
            } else {
                Write-Warning -Message "Get-VirusReport - Using $($PSCmdlet.ParameterSetName) task failed with error: $($_.Exception.Message)"
            }
        }
    }
}


function New-VirusScan {
    <#
    .SYNOPSIS
    Send a file, file hash or url to VirusTotal for a scan.
 
    .DESCRIPTION
    Send a file, file hash or url to VirusTotal for a scan using Virus Total v3 Api
 
    .PARAMETER ApiKey
    ApiKey to use for the scan. This key is available only for registred users (free).
 
    .PARAMETER Hash
    Provide a file hash to scan on VirusTotal (file itself is not sent)
 
    .PARAMETER FileHash
    Porvide a file which hash will be used to send to Virus Total (file itself is not sent)
 
    .PARAMETER File
    Provide a file path for a file to sendto Virus Total.
 
    .PARAMETER Url
    Parameter description
 
    .EXAMPLE
    $VTApi = 'YourApiCode'
 
    New-VirusScan -ApiKey $VTApi -Url 'evotec.pl'
    New-VirusScan -ApiKey $VTApi -Url 'https://evotec.pl'
 
    .EXAMPLE
    $VTApi = 'YourApiCode
 
    # Submit file to scan
    $Output = New-VirusScan -ApiKey $VTApi -File "C:\Users\przemyslaw.klys\Documents\WindowsPowerShell\Modules\AuditPolicy\AuditPolicy.psd1"
    $Output | Format-List
 
    # Since the output will return scan ID we can use it to get the report
    $OutputScan = Get-VirusReport -ApiKey $VTApi -AnalysisId $Output.data.id
    $OutputScan | Format-List
    $OutputScan.Meta | Format-List
    $OutputScan.Data | Format-List
 
    .NOTES
    API Reference: https://developers.virustotal.com/reference/files-scan
 
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string] $ApiKey,
        [Parameter(ParameterSetName = "Hash", ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [string] $Hash,
        [Parameter(ParameterSetName = "FileHash", ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [string] $FileHash,
        [Parameter(ParameterSetName = "FileInformation", ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [System.IO.FileInfo] $File,
        [alias('Uri')][Parameter(ParameterSetName = "Url", ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Uri] $Url
    )
    process {
        $RestMethod = @{}
        if ($PSCmdlet.ParameterSetName -eq 'FileInformation') {
            $Boundary = [Guid]::NewGuid().ToString().Replace('-', '')
            $RestMethod = @{
                Method      = 'POST'
                Uri         = 'https://www.virustotal.com/api/v3/files'
                Headers     = @{
                    "Accept"   = "application/json"
                    'x-apikey' = $ApiKey
                }
                Body        = ConvertTo-VTBody -File $File -Boundary $Boundary
                ContentType = 'multipart/form-data; boundary=' + $boundary
            }
        } elseif ($PSCmdlet.ParameterSetName -eq "Hash") {
            $RestMethod = @{
                Method  = 'POST'
                Uri     = "https://www.virustotal.com/api/v3/ files/$Hash/analyse"
                Headers = @{
                    "Accept"   = "application / json"
                    'X-Apikey' = $ApiKey
                }
            }
        } elseif ($PSCmdlet.ParameterSetName -eq "FileHash") {
            if (Test-Path -LiteralPath $FileHash) {
                $VTFileHash = Get-FileHash -LiteralPath $FileHash -Algorithm SHA256
                $RestMethod = @{
                    Method  = 'POST'
                    Uri     = "https://www.virustotal.com/api/v3/files/$($VTFileHash.Hash)/analyse"
                    Headers = @{
                        "Accept"   = "application/json"
                        'X-Apikey' = $ApiKey
                    }
                }
            } else {
                Write-Warning -Message "New-VirusScan - File $FileHash doesn't exists. Skipping..."
            }
        } elseif ($PSCmdlet.ParameterSetName -eq "Url") {
            $RestMethod = @{
                Method  = 'POST'
                Uri     = 'https://www.virustotal.com/api/v3/urls'
                Headers = @{
                    "Accept"       = "application/json"
                    'X-Apikey'     = $ApiKey
                    "Content-Type" = "application/x-www-form-urlencoded"
                }
                Body    = @{ 'url' = [uri]::EscapeUriString($Url) }
            }
        }
        if ($RestMethod.Count -gt 0) {
            try {
                $InvokeApiOutput = Invoke-RestMethod @RestMethod -ErrorAction Stop
                $InvokeApiOutput
            } catch {
                if ($PSBoundParameters.ErrorAction -eq 'Stop') {
                    throw
                } else {
                    Write-Warning -Message "New-VirusScan - Using $($PSCmdlet.ParameterSetName) task failed with error: $($_.Exception.Message)"
                }
            }
        }
    }
}



# Export functions and aliases as required
Export-ModuleMember -Function @('Get-VirusReport', 'New-VirusScan') -Alias @()
# SIG # Begin signature block
# MIInRAYJKoZIhvcNAQcCoIInNTCCJzECAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCtfnMDf4utV9bo
# gKUSDbThJSMYn3cOY3jnhkVAgfXsL6CCIT0wggO3MIICn6ADAgECAhAM5+DlF9hG
# /o/lYPwb8DA5MA0GCSqGSIb3DQEBBQUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV
# BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBa
# Fw0zMTExMTAwMDAwMDBaMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lD
# ZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
# AQoCggEBAK0OFc7kQ4BcsYfzt2D5cRKlrtwmlIiq9M71IDkoWGAM+IDaqRWVMmE8
# tbEohIqK3J8KDIMXeo+QrIrneVNcMYQq9g+YMjZ2zN7dPKii72r7IfJSYd+fINcf
# 4rHZ/hhk0hJbX/lYGDW8R82hNvlrf9SwOD7BG8OMM9nYLxj+KA+zp4PWw25EwGE1
# lhb+WZyLdm3X8aJLDSv/C3LanmDQjpA1xnhVhyChz+VtCshJfDGYM2wi6YfQMlqi
# uhOCEe05F52ZOnKh5vqk2dUXMXWuhX0irj8BRob2KHnIsdrkVxfEfhwOsLSSplaz
# vbKX7aqn8LfFqD+VFtD/oZbrCF8Yd08CAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGG
# MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEXroq/0ksuCMS1Ri6enIZ3zbcgP
# MB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqGSIb3DQEBBQUA
# A4IBAQCiDrzf4u3w43JzemSUv/dyZtgy5EJ1Yq6H6/LV2d5Ws5/MzhQouQ2XYFwS
# TFjk0z2DSUVYlzVpGqhH6lbGeasS2GeBhN9/CTyU5rgmLCC9PbMoifdf/yLil4Qf
# 6WXvh+DfwWdJs13rsgkq6ybteL59PyvztyY1bV+JAbZJW58BBZurPSXBzLZ/wvFv
# hsb6ZGjrgS2U60K3+owe3WLxvlBnt2y98/Efaww2BxZ/N3ypW2168RJGYIPXJwS+
# S86XvsNnKmgR34DnDDNmvxMNFG7zfx9jEB76jRslbWyPpbdhAbHSoyahEHGdreLD
# +cOZUbcrBwjOLuZQsqf6CkUvovDyMIIFMDCCBBigAwIBAgIQBAkYG1/Vu2Z1U0O1
# b5VQCDANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGln
# aUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtE
# aWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMTMxMDIyMTIwMDAwWhcNMjgx
# MDIyMTIwMDAwWjByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5j
# MRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBT
# SEEyIEFzc3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEF
# AAOCAQ8AMIIBCgKCAQEA+NOzHH8OEa9ndwfTCzFJGc/Q+0WZsTrbRPV/5aid2zLX
# cep2nQUut4/6kkPApfmJ1DcZ17aq8JyGpdglrA55KDp+6dFn08b7KSfH03sjlOSR
# I5aQd4L5oYQjZhJUM1B0sSgmuyRpwsJS8hRniolF1C2ho+mILCCVrhxKhwjfDPXi
# TWAYvqrEsq5wMWYzcT6scKKrzn/pfMuSoeU7MRzP6vIK5Fe7SrXpdOYr/mzLfnQ5
# Ng2Q7+S1TqSp6moKq4TzrGdOtcT3jNEgJSPrCGQ+UpbB8g8S9MWOD8Gi6CxR93O8
# vYWxYoNzQYIH5DiLanMg0A9kczyen6Yzqf0Z3yWT0QIDAQABo4IBzTCCAckwEgYD
# VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYB
# BQUHAwMweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5k
# aWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0
# LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4
# oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv
# b3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy
# dEFzc3VyZWRJRFJvb3RDQS5jcmwwTwYDVR0gBEgwRjA4BgpghkgBhv1sAAIEMCow
# KAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCgYIYIZI
# AYb9bAMwHQYDVR0OBBYEFFrEuXsqCqOl6nEDwGD5LfZldQ5YMB8GA1UdIwQYMBaA
# FEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqGSIb3DQEBCwUAA4IBAQA+7A1aJLPz
# ItEVyCx8JSl2qB1dHC06GsTvMGHXfgtg/cM9D8Svi/3vKt8gVTew4fbRknUPUbRu
# pY5a4l4kgU4QpO4/cY5jDhNLrddfRHnzNhQGivecRk5c/5CxGwcOkRX7uq+1UcKN
# JK4kxscnKqEpKBo6cSgCPC6Ro8AlEeKcFEehemhor5unXCBc2XGxDI+7qPjFEmif
# z0DLQESlE/DmZAwlCEIysjaKJAL+L3J+HNdJRZboWR3p+nRka7LrZkPas7CM1ekN
# 3fYBIM6ZMWM9CBoYs4GbT8aTEAb8B4H6i9r5gkn3Ym6hU/oSlBiFLpKR6mhsRDKy
# ZqHnGKSaZFHvMIIFPTCCBCWgAwIBAgIQBNXcH0jqydhSALrNmpsqpzANBgkqhkiG
# 9w0BAQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkw
# FwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEy
# IEFzc3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMB4XDTIwMDYyNjAwMDAwMFoXDTIz
# MDcwNzEyMDAwMFowejELMAkGA1UEBhMCUEwxEjAQBgNVBAgMCcWabMSFc2tpZTER
# MA8GA1UEBxMIS2F0b3dpY2UxITAfBgNVBAoMGFByemVteXPFgmF3IEvFgnlzIEVW
# T1RFQzEhMB8GA1UEAwwYUHJ6ZW15c8WCYXcgS8WCeXMgRVZPVEVDMIIBIjANBgkq
# hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv7KB3iyBrhkLUbbFe9qxhKKPBYqDBqln
# r3AtpZplkiVjpi9dMZCchSeT5ODsShPuZCIxJp5I86uf8ibo3vi2S9F9AlfFjVye
# 3dTz/9TmCuGH8JQt13ozf9niHecwKrstDVhVprgxi5v0XxY51c7zgMA2g1Ub+3ti
# i0vi/OpmKXdL2keNqJ2neQ5cYly/GsI8CREUEq9SZijbdA8VrRF3SoDdsWGf3tZZ
# zO6nWn3TLYKQ5/bw5U445u/V80QSoykszHRivTj+H4s8ABiforhi0i76beA6Ea41
# zcH4zJuAp48B4UhjgRDNuq8IzLWK4dlvqrqCBHKqsnrF6BmBrv+BXQIDAQABo4IB
# xTCCAcEwHwYDVR0jBBgwFoAUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHQYDVR0OBBYE
# FBixNSfoHFAgJk4JkDQLFLRNlJRmMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAK
# BggrBgEFBQcDAzB3BgNVHR8EcDBuMDWgM6Axhi9odHRwOi8vY3JsMy5kaWdpY2Vy
# dC5jb20vc2hhMi1hc3N1cmVkLWNzLWcxLmNybDA1oDOgMYYvaHR0cDovL2NybDQu
# ZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwTAYDVR0gBEUwQzA3
# BglghkgBhv1sAwEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQu
# Y29tL0NQUzAIBgZngQwBBAEwgYQGCCsGAQUFBwEBBHgwdjAkBggrBgEFBQcwAYYY
# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tME4GCCsGAQUFBzAChkJodHRwOi8vY2Fj
# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEyQXNzdXJlZElEQ29kZVNpZ25p
# bmdDQS5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAmr1sz4ls
# LARi4wG1eg0B8fVJFowtect7SnJUrp6XRnUG0/GI1wXiLIeow1UPiI6uDMsRXPHU
# F/+xjJw8SfIbwava2eXu7UoZKNh6dfgshcJmo0QNAJ5PIyy02/3fXjbUREHINrTC
# vPVbPmV6kx4Kpd7KJrCo7ED18H/XTqWJHXa8va3MYLrbJetXpaEPpb6zk+l8Rj9y
# G4jBVRhenUBUUj3CLaWDSBpOA/+sx8/XB9W9opYfYGb+1TmbCkhUg7TB3gD6o6ES
# Jre+fcnZnPVAPESmstwsT17caZ0bn7zETKlNHbc1q+Em9kyBjaQRcEQoQQNpezQu
# g9ufqExx6lHYDjCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZI
# hvcNAQEMBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ
# MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNz
# dXJlZCBJRCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVow
# YjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ
# d3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290
# IEc0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjww
# IjBpM+zCpyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J5
# 8soR0uRf1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMH
# hOZ0O21x4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6
# Zu53yEioZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQ
# ecN4x7axxLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4b
# A3VdeGbZOjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9
# WV1CdoeJl2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCU
# tNJhbesz2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvo
# ZKYz0YkH4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/J
# vNNBERJb5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCP
# orF+CiaZ9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMB
# Af8wHQYDVR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXr
# oq/0ksuCMS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRt
# MGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEF
# BQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJl
# ZElEUm9vdENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgw
# BgYEVR0gADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cH
# vZqsoYcs7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8
# UgPITtAq3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTn
# f+hZqPC/Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxU
# jG/voVA9/HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8j
# LfR+cWojayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDCCBq4w
# ggSWoAMCAQICEAc2N7ckVHzYR6z9KGYqXlswDQYJKoZIhvcNAQELBQAwYjELMAkG
# A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
# Z2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MB4X
# DTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIzNTk1OVowYzELMAkGA1UEBhMCVVMxFzAV
# BgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVk
# IEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQTCCAiIwDQYJKoZIhvcN
# AQEBBQADggIPADCCAgoCggIBAMaGNQZJs8E9cklRVcclA8TykTepl1Gh1tKD0Z5M
# om2gsMyD+Vr2EaFEFUJfpIjzaPp985yJC3+dH54PMx9QEwsmc5Zt+FeoAn39Q7SE
# 2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+QtxnjupRPfDWVtTnKC3r07G1decfBmWN
# lCnT2exp39mQh0YAe9tEQYncfGpXevA3eZ9drMvohGS0UvJ2R/dhgxndX7RUCyFo
# bjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbFHc02DVzV5huowWR0QKfAcsW6Th+xtVhN
# ef7Xj3OTrCw54qVI1vCwMROpVymWJy71h6aPTnYVVSZwmCZ/oBpHIEPjQ2OAe3Vu
# JyWQmDo4EbP29p7mO1vsgd4iFNmCKseSv6De4z6ic/rnH1pslPJSlRErWHRAKKtz
# Q87fSqEcazjFKfPKqpZzQmiftkaznTqj1QPgv/CiPMpC3BhIfxQ0z9JMq++bPf4O
# uGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2LINIsVzV5K6jzRWC8I41Y99xh3pP+OcD5
# sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJjAw7W4oiqMEmCPkUEBIDfV8ju2TjY+Cm
# 4T72wnSyPx4JduyrXUZ14mCjWAkBKAAOhFTuzuldyF4wEr1GnrXTdrnSDmuZDNIz
# tM2xAgMBAAGjggFdMIIBWTASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBS6
# FtltTYUvcyl2mi91jGogj57IbzAfBgNVHSMEGDAWgBTs1+OC0nFdZEzfLmc/57qY
# rhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYB
# BQUHAQEEazBpMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20w
# QQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy
# dFRydXN0ZWRSb290RzQuY3J0MEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwz
# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZ
# MBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEAfVmO
# wJO2b5ipRCIBfmbW2CFC4bAYLhBNE88wU86/GPvHUF3iSyn7cIoNqilp/GnBzx0H
# 6T5gyNgL5Vxb122H+oQgJTQxZ822EpZvxFBMYh0MCIKoFr2pVs8Vc40BIiXOlWk/
# R3f7cnQU1/+rT4osequFzUNf7WC2qk+RZp4snuCKrOX9jLxkJodskr2dfNBwCnzv
# qLx1T7pa96kQsl3p/yhUifDVinF2ZdrM8HKjI/rAJ4JErpknG6skHibBt94q6/ae
# sXmZgaNWhqsKRcnfxI2g55j7+6adcq/Ex8HBanHZxhOACcS2n82HhyS7T6NJuXdm
# kfFynOlLAlKnN36TU6w7HQhJD5TNOXrd/yVjmScsPT9rp/Fmw0HNT7ZAmyEhQNC3
# EyTN3B14OuSereU0cZLXJmvkOHOrpgFPvT87eK1MrfvElXvtCl8zOYdBeHo46Zzh
# 3SP9HSjTx/no8Zhf+yvYfvJGnXUsHicsJttvFXseGYs2uJPU5vIXmVnKcPA3v5gA
# 3yAWTyf7YGcWoWa63VXAOimGsJigK+2VQbc61RWYMbRiCQ8KvYHZE/6/pNHzV9m8
# BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ8GV2QqYphwlHK+Z/GqSFD/yYlvZVVCsf
# gPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr9u3WfPwwggbGMIIErqADAgECAhAKekqI
# nsmZQpAGYzhNhpedMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVTMRcwFQYD
# VQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBH
# NCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwHhcNMjIwMzI5MDAwMDAw
# WhcNMzMwMzE0MjM1OTU5WjBMMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl
# cnQsIEluYy4xJDAiBgNVBAMTG0RpZ2lDZXJ0IFRpbWVzdGFtcCAyMDIyIC0gMjCC
# AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALkqliOmXLxf1knwFYIY9DPu
# zFxs4+AlLtIx5DxArvurxON4XX5cNur1JY1Do4HrOGP5PIhp3jzSMFENMQe6Rm7p
# o0tI6IlBfw2y1vmE8Zg+C78KhBJxbKFiJgHTzsNs/aw7ftwqHKm9MMYW2Nq867Lx
# g9GfzQnFuUFqRUIjQVr4YNNlLD5+Xr2Wp/D8sfT0KM9CeR87x5MHaGjlRDRSXw9Q
# 3tRZLER0wDJHGVvimC6P0Mo//8ZnzzyTlU6E6XYYmJkRFMUrDKAz200kheiClOEv
# A+5/hQLJhuHVGBS3BEXz4Di9or16cZjsFef9LuzSmwCKrB2NO4Bo/tBZmCbO4O2u
# fyguwp7gC0vICNEyu4P6IzzZ/9KMu/dDI9/nw1oFYn5wLOUrsj1j6siugSBrQ4nI
# fl+wGt0ZvZ90QQqvuY4J03ShL7BUdsGQT5TshmH/2xEvkgMwzjC3iw9dRLNDHSNQ
# zZHXL537/M2xwafEDsTvQD4ZOgLUMalpoEn5deGb6GjkagyP6+SxIXuGZ1h+fx/o
# K+QUshbWgaHK2jCQa+5vdcCwNiayCDv/vb5/bBMY38ZtpHlJrYt/YYcFaPfUcONC
# leieu5tLsuK2QT3nr6caKMmtYbCgQRgZTu1Hm2GV7T4LYVrqPnqYklHNP8lE54CL
# KUJy93my3YTqJ+7+fXprAgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYD
# VR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgG
# BmeBDAEEAjALBglghkgBhv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxq
# II+eyG8wHQYDVR0OBBYEFI1kt4kh/lZYRIRhp+pvHDaP3a8NMFoGA1UdHwRTMFEw
# T6BNoEuGSWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH
# NFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGD
# MIGAMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYB
# BQUHMAKGTGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0
# ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQEL
# BQADggIBAA0tI3Sm0fX46kuZPwHk9gzkrxad2bOMl4IpnENvAS2rOLVwEb+EGYs/
# XeWGT76TOt4qOVo5TtiEWaW8G5iq6Gzv0UhpGThbz4k5HXBw2U7fIyJs1d/2Wcuh
# wupMdsqh3KErlribVakaa33R9QIJT4LWpXOIxJiA3+5JlbezzMWn7g7h7x44ip/v
# EckxSli23zh8y/pc9+RTv24KfH7X3pjVKWWJD6KcwGX0ASJlx+pedKZbNZJQfPQX
# podkTz5GiRZjIGvL8nvQNeNKcEiptucdYL0EIhUlcAZyqUQ7aUcR0+7px6A+TxC5
# MDbk86ppCaiLfmSiZZQR+24y8fW7OK3NwJMR1TJ4Sks3KkzzXNy2hcC7cDBVeNaY
# /lRtf3GpSBp43UZ3Lht6wDOK+EoojBKoc88t+dMj8p4Z4A2UKKDr2xpRoJWCjihr
# pM6ddt6pc6pIallDrl/q+A8GQp3fBmiW/iqgdFtjZt5rLLh4qk1wbfAs8QcVfjW0
# 5rUMopml1xVrNQ6F1uAszOAMJLh8UgsemXzvyMjFjFhpr6s94c/MfRWuFL+Kcd/K
# l7HYR+ocheBFThIcFClYzG/Tf8u+wQ5KbyCcrtlzMlkI5y2SoRoR/jKYpl0rl+CL
# 05zMbbUNrkdjOEcXW28T2moQbh9Jt0RbtAgKh1pZBHYRoad3AhMcMYIFXTCCBVkC
# AQEwgYYwcjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcG
# A1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBB
# c3N1cmVkIElEIENvZGUgU2lnbmluZyBDQQIQBNXcH0jqydhSALrNmpsqpzANBglg
# hkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3
# DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEV
# MC8GCSqGSIb3DQEJBDEiBCCfHCamXMlBcbkG3YUu7T9NbjU9y2UjgxPOEraxys47
# yDANBgkqhkiG9w0BAQEFAASCAQA1NOSvo0Ant3hVGm0Q5BOsnkc/HxH3mzsB8gJP
# Fo+zQ/gyGvkXxTQs3nfyu1lXpKjjwwkdQv3l/TsRfHTB5yHMlTh+5YdR9iSdK06L
# mH2QrPRA1/y3AuXThhGYVhSUHo45RgYycM0+eM03raLEkCxQPm4OQSyFiY0Cz11N
# YYLqCAGhoGa2bsxKXc8xH7I05vtWj4H/EkwGqytvKKDfrxJl+w1h5kmcyiJrSCf4
# iwrXZjSGOAam8SEH+cRoDW/Ey16asOJzwikF07XoWiwqnVEyEu3o4lvQsLJa4JFq
# z+k/K8XtAFtE+xi8whWDX1hvIArW0rv499lPTvE4s9zfKSBFoYIDIDCCAxwGCSqG
# SIb3DQEJBjGCAw0wggMJAgEBMHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRp
# Z2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQw
# OTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQQIQCnpKiJ7JmUKQBmM4TYaXnTANBglg
# hkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcN
# AQkFMQ8XDTIyMDgxMDE1NDMxNFowLwYJKoZIhvcNAQkEMSIEIMpovdOwEbqEFDm6
# pkSLj/ZPgLxRE26IRw3cpuCevEU9MA0GCSqGSIb3DQEBAQUABIICAAMu36Nehq3N
# Q2pIsQEF5nshDmqRiRQ9par0PNJpMTgbQ8FXpXvfAu+keUb9jSaiFuudGyEl5RwR
# zwnEpbBEwO/izUM1mCxq1JuQ9m2C+QVfJAmzgJw3FsCiSrn/P0Bw7SFj90Ca+Qpb
# efq82njasLveEKU6JKgu2r6qO9Da1x36L/NOB1YW8V3G0Sv6qyu4rHeQqmAwS7DJ
# +8foaQEWWyPd//tRIkC4Etx4p736bwmGJxUdE/ymFZrxNWoyS0RgoIE7YBXWIg6n
# cU2g5MCC6J8hwJAdpqAJ9aaYPXSa1sDE/w90cyksFN2ZI6GmmVQfBm57IX4hBS7a
# sS36CPN7Rxn/Ta8z0kJWWoKByFLJO0c/oP9w0hlrysIGtmt2zgTC/958UObMzGEE
# +v7QVzaaaUnT0qlmdPF6HwUQT+0gSF9yVdBGvs+FlZCxgzkJTnOrCgxgm51lAi9K
# 1hWHWax5Z3flr6Zlr5fYFxKEge/+ix/PrxyqyWnjfAMJQzFM5k6vcu+dq/3k0mAy
# VNxfRNGPgxrECeGQ4rdjEXWIHDoDrX5sBxfsJzvAQrjgNVr03K9YZQ+hNlOotVTD
# ALjpf2KYaeor2a4nXD2EYX5tWcn8bLMDrAvHvUrUuGXVlngE56+U5Llmkn0RSn6f
# v08cS5vfybgNPaETrG5uFfUA9F1BWi1Q
# SIG # End signature block