• PowerShell笔记


    本系列是一个重新学习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的子键,和它们的属性DisplayNameMoreInfoURL.。这也为你提供了一个简约版已安装程序列表:

    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可以接受三个脚本块用于管道的流模式处理,分别代表beginprocessend

    键的子键

    在注册表编辑器中,某个键的子键在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
    
  • 相关阅读:
    519,伪类和伪元素的区别
    518,自定义字体的使用场景
    517,sytlus/sass/less的区别
    516,base64的原理及优缺点
    515,前端性能优化--减少http请求(待补充)
    514 ,css不同选择器的权重(css层叠的规则)
    513,如果需要手写动画,你认为最小时间间隔是多久,为什么?
    512,a标签的target属性
    511,display:inline-block什么时候不会显示间隙?
    510,position的值,relative和absolute定位原点是
  • 原文地址:https://www.cnblogs.com/MerLin-LiuNian/p/15342898.html
Copyright © 2020-2023  润新知