PowerShell 技能连载 - 解析 URL

URL 并不总是(直接)指向资源。通常,URL 充当始终指向最新版本的快捷方式或静态地址。PowerShell 可以揭示资源的真实URL,您可以在许多情况下使用它。

这是一个如何解析快捷链接的示例:

1
2
3
4
5
6
7
8
# this is the URL we got:
$URLRaw = 'http://go.microsoft.com/fwlink/?LinkID=135173'
# we do not allow automatic redirection and instead read the information
# returned by the webserver ourselves:
$page = Invoke-WebRequest -Uri $URLRaw -UseBasicParsing -MaximumRedirection 0 -ErrorAction Ignore
$target = $page.Headers.Location

"$URLRaw -> $target"

这是关于如何从静态链接解析产品版本的示例。最新版的 PowerShell 总是可以使用此静态 URL 获取:

https://github.com/PowerShell/PowerShell/releases/latest

如果您想知道最新版本的实际版本号,请尝试解析 URL:

1
2
3
4
5
6
7
8
$URLRaw = 'https://github.com/PowerShell/PowerShell/releases/latest'
# we do not allow automatic redirection and instead read the information
# returned by the webserver ourselves:
$page = Invoke-WebRequest -Uri $URLRaw -UseBasicParsing -MaximumRedirection 0 -ErrorAction Ignore
$realURL = $page.Headers.Location
$version = Split-Path -Path $realURL -Leaf

"PowerShell 7 latest version: $version"

同样的方法也适用于 PowerShell Gallery 模块:

1
2
3
4
5
6
7
8
9
10
# name of a module published at powershellgallery.com
$ModuleName = 'ImportExcel'

$URL = "https://www.powershellgallery.com/packages/$ModuleName"
# get full URL (including latest version):
$page = Invoke-WebRequest -Uri $URL -UseBasicParsing -MaximumRedirection 0 -ErrorAction Ignore
$realURL = $page.Headers.Location
# return version only:
$latest = Split-Path -Path $realURL -Leaf
"Module $ModuleName latest version: $latest"

PowerShell 技能连载 - 简单的类似 grep 的文本过滤器(第 1 部分)

PowerShell 是面向对象的,因此与 Linux 和 grep 相比,文本过滤和正则表达式的应用不多。然而,有时候通过简单的文本模式来过滤命令对象是高效舒适的。

它的工作方式类似。PowerShell 确实带有类似 grep 的命令,即 Select-String cmdlet 。虽然它擅长过滤文件内容,但在尝试过滤命令输出时,它没什么用。

假设您只对运行中的服务感兴趣。按 “running” 来过滤,结果一无所获:

1
PS> Get-Service | Select-String Running

不过,这不是 Select-String 的错。Select-String 需要文本输入,而 cmdlet 通常返回强类型的对象,而不是文本。 通过将命令输出转换为字符串,就能正常工作了:

1
2
3
4
5
6
7
8
PS> Get-Service | Out-String -Stream | Select-String Running

Running AdobeARMservice Adobe Acrobat Update Service
Running AgentShellService Spiceworks Agent Shell Service
Running Appinfo Application Information
Running AppMgmt Application Management
Running AppXSvc AppX Deployment Service (AppXSVC)
...

如果您不介意最终结果是纯文本,那就太好了。这基本上就是简单文本过滤的处理代价。

不,其实不是。您可以创建自己的过滤器命令,该命令 暂时 将传入的对象转换为文本,进行文本模式匹配,然后返回未更改的强类型对象。听起来很简单,确实如此。这是 Powershell 的 grep:

1
2
3
4
filter grep ([string]$Pattern)
{
if ((Out-String -InputObject $_) -match $Pattern) { $_ }
}

运行代码,然后尝试:

1
2
3
4
5
6
7
8
9
PS> Get-Service | grep running

Status Name DisplayName
------ ---- -----------
Running AdobeARMservice Adobe Acrobat Update Service
Running AgentShellService Spiceworks Agent Shell Service
Running Appinfo Application Information
Running AppMgmt Application Management
...

它的使用非常易于使用,最重要的是,输出强类型的对象,因此您仍然可以访问其属性

1
2
3
4
5
6
7
8
9
10
PS> Get-Service | grep running | Select-Object Name, StartType, Status

Name StartType Status
---- --------- ------
AdobeARMservice Automatic Running
AgentShellService Automatic Running
Appinfo Manual Running
AppMgmt Manual Running
AppXSvc Manual Running
AudioEndpointBuilder Automatic Running

PowerShell 技能连载 - 读取 Windows 10 产品序列号

有很多脚本可以通过转换一系列二进制值来读取注册表的原始 Windows 10 产品序列号。

不幸的是,这些脚本中找到的大多数算法都是弃用的,并且计算出的产品密钥是错误的。如果您计划重新安装操作系统,则有可能在删除了原来的系统后才发现。

这行代码可能是一种更好,更简单的方法:

1
PS> (Get-CimInstance -ClassName SoftwareLicensingService).OA3xOriginalProductKey

如果它返回 “nothing”,则机器中没有安装产品序列号。否则,它将原样返回。

PowerShell 技能连载 - 为 PowerShell 创建 sudo(第 2 部分)

当我们尝试为 PowerShell 创建一个 sudo 命令——来提升单个命令的权限——在第一部分中我们创建了 sudo 函数体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function sudo
{
param
(
[Parameter(Mandatory)]
[string]
$FilePath,

[Parameter(ValueFromRemainingArguments)]
[string[]]
$ArgumentList

)

$PSBoundParameters
}

现在,让我们用实际的逻辑替换 $PSBoundParameters,以运行提升权限的命令。Start-Process 可以解决这个问题。例如,这段代码将在用户 “Tobias” 下启动提升权限的记事本:

1
PS> Start-Process -FilePath notepad -ArgumentList $env:windir\system32\drivers\etc\hosts -Verb runas

巧合的是,我们的 sudo 函数体的参数名称与 Start-Process 所需的参数名称匹配,因此实现很简单:使用 splatting,并且将用户在自动定义的 $PSBoundParameters 哈希表中传入的参数传递给 Start-Process——这就是所有步骤:

并传递在自动定义的哈希表$ psboundparameters中找到的用户供给参数,以启动过程 - 全部:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function sudo
{
param
(
[Parameter(Mandatory)]
[string]
$FilePath,

[Parameter(ValueFromRemainingArguments)]
[string[]]
$ArgumentList

)

Start-Process @PSBoundParameters -Verb Runas
}

运行代码,然后测试新的 sudo 命令。事实证明,您现在可以在脚本中运行提升权限的单个命令。同时,您将体会到 Windows 中的主要设计差异:所有命令都在自己的窗口中运行,并且无法从提升权限的命令中将结果重定向到您的 PowerShell。

虽然我们的 sudo 命令可能对提升权限很有用,但是当您想从提升权限的命令中获取信息时会受到限制。Windows 架构禁止该操作。

您在过程中学到了如何创建函数参数,例如接受可变数量参数的 “ArgumentList”。

PowerShell 技能连载 - 为 PowerShell 创建 sudo(第 1 部分)

在 Linux Shells 中,有一个名为 “sudo“ 的命令,可以通过它运行具有提升特权的命令。在 Powershell 中,您必须打开一个具有更高特权的全新 shell。

让我们尝试将 sudo 命令添加到 PowerShell。 我们想要一个名为 “sudo” 的新命令,它至少需要一个命令名称,但随后还需要一个可变的空格分隔的参数。这是函数的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function sudo
{
param
(
[Parameter(Mandatory)]
[string]
$FilePath,

[Parameter(ValueFromRemainingArguments)]
[string[]]
$ArgumentList
)

$PSBoundParameters
}

param() 块定义输入参数。$FilePath 是强制性的(必须)。$Arguments 是可选的,但通过属性 ValueFromRemainingArguments 装饰,因此它是一个所谓的“参数数组”,它能接受剩下的所有未绑定到其它形参的输入参数。

运行这段代码,然后尝试一些用例。$PSBoundParameters 显示该函数如何接收您的输入参数:

这是我测试的内容,它似乎按预期工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PS> sudo notepad c:\test

Key Value
--- -----
FilePath notepad
ArgumentList {c:\test}



PS> sudo ping 127.0.0.1 -n 1

Key Value
--- -----
FilePath ping
ArgumentList {127.0.0.1, -n, 1}

Now that the sudo function body works, in part 2 we look at the actual implantation of running commands elevated.
现在,sudo 函数体能正常工作,在第二部分中,我们将学习实际注入一个函数并提升权限。

PowerShell 技能连载 - 签名 PowerShell 脚本(第 3 部分)

在上一部分中,我们创建了一个代码签名证书,并将其用于将数字签名添加到 PowerShell 脚本文件中。 然而,将数字签名添加到 PowerShell 脚本文件中可以做什么?

使用 Get-AuthenticodeSignature 可以查看数字签名中的秘密。只需确保您调整 $Path 以指向具有数字签名的文件:

1
2
3
4
# path to a digitally signed file (adjust path to an existing signed ps1 file):
$Path = "$env:temp\test.ps1"
$Status = Get-AuthenticodeSignature -FilePath $Path | Select-Object -Property *
$Status

结果类似这样:

SignerCertificate      : [Subject]
                           CN=MyPowerShellCode

                         [Issuer]
                           CN=MyPowerShellCode

                         [Serial Number]
                           1B98E986A1D7BCB245034A0225381CA4

                         [Not Before]
                           01.05.2022 17:39:56

                         [Not After]
                           01.05.2027 17:49:56

                         [Thumbprint]
                           F4C1F9978D564E143D554F3679746B3A79E1FF87

TimeStamperCertificate :
Status                 : UnknownError
StatusMessage          : A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider
Path                   : C:\Users\tobia\AppData\Local\Temp\test.ps1
SignatureType          : Authenticode
IsOSBinary             : False

最重要的返回属性是 “Status”(及其在 “StatusMessage” 中找到的友好信息)。该属性告诉您签名的文件外的印章是否值得信赖和未被篡改:

| Status | Description |
|— |— |
| UnknownError | 文件未被篡改,但是签名用的数字证书可能不受信任 |
| HashMismatch | 自上次签名依赖,文件内容已改变 |
| Valid | 文件未被篡改,且数字证书受信任 |
| NotSigned | 文件未携带数字证书 |

最有可能的是,您会看到状态 “UnknownError”(如果您添加签名以来文件内容没有更改)或 “HashMismatch”(您或其他人确实更改了文件)。

您可能看不到 “Valid” 的原因是我们在此迷你系列中使用的证书类型:任何人都可以创建一个自签名的证书,以便攻击者可以更改脚本文件,然后使用他拥有自签名的证书以重新签名文件。

由于每个证书——公司或自签名——始终都有其独特的指纹,因此即使是对于自签名证书,您也可以通过同时检查 “Status” 和 “SignerCertificate.Thumbprint”,来确认脚本的完整性。当您还检查证书指纹时,邪恶的人就无法在不更改指纹的情况下重新签名脚本:

1
2
3
4
5
6
7
8
# thumbprint of your certificate (adjust to match yours)
$thumbprint = 'F4C1F9978D564E143D554F3679746B3A79E1FF87'

# path to a digitally signed file (adjust path to an existing signed ps1 file):
$Path = "$env:temp\test.ps1"
$Status = Get-AuthenticodeSignature -FilePath $Path | Select-Object -Property *
$ok = $Status.Status -eq 'Valid' -or ($status.Status -eq 'UnknownError' -and $status.SignerCertificate.Thumbprint -eq $thumbprint)
"Script ok: $ok"

PowerShell 技能连载 - 签名 PowerShell 脚本(第 2 部分)

在上一个技能中,我们解释了如何使用 New-SeftSignedCert 创建自签名的代码签名证书。今天,我们将使用自签名或公司代码签名证书真实地对 PowerShell 脚本进行数字签名。

为此,请使用您想要的任何 PowerShell 脚本文件。您所需要的只是它的路径。另外,您需要一个在 Windows 证书存储中存储的有效代码签名证书的路径。这是第一部分的快速回顾,以创建自签名证书,以防您没有公司证书:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$Subject = 'MyPowerShellCode'
$FriendlyName = 'My Valid PowerShell Code'

# expires 5 years from now:
$ExpirationDate = (Get-Date).AddYears(5)

# store in user personal store:
$certStore = 'Cert:\CurrentUser\my'

# create certificate:
$cert = New-SelfSignedCertificate -Subject $Subject -Type CodeSigningCert -CertStoreLocation $certStore -FriendlyName $FriendlyName -NotAfter $ExpirationDate

$thumbprint = $cert.Thumbprint

$Path = Join-Path -Path $certStore -ChildPath $thumbprint
Write-Warning "Certificate Path: $Path"

运行此代码时,您将获得一个自签名的代码签名证书,并且该代码将返回生成证书的路径,即:

Cert:\CurrentUser\my\F4C1F9978D564E143D554F3679746B3A79E1FF87

要使用您的证书,请像这样通过 Get-Item 读取它(确保修改匹配证书的路径 - 每个证书都有唯一的指纹):

1
PS> $myCert = Get-Item -Path Cert:\CurrentUser\my\F4C1F9978D564E143D554F3679746B3A79E1FF87

要将数字签名添加到 PowerShell 脚本文件(或其他能够为此问题携带数字签名的文件),请使用 Set-AuthenticodeSignature。运行以下演示代码(根据需要调整文件和证书的路径):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# digitally sign this file (adjust path to an existing ps1 file):
$Path = "$env:temp\test.ps1"

# adjust this path to point to a valid code signing certificate:
$CertPath = 'Cert:\CurrentUser\my\F4C1F9978D564E143D554F3679746B3A79E1FF87'

# if it does not exist, create a dummy file
$exists = Test-Path -Path $Path
if ($exists -eq $false)
{
'Hello World!' | Set-Content -Path $Path -Encoding UTF8
}

# read a code signing certificate to use for signing:
$myCert = Get-Item -Path $CertPath

# add a digital signature to a PS script file:
Set-AuthenticodeSignature -FilePath $Path -Certificate $myCert

# show changes inside script file:
notepad $Path

运行此代码时,在 $Path 中指定的脚本文件将打开并显示添加到脚本底部的数字签名:

Hello World!

# SIG # Begin signature block
# MIIFcAYJKoZIhvcNAQcCoIIFYTCCBV0CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU4QK+x7NLicgrIdzN+Nvxqbuq
# Qv2gggMKMIIDBjCCAe6gAwIBAgIQG5jphqHXvLJFA0oCJTgcpDANBgkqhkiG9w0B
...

恭喜,您刚刚已经对一个 PowerShell 脚本进行了数字签名!我们将在第三部分中探讨这种签名的好处。

PowerShell 技能连载 - 签名 PowerShell 脚本(第 1 部分)

如今,将数字签名添加到 PowerShell 脚本不再是黑魔法,尽管理想情况下需要从公司或可信赖的权威中获得正式的“信任”代码签名证书,但现在不再是强制性的。

在进行代码签名之前,首先了解为什么要对 PowerShell 脚本进行签名?

数字签名是脚本代码的哈希,并使用数字证书的私钥进行加密。将其视为针对脚本的“包装器”或“密封”。有了它,您现在可以随时分辨出某人是否篡改了您的脚本。没有它,您将无法实现这个目的。

如果您尚未拥有适合代码签名的数字证书,那么在 Windows 机器上,您可以使用 New-SelfSignedCertificate cmdlet 快速创建一个。

运行此行代码以创建自己的全新代码签名证书,并将其存储在您的个人证书存储中(除非您使用 -NotAfter 指定不同的到期日期,否则有效期为一年):

1
2
3
4
5
6
7
8
PS> New-SelfSignedCertificate -Subject MyPowerShellCode -Type CodeSigningCert -CertStoreLocation Cert:\CurrentUser\my -FriendlyName 'My Valid PowerShell Code'


PSParentPath: Microsoft.PowerShell.Security\Certificate::CurrentUser\my

Thumbprint Subject
---------- -------
57402F9D82231CABA4586127C99819F055AA2AF2 CN=MyPowerShellCode

要稍后在任何时候检索它,请记住它的指纹并像这样访问它(修改指纹以匹配您的其中一个证书):

1
2
3
4
5
6
7
8
9
10
11
PS> $cert = Get-Item -Path Cert:\CurrentUser\My\57402F9D82231CABA4586127C99819F055AA2AF2

PS> $cert.Subject
CN=MyPowerShellCode

PS> $cert.FriendlyName
My Valid PowerShell Code

PS> $cert.NotAfter

Monday, May 1, 2023 17:47:43

或者,如果您只记得主题或友好名称,则可以使用过滤器:

1
2
3
4
5
6
7
8
PS> Get-ChildItem -Path Cert:\CurrentUser\My -CodeSigningCert | Where-Object Subject -like *MyPowerShell*


PSParentPath: Microsoft.PowerShell.Security\Certificate::CurrentUser\My

Thumbprint Subject
---------- -------
57402F9D82231CABA4586127C99819F055AA2AF2 CN=MyPowerShellCode

在下一个技能中,我们将开始使用此证书签署 PowerShell 脚本。

PowerShell 技能连载 - 专业地处理错误

通常,PowerShell 脚本使用这样简单的错误报告形式,该报告的结构是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
# clearing global error list:
$error.Clear()
# hiding errors:
$ErrorActionPreference = 'SilentlyContinue'

# do stuff:
Stop-Service -Name Spooler
dir c:\gibtsnichtabc


# check errors at end:
$error.Count
$error | Out-GridView

尽管这没有错,但您应该了解 $error 是一个全局变量,因此,如果您在脚本中使用外部代码(即其他人写的功能或模块),这些作者可能已经使用了相同的技术,并且如果他们产生了错误并清除了全局错误列表,那么您将失去以前记录的错误。

一种更好,更健壮的方式是使用私有变量进行记录。 实际上并没有重写太多的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# hiding errors:
$ErrorActionPreference = 'SilentlyContinue'
# telling all cmdlets to use a private variable for error logging:
$PSDefaultParameterValues.Add('*:ErrorVariable', '+myErrors')
# initializing the variable:
$myErrors = $null

# do stuff:
Stop-Service -Name Spooler
dir c:\gibtsnichtabc


# check errors at end USING PRIVATE VARIABLE:
$myErrors.Count
$myErrors | Out-GridView

基本技巧是定义 -ErrorVariable 的默认参数值,并为其分配私有变量的名称。确保在名称之前添加一个 “+”,以便附加新错误,而不是覆盖现有错误。

PowerShell 技能连载 - 清理硬盘(第 2 部分)

在上一篇文章中,我们介绍了 Windows 工具 cleanmgr 及其参数 /sageset/sagerun,您可以用它们来定义和运行自动硬盘清理。

今天,我们将研究如何自定义 cleanmgr.exe 执行的实际清理任务。

此工具将所有配置存储在 Windows 注册表的这个位置:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches

在下面,您可以找到 cleanmgr 可以执行的每项清理任务的关键。在上一个技能中,我们定义了这样的自定义清理任务(请记住,使用提升的管理员权限执行它):

1
PS> cleanmgr.exe /sageset:5388

这行命令打开一个对话框窗口,您可以在其中检查应绑定到已提交 ID 5388 的清理任务。

关闭对话框后,可以在 Windows 注册表中找到这些设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
# the ID you picked when saving the options:
$id = 5388

# the name of the reg value that stores your choices:
$flag = "StateFlags$id"

# the location where user choices are stored:
$path = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\*"

# get all subkeys where your user choice was enabled:
Get-Item -Path $path |
Where-Object { $_.Property -contains $flag } # contains your registry value (StateFlags5388)
Where-Object { $_.GetValue($flag) -gt 0 } # your registry value contains a number greater than 0

执行结果准确列出了您先前在选择对话框中检查的清理器模块:

Name                           Property
----                           --------
Active Setup Temp Folders      (default)      : {C0E13E61-0CC6-11d1-BBB6-0060978B2AE6}
                               Autorun        : 1
                               Description    : These files should no longer be needed. They were originally
                                                created by a setup program that is no longer running.
                               FileList       : *.tmp
                               Flags          : {124, 0, 0, 0}
                               Folder         : C:\Windows\msdownld.tmp|?:\msdownld.tmp
                               LastAccess     : {2, 0, 0, 0}
                               Priority       : 50
                               StateFlags0001 : 2
                               StateFlags0003 : 2
                               StateFlags0033 : 0
                               StateFlags6254 : 0
                               StateFlags5388 : 2
BranchCache                    (default)      : {DE661907-527D-4d6a-B6A6-EBC7F88D9B95}
                               StateFlags0001 : 0
                               StateFlags0003 : 0
                               StateFlags0033 : 0
                               StateFlags6254 : 0
                               StateFlags5388 : 2
D3D Shader Cache               (default)      : {D8D133CD-3F26-402F-86DA-90B710751C2C}
                               Autorun        : 1
                               ReserveIDHint  : 2
                               StateFlags0001 : 0
                               StateFlags0003 : 0
                               StateFlags0033 : 0
                               StateFlags6254 : 0
                               StateFlags5388 : 2
Delivery Optimization Files    (default)      : {4057C1AD-A51F-40BB-B960-22888CEB9812}
                               Autorun        : 0
                               Description    : @C:\WINDOWS\system32\domgmt.dll,-104
                               Display        : @C:\WINDOWS\system32\domgmt.dll,-103
                               Flags          : 128
                               ReserveIDHint  : 2
                               StateFlags0001 : 0
                               StateFlags0003 : 0
                               StateFlags0033 : 0
                               StateFlags6254 : 0
                               StateFlags5388 : 2
Diagnostic Data Viewer         (default)        : {C0E13E61-0CC6-11d1-BBB6-0060978B2AE6}
database files                 Autorun          : 0
                               CleanupString    : rundll32.exe utcutil.dll,DiskCleanupEnd
                               Description      : @C:\WINDOWS\system32\utcutil.dll,-302
                               Display          : @C:\WINDOWS\system32\utcutil.dll,-301
                               FileList         : *.*
                               Flags            : 573
                               Folder           : C:\ProgramData\Microsoft\Diagnosis\EventTranscript\
                               IconPath         : C:\WINDOWS\system32\utcutil.dll,0
                               PreCleanupString : rundll32.exe utcutil.dll,DiskCleanupStart
                               Priority         : 100
                               StateFlags0001   : 0
                               StateFlags0003   : 0
                               StateFlags0033   : 0
                               StateFlags6254   : 0
                               StateFlags5388   : 2
Downloaded Program Files       (default)          : {8369AB20-56C9-11D0-94E8-00AA0059CE02}
                               AdvancedButtonText : @C:\Windows\System32\occache.dll,-1072
                               Autorun            : 1
                               Description        : @C:\Windows\System32\occache.dll,-1071
                               Display            : @C:\Windows\System32\occache.dll,-1070
                               Priority           : {100, 0, 0, 0}
                               StateFlags0001     : 2
                               StateFlags0003     : 2
                               StateFlags0033     : 0
                               StateFlags6254     : 2
                               StateFlags5388     : 2
Internet Cache Files           (default)          : {9B0EFD60-F7B0-11D0-BAEF-00C04FC308C9}
                               AdvancedButtonText : &View Files
                               Autorun            : 1
                               Description        : The Temporary Internet Files folder contains Web pages stored
                                                    on your hard disk for quick viewing.
                                                    Your personalized settings for Web pages will be left intact.
                               Display            : Temporary Internet Files
                               Priority           : 100
                               StateFlags0001     : 2
                               StateFlags0003     : 2
                               StateFlags0033     : 0
                               StateFlags6254     : 2
                               StateFlags5388     : 2
Language Pack                  (default)      : {191D5A6B-43B9-477A-BB22-656BF91228AB}
                               Autorun        : 1
                               StateFlags0001 : 0
                               StateFlags0003 : 0
                               StateFlags0033 : 0
                               StateFlags6254 : 0
                               StateFlags5388 : 2
Old ChkDsk Files               (default)      : {C0E13E61-0CC6-11d1-BBB6-0060978B2AE6}
                               Autorun        : 1
                               FileList       : *.CHK
                               Flags          : 288
                               Folder         : ?:\FOUND.000|?:\FOUND.001|?:\FOUND.002|?:\FOUND.003|?:\FOUND.004|
                                                ?:\FOUND.005|?:\FOUND.006|?:\FOUND.007|?:\FOUND.008|?:\FOUND.009
                               IconPath       : C:\WINDOWS\System32\DATACLEN.DLL,3
                               Priority       : 50
                               PropertyBag    : {60F6E464-4DEF-11d2-B2D9-00C04F8EEC8C}
                               StateFlags0001 : 2
                               StateFlags0003 : 2
                               StateFlags0033 : 0
                               StateFlags6254 : 2
                               StateFlags5388 : 2
Recycle Bin                    (default)      : {5ef4af3a-f726-11d0-b8a2-00c04fc309a4}
                               PluginType     : 2
                               StateFlags0001 : 2
                               StateFlags0003 : 0
                               StateFlags0033 : 0
                               StateFlags6254 : 0
                               StateFlags5388 : 2

每个清理器模块在 “(default)” 中具有唯一的GUID。 如您所见,GUID “{C0E13E61-0CC6-11d1-BBB6-0060978B2AE6}” 用于许多清理模块。这是一个通用的文件删除模块,您可以轻松地在自己的文件清理器模块中使用。只需在注册表键 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches 中添加新的子键,使用上面提到的 GUID,然后配置该清理器应找到并删除的文件。您可能需要查看现有的清理器,例如 regedit.exe 中的 “Old ChkDsk Files”,以找出定义要删除文件的注册表值的名称。