neoxic / php-amf3

PHP AMF3 extension

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Parse AMF request

nvazquezg opened this issue · comments

I'm trying to parse an "application/x-amf" request using amf3_decode, but it doesn't return anything nor write any warning ($decoded is empty):

if(!isset($GLOBALS['HTTP_RAW_POST_DATA'])){
        $GLOBALS['HTTP_RAW_POST_DATA'] = file_get_contents('php://input');
}
$decoded = amf3_decode($GLOBALS['HTTP_RAW_POST_DATA']);

Is something wrong with this code? Can you post any example to parse an AMF request on the wiki?

Thanks

The code looks ok to me. There are two ways here:

  1. If the AMF input for amf3_decode() in your case is correct, you get the decoded object in $decoded.
    Call
var_dump($decoded)

to see it.

  1. If the input is invalid and you do not get any warnings, you probably either have the warnings suppressed (see your php.ini for details) or do not see them for some other specific reason. You may want to pass the second argument to know exactly what's going on.
$decoded = amf3_decode($GLOBALS['HTTP_RAW_POST_DATA'], $count);
if ($count > 0) var_dump($decoded);
else print 'DECODE ERROR';

I'll work on the wiki as soon as I have some spare time.

Using your code:

$decoded = amf3_decode($GLOBALS['HTTP_RAW_POST_DATA'], $count);
if ($count > 0) var_dump($decoded);
else print 'DECODE ERROR';

Prints NULL and $count value is 1.

Maybe is because the request I'm trying to decode which is the first one that is sended in an AMFChannel from Flex. It seems that it has no parameters, but I can't find this info from HTTP_RAW_POST_DATA decoded:

^@^C^@^@^@^A^@^Dnull^@^B/1^@^@^@à
^@^@^@^A^Q
<81>^SMflex.messaging.messages.CommandMessage^Soperation^[correlationId body^QclientId^Wdestination^SmessageId^UtimeToLive^Stimestamp^Oheaders^D^E^F^A
^K^A^A^A^F^A^FI3A2D486A-EB49-A800-D92B-A47C5C43077D^D^@^D^@
^E      DSId^F^Gnil%DSMessagingVersion^D^A^A

¿Is there any way to get this info decoded? Thanks again!

There is no way for me to understand whether there is any valid AMF chunk to decode other than to see a hex dump of the data. You probably act too naively putting everything into decode without trimming the correct data first.

You get NULL because the first byte of your data is 0x00 which is the AMF3 NULL type. Thus, it's decoded correctly.
Try calling

print(bin2hex($your_data))

to see the hex dump.

Here is the output of bin2hex:

00030000000100046e756c6c00022f31000000e00a00000001110a81134d666c65782e6d6573736167696e672e6d657373616765732e436f6d6d616e644d657373616765136f7065726174696f6e1b636f7272656c6174696f6e49640f6865616465727309626f6479136d65737361676549641574696d65546f4c6976651764657374696e6174696f6e1374696d657374616d7011636c69656e744964040506010a0b01094453496406076e696c2544534d6573736167696e6756657273696f6e0401010a0501064936454533353933452d434142352d323936362d313836412d41383333314546374535453504000601040001

I've been trying to decode it using the AMF3 Specification but don't know if I am on the right path...

Ok, I wrote a tiny research script for your data. Here it is:

$h = "00030000000100046e756c6c00022f31000000e00a00000001110a81134d666c65782e6d6573736167696e672e6d657373616765732e436f6d6d616e644d657373616765136f7065726174696f6e1b636f7272656c6174696f6e49640f6865616465727309626f6479136d65737361676549641574696d65546f4c6976651764657374696e6174696f6e1374696d657374616d7011636c69656e744964040506010a0b01094453496406076e696c2544534d6573736167696e6756657273696f6e0401010a0501064936454533353933452d434142352d323936362d313836412d41383333314546374535453504000601040001";
$d = pack("H*", $h);
$l = strlen($d);
for ($p = 0; $p < $l; ++$p) {
    $c = substr($d, $p);
    $o = @amf3_decode($c, $r);
    if ($r > 0) {
        $e = $l - $p - $r;
        print "----------------------------\n";
        print "pos: $p, read: $r, remainder: $e\n";
        print "header: " . substr($h, 0, $p * 2) . "\n";
        print "footer: " . substr($h, ($p + $r) * 2) . "\n";
        var_dump($o);
    }
}

At offset 26, there seems to be the AMF3 chunk you are looking for:

pos: 26, read: 218, remainder: 0
header: 00030000000100046e756c6c00022f31000000e00a0000000111
footer:
array(10) {
  ["operation"]=>
  int(5)
  ["correlationId"]=>
  string(0) ""
  ["headers"]=>
  array(2) {
    ["DSId"]=>
    string(3) "nil"
    ["DSMessagingVersion"]=>
    int(1)
  }
  ["body"]=>
  array(0) {
  }
  ["messageId"]=>
  string(36) "6EE3593E-CAB5-2966-186A-A8331EF7E5E5"
  ["timeToLive"]=>
  int(0)
  ["destination"]=>
  string(0) ""
  ["timestamp"]=>
  int(0)
  ["clientId"]=>
  NULL
  ["_class"]=>
  string(38) "flex.messaging.messages.CommandMessage"
}

So basically, your AMFChannel forms a packet consisting of a header of unknown structure following by an AMF3 data block decoded above. The header (the data before the AMF3 chunk) is:

0003 00000001 0004 6e756c6c 0002 2f31 0000 00e0 0a00 0000 0111

It looks like a sequence of some fields. Notably, there is:

... 0004 6e756c6c 0002 2f31 ...

which seems to be two encoded strings in the form of LENGTH (2 bytes), DATA (4 bytes), LENGTH (2 bytes), DATA (2 bytes). Accordingly, the values of the strings are "null" and "/1". I have no idea what these are along with the rest of the header. So you probably need to look for a documentation on AMFChannel and its formats. Good luck! Btw, your data helped to find a tiny bug in error reporting, so thanks!

That's great, thank you.
I'm glad I helped you in some way :)
Regards

it appears amf3_decode() only decodes the body data, not the whole AMF3 packet, here's a breakdown of the header:

0003 = version 3
0000 = header count (zero headers)
0001 = message count (1 message)
0004 = target uri length (4 bytes)
6e756c6c = target uri (literally the word "null")
0002 = response uri length (2 characters)
2f31 = response uri (/1)
000000e0 = message length (224 characters)
(remaining bytes form the message body, which gets decoded)

Thanks to wiki for the spec: http://en.wikipedia.org/wiki/Action_Message_Format

Perhaps there could be a method added, something like amf3_decode_headers() ?

This project deals with the AMF3 format according to its specification: www.adobe.com/go/amfspec

"Packets", "headers", etc. is project specific stuff a user usually deals with in their particular case. There's no way to know ahead what packet format one will choose. For example, pack() and unpack() could be used for the purpose of packing/unpacking headers or prefixes in the desired way. Generally speaking, it's up to the user.