0

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.

nguaman
  • 925
  • 1
  • 9
  • 23
  • 1
    `a2C` is not 4419 in decimal either. I notice the `C` is uppercase, (as is `I` in the next line). – Martijn Pieters Mar 07 '16 at 13:04
  • thanks! I update the question! – nguaman Mar 07 '16 at 13:07
  • How do you get these values? Where do they come from? I see no evidence that AMF uses `\x` prefixes, so I suspect you are looking at *Python `repr()` output*, where printable ASCII characters are expressed as such. – Martijn Pieters Mar 07 '16 at 13:08
  • `C` in ASCII is hex `43`. `4419` in hex is `1143`, so I see a correlation there. That leaves you with a remainder of hex 91 for the left two 'digits' of the binary value. However, `I` is `49` in hex and that has little relation to the `11977` value in hex. I guess you'll need to study the [AMF format](https://en.wikipedia.org/wiki/Action_Message_Format) to figure that out. – Martijn Pieters Mar 07 '16 at 13:10
  • Since AMF is a *binary* encoding format, without further context there is *almost nothing* we can say about these values. There is more (binary data) context around these pieces of info that inform how they must be interpreted by a decoder, and you didn't share that context. – Martijn Pieters Mar 07 '16 at 13:12
  • I get this values from a flex app. came from a webservice. I got this values from the response in google chrome. This are not hex value, but have some similar values. – nguaman Mar 07 '16 at 13:15

1 Answers1

1

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
Ventero
  • 10,935
  • 2
  • 23
  • 21
  • one question, how I can do the reverse of this 4352 | 67 = 4419 how I can get the 4352 from the 67 and the 4419 ?? Thanks Ventero! – nguaman Mar 08 '16 at 21:47
  • 1
    @NelsonGuamanLeiva Take a look at section 1.3.1 in the spec, the first list shows how to convert numbers in certain ranges to amf integers. For numbers between 0x80 and 0x3fff, you split up the number in two bytes, where the first one has its highest bit set, followed by the highest 7 bits of the original number, and the second one gets the rest, i.e. `(4419 >> 7) | 0x80 = 162` and `4419 & 0x7f = 67`. That's how you get the 162 and 67 from your original byte sequence. – Ventero Mar 08 '16 at 22:08
  • ! Thanks you! for all you responses! – nguaman Mar 09 '16 at 12:30