如何将 .reg 文件自动转换为 PowerShell set-itemproperty 命令?

如何将 .reg 文件自动转换为 PowerShell set-itemproperty 命令?

我是一名修补匠,经常对注册表进行修改,我讨厌逐个点击多个.reg文件;如何将.reg文件自动转换为 PowerShellSet-ItemProperty命令?

  • 我找到了一个可以这样做的网站[注册表到 PowerShell 转换器],但是输出不是我想要的格式;我希望它具有与下面完全相同的语法,使用Set-ItemProperty//而不使用其他任何内容 Remove-ItemNew-Item
    Windows Registry Editor Version 5.00
    
    [HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\CurrentVersion\PushNotifications]
    "NoToastApplicationNotification"=dword:00000001
    
    • cmd
      Reg Add "HKLM\Software\Policies\Microsoft\Windows\CurrentVersion\PushNotifications" /v "NoToastApplicationNotification" /t REG_DWORD /d 1
      
    • powershell
      Set-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows\CurrentVersion\PushNotifications" -Name "NoToastApplicationNotification" -Type DWord -Value 1
      

  • 产生预期结果的命令应该是:
    "Set-ItemProperty -Path " + $path + "-Name " + $name + "-Value " + $value
    
    • 我已经创建了一个 ASCII 表,其中包含找到的信息这里并上传这里,管理这个[输出]:
      $ASCII=import-csv ".\desktop\ascii.csv"
      [array]$AsciiTable=0..255 | foreach-object{
        $Decimal=$ASCII[$_].DEC
        $Hexadecimal=$ASCII[$_].HEX
        $Binary=$ASCII[$_].BIN
        $Octonary=$ASCII[$_].OCT
        $Symbol=$ASCII[$_].Symbol
        $Value=[char]$_
        $Description=$ASCII[$_].Description
        $HTMLName=$ASCII[$_].HTMLName
        $HTMLNumber=$ASCII[$_].HTMLNumber
        [pscustomobject]@{Decimal=$Decimal;Hexadecimal=$Hexadecimal;Binary=$Binary;Octonary=$Octonary;Symbol=$Symbol;Value=$Value;Description=$Description;HTMLName=$HTMLName;HTMLNumber=$HTMLNumber}
      }
      $AsciiTable | Export-csv ".\Desktop\AsciiTable.csv"
      


目前我已经完成了这个,虽然不完整,但想法是通过循环文件index,通过正则表达式匹配为变量分配值,将类型和配置单元名称更改为 PowerShell 中使用的类型和配置单元名称:

$registry=get-content $regfile

for ($i=0;$i -lt $registry.count;$i++){
  $line=$registry | select-object -index $i
  if ($line -match '\[' -and '\]') {
    $path=$line -replace '\[|\]'
    switch ($path)
    {
      {$path -match "HKEY_CLASSES_ROOT"}    {$path=$path -replace "HKEY_CLASSES_ROOT","HKCR:"}
      {$path -match "HKEY_CURRENT_USER"}    {$path=$path -replace "HKEY_CURRENT_USER","HKCU:"}
      {$path -match "HKEY_LOCAL_MACHINE"}   {$path=$path -replace "HKEY_LOCAL_MACHINE","HKLM:"}
      {$path -match "HKEY_USERS"}           {$path=$path -replace "HKEY_USERS","HKU:"}
      {$path -match "HKEY_CURRENT_CONFIG"}  {$path=$path -replace "HKEY_CURRENT_CONFIG","HKCC:"}
    }
  }
  else {
    $name=($line | select-string -pattern "`"([^`"=]+)`"").matches.value | select-object -first 1
    switch ($line)
  {
  {$line -match}
}


注册表值有六种类型 [ REG_SZREG_BINARYREG_DWORDREG_QWORDREG_MULTI_SZ] ,尽管我设法创建了一个包含所有类型的注册表项,但REG_EXPAND_SZ我在文件中只看到一种DWORD值类型:.reg

  • RegEdit
    在此处输入图片描述
  • .reg
    Windows Registry Editor Version 5.00
    
    [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\AarSvc]
    "DependOnService"=hex(7):41,00,75,00,64,00,69,00,6f,00,73,00,72,00,76,00,00,00,\
      00,00
    "Description"="@%SystemRoot%\\system32\\AarSvc.dll,-101"
    "DisplayName"="@%SystemRoot%\\system32\\AarSvc.dll,-100"
    "ErrorControl"=dword:00000001
    "FailureActions"=hex:80,51,01,00,00,00,00,00,00,00,00,00,04,00,00,00,14,00,00,\
      00,01,00,00,00,10,27,00,00,01,00,00,00,10,27,00,00,01,00,00,00,10,27,00,00,\
      00,00,00,00,00,00,00,00
    "ImagePath"=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,\
      74,00,25,00,5c,00,73,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,73,\
      00,76,00,63,00,68,00,6f,00,73,00,74,00,2e,00,65,00,78,00,65,00,20,00,2d,00,\
      6b,00,20,00,41,00,61,00,72,00,53,00,76,00,63,00,47,00,72,00,6f,00,75,00,70,\
      00,20,00,2d,00,70,00,00,00
    "ObjectName"="NT Authority\\LocalService"
    "RequiredPrivileges"=hex(7):53,00,65,00,49,00,6d,00,70,00,65,00,72,00,73,00,6f,\
      00,6e,00,61,00,74,00,65,00,50,00,72,00,69,00,76,00,69,00,6c,00,65,00,67,00,\
      65,00,00,00,00,00
    "ServiceSidType"=dword:00000001
    "Start"=dword:00000003
    "Type"=dword:00000060
    "UserServiceFlags"=dword:00000003
    "New Value #1"=hex(b):00,00,00,00,00,00,00,00
    
    [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\AarSvc\Parameters]
    "ServiceDll"=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,\
      00,74,00,25,00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,\
      41,00,61,00,72,00,53,00,76,00,63,00,2e,00,64,00,6c,00,6c,00,00,00
    "ServiceDllUnloadOnStop"=dword:00000001
    "ServiceMain"="ServiceMain"
    


如何确定注册表类型.reg,因为预期的最终结果是包含转换命令的文本文件/字符串数组/ PowerShell 脚本?

  • 在中.reg,我知道类型的值REG_DWORD写为dwordREG_SZ作为引号括起来的纯文本,REG_QWORD如下qword图所示这里),并且已将注册表类型映射到其相应的 PowerShell 属性:
    REG_SZ        → String
    REG_EXPAND_SZ → ExpandString
    REG_MULTI_SZ  → MultiString
    REG_BINARY    → Binary
    REG_DWORD     → DWord
    REG_QWORD     → QWord
    
    根据上面推断的关系:
    switch ($line)
    {
      {$line -match '"="'}      {$type="string"}
      {$line -match "dword"}    {$type="dword"}
      {$line -match "qword"}    {$type="qword"}
      {$line -match "hex\(2\)"} {$type="expandstring";break}
      {$line -match "hex\(7\)"} {$type="multistring";break}
      {$line -match "hex\(b\)"} {$type="qword";break}
      {$line -match "hex"}      {$type="binary"}
    }
    


我如何检测和解码注册表十六进制混乱信息,是否有其他方法可以在 a 中写入REG_EXPAND_SZREG_MULTI_SZ和类型(即分别为、和)?REG_BINARY.regExpandStringMultiStringBinary

  • 将注册表可扩展字符串值解析为纯文本的脚本:
    function parse-expandstring {
      PARAM (
        [Parameter(ValueFromPipeline=$true, Mandatory=$true)] [System.String]$expandstring
      )
    
      $AsciiTable=import-csv ".\desktop\AsciiTable.csv"
      [array]$hex=$expandstring -split'[\,\\]' | where {-not ([string]::IsNullOrWhiteSpace($_))} | %{$_.trimstart()}
      $hexadecimal=0..($hex.count-1) | where {$_ % 2 -ne 1} | foreach-object {$hex[$_]}
      $text=@()
      foreach ($hexadecima in $hexadecimal) {
        for ($i=0;$i -le 255;$i++) {
          if ($AsciiTable[$i].hexadecimal -eq $hexadecima) {
            $text+=$AsciiTable[$i].value
          }
        }
      }
      $text=$text -join ""
      $text
    }
    
  • Function解析REG_QWORD
    function parse-qword {
      PARAM (
        [Parameter(ValueFromPipeline=$true, Mandatory=$true)] [System.String]$qword
      )
      [array]$qword=$qword -split','
      $qword=for ($i=$qword.count-1;$i -ge 0;$i--) {$qword[$i]}
      $hexvalue=$qword -join ""
      $hexvalue=$hexvalue.trimstart("0")
      $hexvalue
    }
    
  • Function解析REG_BINARY
    function parse-binary {
      PARAM (
        [Parameter(ValueFromPipeline=$true, Mandatory=$true)] [System.String]$binary
      )
      [array]$hex=$binary -split'[,\\]' | where {-not ([string]::IsNullOrWhiteSpace($_))} | %{$_.trimstart()}
      $hex=$hex -join ""
        $hex
    }
    
  • Function解析REG_MULTI_SZ
    function parse-multistring {
      PARAM (
        [Parameter(ValueFromPipeline=$true, Mandatory=$true)] [System.String]$multistring
        )
    
      $AsciiTable=import-csv ".\desktop\AsciiTable.csv"
      [array]$hex=$multistring -split'[\,\\]' | where {-not ([string]::IsNullOrWhiteSpace($_))} | %{$_.trimstart()}
      $hexadecimal=0..($hex.count-1) | where {$_ % 2 -ne 1} | foreach-object {$hex[$_]}
      $text=@()
      foreach ($hexadecima in $hexadecimal) {
        for ($i=0;$i -le 255;$i++) {
          if ($AsciiTable[$i].hexadecimal -eq $hexadecima) {
            if ($i -ne 0) {$text+=$AsciiTable[$i].value}
            else {$text+="\0"}
          }
        }
      }
      $text=$text -join ""
      $text
    }
    


脚本已基本完成,已创建Remove-ItemNew-ItemRemove-ItemProperty切换条件;现在,难题的最后一部分是编写与值匹配的正则表达式。完成后,我会将其作为答案发布在此处。

  • 伪代码:
    if $line match [ and ]->$line match [-HKEY -> Remove-Item
    else $registry[$i+1] eq ""->New-Item
    elseif $line match "=-" -> Remove-ItemProperty
    
  • 我创建了一个 ASCII 哈希表作为字典使用:
    $asciihex=@{}
    0..255 | % {
      $number=$_
      [string]$hex=$number.tostring('x')
      if ($hex.length -eq 1) {$hex='{1}{0}' -f $hex,'0'}
      $char=[char]$number
      $asciihex.add($hex,$char)
      }
    
    • 要在给定的代码点处查找字符:
      # Change:
        $asciihex.'00'
      
      # to:
        $asciihex.'ff'
      
    • 要查找任意代码点处的字符:
      # Don't use $asciihex to print it
        $asciihex.$codepoint
      

要从行中 grep 值,请使用-replace $name+$type获取值。

答案1


最终版本:

Function reg2ps1 {

    [CmdLetBinding()]
    Param(
        [Parameter(ValueFromPipeline=$true, Mandatory=$true)]
        [Alias("FullName")]
        [string]$path,
        $Encoding = "utf8"
    )

    Begin {
        $hive = @{
            "HKEY_CLASSES_ROOT" = "HKCR:"
            "HKEY_CURRENT_USER" = "HKCU:"
            "HKEY_LOCAL_MACHINE" = "HKLM:"
            "HKEY_USERS" = "HKU:"
            "HKEY_CURRENT_CONFIG" = "HKCC:"
        }
        [system.boolean]$isfolder=$false
        $addedpath=@()
    }
    Process {
        switch (test-path $path -pathtype container)
        {
            $true {$files=(get-childitem -path $path -recurse -force -file -filter "*.reg").fullname;$isfolder=$true}
            $false {if($path.endswith(".reg")){$files=$path}}
        }
        foreach($File in $Files) {
            $Commands = @()
            [string]$text=$nul
            $FileContent = Get-Content $File | Where-Object {![string]::IsNullOrWhiteSpace($_)} | ForEach-Object { $_.Trim() }
            $joinedlines = @()
            for ($i=0;$i -lt $FileContent.count;$i++){
                if ($FileContent[$i].EndsWith("\")) {
                    $text=$text+($FileContent[$i] -replace "\\").trim()
                } else {
                    $joinedlines+=$text+$FileContent[$i]
                    [string]$text=$nul
                }
            }

            foreach ($joinedline in $joinedlines) {
                if ($joinedline -match '\[' -and $joinedline -match '\]' -and $joinedline -match 'HKEY') {
                    $key=$joinedline -replace '\[|\]'
                    switch ($key.StartsWith("-HKEY"))
                    {
                        $true {
                            $key=$key.substring(1,$key.length-1)
                            $hivename = $key.split('\')[0]
                            $key = "`"" + ($key -replace $hivename,$hive.$hivename) + "`""
                            $Commands += 'Remove-Item -Path {0} -Force -Recurse' -f $key
                        }
                        $false {
                            $hivename = $key.split('\')[0]
                            $key = "`"" + ($key -replace $hivename,$hive.$hivename) + "`""
                            if ($addedpath -notcontains $key) {
                                $Commands += 'New-Item -Path {0} -ErrorAction SilentlyContinue | Out-Null'-f $key
                                $addedpath+=$key
                            }
                        }
                    }
                }
                elseif ($joinedline -match "`"([^`"=]+)`"=") {
                    [System.Boolean]$delete=$false
                    $name=($joinedline | select-string -pattern "`"([^`"=]+)`"").matches.value | select-object -first 1
                    switch ($joinedline)
                    {
                        {$joinedline -match "=-"} {$commands+=$Commands += 'Remove-ItemProperty -Path {0} -Name {1} -Force' -f $key, $Name;$delete=$true}
                        {$joinedline -match '"="'} {
                            $type="string"
                            $value=$joinedline -replace "`"([^`"=]+)`"="
                        }
                        {$joinedline -match "dword"} {
                            $type="dword"
                            $value=$joinedline -replace "`"([^`"=]+)`"=dword:"
                            $value="0x"+$value
                        }
                        {$joinedline -match "qword"} {
                            $type="qword"
                            $value=$joinedline -replace "`"([^`"=]+)`"=qword:"
                            $value="0x"+$value
                        }
                        {$joinedline -match "hex(\([2,7,b]\))?:"} {
                            $value=($joinedline -replace "`"[^`"=]+`"=hex(\([2,7,b]\))?:").split(",")
                            $hextype=($joinedline | select-string -pattern "hex(\([2,7,b]\))?").matches.value
                            switch ($hextype)
                            {
                                {$hextype -eq 'hex(2)' -or $hextype -eq 'hex(7)'} {
                                    $value=for ($i=0;$i -lt $value.count;$i+=2) {
                                        switch ($hextype)
                                        {
                                            'hex(2)' {if ($value[$i] -ne '00') {[string][char][int]('0x'+$value[$i])}}
                                            'hex(7)' {if ($value[$i] -ne '00') {[string][char][int]('0x'+$value[$i])} else {"\0"}}
                                        }
                                    }
                                    $value=$value -join ""
                                    switch ($hextype)
                                    {
                                        'hex(2)' {$type="expandstring"}
                                        'hex(7)' {$type="multistring"}
                                    }
                                }
                                'hex(b)' {
                                    $type="qword"
                                    $value=for ($i=$value.count-1;$i -ge 0;$i--) {$value[$i]}
                                    $value='0x'+($value -join "").trimstart('0')
                                }
                                'hex' {
                                    $type="binary"
                                    $value='0x'+($value -join "")
                                }
                            }
                        }
                    }
                    if ($delete -eq $false) {$commands+='Set-ItemProperty -Path {0} -Name {1} -Type {2} -Value {3}' -f $key, $name, $type, $value}
                }
                elseif ($joinedline -match "@=") {
                    $name='"(Default)"';$type='string';$value=$joinedline -replace '@='
                    $commands+='Set-ItemProperty -Path {0} -Name {1} -Type {2} -Value {3}' -f $key, $name, $type, $value
                }
            
            }
            $parent=split-path $file -parent
            $filename=[System.IO.Path]::GetFileNameWithoutExtension($file)
            $Commands | out-file -path "${parent}\${filename}_reg.ps1" -encoding $encoding
        }
        if ($isfolder -eq $true) {
            $allcommands=(get-childitem -path $path -recurse -force -file -filter "*_reg.ps1").fullname | where-object {$_ -notmatch "allcommands_reg"} | foreach-object {get-content $_}
            $allcommands | out-file -path "${path}\allcommands_reg.ps1" -encoding $encoding
        }
    }
}
$path = Read-Host "input path"
reg2ps1 $path

这是最终版本,基于我之前的脚本和 SimonS 提供的脚本。该脚本非常完整,所有错误都已修复,它可以正确解析所有 6 种注册表值类型:、、、、REG_SZ和,将每行转换为一行,每个转换为一行,每行转换为一行,以及根据属性类型将每行REG_DWORD转换为适当的行。它接受输入的路径,自动检测路径指向文件还是文件夹,如果文件扩展名为 .reg,则将转换后的命令输出到该文件的父文件夹中的文件中,并将其作为文件名;如果是文件夹,则转换该文件夹内的所有 .reg 文件,并为每个 .reg 文件输出一个文件到该文件夹​​,然后将所有命令放入文件夹中的一个文件中。REG_QWORDREG_BINARYREG_MULTI_SZREG_EXPAND_SZ[HKEY_*New-Item[-HKEY_*Remove-Item"([^"=]+)"=-Remove-ItemProperty"([^"=]+)"=Set-ItemProperty${filename}_reg.ps1${filename}_reg.ps1_reg.ps1allcommands.ps1

我进行了多次测试,确认它确实有效。脚本现已完成。我进行了重大改进,使用了更好的格式,大大简化了代码,使用了更好的逻辑,并进行了许多其他增强。

这确实完成了,要使用我的最终版本,请将函数复制粘贴到打开的 powershell 窗口中,然后像 reg2ps1 "full\path\to\content" 一样调用它,或者将其保存为 .ps1 文件并通过 cd $scriptdir 和 .\reg2ps1.ps1 运行它,然后输入 full\path\to\content,注意您不应该使用任何引号,否则找不到路径...


更新

我在代码中犯了一个错误,通过-Force在使用时指定参数New-Item,如果该项目已经存在,它将重新创建该项目,并在过程中清空该项目,这不是我想要的,现在已修复。通过删除行-Force中的参数New-Item,尝试创建一个已经存在的项目将生成一个错误,提示该项目已存在,并且不会重置该项目。错误消息被隐藏-ErrorAction SilentlyContinue。如果该项目不存在,它将被创建,该项目将为空,过程将提示一条消息,提示该项目已创建,该消息被隐藏| Out-Null

答案2

这非常好,但是您的转换代码中存在一些错误。

  1. 在包含转义字符(如“cmd.exe /s /k pushd“%V””)的注册值文本中,您的转换不会将“替换为`”或类似的字符。
  2. 使用 -Type 属性时,需要 New-ItemProperty 而不是 Set-ItemProperty。
  3. 如果不先设置驱动器,PowerShell 将无法处理“HKCR:”等。
  4. Out-File 路径参数是 -FilePath

可能值得修复

答案3

根据Microsoft 参考-path,第 125 行的命令语法中没有参数Out-File,因此需要将其替换为正确的参数-FilePath

更改此行:

$Commands | Out-File -Path "${parent}\${filename}_reg.ps1" -encoding $encoding

到:

$Commands | Out-File -FilePath "${parent}\${filename}_reg.ps1" -encoding $encoding

此外,在发布“最终版本”之前,您可能需要使用这些示例注册表项检查您的代码:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\MyNewKey]
"Value01"=dword:00000a00
"Value02"=hex:c4,ff,cc,ff,00,00,00,00,e2,ff,bb,ff,00,00,09,00,01,00,03,00,27,00,\
  55,00,2e,00,e7,03,00,00,03,00,00,00,03,00,c4,00,00,00,30,00,00,00
"Value03"="@tzres.dll,-243"
"Value04"=hex:00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
"Value05"=dword:ffffff2e
  
[HKEY_CURRENT_USER\SOFTWARE\MyNewKey]
"Value06"="1445855232"

[HKEY_USERS\S-1-5-18\Software\MyNewKey]
"Value07"=dword:00000000

使用这些案例来测试代码性能是一个好主意。我在转换时发现了很多错误...

尝试运行从 test.reg 转换而来的 test_reg.ps1 文件时出现错误代码

答案4

https://reg2ps.azurewebsites.net/有一个输入表单,用户可以在其中输入.reg代码并输出.ps1代码。

相关内容