New file transfer protocol analyzed

I have successfully managed to reverse-engineer a new file transfer protocol for GG11. Good news is, that I implemented my own client, able to send a file. Bad news is, that it requires a token, which is send by Gadu-Gadu service after successfully logging in using GG11 protocol.

That means, Pidgin (and other clients, like Kadu) will be able to support new file transfer method just after introducing GG11 protocol support for libgadu. On the other hand, old file transfer protocol implementation for Pidgin have questionable value, because original Gadu-Gadu client seems to be in middle of dropping support for it.

General information

File transfer in GG11 is based mostly on HTTP queries. Original client uses https protocol, but unencrypted connections are supported too. To begin a transfer, we need IMToken value, which is sent by Gadu-Gadu service just after successfully logging in using GG11 protocol (GG10.5 doesn’t get such packet). Token is valid until closing GG11 connection.

Client, who wants to send file creates a “ticket”, which is file transfer identifier. After accepting it by the receiving side, file is uploaded to GG drive (service like Dropbox or Google Drive), to special folder (not accessible for end-user). Then, receiving side downloads this file from server.

Protocol doesn’t provide information about downloading progress. That means, after file upload, user is notified about successful file transfer.

Example PHP script, which sends a file, requires only the token mentioned before. This must be sniffed (ie. using Wireshark tool) from original GG11 client session. While transferring a file, original session must not be closed.

Sending a file

Receiving side have to use client compatible with GG11. Protocol communication is described below. Details about sending and receiving mentioned fields are contained in example implementation.

#1 we log in using GG11 protocol.

#2 we get a packet with IMToken (packet id: 0x8C)

struct gg_imtoken
{
	char magic; /* 0x0A */
	char token_len;
	char token[token_len];
}

#3 authorize (once per session), sending PUT https://drive.mpa.gg.pl/signin request. Provide:

  • own GG number;
  • received IMToken;
  • client id (?) – it’s probably constant for certain client version, it may be anything (it’s not validated);
  • computer information (hostname, system, client version, client type – desktop).

Session cookie and security_token (additional protection, constant during a session) is send in response.

#4 offer a file transfer. Original client does it before authorization and gets 401 error. Send PUT https://drive.mpa.gg.pl/send_ticket request, providing:

  • receiver GG number;
  • file name and size;
  • security_token and session cookie.

We get json struct, SEND_TICKET_STATUS (temporary name) with fields:

  • id: transfer identifier (ticket id);
  • sender, recipient: GG numbers for sender and recipient;
  • file_name, file_size: name and size of file;
  • send_progress (float): file uploading progress – a number between 0 and 1.0;
  • ack_status: what was receiver decision – possible values are unknown, rejected, allowed;
  • send_mode: (?) always normal;
  • send_status: status of upload – possible values are in_progress, completed.

#5 we can now ask for file transfer status, by GET https://drive.mpa.gg.pl/send_ticket/[ticket_id] request. Provide security_token and session cookie, receive SEND_TICKET_STATUS struct.

#6 if receiver accepts the file, we may upload it to GG drive with PUT https://drive.mpa.gg.pl/me/file/outbox/[ticket_id]%2C[file name] request. Provide security_token, session cookie, type of sent data (file) and file content. File name should be encoded with function like php’s rawurlencode. Original client have issues with receiving files with filenames containing non-alphanumeric characters. This affects files sent by original client too.

Status notifications

Less detailed information about transfer status are sent in original GG11 session too. That means, there is a possibility not to use https://drive.mpa.gg.pl/send_ticket/[…] requests at all. It’s done with packets described below (names are temporary).

Received notifications are common for various services from new protocol (among others, there are notifications for GG drive too):

struct gg_mpa_notify /* ID: 0x84 */
{
    char magic1[3]; /* 08 02 10 */
    uint8_t seq;
    char magic2; /* 1a */
    uint8_t data_len;
    char magic3; /* 01, not always present */
    char data[data_len];
    uint8_t ntype_len;
    char ntype[ntype_len];
    char magic4[10];
}

After receiving any of gg_mpa_notify packets, we have to reply with its sequence number:

struct gg_mpa_ack /* ID: 0x86 */
{
    char magic1[3]; /* 08 06 10 */
    uint8_t seq;
    char magic2[2]; /* 18 01 */
}

Possible types (ntype field) of json packets, related to file transfer:

  • edisc/send_ticket_changed: simplified version of SEND_TICKET_STATUS, containing fields id, ack_status, send_status;
  • edisc/scope_files_changed: sent after uploading file to GG drive.

Possible sequence of packets for proper transfer:

  1. send_ticket_changed [ack_status: unknown, send_status: in_progress]
  2. send_ticket_changed [ack_status: allowed, send_status: in_progress]
  3. send_ticket_changed [ack_status: allowed, send_status: completed]
  4. scope_files_changed

Summary

File transfer protocol is revealed enough for implementing it (see example script). Unfortunately, it doesn’t seems to be possible to get IMToken value using old GG protocol, supported by libgadu. That means, there is a need for additional effort in enabling libgadu support for whole new protocol. Positive aspect of such changes will be getting much more reliable method of file transfer – that doesn’t depend on client visibility behind NAT.

Leave a Reply

Your email address will not be published. Required fields are marked *