pythonhexamfpyamf

How I can convert this value to hex? (amf value)


the context: I decode a amf response from an flex app with python.

With pyamf I can decode all the response, but one value got my attention.

this value \xa2C is transformed to 4419

#\xa2C -> 4419
#\xddI -> 11977 

I know \x is related with a hex value, but I cant get the function to transform 4419 to \xa2C.

the 4419 is an integer.

--- Update 1

This original value, are not hex.

because I transform this value \xa2I to 4425.

So what kind of value is \xa2I ??? Thanks!

-- Update 2.

DJ = 5834
0F = 15
0G = error
1F = 31
a1f = 4294
adI = 5833
adg = 5863
adh = 5864

Is strange some time accept values after F and in other situation show an error. But are not hex value that is for sure.


Solution

  • What you're seeing is the string representation of the bytes of an AmfInteger. The first example, \xa2C consists of two bytes: 0xa2 aka 162, and C, which is the ASCII representation of 67:

    >>> ord("\xa2C"[0])
    162
    >>> ord("\xa2C"[1])
    67
    

    To convert this into an AmfInteger, we have to follow the AMF3 specifications, section 1.3.1 (the format of an AmfInteger is the same in AMF0 and AMF3, so it doesn't matter what specification we look at).

    In that section, a U29 (variable length unsigned 29-bit integer, which is what AmfIntegers use internally to represent the value) is defined as either a 1-, 2-, 3- or 4-byte sequence. Each byte encodes information about the value itself, as well as whether another byte follows. To figure out whether another byte follows the current one, one just needs to check whether the most significant bit is set:

    >>> (162 & 0x80) == 0x80
    True
    >>> (67 & 0x80) == 0x80
    False
    

    So we now confirmed that the byte sequence you see is indeed a full U29: the first byte has its high bit set, to indicate that it's followed by another byte. The second byte has the bit unset, to indicate the end of the sequence. To get the actual value from those bytes, we now only need to combine their values, while masking out the high bit of the first byte:

    >>> 162 & 0x7f
    34
    >>> 34 << 7
    4352
    >>> 4352 | 67
    4419
    

    From this, it should be easy to figure out why the other values give the results you observe.

    For completeness sake, here's also a Python snippet with an example implementation that parses a U29, including all corner cases:

    def parse_u29(byte_sequence):
        value = 0
        # Handle the initial bytes
        for byte in byte_sequence[:-1]:
            # Ensure it has its high bit set.
            assert ord(byte) & 0x80
    
            # Extract the value and add it to the accumulator.
            value <<= 7
            value |= ord(byte) & 0x7F
    
        # Handle the last byte.
        value <<= 8 if len(byte_sequence) > 3 else 7
        value |= ord(byte_sequence[-1])
    
        # Handle sign.
        value = (value + 2**28) % 2**29 - 2**28
    
        return value
    
    
    print parse_u29("\xa2C"), 4419
    print parse_u29(map(chr, [0x88, 0x00])), 1024
    print parse_u29(map(chr, [0xFF, 0xFF, 0x7E])), 0x1ffffe
    print parse_u29(map(chr, [0x80, 0xC0, 0x80, 0x00])), 0x200000
    print parse_u29(map(chr, [0xBF, 0xFF, 0xFF, 0xFE])), 0xffffffe
    print parse_u29(map(chr, [0xC0, 0x80, 0x80, 0x01])), -268435455
    print parse_u29(map(chr, [0xFF, 0xFF, 0xFF, 0x81])), -127