本系列是一个重新学习PowerShell的笔记,内容引用自
PowerShell中文博客
基础命令
你可以像导航文件系统那样来访问注册表,因为正像我们在上一章中讨论的那样,PowerShell把文件系统和注册表都当作层次信息系统来处理。
注册表中的键对应于文件系统的目录,但是键对应的值和文件系统中的文件不是非常类似。相反的,它们显示在属性栏,被以键的属性来管理。
PS C:PowerShell> cd HKCU:
PS HKCU:> dir | Select-Object -First 10 | Format-Table Name,View
Name View
---- ----
HKEY_CURRENT_USERAppEvents Default
HKEY_CURRENT_USERConsole Default
HKEY_CURRENT_USERControl Panel Default
HKEY_CURRENT_USEREnvironment Default
HKEY_CURRENT_USEREUDC Default
HKEY_CURRENT_USERKeyboard Layout Default
HKEY_CURRENT_USERMicrosoft Default
HKEY_CURRENT_USERNetwork Default
HKEY_CURRENT_USERPrinters Default
HKEY_CURRENT_USERSoftware 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_USERAppEvents
HKEY_CURRENT_USERConsole
HKEY_CURRENT_USERControl Panel
HKEY_CURRENT_USEREnvironment
HKEY_CURRENT_USEREUDC
HKEY_CURRENT_USERKeyboard Layout
HKEY_CURRENT_USERMicrosoft
HKEY_CURRENT_USERNetwork
HKEY_CURRENT_USERPrinters
HKEY_CURRENT_USERSoftware
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_MACHINESOFTWAREClasses
。这意味着你可以以这个路径为起点,来创建一个新的驱动器。
New-PSDrive -name HKCR -PSProvider registry -root HKLM:SOFTWAREClasses
Dir HKCR:
现在,你可以去访问这个分支了。事实上,你可以访问上面表格中列出的任意根节点。
小技巧:你可以自由地创建任何额外的驱动器,尤其在你有频繁操作某个特定的注册表区域时。
New-PSDrive job1 registry "HKLM:SoftwareMicrosoftWindows NTCurrentVersion"
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_USERConsole
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_USERSoftwareMicrosoftVisualStudioTelemetryPersistentPropertyBag
Name Property
---- --------
powershell VS.TelemetryApi.ChannelsDisposeLatency : 18
VS.TelemetryApi.DroppedEventsDuringDisposing : 0
VS.TelemetryApi.TotalDisposeLatency : 674
Hive: HKEY_CURRENT_USERSoftwareMicrosoftWindowsCurrentVersionNotificationsSettings{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.CoreRegistry::HKEY_CURRENT_USER
PSPath NoteProperty string PSPath=Microsoft.PowerShell.CoreRegistry::HKEY_CURRENT_USERAppEvents
PSProvider NoteProperty ProviderInfo PSProvider=Microsoft.PowerShell.CoreRegistry
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:SoftwareMicrosoftPowerShell1 键,这里存放的是PowerShell的一些内部设置。
在PowerShell中使用Get-Item访问该键:
PS C:PowerShell> $key = Get-Item HKLM:SoftwareMicrosoftPowerShell1
PS C:PowerShell> $key.Name
HKEY_LOCAL_MACHINESoftwareMicrosoftPowerShell1
PS C:PowerShell> $key | Format-List ps*
PSPath : Microsoft.PowerShell.CoreRegistry::HKEY_LOCAL_MACHINESoftwareMicrosoftPowerShell1
PSParentPath : Microsoft.PowerShell.CoreRegistry::HKEY_LOCAL_MACHINESoftwareMicrosoftPowerShell
PSChildName : 1
PSDrive : HKLM
PSProvider : Microsoft.PowerShell.CoreRegistry
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.CoreRegistry::HKEY_LOCAL_MACHINESoftwareMicrosoftPowerShell1
PSParentPath : Microsoft.PowerShell.CoreRegistry::HKEY_LOCAL_MACHINESoftwareMicrosoftPowerShell
PSChildName : 1
PSProvider : Microsoft.PowerShell.CoreRegistry
这种方式会把该键对应的所有值自动获取并输出。另外,除了一些常规属性,这些值还包括了PowerShell添加的其他属性。如果你只想获取特定的值:
# 获取注册表键的所有值
$values = Get-ItemProperty $key.PSPath
# 获取Install的值:
$values.Install
1
# 获取PID的值:
$values.PID
89383-100-0001260-04309
如果你想获取键的所有值,但是不想包含PowerShell自动添加的属性。可以这样做:
$key = Get-Item HKLM:SoftwareMicrosoftPowerShell1
$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:SoftwareMicrosoftPowerShell1
PS HKLM:SoftwareMicrosoftPowerShell1> dir
Hive: HKEY_LOCAL_MACHINESoftwareMicrosoftPowerShell1
Name Property
---- --------
PowerShellEngine ApplicationBase : C:WindowsSystem32WindowsPowerShellv1.0
ConsoleHostAssemblyName : Microsoft.PowerShell.ConsoleHost, Version=1.0.0.0, Culture=neu
tral, PublicKeyToken=31bf3856ad364e35, ProcessorArchitecture=msil
ConsoleHostModuleName : C:WindowsSystem32WindowsPowerShellv1.0Microsoft.PowerShel
l.ConsoleHost.dll
PowerShellVersion : 2.0
PSCompatibleVersion : 1.0, 2.0
RuntimeVersion : v2.0.50727
PowerShellSnapIns
ShellIds
PS HKLM:SoftwareMicrosoftPowerShell1> (Get-ItemProperty .).PID
89383-100-0001260-04309
这里将”.
”传递给Get-ItemProperty
。当然前提条件是先得CD到目标键的路径下。
如果你想输出多个键的多个值,应当使用Dir。Dir的结果可以通过管道传递给ForEach-Object
。这样,你就可以一次性获取某个键的所有子键,并且分别访问它们的属性值。下面的脚本会列出Uninstall
的子键,和它们的属性DisplayName
与MoreInfoURL.
。这也为你提供了一个简约版已安装程序列表:
Dir hklm:softwaremicrosoftwindowscurrentversionuninstall |
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
可以接受三个脚本块用于管道的流模式处理,分别代表begin
,process
和end
。
键的子键
在注册表编辑器中,某个键的子键在PowerShell中可以这样调用:
PS C:PowerShell> $key.SubKeyCount
3
Dir
也能获取子键的名称。需要将PSPath
这样的PowerShell路径传递给Dir:
PS C:PowerShell> Dir $key.PSPath
Hive: HKEY_LOCAL_MACHINESoftwareMicrosoftPowerShell1
Name Property
---- --------
PowerShellEngine ApplicationBase : C:WindowsSystem32WindowsPowerShellv1.0
ConsoleHostAssemblyName : Microsoft.PowerShell.ConsoleHost, Version=1.0.0.0, Culture=neu
tral, PublicKeyToken=31bf3856ad364e35, ProcessorArchitecture=msil
ConsoleHostModuleName : C:WindowsSystem32WindowsPowerShellv1.0Microsoft.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:SoftwareTest1
Hive: HKEY_CURRENT_USERSoftware
Name Property
---- --------
Test1
> md HKCU:SoftwareTest2
Hive: HKEY_CURRENT_USERSoftware
Name Property
---- --------
Test2
但是上面的两条命令创建的键是空的:它的默认值为没有设置。如果你想给一个键定义默认值,使用New-Item
替代md
吧,同时给它指定值的类型(-itemType
参数),和值的内容(-value
参数):
> New-Item -itemType String HKCU:SoftwareTest3 -value "一个默认值而已"
Hive: HKEY_CURRENT_USERSoftware
Name Property
---- --------
Test3 (default) : 一个默认值而已
如果你想删除刚才测试时创建的三个注册表键,可以像在文件系统中那样,使用Remove-Item
,或者短别名Del
:
Remove-Item HKCU:SoftwareTest1
Del HKCU:SoftwareTest2
Del HKCU:SoftwareTest3
下面的表格列出所有支持的注册表值类型(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:SoftwareFirst keySecond key"
md : 指定路径下的注册表项不存在。
必须将上面的语句分成多个语句来执行
> md "HKCU:SoftwareFirst key" | Out-Null
> md "HKCU:SoftwareFirst keySecond key" | Out-Null
如果尝试删除第一个键,像文件系统中那样,需要确认。因为它包含了非空子键:
> Del "HKCU:SoftwareFirst key"
确认
HKCU:SoftwareFirst key 处的项具有子项,并且未指定 Recurse 参数。如果继续,所有子项均将随该项删除。是否确实要继续?
[Y] 是(Y) [A] 全是(A) [N] 否(N) [L] 全否(L) [S] 挂起(S) [?] 帮助 (默认值为“Y”): n
当然可以使用-recurse
参数来明确指定要删除子键和它的内容:
> Del "HKCU:SoftwareFirst key" -recurse
设置,更改,和删除键的值
注册表编辑器通过良好的组织结构来区别键和值:左边为键,右边为值。键对应于文件系统中的文件夹,值对应于文件系统中的目录中的文件。所以要添加一个新键,可以使用md,或者最好使用New-Item
,方便使用-itemType
和 -value
参数给你新创建的键来赋一个新值。
New-Item HKCU:SoftwareTestkey -itemType String -value "一代宗师" | Out-Null
添加新值
很可惜,你如果想给一个键添加值时,文件系统这个比喻似乎不太奏效。因为常规情况下,可以使用Set-Content
往文件夹下写文件。但是注册表似乎不买账。
> Set-Content HKCU:SoftwareTestkeyValue1 "Contents"
Set-Content : 无法使用接口。此提供程序未实现 IContentCmdletProvider 接口。
取而代之,使用Set-ItemProperty
给一个键添加值。
Set-ItemProperty HKCU:SoftwareTestkey -name "Blog" -value www.pstips.net
你添加的这个值会在注册表中自动注册为REG_SZ
类型。如果你想尝试其它类型,上面的表格中已经列出来了。下面就创建一系列的值作为测试吧:
$testKey ='HKCU:SoftwareTestkey'
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:SoftwareTest2
$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:SoftwareTestkey Entry3
Entry3 : Windows is in %windir%
PSPath : Microsoft.PowerShell.CoreRegistry::HKEY_CURRENT_USERSoftwareTestkey
PSParentPath : Microsoft.PowerShell.CoreRegistry::HKEY_CURRENT_USERSoftware
PSChildName : Testkey
PSDrive : HKCU
PSProvider : Microsoft.PowerShell.CoreRegistry
其原因应当是 PowerShell 对注册表的处理和对文件系统等一样使用的是统一的层次信息模型。
所以要获取正确的属性值,还得针对返回的对象,再次指定属性比如:
> (Get-ItemProperty HKCU:SoftwareTestkey Entry3).Entry3
Windows is in %windir%
> (Get-ItemProperty HKCU:SoftwareTestkey Entry4).Entry4
Windows is in C:Windows
Entry4和Entry3有所区别,因为Entry3类型为REG_SZ
,普通字符串,是什么就是什么,但是Entry4的类型为REG_EXPAND_SZ
,Windows 会在程序读取该值之前自动解析包含在其中的处理环境变量。这就是为什么你看到了真实的Windows系统目录,而非一个生硬的环境变量半成品。
注意:上面读取值的方法还可写为:
(Get-ItemProperty HKCU:SoftwareTestkey).Entry4
貌似简练,可读性强。但是事实上,它先得读取所有值,效率稍低。
删除值
使用Remove-ItemProperty
删除一个值,下面的指令会删除之前例子中创建的:Entry5
Remove-ItemProperty HKCU:SoftwareTestkey Entry5
只会删除Entry5值,不会删除Testkey。
默认项
默认项扮演着一个特殊的角色。它显示在注册表编辑器的右边属性列,名为(默认)。但事实上,该项的属于未命名项:它无名却有值。
默认项并非一定得定义。如果你没有设置,它在注册表中显示为“数值未设置”。正常情况下在使用New-Item
和-Value
参数添加键,并赋给它默认值。但是也可以直接通过属性名(default)来访问它:
# 添加一个空键
> md HKCU:SoftwareTest3
Hive: HKEY_CURRENT_USERSoftware
Name Property
---- --------
Test3
# 验证默认值:
> Get-ItemProperty HKCU:SoftwareTest3 "(default)"
Get-ItemProperty : 路径 HKEY_CURRENT_USERSoftwareTest3 处不存在属性 (default)。
# 添加默认值:
> New-ItemProperty HKCU:SoftwareTest3 "(default)" -value "A value"
(default) : A value
PSPath : Microsoft.PowerShell.CoreRegistry::HKEY_CURRENT_USERSoftwareTest3
PSParentPath : Microsoft.PowerShell.CoreRegistry::HKEY_CURRENT_USERSoftware
PSChildName : Test3
PSDrive : HKCU
# 验证添加的默认值:
> Get-ItemProperty HKCU:SoftwareTest3 "(default)"
(default) : A value
PSPath : Microsoft.PowerShell.CoreRegistry::HKEY_CURRENT_USERSoftwareTest3
PSParentPath : Microsoft.PowerShell.CoreRegistry::HKEY_CURRENT_USERSoftware
PSChildName : Test3
PSDrive : HKCU
PSProvider : Microsoft.PowerShell.CoreRegistry
# 读取默认值的内容
> (Get-ItemProperty HKCU:SoftwareTest3 "(default)")."(default)"
A value
# 删除默认值:
# 鉴于PowerShell的bug,不能删除默认值
# 只能使用Clear-ItemProperty清空默认值
> Clear-ItemProperty HKCU:SoftwareTest3 "(default)"
重要:确保删除刚才添加的这些测试键值:
Del HKCU:SoftwareTestkey -recurse
Del HKCU:SoftwareTest2 -recurse
Del HKCU:SoftwareTest3 -recurse
实例:扩展上下文菜单
注册表中键值用途广泛而丰富。其中就包括了资源管理器的上下文菜单在Windows系统中的设置。在下面的例子中,我们会测试在上下文菜单中针对PowerShell脚本添加三个新的命令项:“执行完停留”,“执行完关闭”,”记事本编辑”。
执行和编辑PowerShell脚本
要继续上面的测试,你先得知道怎样在PowerShell控制台外面运行PowerShell脚本,这方便啦。首先创建一个示例脚本:
Cd $home
# 创建示例脚本
'"向世界人民问好!"' | Out-File test.ps1
如果在PowerShell控制台中,可以使用相对路径或者绝对路径执行它:
. est.ps1
但是怎样在PowerShell控制台外面执行它呢?启动PowerShell.exe,并且指定参数-NoExit
选项,这样就会让脚本处理完毕后,进程不退出,停留在控制台界面上,方便用户查看和评估脚本的结果。在-Command
参数后,指定PowerShell支持的命令行,将路径包在单引号中,前面加上调用操作符,为啥要多次一举呢?因为我们不知道,你的脚本路径中是否包含了空格,然后再将命令放在双引号中。
powershell.exe -NoExit -Command "& '. est.ps1'"
如果你要编辑脚本,命令要简单的多,根据你的选择指定编辑器,然后将脚本路径传递过去:
notepad.exe ". est.ps1"
上下文菜单的扩展接下来会被写入注册表,当然需要管理员权限:
# 创建HKEY_CLASSES_ROOT快捷方式:
New-PSDrive -Name HKCR -PSProvider registry -root HKEY_CLASSES_ROOT | Out-Null
# 找出关联PS1文件的键:
$keyname = (Get-ItemProperty HKCR:.ps1)."(default)"
# 添加三个菜单命令:
$psExe= "$pshomepowershell.exe"
New-Item ("HKCR:$keynameshellmyexecute1") -value '执行完停留' -type String
New-Item ("HKCR:$keynameshellmyexecute1command") -value "$psExe -NoExit -Command `"& '%L'`"" -type String
New-Item ("HKCR:$keynameshellmyexecute2") -value '执行完关闭' -type String
New-Item ("HKCR:$keynameshellmyexecute2command") -value "$psExe -Command `"& '%L'`"" -type String
New-Item ("HKCR:$keynameshellmyeditnotepad") -value '记事本编辑' -type String
New-Item ("HKCR:$keynameshellmyeditnotepadcommand") -value 'notepad.exe "%L"' -type String
# 设置图标
# 如果存在删除它:
if (Test-Path ("HKCR:$keynameDefaultIcon")) {
Del ("HKCR:$keynameDefaultIcon") }
$icon = '%windir%System32WindowsPowerShellv1.0powershell.exe,0'
New-Item ("HKCR:$keynameDefaultIcon") -value $icon -type ExpandString
只有执行脚本,只让别人安装,不给卸载的行为都是耍流氓,所以测试完毕后可以删除:
dir HKCR:$keynameshellmy* | Remove-Item -Force
注册表权限
在上一章我们详细的学习了怎样使用PowerShell来控制文件和文件夹的权限。同样的机制也可以在注册表中使用,使用Get-Acl
查看键的当前权限:
PS> md HKCU:SoftwareTestkey
Hive: HKEY_CURRENT_USERSoftware
Name Property
---- --------
Testkey
PS> Get-Acl HKCU:SoftwareTestkey
Path Owner Access
---- ----- ------
Microsoft.PowerShell.CoreRegistry::... 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:SoftwareTestkey
$acl.Owner
mosser
$me = [System.Security.Principal.NTAccount]"$env:userdomain$env:username"
$acl.SetOwner($me)
设置新的访问权限
下面的步骤会给这个键分配新的权限,让“所有人”组禁止更改此键。
$acl = Get-Acl HKCU:SoftwareTestkey
$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:SoftwareTestkey $acl
更改会立刻生效,当你尝试通过注册表编辑器或者在PowerShell中创建子键时,会得到错误信息:
md HKCU:SoftwareTestkeysubkey
md : 不允许所请求的注册表访问权。
At line:1 char:34
+ param([string[]]$paths); New-Item <<<< -type directory -path $paths
小技巧:你可能会问,为什么连我自己都限制,我是管理员啊,我应当拥有完全的访问权啊。这是因为在访问权限中,“拒绝”的优先权比“允许”高。哪怕你是管理员,你也是人,既然所有人都被拒绝,你自然也会被拒绝,除非你不是人。
移除一条访问规则
给所有人的访问权限完全是在浪费时间,也经不起考验。怎样删除这条规则呢?你可以使用RemoveAccessRule()
方法来移除特定的权限,还可以使用RemoveAccessRuleAll()
方法来移除指定用户的在某条规则中的所有权限(包括授权和拒绝),ModifyAccessRule()
方法更新已存在的规则,PurgeAccessRules()
,异常特定用户的所有权限。
要移除刚才创建的规则,可以这样处理:
$acl = Get-Acl HKCU:SoftwareTestkey
$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:SoftwareTestkey $acl -force
但是这样移除自己的访问规则后,并不是期望的结果。因为你把自己锁在了门外,因为你不在有权去更新这个键,本来是可以更改自己的安全设置的。此时如果你接管了所有权后。还是可以纠正遇到的这个问题的。此类情况发生后,你可以打开注册表编辑器,定位到这个键,右键鼠标选择权限,打开安全设置对话框,手动删除“所有人”组。
重要:刚才已经看到了,把自己锁在门外有多容易。所以在操作“所有人”这个组时,一定要格外小心。如果可以,尽量不要给这个组分配“拒绝”规则,因为它通常可能引入超出你预期的恶劣影响。
控制子键的访问
下面的例子,使用“允许”规则,而非“拒绝”规则,可以让世界稍微变得美好一点。在下面的测试键中,只有管理员才能更改这个键的值,而其它闲杂人等,只能读取和创建子键。
md HKCU:SoftwareTestkey2
$acl = Get-Acl HKCU:SoftwareTestkey2
# 管理员无所不能:
$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:SoftwareTestkey2 $acl
稍微注意下,这种情况下没有使用AddAccessRule()
而是使用ResetAccessRule()
添加规则。其结果导致删除了各个用户已存在的权限。尽管如此,结果仍旧不对,因为正常的用户还是可以创建子键和写值。
md hkcu:softwareTestkey2Subkey
Hive: Microsoft.PowerShell.CoreRegistry::HKEY_CURRENT_USERsoftwareTestkey2
SKC VC Name Property
--- -- ---- --------
0 0 Subkey {}
Set-ItemProperty HKCU:SoftwareTestkey2 Value1 "Here is text"
揭示继承权
查看键的当前权限,可以弄清为什么上面的设置没有如期生效:
PS> (Get-Acl HKCU:SoftwareTestkey2).Access | Format-Table -wrap
RegistryRights AccessControlType IdentityReference IsInherited InheritanceFlags PropagationFlags
-------------- ----------------- ----------------- ----------- ---------------- ----------------
ReadKey Allow Everyone False None None
FullControl Allow BUILTINAdministrat False None None
ors
FullControl Allow mosser True ContainerInherit, None
ObjectInherit
FullControl Allow NT AUTHORITYSYSTEM True ContainerInherit, None
ObjectInherit
FullControl Allow BUILTINAdministrat True ContainerInherit, None
ors ObjectInherit
ReadKey Allow NT AUTHORITYRESTRI True ContainerInherit, None
CTED ObjectInherit
该键包含的权限可不止我们设置的那两条规则,还有其它一些从父键中继承过来的阿猫阿狗规则。如果你想清理掉它们,可以使用SetAccessRuleProtection()
方法。
$acl = Get-Acl HKCU:SoftwareTestkey2
$acl.SetAccessRuleProtection($true, $false)
Set-Acl HKCU:SoftwareTestkey2 $acl
现在再回过头来看看,该键的访问规则只有我们明确指定的两条规则,其它继承权限已不复存在。
PS> (Get-Acl HKCU:SoftwareTestkey2).Access | Format-Table -wrap
RegistryRights AccessControlType IdentityReference IsInherited InheritanceFlags PropagationFlags
-------------- ----------------- ----------------- ----------- ---------------- ----------------
ReadKey Allow Everyone False None None
FullControl Allow BUILTINAdministrat False None None
ors
此时再让普通用户尝试给该键添加子键或者设置值,应当会包错:Set-ItemProperty : 不允许所请求的注册表访问权。
控制你自己的继承权
继承权是一把双刃剑。你已经从父键中关闭了继承,但是你自己的继承权呢?使用管理员权限打开控制台,能让我们给上面受保护的测试键添加子键。
md HKCU:SoftwareTestkey2Subkey1
md HKCU:SoftwareTestkey2Subkey1Subkey2
然后看看这些新创建的子键的权限:
PS> (Get-Acl HKCU:SoftwareTestkey2Subkey1Subkey2).Access | Format-Table -wrap
RegistryRights AccessControlType IdentityReference IsInherited InheritanceFlags PropagationFlags
-------------- ----------------- ----------------- ----------- ---------------- ----------------
FullControl Allow NT AUTHORITYSYSTEM False None None
FullControl Allow BUILTINAdministrat False None None
ors
CreateLink, ReadKey Allow S-1-5-5-0-205634 False None None
其结果是这些权限仍旧没有与你设置的权限对应起来。理由是:对于你的继承权你啥都没设置,如果你想把自己纯正的血统不折不扣的遗传给下一代,请更改设置:
del HKCU:SoftwareTestkey2
md HKCU:SoftwareTestkey2
$acl = Get-Acl HKCU:SoftwareTestkey2
# 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:SoftwareTestkey2 $acl