PowerShell笔记 - 16.注册表

本系列是一个重新学习PowerShell的笔记,内容引用自PowerShell中文博客

基础命令

你可以像导航文件系统那样来访问注册表,因为正像我们在上一章中讨论的那样,PowerShell把文件系统和注册表都当作层次信息系统来处理。
注册表中的键对应于文件系统的目录,但是键对应的值和文件系统中的文件不是非常类似。相反的,它们显示在属性栏,被以键的属性来管理。

PS C:\PowerShell> cd HKCU:
PS HKCU:\> dir | Select-Object -First 10 | Format-Table Name,View

Name                                 View
----                                 ----
HKEY_CURRENT_USER\AppEvents       Default
HKEY_CURRENT_USER\Console         Default
HKEY_CURRENT_USER\Control Panel   Default
HKEY_CURRENT_USER\Environment     Default
HKEY_CURRENT_USER\EUDC            Default
HKEY_CURRENT_USER\Keyboard Layout Default
HKEY_CURRENT_USER\Microsoft       Default
HKEY_CURRENT_USER\Network         Default
HKEY_CURRENT_USER\Printers        Default
HKEY_CURRENT_USER\Software        Default

PS HKCU:\> dir

    Hive: HKEY_CURRENT_USER

Name                           Property
----                           --------
AppEvents
Console                        ColorTable00             : 789516
                               ColorTable01             : 14300928

下面的表格列出了访问注册表所需的所有命令。

命令 描述
Dir, Get-ChildItem 列出键的内容
Cd, Set-Location 更改当前(键)目录
HKCU:, HKLM: 预定义的两个重要注册表根目录虚拟驱动器
Get-ItemProperty 读取键的值
Set-ItemProperty 设置键的值
New-ItemProperty 给键创建一个新值
Clear-ItemProperty 删除键的值内容
Remove-ItemProperty 删除键的值
New-Item, md 创建一个新键
Remove-Item, Del 删除一个键
Test-Path 验证键是否存在

注意:注册表几乎存储了Windows的核心配置。所以这也是它为什么成为我们用来读取和更改Windows配置的重要位置。因此乱搞或者误删非常危险,可能造成Windows不能启动。

“提供程序”文件系统以外的位置

PowerShell拥有一个被叫做“提供程序”的负责特定信息存储的模型化结构。在上一章,你已经使用过了文件系统提供程序,所以如果你想访问注册表,就需要一个替代文件系统的注册表提供者。在其它方面,就和上一章的操作没什么区别了。你可以在注册表中使用你在文件系统中使用过的命令。

支持的提供程序

Get-PSProvider能获取安装的提供程序列表。下面的例子中,可能没有列出你自定义的列表,因为提供程序可以随后添加。比如,PowerShell默认就没有活动目录的提供程序。

PS C:\PowerShell> Get-PSProvider

Name                 Capabilities                                      Drives
----                 ------------                                      ------
Registry             ShouldProcess                                     {HKLM, HKCU}
Alias                ShouldProcess                                     {Alias}
Environment          ShouldProcess                                     {Env}
FileSystem           Filter, ShouldProcess, Credentials                {C, Temp}
Function             ShouldProcess                                     {Function}
Variable             ShouldProcess                                     {Variable}

这里你感兴趣的可能只是Drives列,它就是用来管理各自驱动器的名称。
你也看到了,注册表提供程序挂载了驱动器HKLM:(根目录HKEY_LOCAL_MACHINE)和HKCU:(根目录HKEY_CURRENT_USER)。这些驱动器用起来,很像传统的文件系统驱动器。

PS C:\PowerShell> cd HKCU:
PS HKCU:\> dir | select -f 10 | ft Name

Name
----
HKEY_CURRENT_USER\AppEvents
HKEY_CURRENT_USER\Console
HKEY_CURRENT_USER\Control Panel
HKEY_CURRENT_USER\Environment
HKEY_CURRENT_USER\EUDC
HKEY_CURRENT_USER\Keyboard Layout
HKEY_CURRENT_USER\Microsoft
HKEY_CURRENT_USER\Network
HKEY_CURRENT_USER\Printers
HKEY_CURRENT_USER\Software

PS HKCU:\> cd ..
PS HKCU:\> cd C:\PowerShell\
PS C:\PowerShell>

从这个位置,你可以像你在真正的文件系统中做的那样,通过子目录来导航。相同的特殊字符功能也一样,除了~符号在注册表中不能识别,并且会产生错误。

然而其它驱动器,不属于这章的内容,因为我们的重点在注册表,但是作为引用还是在下面的表格中列出了所有的驱动器:

提供者 描述 示例
Alias 管理别名 Dir Alias:$alias:Dir
Environment 访问环境变量的提供者 Dir env:$env:windir
Function 列出所有定义的函数, Dir function:$function:tabexpansion
FileSystem 访问驱动器,目录和文件 Dir c:$(c:\autoexec.bat)
Registry 访问Windows注册表 Dir variable:$variable:pshome
Variable 管理定义在控制台中的所有变量 Dir variable:$variable:pshoMe
Certificate 访问证书存储区的所有证书 Dir cert:Dir cert:-recurse

创建提供程序

注册表提供程序提供访问注册表。你可以通过驱动器来定位它们。如果你想查看哪些注册表驱动器已经被注册表提供程序使用了,可以通过Get-PSDrive命令,和参数-PSProvider

PS C:\PowerShell> Get-PSDrive -PSProvider Registry

Name           Used (GB)     Free (GB) Provider      Root                                               CurrentLocation
----           ---------     --------- --------      ----                                               ---------------
HKCU                                   Registry      HKEY_CURRENT_USER
HKLM                                   Registry      HKEY_LOCAL_MACHINE

这里稍微注意下,你可能会困惑,注册表包含的根节点远不止两个。
事实上HKEY_CLASSES_ROOT根节点不是一个独立的根节点,而是指向HKEY_LOCAL_MACHINE\SOFTWARE\Classes。这意味着你可以以这个路径为起点,来创建一个新的驱动器。

New-PSDrive -name HKCR -PSProvider registry -root HKLM:\SOFTWARE\Classes
Dir HKCR:

现在,你可以去访问这个分支了。事实上,你可以访问上面表格中列出的任意根节点。
小技巧:你可以自由地创建任何额外的驱动器,尤其在你有频繁操作某个特定的注册表区域时。

New-PSDrive job1 registry "HKLM:\Software\Microsoft\Windows NT\CurrentVersion"
dir job1:

搜索注册表

使用Dir,你可以像在文件系统中那样来搜索注册表。使用注册表提供程序提供的虚拟驱动器,非常方便。驱动器HKCU:提供了KEY_CURRENT_USER根目录键的。

Cd HKCU:
Dir

如果你想像下面那样列出内容,可以使用Format-List:

Dir | Format-List
Dir | Format-List Name
Dir | Format-List *

递归搜索

注册表提供程序不支持任何过滤器,因此你不能在Dir中使用类似于-filter这样的参数。但是参数-recurse-include -exclude还是支持的。在上一节中,我们使用过它来递归地搜索文件系统。这个活在注册表中也可以这么干。比如,你想知道注册表中的那个位置包含了“PowerShell”,可以这样使用:

PS C:\PowerShell> Dir HKCU:, HKLM: -recurse -include *PowerShell*

    Hive: HKEY_CURRENT_USER\Console

Name                           Property
----                           --------
%SystemRoot%_System32_WindowsP ColorTable05     : 5645313
owerShell_v1.0_powershell.exe  ColorTable06     : 15789550
                               FaceName         : Lucida Console
                               FontFamily       : 54
                               FontWeight       : 400
                               PopupColors      : 243
                               QuickEdit        : 1
                               ScreenBufferSize : 196608120
                               ScreenColors     : 86
                               WindowSize       : 3276920
%SystemRoot%_SysWOW64_WindowsP ColorTable05     : 5645313
owerShell_v1.0_powershell.exe  ColorTable06     : 15789550
                               FaceName         : Lucida Console
                               FontFamily       : 54
                               FontWeight       : 400
                               PopupColors      : 243
                               QuickEdit        : 1
                               ScreenBufferSize : 196608120
                               ScreenColors     : 86
                               WindowSize       : 3276920

    Hive: HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\Telemetry\PersistentPropertyBag

Name                           Property
----                           --------
powershell                     VS.TelemetryApi.ChannelsDisposeLatency       : 18
                               VS.TelemetryApi.DroppedEventsDuringDisposing : 0
                               VS.TelemetryApi.TotalDisposeLatency          : 674

    Hive: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Notifications\Settings\{1AC14E77-02E7-4E5D-B744-2
EB1AE5198B7}

上面的命令会搜索HKEY_CURRENT_USER根节点和HKEY_LOCAL_MACHINE根节点。它会找出所有包括”PowerShell”单词的键。因为可能有很多键包含了单词“PowerShell”,所以使用了通配符。这样的搜索操作可能会产生错误信息,因为搜索的过程,也是读取注册表的每个子键的过程,但是部分键值没有访问权限就会报错。如果想从结果中过滤掉这些错误信息,可以使用参数-ErrorAction,并给它指定值为SilentlyContinue

Dir HKCU:, HKLM: -recurse -include PowerShell -ErrorAction SilentlyContinue

单个注册表键

Dir获取的每一个注册表键(Microsoft.Win32.Registry 对象)对应下面的属性。

PS C:\PowerShell>  $key = Dir HKCU: | Select-Object -first 1
PS C:\PowerShell> $key.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    RegistryKey                              System.MarshalByRefObject

PS C:\PowerShell> $key.GetType().FullName
Microsoft.Win32.RegistryKey
PS C:\PowerShell> $key | Get-Member -MemberType *property

   TypeName: Microsoft.Win32.RegistryKey

Name          MemberType   Definition
----          ----------   ----------
Property      NoteProperty string[] Property=System.String[]
PSChildName   NoteProperty string PSChildName=AppEvents
PSDrive       NoteProperty PSDriveInfo PSDrive=HKCU
PSIsContainer NoteProperty bool PSIsContainer=True
PSParentPath  NoteProperty string PSParentPath=Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER
PSPath        NoteProperty string PSPath=Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\AppEvents
PSProvider    NoteProperty ProviderInfo PSProvider=Microsoft.PowerShell.Core\Registry
Handle        Property     Microsoft.Win32.SafeHandles.SafeRegistryHandle Handle {get;}
Name          Property     string Name {get;}
SubKeyCount   Property     int SubKeyCount {get;}
ValueCount    Property     int ValueCount {get;}
View          Property     Microsoft.Win32.RegistryView View {get;}

下面的表格列出一个Microsoft.Win32.Registry(注册表键)对象的重要属性:

属性 描述
Name 在注册编辑器中显示的键的路径
Property 当前键的名称
PSChildName 存储在键中的值的名称数组
PSDrive 键的注册表根节点
PSParentPath 父键
PSPath 键的PowerShell路径,使用Dir可以查看该路径下键的内容
PSProvider 提供程序的名称:注册表
SubKeyCount(SKC) 子键个数
ValueCount (VC) 键中的值个数
PSIsContainer 总是为True

PowerShell怎样寻址注册表

我们来仔细看下分配一个注册表键的属性。比如:通过注册表编辑器打开的HKLM:\Software\Microsoft\PowerShell\1 键,这里存放的是PowerShell的一些内部设置。
在PowerShell中使用Get-Item访问该键:

PS C:\PowerShell> $key = Get-Item HKLM:\Software\Microsoft\PowerShell\1
PS C:\PowerShell> $key.Name
HKEY_LOCAL_MACHINE\Software\Microsoft\PowerShell\1
PS C:\PowerShell> $key | Format-List ps*

PSPath        : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\Software\Microsoft\PowerShell\1
PSParentPath  : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\Software\Microsoft\PowerShell
PSChildName   : 1
PSDrive       : HKLM
PSProvider    : Microsoft.PowerShell.Core\Registry
PSIsContainer : True

果然,Name属性获取的是键的完整名称,更有意思的是这些属性的名称,前面加了‘PS’,它们将注册表键分成多个片段。

键对应的值

在打开的注册表编辑器中,右边的属性列有三个值,汇报给PowerShell的只有两个值:

$key.ValueCount
2

貌似一个值丢了。仔细查看属性“Property”可以发现PowerShell记录了哪些值:

$key.Property
 Install
 PID

这些属性名对应注册表编辑器中的属性名,唯独(Default)没有出现。也对,在编辑器中默认值为空。注册表编辑器识别为:“数值未设置”。
如果你要获取这些值的内容,可以将注册表的PS路径传递给Get-ItemProperty

PS C:\PowerShell>  Get-ItemProperty $key.PSPath

Install      : 1
PID          : 89383-100-0001260-04309
PSPath       : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\Software\Microsoft\PowerShell\1
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\Software\Microsoft\PowerShell
PSChildName  : 1
PSProvider   : Microsoft.PowerShell.Core\Registry

这种方式会把该键对应的所有值自动获取并输出。另外,除了一些常规属性,这些值还包括了PowerShell添加的其他属性。如果你只想获取特定的值:

# 获取注册表键的所有值
$values = Get-ItemProperty $key.PSPath
# 获取Install的值:
$values.Install
1
# 获取PID的值:
$values.PID
89383-100-0001260-04309

如果你想获取键的所有值,但是不想包含PowerShell自动添加的属性。可以这样做:

$key = Get-Item HKLM:\Software\Microsoft\PowerShell\1
$values = Get-ItemProperty $key.PSPath
foreach ($value in $key.Property) 
{ 
  $value + "=" + $values.$value
}
#输出
Install=1
PID=89383-100-0001260-04309

小技巧:如果你已经将路径定位到该注册表路径下,还可以使用另外一种方法获取所有值:

PS C:\PowerShell> Cd HKLM:\Software\Microsoft\PowerShell\1
PS HKLM:\Software\Microsoft\PowerShell\1> dir

    Hive: HKEY_LOCAL_MACHINE\Software\Microsoft\PowerShell\1

Name                           Property
----                           --------
PowerShellEngine               ApplicationBase         : C:\Windows\System32\WindowsPowerShell\v1.0
                               ConsoleHostAssemblyName : Microsoft.PowerShell.ConsoleHost, Version=1.0.0.0, Culture=neu
                               tral, PublicKeyToken=31bf3856ad364e35, ProcessorArchitecture=msil
                               ConsoleHostModuleName   : C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShel
                               l.ConsoleHost.dll
                               PowerShellVersion       : 2.0
                               PSCompatibleVersion     : 1.0, 2.0
                               RuntimeVersion          : v2.0.50727
PowerShellSnapIns
ShellIds

PS HKLM:\Software\Microsoft\PowerShell\1> (Get-ItemProperty .).PID
89383-100-0001260-04309

这里将”.”传递给Get-ItemProperty。当然前提条件是先得CD到目标键的路径下。

如果你想输出多个键的多个值,应当使用Dir。Dir的结果可以通过管道传递给ForEach-Object。这样,你就可以一次性获取某个键的所有子键,并且分别访问它们的属性值。下面的脚本会列出Uninstall的子键,和它们的属性DisplayNameMoreInfoURL.。这也为你提供了一个简约版已安装程序列表:

Dir hklm:\software\microsoft\windows\currentversion\uninstall |
ForEach-Object { 
 Write-Host -ForegroundColor Yellow "Installed Products:" }{
 $values = Get-ItemProperty $_.PSPath;
 "{0:-30} {1:20}" -f $values.DisplayName, $values.MoreInfoURL
 }{Write-Host -ForegroundColor Yellow "Finished!"}

上面的脚本写法稍微有点坑爹,其实是ForEach-Object可以接受三个脚本块用于管道的流模式处理,分别代表beginprocessend

键的子键

在注册表编辑器中,某个键的子键在PowerShell中可以这样调用:

PS C:\PowerShell> $key.SubKeyCount
3

Dir也能获取子键的名称。需要将PSPath这样的PowerShell路径传递给Dir:

PS C:\PowerShell> Dir $key.PSPath

    Hive: HKEY_LOCAL_MACHINE\Software\Microsoft\PowerShell\1

Name                           Property
----                           --------
PowerShellEngine               ApplicationBase         : C:\Windows\System32\WindowsPowerShell\v1.0
                               ConsoleHostAssemblyName : Microsoft.PowerShell.ConsoleHost, Version=1.0.0.0, Culture=neu
                               tral, PublicKeyToken=31bf3856ad364e35, ProcessorArchitecture=msil
                               ConsoleHostModuleName   : C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShel
                               l.ConsoleHost.dll
                               PowerShellVersion       : 2.0
                               PSCompatibleVersion     : 1.0, 2.0
                               RuntimeVersion          : v2.0.50727
PowerShellSnapIns
ShellIds

创建和删除键值

使用New-item或者md函数来创建注册表键。注册表中的键行为类似文件系统的中的目录。

> New-Item -type Directory HKCU:\Software\Test1

    Hive: HKEY_CURRENT_USER\Software

Name                           Property
----                           --------
Test1

> md HKCU:\Software\Test2

    Hive: HKEY_CURRENT_USER\Software

Name                           Property
----                           --------
Test2

但是上面的两条命令创建的键是空的:它的默认值为没有设置。如果你想给一个键定义默认值,使用New-Item替代md吧,同时给它指定值的类型(-itemType参数),和值的内容(-value参数):

> New-Item -itemType String HKCU:\Software\Test3 -value "一个默认值而已"

    Hive: HKEY_CURRENT_USER\Software

Name                           Property
----                           --------
Test3                          (default) : 一个默认值而已

如果你想删除刚才测试时创建的三个注册表键,可以像在文件系统中那样,使用Remove-Item,或者短别名Del

Remove-Item HKCU:\Software\Test1
Del HKCU:\Software\Test2
Del HKCU:\Software\Test3

下面的表格列出所有支持的注册表值类型(itemType):

ItemType 描述 数据类型
String 一个字符串 REG_SZ
ExpandString 包含环境变量的字符串在执行时可以自动处理 REG_EXPAND_SZ
Binary 二进制值 REG_BINARY
DWord 32位数值 REG_DWORD
MultiString 多行文本 REG_MULTI_SZ
QWord 64位数值 REG_QWORD

删除键和内容

如果一个键名称包含空格,需要将该键用括号引起来。非常不幸,不能像在文件系统那样一次性创建多个键。因为父键必须存在。这也就为什么下面的脚本会报错:父键和子键均不存在。

md "HKCU:\Software\First key\Second key"

md : 指定路径下的注册表项不存在。

必须将上面的语句分成多个语句来执行

> md "HKCU:\Software\First key" | Out-Null
> md "HKCU:\Software\First key\Second key" | Out-Null

如果尝试删除第一个键,像文件系统中那样,需要确认。因为它包含了非空子键:

> Del "HKCU:\Software\First key"

确认
HKCU:\Software\First key 处的项具有子项,并且未指定 Recurse 参数。如果继续,所有子项均将随该项删除。是否确实要继续?
[Y] 是(Y)  [A] 全是(A)  [N] 否(N)  [L] 全否(L)  [S] 挂起(S)  [?] 帮助 (默认值为“Y”): n

当然可以使用-recurse参数来明确指定要删除子键和它的内容:

> Del "HKCU:\Software\First key" -recurse

设置,更改,和删除键的值

注册表编辑器通过良好的组织结构来区别键和值:左边为键,右边为值。键对应于文件系统中的文件夹,值对应于文件系统中的目录中的文件。所以要添加一个新键,可以使用md,或者最好使用New-Item,方便使用-itemType -value 参数给你新创建的键来赋一个新值。

New-Item HKCU:\Software\Testkey -itemType String -value "一代宗师" | Out-Null

添加新值

很可惜,你如果想给一个键添加值时,文件系统这个比喻似乎不太奏效。因为常规情况下,可以使用Set-Content往文件夹下写文件。但是注册表似乎不买账。

> Set-Content HKCU:\Software\Testkey\Value1 "Contents"
Set-Content : 无法使用接口。此提供程序未实现 IContentCmdletProvider 接口。

取而代之,使用Set-ItemProperty给一个键添加值。

Set-ItemProperty HKCU:\Software\Testkey -name "Blog" -value www.pstips.net

你添加的这个值会在注册表中自动注册为REG_SZ类型。如果你想尝试其它类型,上面的表格中已经列出来了。下面就创建一系列的值作为测试吧:

$testKey ='HKCU:\Software\Testkey'
if ( -not (Test-Path $testKey)) { md $testKey }
New-ItemProperty $testKey -name "Entry2" -value "123" -propertyType dword
New-ItemProperty $testKey Entry3 -value "Windows is in %windir%" -propertyType string
New-ItemProperty $testKey Entry4 -value "Windows is in %windir%" -propertyType expandstring
New-ItemProperty $testKey Entry5 -value "One","Two","Three" -propertyType multistring
New-ItemProperty $testKey Entry6 -value 1,2,3,4,5 -propertyType binary
New-ItemProperty $testKey Entry7 -value 100 -propertyType dword
New-ItemProperty $testKey Entry8 -value 100 -propertyType qword

如果你已经拿到了Microsoft.Win32.Registry对象,你还可以通过该对象的SetValue() GetValue()方法来读写值。在你使用New-Item来创建新键时,返回的结果已然是Microsoft.Win32.Registry了。你需要做的无非是把它保存起来,然后按照下面的步骤操作即可:

# 创建一个包含多个值的键:
$key = md HKCU:\Software\Test2
$key.SetValue("Entry1", "123")
$key.SetValue("Entry2", "123", "Dword")
$key.SetValue("Entry3", "%windir%", "ExpandString")
$key.GetValue("Entry3")
 C:\Windows
小技巧:SetValue()方法只对刚创建的键有效,因为添加新键时,PowerShell会以写权限打开它。但是对于已存在的键使用Get-Item是以只读模式打开,不能在这种情况下使用SetValue()。相反,可以使用Set-ItemProperty(参见上文)。

读取值

读取注册表的值,是注册表操作中唯一不太清楚的地方了。一般情况下,我们会想既然可以使用Set-ItemProperty添加值,应当就是可以通过Get-ItemProperty读取值。想法是对的,但是这个蛋疼的PowerShell获取的不是一个值,而是包括了额外PowerShell属性的许多值:

 Get-ItemProperty HKCU:\Software\Testkey Entry3

Entry3       : Windows is in %windir%
PSPath       : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\Testkey
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software
PSChildName  : Testkey
PSDrive      : HKCU
PSProvider   : Microsoft.PowerShell.Core\Registry
其原因应当是 PowerShell 对注册表的处理和对文件系统等一样使用的是统一的层次信息模型。

所以要获取正确的属性值,还得针对返回的对象,再次指定属性比如:

> (Get-ItemProperty HKCU:\Software\Testkey Entry3).Entry3
Windows is in %windir%
> (Get-ItemProperty HKCU:\Software\Testkey Entry4).Entry4
Windows is in C:\Windows

Entry4和Entry3有所区别,因为Entry3类型为REG_SZ,普通字符串,是什么就是什么,但是Entry4的类型为REG_EXPAND_SZ,Windows 会在程序读取该值之前自动解析包含在其中的处理环境变量。这就是为什么你看到了真实的Windows系统目录,而非一个生硬的环境变量半成品。

注意:上面读取值的方法还可写为:
(Get-ItemProperty HKCU:\Software\Testkey).Entry4

貌似简练,可读性强。但是事实上,它先得读取所有值,效率稍低。

删除值

使用Remove-ItemProperty删除一个值,下面的指令会删除之前例子中创建的:Entry5

Remove-ItemProperty HKCU:\Software\Testkey Entry5

只会删除Entry5值,不会删除Testkey。

默认项

默认项扮演着一个特殊的角色。它显示在注册表编辑器的右边属性列,名为(默认)。但事实上,该项的属于未命名项:它无名却有值。
默认项并非一定得定义。如果你没有设置,它在注册表中显示为“数值未设置”。正常情况下在使用New-Item-Value参数添加键,并赋给它默认值。但是也可以直接通过属性名(default)来访问它:

# 添加一个空键
> md HKCU:\Software\Test3

	Hive: HKEY_CURRENT_USER\Software
Name                           Property
----                           --------
Test3

# 验证默认值:
> Get-ItemProperty HKCU:\Software\Test3 "(default)"
Get-ItemProperty : 路径 HKEY_CURRENT_USER\Software\Test3 处不存在属性 (default)。

# 添加默认值:
> New-ItemProperty HKCU:\Software\Test3 "(default)" -value "A value"

(default)    : A value
PSPath       : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\Test3
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software
PSChildName  : Test3
PSDrive      : HKCU

# 验证添加的默认值:
> Get-ItemProperty HKCU:\Software\Test3 "(default)"

(default)    : A value
PSPath       : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\Test3
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software
PSChildName  : Test3
PSDrive      : HKCU
PSProvider   : Microsoft.PowerShell.Core\Registry

# 读取默认值的内容
> (Get-ItemProperty HKCU:\Software\Test3 "(default)")."(default)"
A value

# 删除默认值:
# 鉴于PowerShell的bug,不能删除默认值
# 只能使用Clear-ItemProperty清空默认值
> Clear-ItemProperty HKCU:\Software\Test3 "(default)"

重要:确保删除刚才添加的这些测试键值:

Del HKCU:\Software\Testkey -recurse
Del HKCU:\Software\Test2 -recurse
Del HKCU:\Software\Test3 -recurse

实例:扩展上下文菜单

注册表中键值用途广泛而丰富。其中就包括了资源管理器的上下文菜单在Windows系统中的设置。在下面的例子中,我们会测试在上下文菜单中针对PowerShell脚本添加三个新的命令项:“执行完停留”,“执行完关闭”,”记事本编辑”。

执行和编辑PowerShell脚本

要继续上面的测试,你先得知道怎样在PowerShell控制台外面运行PowerShell脚本,这方便啦。首先创建一个示例脚本:

Cd $home
# 创建示例脚本
'"向世界人民问好!"' | Out-File test.ps1

如果在PowerShell控制台中,可以使用相对路径或者绝对路径执行它:

.\test.ps1

但是怎样在PowerShell控制台外面执行它呢?启动PowerShell.exe,并且指定参数-NoExit选项,这样就会让脚本处理完毕后,进程不退出,停留在控制台界面上,方便用户查看和评估脚本的结果。在-Command参数后,指定PowerShell支持的命令行,将路径包在单引号中,前面加上调用操作符,为啥要多次一举呢?因为我们不知道,你的脚本路径中是否包含了空格,然后再将命令放在双引号中。

powershell.exe -NoExit -Command "& '.\test.ps1'"

如果你要编辑脚本,命令要简单的多,根据你的选择指定编辑器,然后将脚本路径传递过去:

notepad.exe ".\test.ps1"

上下文菜单的扩展接下来会被写入注册表,当然需要管理员权限:

# 创建HKEY_CLASSES_ROOT快捷方式:
New-PSDrive -Name HKCR -PSProvider registry -root HKEY_CLASSES_ROOT | Out-Null
 
# 找出关联PS1文件的键:
$keyname = (Get-ItemProperty HKCR:\.ps1)."(default)"
 
# 添加三个菜单命令:
$psExe= "$pshome\powershell.exe"
New-Item ("HKCR:\$keyname\shell\myexecute1") -value '执行完停留' -type String
New-Item ("HKCR:\$keyname\shell\myexecute1\command") -value "$psExe -NoExit -Command `"& '%L'`"" -type String
New-Item ("HKCR:\$keyname\shell\myexecute2") -value '执行完关闭' -type String
New-Item ("HKCR:\$keyname\shell\myexecute2\command") -value "$psExe -Command `"& '%L'`"" -type String
New-Item ("HKCR:\$keyname\shell\myeditnotepad") -value '记事本编辑' -type String
New-Item ("HKCR:\$keyname\shell\myeditnotepad\command") -value 'notepad.exe "%L"' -type String
 
# 设置图标
# 如果存在删除它:
if (Test-Path ("HKCR:\$keyname\DefaultIcon")) {
Del ("HKCR:\$keyname\DefaultIcon") }
$icon = '%windir%\System32\WindowsPowerShell\v1.0\powershell.exe,0'
New-Item ("HKCR:\$keyname\DefaultIcon") -value $icon -type ExpandString

只有执行脚本,只让别人安装,不给卸载的行为都是耍流氓,所以测试完毕后可以删除:

dir HKCR:\$keyname\shell\my* | Remove-Item -Force

注册表权限

在上一章我们详细的学习了怎样使用PowerShell来控制文件和文件夹的权限。同样的机制也可以在注册表中使用,使用Get-Acl查看键的当前权限:

PS> md HKCU:\Software\Testkey


    Hive: HKEY_CURRENT_USER\Software


Name                           Property
----                           --------
Testkey
PS> Get-Acl HKCU:\Software\Testkey

Path                                    Owner   Access
----                                    -----   ------
Microsoft.PowerShell.Core\Registry::... mosser  mosser Allow  FullControl...

因为注册表权限管理基本和文件系统的权限管理类似,所以你可能需要在给注册表键分配权限前再看看第上一章,或者重温一下基础知识。注册表中所需的权限在.NET中的类和与文件系统的稍微有点区别。此时需要使用的不再是FilesystemAccessRule,而是RegistryAccessRule,两者的根本区别在于可以设置不同的访问权限。在注册表访问规则(RegistryAccessRule)中权限的枚举值并不和文件系统访问规则(FilesystemAccessRule)中的枚举值对应:

PS C:\PowerShell> [System.Enum]::GetNames([System.Security.AccessControl.RegistryRights])
QueryValues
SetValue
CreateSubKey
EnumerateSubKeys
Notify
CreateLink
Delete
ReadPermissions
WriteKey
ExecuteKey
ReadKey
ChangePermissions
TakeOwnership
FullControl

接管所有权

在尝试更改注册表的权限前,请先测试以确保你是该键的“所有者”。除非你是所有者,才能够撤销可能存在的错误。下面的示例会演示怎样接管一个注册表键(当然你还是得先有权限访问)的所有权。

$acl = Get-Acl HKCU:\Software\Testkey
$acl.Owner

mosser
$me = [System.Security.Principal.NTAccount]"$env:userdomain\$env:username"
$acl.SetOwner($me)

设置新的访问权限

下面的步骤会给这个键分配新的权限,让“所有人”组禁止更改此键。

$acl = Get-Acl HKCU:\Software\Testkey
$person = [System.Security.Principal.NTAccount]"Everyone"
$access = [System.Security.AccessControl.RegistryRights]"WriteKey"
$inheritance = [System.Security.AccessControl.InheritanceFlags]"None"
$propagation = [System.Security.AccessControl.PropagationFlags]"None"
$type = [System.Security.AccessControl.AccessControlType]"Deny"
$rule = New-Object System.Security.AccessControl.RegistryAccessRule( `
$person,$access,$inheritance,$propagation,$type)
$acl.AddAccessRule($rule)
Set-Acl HKCU:\Software\Testkey $acl

更改会立刻生效,当你尝试通过注册表编辑器或者在PowerShell中创建子键时,会得到错误信息:

md HKCU:\Software\Testkey\subkey

md : 不允许所请求的注册表访问权。
At line:1 char:34
+ param([string[]]$paths); New-Item <<<< -type directory -path $paths
小技巧:你可能会问,为什么连我自己都限制,我是管理员啊,我应当拥有完全的访问权啊。这是因为在访问权限中,“拒绝”的优先权比“允许”高。哪怕你是管理员,你也是人,既然所有人都被拒绝,你自然也会被拒绝,除非你不是人。

移除一条访问规则

给所有人的访问权限完全是在浪费时间,也经不起考验。怎样删除这条规则呢?你可以使用RemoveAccessRule()方法来移除特定的权限,还可以使用RemoveAccessRuleAll()方法来移除指定用户的在某条规则中的所有权限(包括授权和拒绝),ModifyAccessRule()方法更新已存在的规则,PurgeAccessRules(),异常特定用户的所有权限。

要移除刚才创建的规则,可以这样处理:

$acl = Get-Acl HKCU:\Software\Testkey
$person = [System.Security.Principal.NTAccount]"Everyone"
$access = [System.Security.AccessControl.RegistryRights]"WriteKey"
$inheritance = [System.Security.AccessControl.InheritanceFlags]"None"
$propagation = [System.Security.AccessControl.PropagationFlags]"None"
$type = [System.Security.AccessControl.AccessControlType]"Deny"
$rule = New-Object System.Security.AccessControl.RegistryAccessRule( `
$person,$access,$inheritance,$propagation,$type)
$acl.RemoveAccessRule($rule)
Set-Acl HKCU:\Software\Testkey $acl -force

但是这样移除自己的访问规则后,并不是期望的结果。因为你把自己锁在了门外,因为你不在有权去更新这个键,本来是可以更改自己的安全设置的。此时如果你接管了所有权后。还是可以纠正遇到的这个问题的。此类情况发生后,你可以打开注册表编辑器,定位到这个键,右键鼠标选择权限,打开安全设置对话框,手动删除“所有人”组。

重要:刚才已经看到了,把自己锁在门外有多容易。所以在操作“所有人”这个组时,一定要格外小心。如果可以,尽量不要给这个组分配“拒绝”规则,因为它通常可能引入超出你预期的恶劣影响。

控制子键的访问

下面的例子,使用“允许”规则,而非“拒绝”规则,可以让世界稍微变得美好一点。在下面的测试键中,只有管理员才能更改这个键的值,而其它闲杂人等,只能读取和创建子键。

md HKCU:\Software\Testkey2
$acl = Get-Acl HKCU:\Software\Testkey2
# 管理员无所不能:
$person = [System.Security.Principal.NTAccount]"Administrators"
$access = [System.Security.AccessControl.RegistryRights]"FullControl"
$inheritance = [System.Security.AccessControl.InheritanceFlags]"None"
$propagation = [System.Security.AccessControl.PropagationFlags]"None"
$type = [System.Security.AccessControl.AccessControlType]"Allow"
$rule = New-Object System.Security.AccessControl.RegistryAccessRule( `
$person,$access,$inheritance,$propagation,$type)
$acl.ResetAccessRule($rule)
# 所有人只能读取和创建子键:
$person = [System.Security.Principal.NTAccount]"Everyone"
$access = [System.Security.AccessControl.RegistryRights]"ReadKey"
$inheritance = [System.Security.AccessControl.InheritanceFlags]"None"
$propagation = [System.Security.AccessControl.PropagationFlags]"None"
$type = [System.Security.AccessControl.AccessControlType]"Allow"
$rule = New-Object System.Security.AccessControl.RegistryAccessRule( `
$person,$access,$inheritance,$propagation,$type)
$acl.ResetAccessRule($rule)
Set-Acl HKCU:\Software\Testkey2 $acl

稍微注意下,这种情况下没有使用AddAccessRule()而是使用ResetAccessRule()添加规则。其结果导致删除了各个用户已存在的权限。尽管如此,结果仍旧不对,因为正常的用户还是可以创建子键和写值。

md hkcu:\software\Testkey2\Subkey
Hive: Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\software\Testkey2

SKC   VC  Name  Property
---   --  ----  --------
0     0  Subkey {}

Set-ItemProperty HKCU:\Software\Testkey2 Value1 "Here is text"

揭示继承权

查看键的当前权限,可以弄清为什么上面的设置没有如期生效:

PS> (Get-Acl HKCU:\Software\Testkey2).Access | Format-Table -wrap

     RegistryRights   AccessControlType IdentityReference           IsInherited    InheritanceFlags    PropagationFlags
     --------------   ----------------- -----------------           -----------    ----------------    ----------------
            ReadKey               Allow Everyone                          False                None                None
        FullControl               Allow BUILTIN\Administrat               False                None                None
                                        ors
        FullControl               Allow mosser                             True  ContainerInherit,                 None
                                                                                      ObjectInherit
        FullControl               Allow NT AUTHORITY\SYSTEM                True  ContainerInherit,                 None
                                                                                      ObjectInherit
        FullControl               Allow BUILTIN\Administrat                True  ContainerInherit,                 None
                                        ors                                           ObjectInherit
            ReadKey               Allow NT AUTHORITY\RESTRI                True  ContainerInherit,                 None
                                        CTED                                          ObjectInherit

该键包含的权限可不止我们设置的那两条规则,还有其它一些从父键中继承过来的阿猫阿狗规则。如果你想清理掉它们,可以使用SetAccessRuleProtection()方法。

$acl = Get-Acl HKCU:\Software\Testkey2
$acl.SetAccessRuleProtection($true, $false)
Set-Acl HKCU:\Software\Testkey2 $acl

现在再回过头来看看,该键的访问规则只有我们明确指定的两条规则,其它继承权限已不复存在。

PS> (Get-Acl HKCU:\Software\Testkey2).Access | Format-Table -wrap

     RegistryRights   AccessControlType IdentityReference           IsInherited    InheritanceFlags    PropagationFlags
     --------------   ----------------- -----------------           -----------    ----------------    ----------------
            ReadKey               Allow Everyone                          False                None                None
        FullControl               Allow BUILTIN\Administrat               False                None                None
                                        ors

此时再让普通用户尝试给该键添加子键或者设置值,应当会包错:Set-ItemProperty : 不允许所请求的注册表访问权

控制你自己的继承权

继承权是一把双刃剑。你已经从父键中关闭了继承,但是你自己的继承权呢?使用管理员权限打开控制台,能让我们给上面受保护的测试键添加子键。

md HKCU:\Software\Testkey2\Subkey1
md HKCU:\Software\Testkey2\Subkey1\Subkey2

然后看看这些新创建的子键的权限:

PS> (Get-Acl HKCU:\Software\Testkey2\Subkey1\Subkey2).Access | Format-Table -wrap

     RegistryRights   AccessControlType IdentityReference           IsInherited    InheritanceFlags    PropagationFlags
     --------------   ----------------- -----------------           -----------    ----------------    ----------------
        FullControl               Allow NT AUTHORITY\SYSTEM               False                None                None
        FullControl               Allow BUILTIN\Administrat               False                None                None
                                        ors
CreateLink, ReadKey               Allow S-1-5-5-0-205634                  False                None                None

其结果是这些权限仍旧没有与你设置的权限对应起来。理由是:对于你的继承权你啥都没设置,如果你想把自己纯正的血统不折不扣的遗传给下一代,请更改设置:

del HKCU:\Software\Testkey2
md HKCU:\Software\Testkey2
$acl = Get-Acl HKCU:\Software\Testkey2
# Admins may do anything:
$person = [System.Security.Principal.NTAccount]"Administrators"
$access = [System.Security.AccessControl.RegistryRights]"FullControl"
$inheritance = [System.Security.AccessControl.InheritanceFlags]`
"ObjectInherit,ContainerInherit"
$propagation = [System.Security.AccessControl.PropagationFlags]"None"
$type = [System.Security.AccessControl.AccessControlType]"Allow"
$rule = New-Object System.Security.AccessControl.RegistryAccessRule( `
$person,$access,$inheritance,$propagation,$type)
 
$acl.ResetAccessRule($rule)
# Everyone may only read and create subkeys:
$person = [System.Security.Principal.NTAccount]"Everyone"
$access = [System.Security.AccessControl.RegistryRights]"ReadKey"
$inheritance = [System.Security.AccessControl.InheritanceFlags]`
"ObjectInherit,ContainerInherit"
$propagation = [System.Security.AccessControl.PropagationFlags]"None"
$type = [System.Security.AccessControl.AccessControlType]"Allow"
$rule = New-Object System.Security.AccessControl.RegistryAccessRule( `
$person,$access,$inheritance,$propagation,$type)
$acl.ResetAccessRule($rule)
Set-Acl HKCU:\Software\Testkey2 $acl
posted @ 2021-09-27 14:44  门前有根大呲花  阅读(789)  评论(0编辑  收藏  举报