Jump to content

Help with 2's complement / data conversion/Negative numbers - (Moved)


Recommended Posts

7 hours ago, A-Team said:

have implemented this  this works exactly as I had envisioned

With respect to Andreik, this won't work in all scenarios.  How do you know 193 does not mean 193?
It assumes all numbers in the ranges 0x80 - 0xFF (128-255) and 0x8000 to 0xFFFF (32768-65535) are negative repesentations.

 

A quick google  - https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/basic-encoding-rules.html

INTEGER

    The INTEGER value, positive or negative, is encoded as a 2's complement binary number, with the high order octet in the leftmost (first) position.
    
    The encoded value can be a single octet or may contain hundreds of octets (although handling large integer values is difficult).
    
    Leading octets of all 0's (or all 1's) are not allowed. In other words, the leftmost nine bits of an encoded INTEGER value may not be all 0's or all 1's. This ensures that an INTEGER value is encoded in the smallest possible number of octets.

And examples...

temperatureToday INTEGER ::= 72
                -- 02 01 48

Color ::= INTEGER {red(0), blue(1), yellow(2)}
defaultColor Color ::= blue
                -- 02 01 01

 

Just eyeballing this, (still needs a proper look) -  you've got 0201C1 at the end of rcvData

02 is the datatye (int, generic)
01 is length (1 byte)
C1 is the data.

So I would say:

02 01 C1 is -63
02 02 00 C1 would be the structure for a positive 193

I'd imagine the UDF doesn't check the first bit of the number for the sign.

Edited by MattyD
Link to comment
Share on other sites

2 hours ago, MattyD said:

02 is the datatye (int, generic)
01 is length (1 byte)
C1 is the data.

This is true.

2 hours ago, MattyD said:

So I would say:

02 01 C1 is -63
02 02 00 C1 would be the structure for a positive 193

This is not actually true. A negative value like -63 it is encoded as 02 04 FFFFFFC1 and a positive value of 193 it is encoded as 02 01 C1.

In this case the function can be even shorter:

Func SInt($iValue)
    Return BitAND($iValue, 0x80000000) ? BitNOT(BitAND(BitNOT($iValue), 0xFFFFFFFF)) : $iValue
EndFunc

or simply:

Func SInt($iValue)
    Return Int($iValue, 1)
EndFunc
Edited by Andreik

When the words fail... music speaks.

Link to comment
Share on other sites

To help out with debugging...

#include "SNMP_UDF-v1.7.4.au3"

Local $testVal = Binary("0x302E02010104067075626c6963a22102010102010002010030163014060f2b0601040109095b0101010104880f0201c1")
Global $SNMP_Received[100][3]
_ShowSNMPReceived($testVal)
_ArrayDisplay($SNMP_Received)

and a breakdown of the data

Breakdown

30 2E              - (Sequence, Len=46-bytes)
02 01 01           - (int, 1-byte, data=01)      [SNMP Version = 2 (0-based)]
04 06 7075626C6963 - (string, 6-bytes, data)     [Community = "public"]
A2 21              - (GetResponse, Len=33-bytes)
02 01 01           - (int, 1-byte, data=01)      [request ID = 1]
02 01 00           - (int, 1-byte, data=00)      [error = 0]
02 01 00           - (int, 1-byte, data=00)      [errorIndex = 0]
30 16              - (Sequence, Len=22-bytes)    [varbind list]
30 14              - (Sequence, Len=20-bytes)    [varbind]
06 0f 2b0601040109095b0101010104880f - (oid, 15-bytes) there's special rules for en/decoding this!
02 01 c1           - (int, 1-byte, data=C1)

 

6 hours ago, Andreik said:

 A negative value like -63 it is encoded as 02 04 FFFFFFC1 and a positive value of 193 it is encoded as 02 01 C1

Ae you sure? That violales this rule...

Leading octets of all 0's (or all 1's) are not allowed. In other words, the leftmost nine bits of an encoded INTEGER value may not be all 0's or all 1's. This ensures that an INTEGER value is encoded in the smallest possible number of octets.

My understanding is that:

0xC1 leads with a "1" in binary, so its a negative value.  (flip the bits + 1 gives you 0x3F, or 63)
0x00C1 leads with a "0" in binary, so its positive. (leave as is, value is 193)

0x0000C1 is illegal, as is 0xFFC1, 0x007F etc. or anything else with superfluous leading bytes.

Edited by MattyD
Link to comment
Share on other sites

2 hours ago, MattyD said:

Ae you sure? That violales this rule...

Your understanding might be correct (and seems to be consistent with what OP receives from agents) but this is what @enaiman's library does. Also be aware that the protocol has different versions but only v1 and v2 are supported by this UDF so there might be some additional specifications and rules like what you presented above. I didn't use this UDF but quoting the author it seems the UDF worked well in his tests so the encoding probably was right (I am not willing to spend time to debug the entire UDF to confirm or dismiss that). What I am not so sure is how conversion from binary data is done to match a specific data type so the presentation of data might not be the best.

Quote

[03/10/2008] Finally I've got the code to work 100% correct on all my tests. I have redone quite a significant bit of the code and added a couple more functions.

A basic test would to see how this UDF encode data is this:

$iValue = 193
$sPacket = _SNMPBuildPacket('1.3.0', "public", 1, 1, "A1", "32", "02", $iValue)
$aData = _ShowSNMPReceived($sPacket)

ConsoleWrite($sPacket & @TAB & $aData[1][1] & @CRLF)

$iValue = -63
$sPacket = _SNMPBuildPacket('1.3.0', "public", 1, 1, "A1", "32", "02", $iValue)
$aData = _ShowSNMPReceived($sPacket)

ConsoleWrite($sPacket & @TAB & $aData[1][1] & @CRLF)

and the result is this:

0x302202010004067075626C6963A115020200010201000201003009300706022B000201C1  193
0x302502010004067075626C6963A11802020001020100020100300C300A06022B000204FFFFFFC1    4294967233

 

Basically the resulting bytes already represent the correct signed integer but the implementation of this UDF for some reason return the data as double and a conversion to signed integer is required. So I think @jchd is right about data being butchered.

When the words fail... music speaks.

Link to comment
Share on other sites

As far as I can tell, you only need to change this function _SNMPHexToDec in the UDF to :

deleted (not robust enough)

All my tests work correctly using any numbers (positive and negative).  You may need to check length of the $nbr to confirm that it is a valid number...

Edited by Nine
Link to comment
Share on other sites

4 hours ago, Nine said:

All my tests work correctly using any numbers (positive and negative). 

Hey nine, how did you go about creating your test data? The UDF happily decodes the packets that it creates, but I'm pretty sure those are invalid (when negative numbers are involved).

I tried with A-Team's real world packet, and it's still looks to be incorrect after the above change.

Binary("0x302E02010104067075626c6963a22102010102010002010030163014060f2b0601040109095b0101010104880f0201c1")

 

The value passed to _SNMPHexToDec seems to be a string representation of the data bytes, starting from the point after the int length is specified.

So maybe something like this could work. (see if the first bit is a 1, if so then backfill)

Func _SNMPHexToDec($sNum)
    Local $iLen, $iNum, $iSignTest, $iHighBits

    $iLen = Int(StringLen($sNum)/2, 1)
    $iNum = Dec($sNum)
    $iSignTest = 2^($iLen*8 -1)

    If BitAND($iSignTest, $iNum) Then
        $iHighBits = BitNOT($iSignTest - 1)
        $iNum = BitOR($iHighBits, $iNum)
    EndIf

    ConsoleWrite($iNum & @CRLF)

    Return $iNum
EndFunc

 

Edited by MattyD
Link to comment
Share on other sites

All the tests I used was based on the UDF itself.  If the encoding of the packets is wrong then the tests I made are also faulty.

In fact if C1 is -63 then Dec function is not appropriate, I agree with you, an approach like yours is more robust under all circumstances.

I'll erase the conclusion of my post.  Thanks.

Link to comment
Share on other sites

Link to comment
Share on other sites

15 hours ago, Nine said:

How about this single liner that is supporting up to 64 bits :

ConsoleWrite(_SNMPHexToDec("FFFFFFFFF0") & @CRLF)
Func _SNMPHexToDec($nbr)
  Return Dec(StringReplace(StringFormat("%016s", $nbr), "0", BitAND(Dec(StringLeft($nbr, 1)), 8) ? "F" : "0", 16-stringlen($nbr)))
EndFunc

 

Yep, big numbers will break mine, and this outputs correct values. 👍

Theres a bit happening here - so for those playing at home:

  • This snips the first character of the data string, and coverts it to a number.
  • If the number is negative the 4th bit will be set. We can check this with BitAND($iSnippedChar, 8).
    • note: we only captured 4bits of data here, not the entire byte!
       
  • Now pad the data string with 0s from the front to represent a 64bit value
  • If the numer is negative, replace those padded 0's with F's
  • Convert the string to back to int and return.
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...