【实战】验证可执行文件可靠性| Windows 应急响应

0x01 简介

相信部分朋友已经看过我们的 《Windows 应急响应手册》了,我们这边也得到部分朋友的正向反馈,包括工具、方法等。

Windows 版的应急响应手册中常规安全检查部分第一版就包含了 30 多个检查项目,按照我们的风格,每个检查项基本都给出了 Windows 默认的情况(书中以 Windows Server 2016 为例),对于存在大量检查项的(例如大量的 dll 文件需要检查),基本也都给出了 Powershell 脚本。

如果大家详细看了这些 Powershell 脚本或者看我们的描述可以发现,其实就是找到检查项,进而找到可执行文件(exedll 等),之后验证签名是否通过,这个做法仔细想是存在问题的:恶意程序也可以拥有有效的签名,所以只检查是否验证通过是不可靠的

0x02 目的说明

单纯验证每个可执行文件是否为恶意,这不是我们的工作,这是主机/终端管理程序、杀毒软件、沙箱软件的工作,对某一个可执行文件如何进行分析也不是这篇文章的目的,这是部分恶意软件/代码分析师的工作

这篇文章的目的是在应急响应过程中,面对中等数量的可执行文件等待验证,如何快速将可疑文件挑选出来

我选择的方法是先验证签名,签名通过后再验证发布者或者叫签名者 (Publisher) 名称是否为微软官方,如果两者有一个不满足,则认为是可疑文件

如果大家有更好的方法,欢迎根据应急手册上的联系方式联系我们讨论

0x03 弃用方案

为什么不采用杀毒软件对要检查的文件进行查杀?

首先是在应急响应手册的常规检查阶段包含了全盘查杀,其次并不是所有应急场景都包含杀毒程序并且杀毒程序的病毒库能够及时 联网/不联网 更新

为什么不采用沙箱进行检查?

这是出于保密要求,很多场景下客户内部没有沙箱,如果将可执行文件等直接放在互联网上的沙箱进行检查,可能会导致文件泄漏

为什么不用 Hash 验证?

Windows 升级可能会涉及部分程序,导致 Hash 变化

为什么不收集起来,人工分析?

有些时候量比较大,对于应急响应场景不现实,而且导出文件也可能造成文件泄漏

先验签名再验证发布者的方法会导致误杀吧?

是的,可以说除了微软的都会被列为可疑文件,这是我们在应急响应场景下优于杀毒软件的地方,我们检查的地方都是可以被用来做权限维持等操作的地方,第三方软件不会很多,我们的目的也是找出它们,之后进行针对性的分析

先验证签名再验证发布者的方法检验通过的程序是不是完全问题?

并不是完全是

首先得保证验证本身是没问题的,这里就涉及两个部分:1. 系统存储的证书没问题,2. 验证过程没有被篡改;

其次私钥可能会被盗嘛,或者因为漏洞绕过验证;

需要注意的是还有一种情况:对于嵌入式签名,签名本身存储位置是可以被放置 payload 的,但是默认不会执行,想要执行得有额外的加载器,文章后面部分会详细说这个事,这里拿出来说是担心部分用户在非常规安全检查阶段验证某个程序的时候被这种隐藏 payload 的方法给欺骗了

0x04 Windows 签名简介

Windows 签名一般有两种形式,一种是针对文件的;一种是针对文件集合(.cat文件)的,我们接下来简单介绍一下这两种形式

1. 针对文件集合签名

微软官方叫针对目录文件进行签名,如果看过之前关于 Linux 更新源那期文章的朋友应该还记得,Linux 是将某个文件夹中文件的 hash 等信息集中到一个文件中,之后对该文件进行签名的,而不是对某个应用程序单独签名,Windows 中针对文件集合(某个目录内的所有文件)的签名也是一个道理

这个过程都是大同小异,无非就是收集 hash 等信息,之后使用私钥配合各种算法进行签名,之后操作系统通过分发的公钥验证签名

2. 针对文件签名

针对文件的签名就聚焦到单个文件了,也就是上面提到的方案的验证对象

简单来说就是将可执行文件进行 Hash 运算(可采用不同的 Hash 算法)后,生成一个值,之后通过私钥对该值进行签名,最后将签名相关内容存储到可执行文件中的一个段(或者叫一个部分吧)

图片

图片

这里大家可以思考一下,是不是存在一些逻辑悖论:

我要计算一个文件的 Hash 之后再将计算的结果处理后塞到文件里,那文件的 Hash 是不是就变了,这样的话,操作系统在校验的时候是不是就得忽略签名相关的这个段(其实还有部分要去掉的),采用和签名是一样的算法进行 Hash 计算,之后比对值是否一致

这样一来,签名这个部分本身的校验就被忽略了,所以之前就有安全研究人员提出在这部分添加 payload ,但值得注意的是,这部分不属于可执行部分,只能用来存储 payload ,需要额外的加载器才能运行,除非文件本身可实现加载器的功能

因此,经过 先验证签名,后验证发布者 的方式验证通过的程序不一定与官方程序一模一样,不一定是完全无害的,这里费这么多口舌是为了提醒大家这个问题,有点黑加白的意思,而不是白加黑

0x05 针对文件签名详述

大家可能会觉得奇怪,你刚刚简述了,为什么又来个详述,这是因为看网络上的文章以及各种GPT 时有个问题我觉得不太对,困扰我2天了


图片

http://p7.qhimg.com/t01fc41fe42a2a5e702.png

来自 https://www.anquanke.com/post/id/84470

图片

img

来自 https://www.wosign.com/column/ssl_20220222.htm

图片

img

来自 https://cloud.tencent.com/developer/article/2185414


还有来自官方的系列文章

https://learn.microsoft.com/zh-cn/windows-hardware/drivers/install/digital-signatures

这些内容都是在描述 Windows 签名验证过程,按照他们的描述,以及网络上的文章描述大致如下

  1. 开发者生成密钥对以及证书请求文件

  2. 向证书颁发机构(以下简称CA)

  3. CA 核验开发者身份没问题后,用 CA 自己的私钥签名一张证书,颁发给开发者,证书内容是开发者的公钥以及开发者部分信息

  4. 开发者对二进制程序进行 Hash 运算,之后通过开发者的私钥进行加密也就是数字签名并嵌入到二进制文件中

  5. 将二进制分发给用户,用户的操作系统获取文件后,解密签名,采用签名同样的方法计算 Hash

  6. 对比一致则验证通过

这是最开始看到的,这里产生的疑问是,操作系统是怎么得到开发者的公钥的?没有描述单独下载的过程嘛,证书好像没用上啊?之后带着疑问开始找资料,这时候看的资料就包括上面的几个了

这时候就搞清楚了,原来数字证书和数字签名都是被嵌入到二进制文件中的,这就很好理解为什么操作系统能够获取到开发者的公钥了

  1. 开发者生成密钥对以及证书请求文件

  2. 向证书颁发机构(以下简称CA)

  3. CA 核验开发者身份没问题后,用 CA 自己的私钥签名一张证书,颁发给开发者,证书内容是开发者的公钥以及开发者部分信息

  4. 开发者对二进制程序进行 Hash 运算,之后通过开发者的私钥进行加密也就是数字签名并嵌入到二进制文件中

  5. 同时,将CA签名过的数字证书也嵌入到二进制文件中

  6. 将二进制分发给用户,用户的操作系统获取文件后,验证证书签名,获取开发者公钥,解密数字签名,采用签名同样的方法计算 Hash

  7. 对比一致则验证通过

这个时候流程就和上面的图片里一致了,文章介绍本来到这里就应该结束了,但是我有一个疑问:

操作系统是如何验证开发者证书的签名的呢?要知道开发者证书的签名是 CA (证书颁发机构) 私钥签名的

这时候可能很多朋友说了,Windows 操作系统或者浏览器等都会内置部分证书的,搞过 Burpsuite 的都见过,这部分证书是根证书,不就可以用来验证开发者证书的签名

这应该是在几年前我参加面试的时候,有个面试官就问过我这个问题:离线的操作系统是如何验证签名的,他给出的答案就是系统内置根证书。没想到多年以后,这个问题也成了回旋镖

当然了,熟悉我们的小伙伴肯定明白,如果这样就解决了问题,我肯定不会出来写,我们先看一下 Windows 内置的证书

Windows + r 输入mmc

图片

图片

图片

图片

图片

可以看到,操作系统确实是内置了根证书颁发机构的证书,我细数了一下,这里共有 20 个,第三方根证书颁发机构都包含在这里了,全世界这么多人要申请证书,如果仅仅由这 20 个受信任根证书的颁发机构私钥签名,颁发,吊销,那可能都得忙到冒火星,这还不是最主要的,最关键的受信任的根证书的私钥这么频繁地使用,如果其中一张根证书颁发机构的私钥泄漏了,那影响是非同小可的

所以详细看应用程序的签名,可以看到并不是直接由上面系统内置的根证书签的

图片

颁发者是 Microsoft Code Signing PCA 2011 , 这并不在 Windows 操作系统内置受信任的根证书存储中

证书是证书,存储是存储,这个也说一下,简单来说上面那些像文件夹一样的都是存储,里面存的是证书,这里说是希望大家看别人文章的时候不会混淆

那问题来了,难道说某个根证书的公钥能解密这个根证书下面的所有的CA私钥签名的证书吗?公私钥不是一对一的吗???难道调兵遣将的虎符不止一个?

所以这不得不引起疑问对吧,这个时候又开始查询资料,把官方资料都看了好几遍了,没提这事,官方的知识平台确实比较次,看了很多文章后,有些人提出了一个概念 —— 证书链

图片

可以看到证书是有一个链的, Microsoft Corporation 证书是由 Microsoft Code Signing PCA 2011 签名的,Microsoft Code Signing PCA 2011 是由 Microsoft Root Certificate Authority 2011 签名的

证书链是如何验证的呢?

在  Microsoft Corporation 证书中会记录是哪个 CA 将证书签名后颁布给开发者的

图片

之后操作系统会发起网络请求,寻找包含 Microsoft Code Signing PCA 2011 公钥的证书,当然这个证书是被上一级 CA 的私钥签名的,直到最终到达根证书颁发机构

抓包并测试验证某个程序的签名

图片

确实会发起相关请求,再次测试验证某个程序的签名就不会再次发起请求了

图片

应该是证书公钥被缓存了,准确地说, Microsoft Code Signing PCA 2011 这张证书叫做中间证书,这样就可以层层校验,最终验证开发者的证书中的公钥是否可以使用


本来一切又该结束了,可恶的是咱们又是搞安全的,比较严谨,如果验证开发者证书的时候没有网络怎么办呢?

这是个好问题,带着这个问题我们测试一下

图片

图片

还是能够验证成功,是不是软件的问题,用 Powershell 测试一下

图片

Powershell 也验证成功,说明软件没问题

难道是刚才的缓存问题?重启系统,再次验证

图片

重启后还是验证成功了,会不会是 Wireshark 的中间证书也被系统内置了,在根证书或中间证书那里

图片

它这个还多一层,我们去查查吧

图片

第二层就没查到,这个时候大家得明白,按理说没有网络,这个证书链最多也就找到上一层,甚至上一层的详细情况都看不到,只能看到个名字

难道说刚才的缓存还在吗?

为了验证这个问题,我们在无网络的情况下(直接不分配网卡)新建一个 Windows Server 2016 系统,通过虚拟机工具映射文件夹的方式将二进制文件传递进去

图片

图片

重新安装系统的情况下,完全离线都能够还原证书链,并且验证成功

这个时候又开始查资料了,在官方的资料里发现了一个叫交叉证书的东西,看了几遍,也没有完全理解,但好在似乎在 21 年就已经停止了,于是我们在无网络的情况下(不分配网卡)安装了一个 Windows Server 2022 ,这样总能验证了吧

图片

得,不是交叉证书的事

这个时候有开始找各种资料,并且各种 GPT 问,GPT们给出的答案中比较可信的还是证书缓存,但是如何查看缓存,GPT 没有给出

在系统安装以及启动过程中会涉及到很多签名校验,如果不能获取到证书缓存,就很难确定是不是真的因为证书缓存,关键似乎也没有其他可能了

这怎么办呢?千山鸟飞绝,万径人踪灭呀,我想到一个办法,你能缓存 Windows 自己的那些程序中间签名不奇怪,缓存 Wireshark 的也算是可以接受,你不至于在系统安装和启动过程中把所有软件的中间证书都缓存了吧

于是我们从腾讯管家平台下载了大量的软件进行测试,我就不信你能把这些软件的中间证书都缓存了

图片

结果有签名的程序大部分能够签名通过,欣喜若狂发现几个没通过的,结果放到可联网的 Windows Server 2016 中也是一样不通过,测试了一定数量以后,实在是测试不动了,关键是没有找到任何一个案例

现在还有一种办法,我们自己伪造一整套证书,包括根证书、中间证书、开发者证书,将根证书和中间证书都放到操作系统中,这样就可以模拟正常的签名验证过程,之后再将中间证书删除掉,再次验证,看看能不能通过

如果还是能够通过验证,找出完整的证书链,说明是由缓存导致的验证成功,你听我这个词,“导致的”, 但这也有一个问题,如果没有验证成功,也可能是因为操作系统在删除证书的时候直接把缓存也删除掉了

最关键的是,这个过程也挺复杂的,于是放弃这个方案


到这里本来又应该结束了,因为通过案例证明,其实我们的验证方案是有效且可靠的,但是吧,不搞清楚我会一直想着这个事,而且把没有完全验证过的方法写入到应急响应手册里也不太妥当

于是我打算研究研究 Windows 二进制文件结果,之后详细看看签名和证书部分到底是什么情况,能不能找到一些关键字,之后找到 Windows 的签名验证的程序,大不了再搞一下逆向,看看能不能发现蛛丝马迹

关于 Windows 二进制格式,官方还是有详细资料的

https://learn.microsoft.com/zh-cn/windows/win32/debug/pe-format#the-attribute-certificate-table-image-only

这里面找到 属性证书表(Attribute Certificate Table)

图片

图片

这部分引用了一个 docx 文件  —— Windows 验证码可移植可执行签名格式

https://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/Authenticode_PE.docx

建议大家用 word 而不是 wps 打开,不然里面的图可能会有些问题

图片

这就直接交给有道翻译吧,感谢有道翻译的文档翻译功能,一点一点儿看的时候发现了一个问题

图片

哎呀!!!原来微软官方文档里都写清楚了,属性证书表里不只是开发者证书,还有所有中间证书

GPT 都白学习了呀,没学透啊!不过总算是解释清楚了

正好一个学弟专门写过相关的论文,我的这个结论其实就是他论文中的一部分,感谢学弟

要是早知道就好了,但探究一圈过来以后,对这个过程印象就比较深了,当然本篇文章介绍的也不是全部细节,如果大家感兴趣可以去实践一下

0x06  Powershell 实现验证方案

$file = 'c:\windows\system32\cmd.exe'

$microsoftCNS = @('Microsoft Corporation', 'Microsoft Windows')

# 获取可执行文件的数字签名
$signature = Get-AuthenticodeSignature -FilePath $file

if ($signature.Status -eq 'Valid') {
    Write-Output "文件的数字签名有效。"

    # 获取签名的发布者信息
    $publisher = $signature.SignerCertificate.Subject

    # $publisher = "CN=Microsoft Windows, CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"

    # 解析发布者信息以提取 CN 字段的值
    $cnValues = @(($publisher -split ', ' | Where-Object { $_ -like 'CN=*' }).Substring(3))

    # 验证 CN 字段数量
    if ($cnValues.Count -eq 1) {
        $cnValue = $cnValues[0]
        Write-Output "CN 字段的值: $cnValue"

        # 判断 CN 字段是否为微软官方
        if ($microsoftCNS -contains $cnValue) {
            Write-Output "CN 字段值为微软官方。"
        } else {
            Write-Output "CN 字段值不是微软官方。"
        }
    } elseif ($cnValues.Count -gt 1) {
        Write-Output "错误:找到多个 CN 字段。"
    } else {
        Write-Output "错误:未找到 CN 字段。"
    }
} else {
    Write-Output "文件的数字签名无效。"
}

这是一个 demo ,经过测试发现微软的官方签名发布者(CN) 还不止一个,所以这里使用了数组,方便大家后续添加新的微软官方签名或者大家认可的非微软签名

图片

尝试测试非微软官方的程序

图片

这两个微软官方的签名发布者是通过 system32 文件夹以及 SysinternalsSuite 对比提取出来的,如何才能获取操作系统默认的所有已签名的应用程序的签名发布者信息呢?

我的电脑只有一个 C 盘,通过 Powershell 帮我们来完成

$rootPath = "C:\"

# 用于存储已发现的 CN 值和对应的文件地址
$cnFileMap = @{}

# 处理文件,获取 CN 值并记录对应的文件地址
function Process-File($file) {
    try {
        # 获取可执行文件的数字签名
        $signature = Get-AuthenticodeSignature -FilePath $file.FullName -ErrorAction Stop

        if ($signature -ne $null -and $signature.Status -eq 'Valid') {
            # 获取签名的发布者信息
            $publisher = $signature.SignerCertificate.Subject

            # 解析发布者信息以提取 CN 字段的值
            $cnValue = ($publisher -split ', ' | Where-Object { $_ -like 'CN=*' }).Substring(3)

            if ($cnValue) {
                if (-not $cnFileMap.ContainsKey($cnValue)) {
                    # 添加新的 CN 值到映射表
                    $cnFileMap[$cnValue] = $file.FullName
                }
            }
        }
    } catch {
        Write-Host "无法访问文件: $($file.FullName)"
    }
}

# 递归遍历目录并处理每个文件
Get-ChildItem -Path $rootPath -Recurse -ErrorAction SilentlyContinue | ForEach-Object {
    $item = $_

    if ($item.PSIsContainer) {
        # 如果是目录,则继续递归遍历其中的文件
        Get-ChildItem -Path $item.FullName -File -ErrorAction SilentlyContinue | ForEach-Object {
            Process-File $_
        }
    } else {
        # 如果是文件,则直接处理
        Process-File $item
    }
}

# 打印所有的 CN 值
$cnValues = $cnFileMap.Keys
Write-Host "所有的 CN 值:"
Write-Host "-----------------------------------------------"
foreach ($cn in $cnValues) {
    Write-Host $cn
}
Write-Host "-----------------------------------------------"
Write-Host ""

# 打印每个 CN 值对应的文件地址
foreach ($cn in $cnValues) {
    Write-Host "CN 值: $cn"
    Write-Host "文件地址:"
    Write-Host "- $($cnFileMap[$cn])"
    Write-Host ""
}

这个脚本会将所有可以访问的文件验证成功的签名提取出来,之后进行保存,每一个新签名都会记录第一个发现的使用该签名的文件地址

图片

所有的 CN 值:
-----------------------------------------------
Microsoft Windows Hardware Compatibility Publisher
Microsoft Corporation
Microsoft Windows
Microsoft Update
-----------------------------------------------

CN 值: Microsoft Windows Hardware Compatibility Publisher
文件地址:
- C:\Windows\System32\CatRoot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}\windows-legacy-whql.cat

CN 值: Microsoft Corporation
文件地址:
- C:\Windows\System32\MpSigStub.exe

CN 值: Microsoft Windows
文件地址:
- C:\Windows\bfsvc.exe

CN 值: Microsoft Update
文件地址:
- C:\Windows\SoftwareDistribution\SIH\eng\SIHEng.dll

其中 Microsoft Windows Hardware Compatibility Publisher 对应的文件 C:\Windows\System32\CatRoot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}\windows-legacy-whql.cat 就是我们上面提到的针对文件集合(目录文件)签名

图片

最终的 demo 为

$file = 'c:\windows\system32\cmd.exe'

$microsoftCNS = @('Microsoft Corporation', 'Microsoft Windows', 'Microsoft Windows Hardware Compatibility Publisher', 'Microsoft Update')

# 获取可执行文件的数字签名
$signature = Get-AuthenticodeSignature -FilePath $file

if ($signature.Status -eq 'Valid') {
    Write-Output "文件的数字签名有效。"

    # 获取签名的发布者信息
    $publisher = $signature.SignerCertificate.Subject

    # $publisher = "CN=Microsoft Windows, CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"

    # 解析发布者信息以提取 CN 字段的值
    $cnValues = @(($publisher -split ', ' | Where-Object { $_ -like 'CN=*' }).Substring(3))

    # 验证 CN 字段数量
    if ($cnValues.Count -eq 1) {
        $cnValue = $cnValues[0]
        Write-Output "CN 字段的值: $cnValue"

        # 判断 CN 字段是否为微软官方
        if ($microsoftCNS -contains $cnValue) {
            Write-Output "CN 字段值为微软官方。"
        } else {
            Write-Output "CN 字段值不是微软官方。"
        }
    } elseif ($cnValues.Count -gt 1) {
        Write-Output "错误:找到多个 CN 字段。"
    } else {
        Write-Output "错误:未找到 CN 字段。"
    }
} else {
    Write-Output "文件的数字签名无效。"
}

0x07 COM劫持检查实战

应急响应手册v1.0 给出的版本,大家主要看 Verify-FileSignature 函数就好,这也是当时想到的,把验证步骤单独做一个函数,方便后期调整

# 定义函数来进行签名校验
function Verify-FileSignature {
    param (
        [Parameter(Mandatory=$true)]
        [ValidateScript({Test-Path $_ -PathType Leaf})]
        [string]$FilePath
    )
    
    if (Test-Path -Path $FilePath -PathType Leaf) {
        $signature = Get-AuthenticodeSignature -FilePath $FilePath
        if ($signature.Status -eq 'Valid') {
            return "Valid"
        } else {
            return "Invalid"
        }
    } else {
        return "File Not Found"
    }
}

# 定义函数来检查注册表地址
function Check-RegistryPaths {
    param (
        [Parameter(Mandatory=$true)]
        [string[]]$RegistryPaths
    )

    $invalidSignatures = @()

    foreach ($registryPath in $RegistryPaths) {
        if (Test-Path -Path $registryPath) {
            $subkeys = Get-ChildItem -Path $registryPath
            foreach ($subkey in $subkeys) {
                $inprocServer32Path = Join-Path -Path $subkey.PSPath -ChildPath "InprocServer32"
                if (Test-Path -Path $inprocServer32Path) {
                    $defaultPropertyValue = (Get-ItemProperty -Path $inprocServer32Path -Name "(default)" -ErrorAction SilentlyContinue)."(default)"
                    if ($defaultPropertyValue) {
                        # 此处加 Trim('"') 是为了防止类似于 Defender 这种“有个性”的软件胡乱设置注册表
                        $binaryFilePath = $defaultPropertyValue.Trim().Trim('"')
                        $binaryFilePath = [Environment]::ExpandEnvironmentVariables($binaryFilePath)
                        if (Test-Path $binaryFilePath) {
                            $result = Verify-FileSignature -FilePath $binaryFilePath
                            if ($result -eq "Invalid") {
                                $invalidSignatures += @{
                                    RegistryPath = $subkey
                                    BinaryFilePath = $binaryFilePath
                                } 
                                Write-Host "Signature is invalid for file: $binaryFilePath" -ForegroundColor Red
                            } elseif ($result -eq "Valid") {
                                Write-Host "Signature is valid for file: $binaryFilePath " -ForegroundColor Green
                            }
                        } else {
                            $dllFileName = Split-Path -Leaf $binaryFilePath
                            $found = $false
                            $searchPaths = @(
                                (Join-Path -Path $env:SystemRoot -ChildPath $dllFileName),
                                (Join-Path -Path $env:SystemRoot -ChildPath "System32\$dllFileName")
                            )
                            foreach ($path in $searchPaths) {
                                if (Test-Path $path) {
                                    $found = $true
                                    $result = Verify-FileSignature -FilePath $path
                                    if ($result -eq "Invalid") {
                                        $invalidSignatures += @{
                                            RegistryPath = $subkey
                                            BinaryFilePath = $path
                                        }
                                        Write-Host "Signature is invalid for file: $path" -ForegroundColor Red
                                    } elseif ($result -eq "Valid") {
                                        Write-Host "Signature is valid for file: $path " -ForegroundColor Green
                                    }
                                    break
                                }
                            }
                            if (-not $found) {
                                Write-Host "Could not find file '$dllFileName' in default search paths. Skipping signature verification."  -ForegroundColor Yellow
                            }
                        }
                    } else {
                        Write-Host "Binary file path is empty for subkey $($subkey.PSChildName)."  -ForegroundColor Yellow
                    }
                }
            }
        }
    }

    # 打印不通过的签名验证信息
    if ($invalidSignatures.Count -gt 0) {
        Write-Output ""
        Write-Output ""
        Write-Output "--------------------------------------------------------"
        Write-Host "Invalid signatures:" -ForegroundColor Red
        foreach ($invalidSignature in $invalidSignatures) {
            $registryPath = $invalidSignature.RegistryPath
            $binaryFilePath = $invalidSignature.BinaryFilePath
            Write-Host "Registry path: $registryPath" -ForegroundColor Yellow
            Write-Host "Binary file path: $binaryFilePath" -ForegroundColor Yellow
            Write-Output ""
        }
        Write-Output "--------------------------------------------------------"
    }
}

# 要检查的注册表地址数组
$registryPaths = @(
    "Registry::HKEY_CURRENT_USER\Software\Classes\CLSID",
    "Registry::HKEY_CLASSES_ROOT\CLSID",
    "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Objects\"
)

# 调用函数进行检查
Write-Host "Starting signature verification..."

Check-RegistryPaths -RegistryPaths $registryPaths

Write-Host "Signature verification completed."

增加验证步骤后的版本,大家还是主要看 Verify-FileSignature 函数

$microsoftCNS = @('Microsoft Corporation', 'Microsoft Windows', 'Microsoft Windows Hardware Compatibility Publisher', 'Microsoft Update')

# 定义函数来进行签名校验
function Verify-FileSignature {
    param (
        [Parameter(Mandatory=$true)]
        [ValidateScript({Test-Path $_ -PathType Leaf})]
        [string]$FilePath
    )
    
    if (Test-Path -Path $FilePath -PathType Leaf) {
        $signature = Get-AuthenticodeSignature -FilePath $FilePath
        
        if ($signature.Status -eq 'Valid') {
            $publisher = $signature.SignerCertificate.Subject
            
            # 解析发布者信息以提取 CN 字段的值
            $cnValues = @(($publisher -split ', ' | Where-Object { $_ -like 'CN=*' }).Substring(3))

            if ($cnValues.Count -eq 1) {
                $cnValue = $cnValues[0]
                # Write-Output "CN 字段的值: $cnValue"
        
                # 判断 CN 字段是否为微软官方
                if ($microsoftCNS -contains $cnValue) {
                    # Write-Output "CN 字段值为微软官方。"
                    return "Valid"
                } 
            } 
        } 

        return "Invalid"

    } 
    
    return "File Not Found"

}

# 定义函数来检查注册表地址
function Check-RegistryPaths {
    param (
        [Parameter(Mandatory=$true)]
        [string[]]$RegistryPaths
    )

    $invalidSignatures = @()

    foreach ($registryPath in $RegistryPaths) {
        if (Test-Path -Path $registryPath) {
            $subkeys = Get-ChildItem -Path $registryPath
            foreach ($subkey in $subkeys) {
                $inprocServer32Path = Join-Path -Path $subkey.PSPath -ChildPath "InprocServer32"
                if (Test-Path -Path $inprocServer32Path) {
                    $defaultPropertyValue = (Get-ItemProperty -Path $inprocServer32Path -Name "(default)" -ErrorAction SilentlyContinue)."(default)"
                    if ($defaultPropertyValue) {
                        # 此处加 Trim('"') 是为了防止类似于 Defender 这种“有个性”的软件胡乱设置注册表
                        $binaryFilePath = $defaultPropertyValue.Trim().Trim('"')
                        $binaryFilePath = [Environment]::ExpandEnvironmentVariables($binaryFilePath)
                        if (Test-Path $binaryFilePath) {
                            $result = Verify-FileSignature -FilePath $binaryFilePath
                            if ($result -eq "Invalid") {
                                $invalidSignatures += @{
                                    RegistryPath = $subkey
                                    BinaryFilePath = $binaryFilePath
                                } 
                                Write-Host "Signature is invalid for file: $binaryFilePath" -ForegroundColor Red
                            } elseif ($result -eq "Valid") {
                                Write-Host "Signature is valid for file: $binaryFilePath " -ForegroundColor Green
                            }
                        } else {
                            $dllFileName = Split-Path -Leaf $binaryFilePath
                            $found = $false
                            $searchPaths = @(
                                (Join-Path -Path $env:SystemRoot -ChildPath $dllFileName),
                                (Join-Path -Path $env:SystemRoot -ChildPath "System32\$dllFileName")
                            )
                            foreach ($path in $searchPaths) {
                                if (Test-Path $path) {
                                    $found = $true
                                    $result = Verify-FileSignature -FilePath $path
                                    if ($result -eq "Invalid") {
                                        $invalidSignatures += @{
                                            RegistryPath = $subkey
                                            BinaryFilePath = $path
                                        }
                                        Write-Host "Signature is invalid for file: $path" -ForegroundColor Red
                                    } elseif ($result -eq "Valid") {
                                        Write-Host "Signature is valid for file: $path " -ForegroundColor Green
                                    }
                                    break
                                }
                            }
                            if (-not $found) {
                                Write-Host "Could not find file '$dllFileName' in default search paths. Skipping signature verification."  -ForegroundColor Yellow
                            }
                        }
                    } else {
                        Write-Host "Binary file path is empty for subkey $($subkey.PSChildName)."  -ForegroundColor Yellow
                    }
                }
            }
        }
    }

    # 打印不通过的签名验证信息
    if ($invalidSignatures.Count -gt 0) {
        Write-Output ""
        Write-Output ""
        Write-Output "--------------------------------------------------------"
        Write-Host "Invalid signatures:" -ForegroundColor Red
        foreach ($invalidSignature in $invalidSignatures) {
            $registryPath = $invalidSignature.RegistryPath
            $binaryFilePath = $invalidSignature.BinaryFilePath
            Write-Host "Registry path: $registryPath" -ForegroundColor Yellow
            Write-Host "Binary file path: $binaryFilePath" -ForegroundColor Yellow
            Write-Output ""
        }
        Write-Output "--------------------------------------------------------"
    }
}

# 要检查的注册表地址数组
$registryPaths = @(
    "Registry::HKEY_CURRENT_USER\Software\Classes\CLSID",
    "Registry::HKEY_CLASSES_ROOT\CLSID",
    "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Objects\"
)

# 调用函数进行检查
Write-Host "Starting signature verification..."

Check-RegistryPaths -RegistryPaths $registryPaths

Write-Host "Signature verification completed."

图片

发现的四个程序其中三个是 PD 虚拟机的组件,一个是 Python 组件,这是因为我的虚拟机中安装了PD虚拟机工具和Python

【 Windows Server 2016 】默认情况

图片

红队知识库icon-default.png?t=N7T8https://pc.fenchuan8.com/#/index?forum=70943&yqm=5XBP8

高质量安全学习圈子

下方详情,扫码加入

图片

免责声明

由于传播、利用本公众号所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,本公众号及作者不为此承担任何责任,一旦造成后果请自行承担!如有侵权烦请告知,我们会立即删除并致歉。谢谢!

  • 22
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Windows是由微软公司开发的一种操作系统。它具有易用性、兼容性和强大的功能。Windows系统首次发布于1985年,此后,其不断地升级和发展,发展出了多个版本,如Windows 95、Windows 98、Windows 2000、Windows XP、Windows Vista、Windows 7、Windows 8和Windows 10等。 Windows的界面具有友好的用户操作界面和图形化界面,使得普通用户也能够方便地使用电脑。其系统的兼容性很强,大部分软件和硬件都可以与之兼容。同时,Windows拥有大量的应用程序和游戏,用户可以通过这些来扩展系统的功能和增加趣味性。 除此之外,Windows还提供了一系列高级功能和设置,如安全性、网络连接、多媒体处理、远程控制等。这些功能可满足不同用户的需求。 总之,Windows作为使用最广泛的操作系统之一,其稳定性和易用性受到广泛认可,使得大部分用户能够轻松地利用它进行工作和娱乐,成为了当今世界的主流操作系统之一。 ### 回答2: Windows是一种操作系统,由微软公司开发。它是世界上最流行的操作系统之一,被广泛用于个人电脑和服务器。Windows提供了一个基于图形用户界面(GUI)的交互式桌面环境,用户可以从桌面上访问文件、程序和设备。Windows还提供了许多其他功能,如网络连接、安全性和数据备份。Windows系统的版本包括Windows 10、Windows 8、Windows 7、Windows Vista和Windows XP等。其中,Windows 10是最新和最广泛使用的版本。Windows 10具有新的功能和更新的应用程序,如Windows相机、Windows照片、Cortana助手和Microsoft Edge浏览器。Windows操作系统也提供了广泛的支持和用户社区,使得用户可以轻松获取帮助和支持。总的来说,Windows是一种功能齐全、易于使用和广泛使用的操作系统,它被广泛用于各种设备和应用程序中。 ### 回答3: Windows是由微软公司开发的一款操作系统,是全球使用最广泛的操作系统之一。Windows系统的开发历史可以追溯到1981年,从最初的DOS操作系统到现在的Windows 10系统,经历了多个版本的更新与完善。Windows系统简单易用,具有强大的兼容性和丰富的软件支持,可以满足用户在不同领域的各种需求。 Windows系统的主要功能包括文件管理、网络连接、多媒体播放、游戏娱乐、办公应用等。Windows系统还提供了大量的系统工具和服务,如磁盘清理、防病毒软件、网络安全等,以保障用户的系统安全和稳定性。 作为国际知名软件品牌,Windows系统一直以来都致力于在用户体验、系统性能和数据安全方面不断提升,目前Windows 10系统已经融入了人工智能和云计算等先进技术,为用户提供更加精准和高效的服务体验。 总之,Windows系统是一款功能强大、易用、安全可靠的操作系统,无论是个人用户还是企业机构,都能够从中受益并实现更好的生产和生活效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吉吉说安全

感谢打赏,交个朋友!有困难找我

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值