RTMP/AMF/SOL

From Gnash Project Wiki

Jump to: navigation, search

Contents

Overview

There are a variety of protocols and data formats used when playing swf files. The primary one is AMF, which is the lowest level representation of an swf object. This basic representation is used in both RTMP networking, and SOL Shared Objects.

A SOL file is the disk based file used to store objects. It's a simple header prefixed to a series of AMF objects. A SOL file is used by the ActionScript SharedObj class.

The streaming of audio and video is often done using RTMP, as the stream can be seeked as this is an interactive protocol between a swf player and the media server. All RTMP network packets have AMF objects in them. These are objects like NetConnection, NetStream, Video, etc..., and are used to communicate between the client and the server for the best performance.

Utilizing RTMP, SOL, and AMF enables one to do full interactive video conferencing. There is also a variant of RTMP called RTMPT, which is ASCII based, and tunnels over the HTTP port.

Debugging Tips

od is your friend, when it comes to examining binary files. Other options are hexedit or ghex2. There is a variety of output options for od, but the two we mainly like are -A n (don't print addresses) and -t x1 (print hex as single bytes). The other useful one is -c, as that prints ASCII characters if a byte is in printable range.

{rob@ripple} pts/2$ od -A n -c test.sol

\0 277 \0 \0 001 033 T C S O \0 004 \0 \0 \0 \0
\0 \b s e t t i n g s \0 \0 \0 \0 \0 004
g a i n \0 \0 \b 2 \0 \0 \0 \0 \0 \0 \0 \0
017 e c h o s u p p r e s s i o n
001 \0 001 \0 \0 021 d e f a u l t m i c
r o p h o n e 002 \0 016 / d e v / i

{rob@ripple} pts/2$ od -A n -t x1 test.sol

00 bf 00 00 01 1b 54 43 53 4f 00 04 00 00 00 00
00 08 73 65 74 74 69 6e 67 73 00 00 00 00 00 04
67 61 69 6e 00 00 08 32 00 00 00 00 00 00 00 00
0f 65 63 68 6f 73 75 70 70 72 65 73 73 69 6f 6e
01 00 01 00 00 11 64 65 66 61 75 6c 74 6d 69 63
72 6f 70 68 6f 6e 65 02 00 0e 2f 64 65 76 2f 69
6e 70 75 74 2f 6d 69 63 00 0d 64 65 66 61 75 6c


{rob@ripple} pts/2$ od -A n -t x1 -c test.sol

00 bf 00 00 01 1b 54 43 53 4f 00 04 00 00 00 00
\0 277 \0 \0 001 033 T C S O \0 004 \0 \0 \0 \0
00 08 73 65 74 74 69 6e 67 73 00 00 00 00 00 04
\0 \b s e t t i n g s \0 \0 \0 \0 \0 004
67 61 69 6e 00 00 08 32 00 00 00 00 00 00 00 00
g a i n \0 \0 \b 2 \0 \0 \0 \0 \0 \0 \0 \0
0f 65 63 68 6f 73 75 70 70 72 65 73 73 69 6f 6e
017 e c h o s u p p r e s s i o n
01 00 01 00 00 11 64 65 66 61 75 6c 74 6d 69 63

Needless to mention, like most developers I spend alot of time deep in GDB, trying to examine bufffers of binary data. In these cases, the x command is useful for dumping the data behind a pointer in a width variety of output formats. I predominately use the b modifier to display single bytes, so I can compare with the od dumps from files. GDB does one nice thing which is mixes the character and hex output modes of od into the same line of output.

gdb> x/10b buffer

(gdb) x/10c body
0x80ffd28: 0 '\0' 4 '\004' 103 'g' 97 'a' 105 'i' 110 'n' 0 '\0' 0 '\0'
0x80ffd30: 8 '\b' 50 '2'

I also find it useful to use GDB to convert bytes between hex and ASCII. A simple p/x '\@' will get the hex value for an ASCII character, and p/c 0x64 will get you the ASCII representation for a hex value. When debugging multi-threaded programs like Gnash or Cygnal the other very useful trick is to be able to set breakpoints in multiple threads. To do this, use the thread apply all prefixed before all breakpoint setting commands, and it'll apply to all the threads. People often forget to do this, and can't figure out why their breakpoints aren't working.

Now to know what the data needs to look like, you often have to use a packet sniffer. I primarily tcpdump, mainly because I've used it for a long time, and like to be able to grep through it's ASCII output. I typically save the raw files, and then run tcpdump later to get differing output formats. Lately I've been sniffing Red5 and Cygnal to see the differences in out implementations. As Cygnal runs on port 4080 for my debugging session, Red5 runs on the RTMP port of 1935. So with this command I can grab both:

tcpdump -i lo port 4080 and port 1935 -w out.raw

To later display this in ASCII for, I might use:

tcpdump -A -r out.raw

Other applications for packet sniffing are WireShark and Ethereal. Both offer a nice modern Graphical interface instead of tcpdump's ancient command line interface. They also let you save the packets in ASCII for grepping later.


Shared Object Files

A [SharedObject] is a file on disk that stores AMF objects so data can be shared between successive plays of a swf movie, or between different swf movies. These are a bit like web browser cookies, so there are some security concerns. The other player stores all of it's created .sol files in a directory tree starting at ~/.macromedia/Flash_Player/#SharedObjects/, followed by a number like D6CBAJTS whose value appears to be created at installation time. Gnash has a soldumper utility you can use to display the contents of the .sol file so you can see what is being stored.

LocalConnections

LocalConnections are SYSV style shared memory segments (at least on unix variants) used to transfer data between running swf movies.

Each shared memory segment has a 16 byte header, comprising of 4 32bit words The first 2 words purpose is unknown at this time. The third field however, is the timestamp. Unless the timestamp is modified, waiting clients can't tell when data has been updated. The fourth and final field of the header is the size of the AMF object in memory. The connection file is always 64528 bytes in size, and the SYSV key appears to be different for each installation'. You can find the key for your machine easily by grepping the output from the ipcs for all shared memory segments of this size. Once you have that, you can use the set LCShmKey option in your ~/.gnashrc file so dumpshm and Gnash can access that segment. If you don't care about compatibility with the other player, then you can set this to any unique number you want, or use ftok() to create one.

The dumpshm utility in Gnash now supports two new options for dumping the segment to the screen, or writing the raw file to disk for later decoding.

The maximum size of an AMF object allowed in a connection is 40k bytes. The listener table starts after this, at byte location 40976. The listener table is a simple array of const char * strings. For an application to register itself on a connection, it merely adds itself to the list. When an application goes away, it removes itself from the list. Although they aren't available by ActionScript, the LocalConnection implementation in Gnash allows one to list, add, and remove Listeners from the table. These are currently used only internally.

Running Example

For a running example of RTMP used for streaming video, you can try this "Video Lectures" site. As RTMP server side connections are expensive, I rarely see any web sites using it in the wild. This is useful for both packet sniffing the protocol, but also for testing the client side RTMP implementation in Gnash.

Red5 can also be used for packet sniffing and testing, but this site is useful as it appears to be the actual FMS server, not another reverse engineered one.