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?
Set-ItemProperty/Remove-Item/New-Item and nothing else:\nWindows Registry Editor Version 5.00\n\n[HKEY_LOCAL_MACHINE\\Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\PushNotifications]\n"NoToastApplicationNotification"=dword:00000001\nRun Code Online (Sandbox Code Playgroud)\ncmd:\nReg Add "HKLM\\Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\PushNotifications" /v "NoToastApplicationNotification" /t REG_DWORD /d 1\nRun Code Online (Sandbox Code Playgroud)\npowershell:\nSet-ItemProperty -Path "HKLM:\\Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\PushNotifications" -Name "NoToastApplicationNotification" -Type DWord -Value 1\nRun Code Online (Sandbox Code Playgroud)\n"Set-ItemProperty -Path " + $path + "-Name " + $name + "-Value " + $value\nRun Code Online (Sandbox Code Playgroud)\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"\nRun Code Online (Sandbox Code Playgroud)\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:
$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}\nRun Code Online (Sandbox Code Playgroud)\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:
RegEdit: 
.reg:\nWindows 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"\nRun Code Online (Sandbox Code Playgroud)\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?
.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:\nREG_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\nRun Code Online (Sandbox Code Playgroud)\nWith the relations inferred from above:\nswitch ($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}\nRun Code Online (Sandbox Code Playgroud)\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)?
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}\nRun Code Online (Sandbox Code Playgroud)\nFunction to parse REG_QWORD:\nfunction 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}\nRun Code Online (Sandbox Code Playgroud)\nFunction to parse REG_BINARY:\nfunction 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}\nRun Code Online (Sandbox Code Playgroud)\nFunction to parse REG_MULTI_SZ:\nfunction 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}\nRun Code Online (Sandbox Code Playgroud)\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.
if $line match [ and ]->$line match [-HKEY -> Remove-Item\nelse $registry[$i+1] eq ""->New-Item\nelseif $line match "=-" -> Remove-ItemProperty\nRun Code Online (Sandbox Code Playgroud)\n$asciihex=@{}\n0..255 | % {\n $number=$_\n [string]$hex=$number.tostring(\'x\')\n if ($hex.length -eq 1) {$hex=\'{1}{0}\' -f $hex,\'0\'}\n $char=[char]$number\n $asciihex.add($hex,$char)\n }\nRun Code Online (Sandbox Code Playgroud)\n# Change:\n $asciihex.\'00\'\n\n# to:\n $asciihex.\'ff\'\nRun Code Online (Sandbox Code Playgroud)\n# Don\'t use $asciihex to print it\n $asciihex.$codepoint\nRun Code Online (Sandbox Code Playgroud)\n要从行中查找值,请使用-replace $name+$type来获取该值。
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_SZ、REG_DWORD、REG_QWORD、REG_BINARY和REG_MULTI_SZ,REG_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。