如何在PowerShell中将哈希字符串转换为字节数组?

8
当我的脚本运行时,我读取一个哈希值,我想将它写入注册表。 我找到了以下命令可以完成这个任务:
New-ItemProperty  $RegPath -Name $AttrName -PropertyType Binary -Value $byteArray

我还找到了这个链接:如何使用PowerShell设置二进制注册表值(REG_BINARY)?。然而,所有的答案都假设字符串的格式为:
"50,33,01,00,00,00,00,00,...."

但我只能以以下形式阅读我的哈希值:
"F5442930B1778ED31A....."

我不知道如何将这个转换为字节数组,其中包含F5、44等值。


哈希表应该是二进制对象吗?我以为它们应该是十六进制数字或字符串... - Lee_Dailey
1
为什么不使用reg_sz并将其存储为字符串?话虽如此,SANS有一篇关于字节和十六进制的好文章 - vonPryz
@vonPryz 这是微软的设置,格式不由我决定。 - g.pickardou
@Lee_Dailey哈希值始终是二进制的,像这样的工具应该始终以该格式提供它们。十六进制只是使数据“人类”可读的一种方式(实际上只是为了避免控制字符损坏控制台)。除了十六进制之外,您通常会在传输或文本文件存储中看到它以base64形式出现,这就是我所需要的(我使用这里的答案将其作为字节数组获取,以便我可以将字节数组转换为base64)。 - Hashbrown
@Hashbrown - 啊!我今天学到了一些东西...谢谢![咧嘴笑] - Lee_Dailey
4个回答

24

vonPryz明智地建议直接将哈希值作为字符串 (REG_SZ) 存储在注册表中。

如果您真的想将数据存储为 REG_BINARY 类型,即作为一个字节数组,您需要在字符串表示和字节数组之间进行转换。

将其转换为 [byte[]] 数组(使用缩短的样本哈希字符串):

PS> [byte[]] -split ('F54429' -replace '..', '0x$& ')
245 # 1st byte: decimal representation of 0xF5
68  # 2nd byte: decimal representation of 0x44
41  # ...

-replace '..', '0x$& ' 将每一对字符(16进制数).. 前缀加上0x,并在后面插入一个空格。 -split 把结果字符串分割成0xHH字符串的数组(H代表一个16进制数),PowerShell的自动类型转换将其识别为强制转换成[byte[]]数组的元素。换句话说:以上是等同于下面的代码:
[byte[]] ('0xF5', '0x44', '0x29')

以上是PowerShell输出的默认结果,表示一个 [byte[]] (0xF5, 0x44, 0x29) 数组。

更新:在 PowerShell (Core) 7.1+ / .NET 5+ 中,通过新的 [System.Convert]::FromHexString()[System.Convert]::ToHexString() 方法,现在提供了一个更简单的解决方案:

# PowerShell (Core) 7.1+ / .NET 5+
# -> (0xF5, 0x44, 0x29)
[System.Convert]::FromHexString('F54429')

# -> 'F54429'
[System.Convert]::ToHexString([byte[]] (0xF5, 0x44, 0x29))

要将一个[byte[]]数组转换为字符串,可以使用[System.BitConverter]::ToString()方法进行转换。该方法返回由两个十六进制数表示的字符串,这些数字用-分隔。为得到所需的表示形式,需要使用-replace操作删除所有的-符号。

# -> 'F54429'
[System.BitConverter]::ToString([byte[]] (0xf5, 0x44, 0x29)) -replace '-'

总结起来:

# Sample hash string.
$hashString = 'F54429'

# Convert the hash string to a byte array.
$hashByteArray = [byte[]] -split ($hashString -replace '..', '0x$& ')

# Create a REG_BINARY registry value from the byte array.
Set-ItemProperty -LiteralPath HKCU:\ -Name tmp -Type Binary -Value $hashByteArray

# Read the byte array back from the registry (PSv5+)
$hashByteArray2 = Get-ItemPropertyValue -LiteralPath HKCU:\ -Name tmp

# Convert it back to a string.
$hashString2 = [System.BitConverter]::ToString($hashByteArray2) -replace '-'

# (Clean up.)
Remove-ItemProperty -LiteralPath HKCU:\ -Name tmp

1

.Net有一个内置的函数可以实现这个功能。它有点隐蔽...

$x = 'F5442930B1778ED31A'
$bytes = [System.Runtime.Remoting.Metadata.W3cXsd2001.SoapHexBinary]::Parse($x).Value

而另一个方向则是这样的:

[System.Runtime.Remoting.Metadata.W3cXsd2001.SoapHexBinary]::new($bytes).ToString()

如果您没有使用PowerShell Core,则可以使用此方法。 - Jason

1

如果不想使用正则表达式来解析十六进制字符串:

# parse the hex string into a BigInt
# leading zeros avoids parsing as negative
$bytes = [bigint]::Parse("00$value",'HexNumber').ToByteArray()

# undo the reversed bytes
[array]::Reverse($bytes)

# drop the leading zero byte
$null,$bytes = $bytes

0

这里有另一种方法,它不使用正则表达式。

function HexToByteArray([string]$hex) {
(0..([Math]::Floor( ($hex.Length+1)/2)-1)).ForEach({[Convert]::ToByte($(if ($hex.Length -ge 2*($_+1)) {$hex.Substring($_*2,2)} else {$hex.Substring($_*2,1).PadRight(2,'0')}),16)})

}

它可能比你想要的更加健壮 - 它试图支持奇数长度的输入字符串。如果您希望禁止奇数长度的输入字符串,则可以简化转换表达式。


1
避免使用正则表达式有两个好处:(a)概念复杂性和(b)性能。您的解决方案似乎在这两方面都没有帮助。 - mklement0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接