How to convert .reg files to PowerShell set-itemproperty commands automatically?

Xeн*_*вϵς 6 windows-registry powershell command-line conversion

\n

I am a tinkerer who does many registry hacks and I hate having to click many .reg files one by one; how do I convert .reg files to PowerShell Set-ItemProperty commands automatically?

\n
    \n
  • I found a site that does so [Registry to PowerShell Converter], however the output isn\'t in the format I wanted; I want it to have the exact same syntax as below using Set-ItemProperty/Remove-Item/New-Item and nothing else:\n
    Windows Registry Editor Version 5.00\n\n[HKEY_LOCAL_MACHINE\\Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\PushNotifications]\n"NoToastApplicationNotification"=dword:00000001\n
    Run Code Online (Sandbox Code Playgroud)\n
      \n
    • cmd:\n
      Reg Add "HKLM\\Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\PushNotifications" /v "NoToastApplicationNotification" /t REG_DWORD /d 1\n
      Run Code Online (Sandbox Code Playgroud)\n
    • \n
    • powershell:\n
      Set-ItemProperty -Path "HKLM:\\Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\PushNotifications" -Name "NoToastApplicationNotification" -Type DWord -Value 1\n
      Run Code Online (Sandbox Code Playgroud)\n
    • \n
    \n
    \n
  • \n
  • The command to produce intended result should be:\n
    "Set-ItemProperty -Path " + $path + "-Name " + $name + "-Value " + $value\n
    Run Code Online (Sandbox Code Playgroud)\n
      \n
    • I have created an ASCII table with information found here and uploaded here, managing this [output]:\n
      $ASCII=import-csv ".\\desktop\\ascii.csv"\n[array]$AsciiTable=0..255 | foreach-object{\n  $Decimal=$ASCII[$_].DEC\n  $Hexadecimal=$ASCII[$_].HEX\n  $Binary=$ASCII[$_].BIN\n  $Octonary=$ASCII[$_].OCT\n  $Symbol=$ASCII[$_].Symbol\n  $Value=[char]$_\n  $Description=$ASCII[$_].Description\n  $HTMLName=$ASCII[$_].HTMLName\n  $HTMLNumber=$ASCII[$_].HTMLNumber\n  [pscustomobject]@{Decimal=$Decimal;Hexadecimal=$Hexadecimal;Binary=$Binary;Octonary=$Octonary;Symbol=$Symbol;Value=$Value;Description=$Description;HTMLName=$HTMLName;HTMLNumber=$HTMLNumber}\n}\n$AsciiTable | Export-csv ".\\Desktop\\AsciiTable.csv"\n
      Run Code Online (Sandbox Code Playgroud)\n
    • \n
    \n
  • \n
\n


Currently I have managed this, which is incomplete, but the idea is to loop through file by index, assigning values to variables via regex match, changing type and hivename to ones used in PowerShell:

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


There are six registry value types [REG_SZ, REG_BINARY, REG_DWORD, REG_QWORD, REG_MULTI_SZ, REG_EXPAND_SZ] and I have seen only one DWORD value type in .reg files, although I managed to make a registry key containing all types:

\n
    \n
  • RegEdit:
    在此输入图像描述
  • \n
  • .reg:\n
    Windows Registry Editor Version 5.00\n\n[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\AarSvc]\n"DependOnService"=hex(7):41,00,75,00,64,00,69,00,6f,00,73,00,72,00,76,00,00,00,\\\n  00,00\n"Description"="@%SystemRoot%\\\\system32\\\\AarSvc.dll,-101"\n"DisplayName"="@%SystemRoot%\\\\system32\\\\AarSvc.dll,-100"\n"ErrorControl"=dword:00000001\n"FailureActions"=hex:80,51,01,00,00,00,00,00,00,00,00,00,04,00,00,00,14,00,00,\\\n  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,\\\n  00,00,00,00,00,00,00,00\n"ImagePath"=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,\\\n  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,\\\n  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,\\\n  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,\\\n  00,20,00,2d,00,70,00,00,00\n"ObjectName"="NT Authority\\\\LocalService"\n"RequiredPrivileges"=hex(7):53,00,65,00,49,00,6d,00,70,00,65,00,72,00,73,00,6f,\\\n  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,\\\n  65,00,00,00,00,00\n"ServiceSidType"=dword:00000001\n"Start"=dword:00000003\n"Type"=dword:00000060\n"UserServiceFlags"=dword:00000003\n"New Value #1"=hex(b):00,00,00,00,00,00,00,00\n\n[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\AarSvc\\Parameters]\n"ServiceDll"=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,\\\n  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,\\\n  41,00,61,00,72,00,53,00,76,00,63,00,2e,00,64,00,6c,00,6c,00,00,00\n"ServiceDllUnloadOnStop"=dword:00000001\n"ServiceMain"="ServiceMain"\n
    Run Code Online (Sandbox Code Playgroud)\n
  • \n
\n


How are registry types determined within a .reg, as the intended end-result is a text file/string array/PowerShell script that contains the converted commands?

\n
    \n
  • In a .reg, I know values for type REG_DWORD is written as dword, REG_SZ as plain text enclosed in quotes, REG_QWORD as qword (shown here), and have already mapped registry types to their corresponding PowerShell properties:\n
    REG_SZ        \xe2\x86\x92 String\nREG_EXPAND_SZ \xe2\x86\x92 ExpandString\nREG_MULTI_SZ  \xe2\x86\x92 MultiString\nREG_BINARY    \xe2\x86\x92 Binary\nREG_DWORD     \xe2\x86\x92 DWord\nREG_QWORD     \xe2\x86\x92 QWord\n
    Run Code Online (Sandbox Code Playgroud)\nWith the relations inferred from above:\n
    switch ($line)\n{\n  {$line -match \'"="\'}      {$type="string"}\n  {$line -match "dword"}    {$type="dword"}\n  {$line -match "qword"}    {$type="qword"}\n  {$line -match "hex\\(2\\)"} {$type="expandstring";break}\n  {$line -match "hex\\(7\\)"} {$type="multistring";break}\n  {$line -match "hex\\(b\\)"} {$type="qword";break}\n  {$line -match "hex"}      {$type="binary"}\n}\n
    Run Code Online (Sandbox Code Playgroud)\n
  • \n
\n
\n


How can I detect and decode the registry hex babbles and are there other ways to write REG_EXPAND_SZ, REG_MULTI_SZ, and REG_BINARY types in a .reg (i.e. as ExpandString, MultiString and Binary respectively)?

\n
    \n
  • Script to parse registry expandable string values to plain text:\n
    function parse-expandstring {\n  PARAM (\n    [Parameter(ValueFromPipeline=$true, Mandatory=$true)] [System.String]$expandstring\n  )\n\n  $AsciiTable=import-csv ".\\desktop\\AsciiTable.csv"\n  [array]$hex=$expandstring -split\'[\\,\\\\]\' | where {-not ([string]::IsNullOrWhiteSpace($_))} | %{$_.trimstart()}\n  $hexadecimal=0..($hex.count-1) | where {$_ % 2 -ne 1} | foreach-object {$hex[$_]}\n  $text=@()\n  foreach ($hexadecima in $hexadecimal) {\n    for ($i=0;$i -le 255;$i++) {\n      if ($AsciiTable[$i].hexadecimal -eq $hexadecima) {\n        $text+=$AsciiTable[$i].value\n      }\n    }\n  }\n  $text=$text -join ""\n  $text\n}\n
    Run Code Online (Sandbox Code Playgroud)\n
  • \n
  • Function to parse REG_QWORD:\n
    function parse-qword {\n  PARAM (\n    [Parameter(ValueFromPipeline=$true, Mandatory=$true)] [System.String]$qword\n  )\n  [array]$qword=$qword -split\',\'\n  $qword=for ($i=$qword.count-1;$i -ge 0;$i--) {$qword[$i]}\n  $hexvalue=$qword -join ""\n  $hexvalue=$hexvalue.trimstart("0")\n  $hexvalue\n}\n
    Run Code Online (Sandbox Code Playgroud)\n
  • \n
  • Function to parse REG_BINARY:\n
    function parse-binary {\n  PARAM (\n    [Parameter(ValueFromPipeline=$true, Mandatory=$true)] [System.String]$binary\n  )\n  [array]$hex=$binary -split\'[,\\\\]\' | where {-not ([string]::IsNullOrWhiteSpace($_))} | %{$_.trimstart()}\n  $hex=$hex -join ""\n    $hex\n}\n
    Run Code Online (Sandbox Code Playgroud)\n
  • \n
  • Function to parse REG_MULTI_SZ:\n
    function parse-multistring {\n  PARAM (\n    [Parameter(ValueFromPipeline=$true, Mandatory=$true)] [System.String]$multistring\n    )\n\n  $AsciiTable=import-csv ".\\desktop\\AsciiTable.csv"\n  [array]$hex=$multistring -split\'[\\,\\\\]\' | where {-not ([string]::IsNullOrWhiteSpace($_))} | %{$_.trimstart()}\n  $hexadecimal=0..($hex.count-1) | where {$_ % 2 -ne 1} | foreach-object {$hex[$_]}\n  $text=@()\n  foreach ($hexadecima in $hexadecimal) {\n    for ($i=0;$i -le 255;$i++) {\n      if ($AsciiTable[$i].hexadecimal -eq $hexadecima) {\n        if ($i -ne 0) {$text+=$AsciiTable[$i].value}\n        else {$text+="\\0"}\n      }\n    }\n  }\n  $text=$text -join ""\n  $text\n}\n
    Run Code Online (Sandbox Code Playgroud)\n
  • \n
\n
\n


The script is almost complete, having already created Remove-Item, New-Item, and Remove-ItemProperty switch conditions; now, the final piece of the puzzle is to write a regex that matches the values. When this is done, I will post it as an answer here.

\n\n

要从行中查找值,请使用-replace $name+$type来获取该值。

\n

Xeн*_*вϵς 3


最终版本:

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
Run Code Online (Sandbox Code Playgroud)

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