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 :-/

mklement0
  • 382,024
  • 64
  • 607
  • 775
sv2hui
  • 23
  • 7
  • Would you like to use C#? – Wasif Feb 19 '20 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) .. – sv2hui Feb 19 '20 at 13:32
  • What is this for? 0xD35A71 is 13851249 in decimal. – js2010 Feb 19 '20 at 13:36
  • $MFT FILE record dataruns Look at the mostsignificant bit: 1101 0011 0101 1010 0111 0001‬ This is a negative number – sv2hui Feb 19 '20 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` – leeharvey1 Feb 19 '20 at 13:52
  • Just type: `[convert]::ToString("0xD35A71",2) .length` The result is 24 bits : `110100110101101001110001` (3 bytes). A DWORD is 32 bits..24 bits is an SWORD [link](https://en.wikipedia.org/wiki/24-bit_computing) – sv2hui Feb 19 '20 at 14:01

1 Answers1

3

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, i.e. has its high bit set.

    • 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
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    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! – sv2hui Feb 19 '20 at 17:00