9

When my scripts run, I read a hash, and I would like to write it to the registry. I figured out that the following command will do it:

New-ItemProperty  $RegPath -Name $AttrName -PropertyType Binary -Value $byteArray

I also found the How to set a binary registry value (REG_BINARY) with PowerShell?.

However all answers suppose that the string is in form of:

"50,33,01,00,00,00,00,00,...."

but I only can read my hash in the following form:

"F5442930B1778ED31A....."

I can not figure out, how can I convert this to a byte array, with values F5, 44 etc.

5
  • is a hash supposed to be a binary object? i thot they were supposed to be hexadecimal numbers or strings ... Commented Feb 5, 2019 at 21:21
  • 1
    Why not use reg_sz and store as a string? That being said, SANS has good article about bytes and hex. Commented Feb 5, 2019 at 21:29
  • @vonPryz it is an MS setting, format is not up to me unfortunately. Commented Feb 6, 2019 at 4:30
  • @Lee_Dailey hash's are always binary, and tools like this should always make them available in that format. Hex is just one way of making that data "human" readable (really just so it doesn't corrupt the console with control characters). Other than hex you'll usually see it in base64 for transport or textfile storage, which is what I needed (I used the answers here to get it as a byte array, so I could convert byte array to base64) Commented Jan 28, 2022 at 6:04
  • @Hashbrown - ah! i learned something today ... thank you! [grin] Commented Jan 28, 2022 at 11:58

6 Answers 6

29

vonPryz sensibly suggests simply storing the hash directly as a string (REG_SZ) in the registry.

If you really want to store the data as type REG_BINARY, i.e., as an array of bytes, you must convert back and forth between the string representation.

To convert to a [byte[]] array (using a shortened sample hash string):

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

-replace '..', '0x$& ' prefixes each pair of characters (hex digits) - .., referenced in the replacement operand as $& - with 0x and inserts a space afterwards. -split splits the resulting string into an array of 0xHH strings (H representing a hex digit), which PowerShell's automatic type conversions recognize as the elements of a [byte[]] array in a cast. In other words: the above is the equivalent of:
[byte[]] ('0xF5', '0x44', '0x29')

The above is PowerShell's default output representation of resulting array
[byte[]] (0xF5, 0x44, 0x29).

Update: In PowerShell (Core) 7.1+ / .NET 5+ , a simpler solution is now available, via the new [System.Convert]::FromHexString() and [System.Convert]::ToHexString() methods:

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

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

To convert from a [byte[]] array (back to a string):

[System.BitConverter]::ToString() outputs two-hex-digit representations separated with -, so to get the desired representation, all - instances must be removed, using a -replace operation here:

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

To put it all together:

# 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
Sign up to request clarification or add additional context in comments.

Comments

3

To parse a hex string without the weight of a regular expression:

# 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

2 Comments

Trying to convert old school REG ADD commands to Powershell and this is exactly what I needed to get a proper Bytes array.
For my opinion this solution has the best balance of code-length and speed. Great work!
2

.Net framework has a build-in function for that. It's a bit hidden...

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

..and the other direction goes like this:

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

4 Comments

This helps if you're not using PowerShell Core.
I get an error: InvalidOperation: Unable to find type [System.Runtime.Remoting.Metadata.W3cXsd2001.SoapHexBinary].
@not2qubit The code snippets working fine for me in Powershell 5.x with .NET Framework.
I'm using latest pwsh (7.5.0)...
0

Here is another approach that doesn't use regular expressions.

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)})
}

It's possibly more robust than you might want - it tries to support odd-length input strings. You might prefer to disallow odd-length input strings, in which case the conversion expression could be simplified.

1 Comment

There are two good reasons to avoid regular expressions: (a) conceptual complexity and (b) performance. Your solution appears to help in neither respect.
0

If speed is important then I would recommend an embedded C# code like this:

$x = 'F5442930B1778ED31A'

Add-Type -TypeDefinition @"
using System;
public class HexConverter {
    public static byte[] HexStringToByteArray(string hexString) {
        int length = hexString.Length;
        byte[] bytes = new byte[length / 2];

        for (int i = 0; i < length; i += 2) {
            bytes[i / 2] = (byte)((GetHexVal(hexString[i]) << 4) + GetHexVal(hexString[i + 1]));
        }
        return bytes;
    }

    private static int GetHexVal(char hex) {
        int val = (int)hex;
        return val - (val < 58 ? 48 : 55);
    }
}
"@

[HexConverter]::HexStringToByteArray($x)

If you just want a one-liner then I recommend this:

[byte[]]($x -replace '..', '0x$& ').TrimEnd().Split()

Comments

0

Another method splitting the string after every 2 characters, see https://regex101.com/r/iH6unU/1 for details, and then Convert.ToByte to convert each token to a byte. This is only worth doing if you're in PowerShell 5.1, in PowerShell 7 the preferred and simplest way is to use [Convert]::FromHexString(...).

function ConvertFrom-HexString {
    param([Parameter(Mandatory)] $s)

    [System.Linq.Enumerable]::Select(
        [regex]::Split($s, '(?<=\G.{2})(?!$)'),
        [System.Func[string, byte]] { [System.Convert]::ToByte($args[0], 16) })
}

ConvertFrom-HexString F54429

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.