2

I am trying to convert a (variable length) Hex String to Signed Integer (I need either positive or negative values).

[Int16] [int 32] and [int64] seem to work fine with 2,4+ byte length Hex Strings but I'm stuck with 3 byte strings [int24] (no such command in powershell).

Here's what I have now (snippet):

$start = $mftdatarnbh.Substring($DataRunStringsOffset+$LengthBytes*2+2,$StartBytes*2) -split "(..)"
[array]::reverse($start)
$start = -join $start

if($StartBytes*8 -le 16){$startd =[int16]"0x$($start)"}
elseif($StartBytes*8 -in (17..48)){$startd =[int32]"0x$($start)"}
else{$startd =[int64]"0x$($start)"}

With the above code, a $start value of "D35A71" gives '13851249' instead of '-2925967'. I tried to figure out a way to implement two's complement but got lost. Any easy way to do this right?

Thank you in advance

Edit: Basically, I think I need to implement something like this:
int num = (sbyte)array[0] << 16 | array[1] << 8 | array[2];
as seen here.

Just tried this:

$start = "D35A71"
[sbyte]"0x$($start.Substring(0,2))" -shl 16 -bor "0x$($start.Substring(2,2))" -shl 8 -bor "0x$($start.Substring(4,2))"

but doesn't seem to get the correct result :-/

6
  • Would you like to use C#? Commented Feb 19, 2020 at 13:28
  • I don't know C#, but tried to find a code to do the same & convert it. That's where I got lost (in the translation) .. Commented Feb 19, 2020 at 13:32
  • What is this for? 0xD35A71 is 13851249 in decimal. Commented Feb 19, 2020 at 13:36
  • $MFT FILE record dataruns Look at the mostsignificant bit: 1101 0011 0101 1010 0111 0001‬ This is a negative number Commented Feb 19, 2020 at 13:38
  • A DWORD is represented by 32-bits, not 24-bits. Thus, the most-significant bit is actually a zero (0): 0000 0000 1101 0011 0101 1010 0111 0001 Commented Feb 19, 2020 at 13:52

1 Answer 1

4

To parse your hex.-number string as a negative number you can use [bigint] (System.Numerics.BigInteger):

# Since the most significant hex digit has a 1 as its most significant bit
# (is >= 0x8), it is parsed as a NEGATIVE number.
# To force unconditional interpretation as a positive number, prepend '0'
# to the input hex string.
PS> [bigint]::Parse('D35A71', 'AllowHexSpecifier')
-2925967

You can cast the resulting [bigint] instance back to an [int] (System.Int32).

Note:

  • The result is a negative number, because the most significant hex digit of the hex input string is >= 0x8 (binary 1000), i.e. has its high bit set (meaning: the most significant of the 4 bits that make up a given hex digit is 1).

    • To force [bigint] to unconditionally interpret a hex. input string as a positive number, prepend 0.
  • The internal two's complement representation of a resulting negative number is performed at byte boundaries, so that a given hex number with an odd number of digits (i.e. if the first hex digit is a "half byte") has the missing half byte filled with 1 bits.

  • Therefore, a hex-number string whose most significant digit is >= 0x8 (parses as a negative number) results in the same number as prepending one or more Fs (0xF == 1111) to it; e.g., the following calls all result in -2048:
    [bigint]::Parse('800', 'AllowHexSpecifier'),
    [bigint]::Parse('F800', 'AllowHexSpecifier'),
    [bigint]::Parse('FF800', 'AllowHexSpecifier'), ...

See the docs for details about the parsing logic.


Examples:

# First digit (7) is < 8 (high bit NOT set) -> positive number
[bigint]::Parse('7FF', 'AllowHexSpecifier') # -> 2047

# First digit (8) is >= 8 (high bit IS SET) -> negative number
[bigint]::Parse('800', 'AllowHexSpecifier') # -> -2048

# Prepending additional 'F's to a number that parses as 
# a negative number yields the *same* result
[bigint]::Parse('F800', 'AllowHexSpecifier') # -> -2048
[bigint]::Parse('FF800', 'AllowHexSpecifier') # -> -2048
# ...

# Starting the hex-number string with '0' 
# *unconditionally* makes the result a *positive* number
[bigint]::Parse('0800', 'AllowHexSpecifier') # -> 2048
Sign up to request clarification or add additional context in comments.

1 Comment

It works exactly as I wanted it. Replaced the "IF" part of my code snippet (above) with $startd = [bigint]::Parse($start, 'AllowHexSpecifier') and tested it with a full $MFT parse. The results for each (negative) data run are now correct. Thank you again!

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.