Protocols/YMSG

From NINA Wiki
Jump to navigation Jump to search

Need to convert from markdown to wiki for clarity:

## General structure

YMSG packets are binary-based. They consist of a magic string, version code, vendor ID, packet length, service code, status code, session ID, and an optional payload.

The first field in a YMSG packet is the magic string `YMSG` at offset 0x00, which is 4 bytes in length.

The second field is the version number of the protocol the client wants to use for the current YMSG session packed as a short on offset 0x04, and is 2 bytes long. For server-side packets, this field is usually 0.

The third field, on offset 0x06, is the vendor ID of the client connecting to the YMSG server, which is a 2-byte long short-packed number. Supported values for the vendor ID are 0 (`\x00\x00`; Windows/NULL) and 100 (`\x00\x64`; Macintosh). For server-side packets, this value is usually set to 0, but we are not sure of this yet. Prior to YMSG16, it was believed that the vendor ID was part of the version code, effectively making the version a DWORD.

The fourth field is the length of the packet's payload packed as a short, which is located on offset 0x08 and is also 2 bytes long.

The fifth field is the service code the packet requests the server to execute the payload with. It is located on offset 0x0a and is 2 bytes long. There are a variety of services that a YMSG packet can utilize, and it has been observed that Yahoo! Messenger can swap protocol versions, meaning that features can be used on YMSG protocol versions that initially didn't support those features. Whether this mean older clients can potentially receive packets with services not recognized by their understanding of the protocol version they use hasn't been confirmed.

The sixth field is the status code, which is a packed DWORD located at offset 0x0c and is 4 bytes long. This status code doesn't correspond to the logged in user's status, and is moreso used as a subcommand for most packet services. Interestingly enough, most of the status code in YMSG packets corresponds to the status codes used for contact presence. During authentication, this is used to determine if the user wants to log in under invisible mode. There are 19 YMSG statuses (that we've recorded as of now) that may or may not be used for contact statuses:

```
AVAILABLE/REQUEST: 0x00000000
BRB/RESPONSE: 0x00000001
BUSY: 0x00000002
NOTATHOME/BADUSERNAME: 0x00000003
NOTATDESK: 0x00000004
NOTINOFFICE/OFFLINEMSG/MULTIPACKET: 0x00000005
ONPHONE: 0x00000006
ONVACATION: 0x00000007
OUTTOLUNCH: 0x00000008
STEPPEDOUT: 0x00000009
INVISIBLE: 0x0000000C
BAD: 0x0000000D
LOCKED: 0x0000000E
TYPING: 0x00000016
CUSTOM: 0x00000063
IDLE: 0x000003E7
WEBLOGIN: 0x5A55AA55
OFFLINE: 0x5A55AA56
LOGINERROR: 0xFFFFFFFF
```

The seventh field is the session ID, which is also a packed DWORD. It is located at offset 0x10 and is also 4 bytes long. The purpose of this field is to identify sessions and to verify if a user has the greenlight to use the main YMSG services (anything after authentication). It is believed that the official YMSG servers verified the session ID after authentication, but there is no hard evidence of this being the case, considering YMSG by design didn't validate the payloads of most services.

The eighth, optional field is the payload for the service the packet wants to request. The payload is located at offset 0x14 and is variable-length or null. If not null, payloads are formatted in a generic key/value format, with the key usually being numeric. The key and value are separated by the delimiter `\xc0\x80`. An example of this format is below:

```
1[\xc0\x80]yahoo.id[\xc0\x80]
```

`1` is the key and `yahoo.id` is the value of the key in this example.

There is no limit to the amount of key/value pairs that can make up the payload, but due to the packet length in the packet's header being short-packed, the payload is limited to 65,535 bytes.

## YMSG8

Y!M < 5.0

### Auth

1. Client sends GET request to `http://msg.edit.yahoo.com/config/ncclogin?.src=bl&login=[username]&passwd=[password]&n=1`

2. Server responds with cookie `Y` and body:

    ```
    OK
    BEGIN BUDDYLIST
    [contact category]:[comma separated list of Yahoo! IDs associated with the contact category]
    END BUDDYLIST
    BEGIN IGNORELIST
    [comma separated list of Yahoo IDs in the user's ignore list]
    END IGNORELIST
    BEGIN IDENTITIES
    [comma-separated list of user's main and alternate usernames go here]
    END IDENTITIES
    Mail=1
    Login=[username]
    ```

3. Client opens a connection to a YMSG server via port 5050 and sends:

    ```
    Service: \x00\x01 (LOGON)

    Status: \x5A\x55\xAA\x55 (WEBLOGON)
    ```
    
    ```
    Payload:
      
      '0' -> [YahooId]
      '6' -> [md5-crypt(3)'d password]
      '1' -> [YahooId]
    ```


4. If the verification of the password goes well, the server responds with:

    ```
    Service: \x00\x01 (LOGON)

    Status: \x00\x00\x00\x00 (0; [AVAILABLE]/REQUEST)
    ```

    ```
    Payload:
    
      '0' -> [YahooId]
      '1' -> [YahooId]
    ```

## YMSG9

YMSG9: Y!M 5.0 - Y!M 5.4

### Auth

1. Client opens a connection to a YMSG server via port 5050 and sends:

    ```
    Service: \x00\x4C (76; HANDSHAKE)
    
    Status: \x00\x00\x00\x00 (0; AVAILABLE/[REQUEST])
    ```

2. Server responds with:

    ```
    Service: \x00\x4C (76; HANDSHAKE)

    Status: \x00\x00\x00\x01 (1; BRB/[RESPONSE])
    ```

3. Client sends:

    ```
    Service: \x00\x57 (87; AUTH)

    Status: \x00\x00\x00\x00 (0; AVAILABLE/[REQUEST])
    ```

    ```
    Payload:
    
      '1' -> [YahooId]
    ```

4. On successful verification of the username, the server responds with the following packet (this is also when the server generates the session ID the client should use for the whole server session):

    ```
    Service: \x00\x57 (87; AUTH)
    
    Status: \x00\x00\x00\x01 (1; BRB/[RESPONSE])
    ```

    ```
    Payload:
    
      '1' -> [YahooId]
      '94' -> [24-byte Yahoo64-encoded challenge string] ('AA.BBCCDDEEGGHHIIH_HII--')
    ```

5. The client then crafts two response strings with the challenge strings (details will be added onto later).

6. After the client is done crafting the two response strings, the client responds with:

    ```
    Service: \x00\x54 (54; AUTHRESP)
    
    Status: [Status the user wants to log in as; usually either AVAILABLE or INVISIBLE]
    ```

    ```
    Payload:
    
      '0' -> [YahooId]
      '6' -> [Challenge response string 1] ('AA.BBCCDDEEGGHHIIH_HII--')
      '96' -> [Challenge response string 2] ('AA.BBCCDDEEGGHHIIH_HII--')
      '2' -> '1'
      '1' -> [YahooId]
    ```

7. Server verifies the first and second response strings, and if all is well, responds with this:

    ```
    Service: \x00\x55 (85; LIST)

    Status: \x00\x00\x00\x00
    ```

    ```
    Payload:
    
      '87' -> '[contact category]:[comma separated list of Yahoo! IDs associated with the contact category]\x0A'
      '88' -> [comma separated list of Yahoo IDs in the user's ignore list]
      '89' -> [comma-separated list of user's main and alternate usernames go here]
      '59' -> 'Y\x09[data of "Y" cookie]'
      '59' -> 'T\x09[data of "T" cookie]'
      '59' -> 'C\x09mg=1'
      '3' -> [YahooId]
      '100' -> '0'
      '101' -> (blank)
      '102' -> (blank)
      '93' -> '86400'
    ```
    
    If the contact, ignore, or identity list were too large for the server's taste, then multiple LIST packets would be sent with status 0x00000005 (NOTINOFFICE) containing chunks of the key-value pair for one type of list each before sending the rest of the LIST packet. 

8. The server will also send you a LOGON packet containing structures of your contacts statuses and the number of them contained:

    ```
    Service: \x00\x01 (LOGON)
    
    Status: \x00\x00\x00\x00
    ```
    
    ```
    Payload:
    
      '0' -> [YahooId]
      '1' -> [YahooId]
      '8' -> # of contact structures
      --- This collection of key-value pairs are added times the number specified in key 8 ---
      '7' -> Contact's Yahoo ID
      '10' -> Contact's YMSG status in integer format (99 (CUSTOM) if using a PSM or 0 (AVAILABLE) if offline)
      '11' -> Contact's YMSG session ID in uppercase hexadecimal
      '19' -> Contact's PSM (key-value pair only included if status == 99 (CUSTOM))
      '47' -> Flag for if contact's PSM is an away message or not (key-value pair only included if status == 99 (CUSTOM))
      '17' -> Contact is online on Yahoo! Chat (1 for yes, 0 for no; apparently this was only set to 1 if you were on Yahoo Chat's own protocol)
      '13' -> Contact is online on Yahoo! Messenger service (1 for yes, 0 for no)
    ```

The responses detailed in 7 and 8 will also be sent if the client queries the LIST service itself.

9. If any, the server will send packets containing the logged on user's OIMs (TODO: packet structure)

### Presence

When a user logs on successfully, their status set in the AUTHRESP packet will automatically be dispatched to their contacts.

When a user wants to change their status to something other than online (this includes going invisible), Yahoo! Messenger will send an ISAWAY packet containing the following info:

```
Service: \x00\x03 (ISAWAY)

Status: 0x00000000 (0; AVAILABLE/[REQUEST])
```

```
Payload:
  
  '10' -> Numerical YMSG status (99 for CUSTOM)
  '19' -> PSM (only included if status == 99)
  '47' -> Going away? (usually 1)
```

If a user wants to go back as online, the client will send this:

```
Service: \x00\x04 (ISBACK)

Status: 0x00000000 (0; AVAILABLE/[REQUEST])
```

```
Payload:
  
  (None)
```

No responses are sent to the user setting their status for either service.

When contact statuses are dispatched to other users, the server sends the same contact structure with the appropriate info with various services depending on the status the remote contact is on:

```
Service: Various (LOGON if remote contact status is "offline -> online"; ISAWAY if remote contact status is "any available (no PSM)/idle status -> any available (with PSM)/idle status"; ISBACK if remote contact status is "any available (with PSM)/idle status -> regular available (no PSM) status")

Status 0x00000001 (1; BRB/RESPONSE)
```

```
Payload:
  
  '0' -> [Receiver's Yahoo ID] (only included if service != LOGOFF)
  '7' -> Contact's Yahoo ID
  '10' -> Contact's YMSG status in integer format (99 (CUSTOM) if using a PSM or 0 (AVAILABLE) if offline)
  '11' -> Contact's YMSG session ID in uppercase hexadecimal
  '19' -> Contact's PSM (key-value pair only included if status == 99 (CUSTOM))
  '47' -> Flag for if contact's PSM is an away message or not (key-value pair only included if status == 99 (CUSTOM))
  '17' -> Contact is online on Yahoo! Chat (1 for yes, 0 for no; apparently this was only set to 1 if you were on Yahoo Chat's own protocol)
  '13' -> Contact is online on Yahoo! Messenger service (1 for yes, 0 for no)
```

## YMSG10 - 14(?)

YMSG10: Y!M 5.5
YMSG11: Y!M 5.6
YMSG12: Y!M 6.0
YMSG13-14: ???

1. (Same as YMSG9 steps 1 to 3; in version 11 the version field is now in the correct (network) endianness).

2. Server responds with:

    ```
    Service: \x00\x57(W) (87; AUTH)

    Status: \x00\x00\x00\x01 (1; BRB/[RESPONSE])
    ```

    ```
    Payload:
    
      '1' -> [YahooId]
      '94' -> [mathematical expression-like challenge string (64 bytes without parentheses)] ('g|i/p^h&z-d+2%v%x&j|e+(m^k-i%h*(s+8%a/u/x*(b-4*i%h^g^j|m^n-r*f+p+j)))')
      '13' -> '1'
    ```

3. Other, different complicated stuff ensues: https://web.archive.org/web/20031206174432if_/http://venkydude.com:80/articles/yahooencription.htm.

4. After crafting the two response strings, the client sends:

    ```
    Service \x00\x57(W) (87; AUTH)
    
    Status: \x5A\x55\xAA\x55
    ```

    ```
    Payload:
    
      '6' -> [First response string consisting of comma and semicolon separated "X=YZ" values] ('X=7e,H=je,H=j7;m=Cj,H=Cc,E=33;Q=ml;H=37;F=gg;w=F5;')
      '96' -> [Second response string also consisting of comma and semicolon separated "X=YZ" values] ('N=ah,m=F1,m=3A,Q=A2;T=Al,Z=Ep,h=he,S=0o;r=1B,C=h2;')
      '0' -> [YahooId]
      '2' -> '1'
      '1' -> [YahooId]
    ```

4. The rest is same as YMSG9's post-auth (to our understanding)

    a. After the initial LOGON packet and before the OIM packets, the server will also send ping configuration in the form of a "tick" and "tock" interval that the official client uses to send pings:

    ```
    Service: \x00\x12 (PINGCONFIGURATION)
    
    Status: \x00\x00\x00\x00
    ```
    
    ```
    Payload:
    
    '143' -> Tick interval (usually 60)
    '144' -> Tock interval (usually 13)
    ```

## YMSG15-16

YMSG15: Y!M 8.0 (as of our understanding)

YMSG16: Y!M 9.0 (as of our understanding)

1. (YMSG16 only)

    a. Client gets `vcs1.msg.yahoo.com/capacity`, which responds with

    ```
    COLO_CAPACITY=1
    CS_IP_ADDRESS=[IP address to YMSG server]
    ```

    b. If `COLO_CAPACITY` is != 0, Y!M will try connecting to the specified IP.

2. (Same as YMSG10-14 steps 1 to 3.)

3. Server responds with:

    ```
    Service: \x00\x57(W) (87; AUTH)
    
    Status: \x00\x00\x00\x00 (0; AVAILABLE)
    ```

    ```
    Payload:
      '1' -> [YahooId]
      '13' -> '2'
      '94' -> [mathematical expression-like challenge string (64 bytes without parentheses)] ('g|i/p^h&z-d+2%v%x&j|e+(m^k-i%h*(s+8%a/u/x*(b-4*i%h^g^j|m^n-r*f+p+j)))')
    ```

4. Now the client just sends that stuff over to an HTTPS auth server to do the heavy lifting at `https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts=&login=[username]&passwd=[password]&chal=[challenge string]`. If the password and Yahoo! ID are correct, the following response is returned:

    ```
    0
    ymsgr=[token]
    partnerid=[partnerid]
    ```
5. Client now grabs the token and GET requests `https://login.yahoo.com/pwtoken_login?src=ymsgr&ts=&token=[token]`, which sets the `Y`, `T`, and `B` cookies, and replies with:

    ```
    0
    crumb=[crumb]
    Y=[Y cookie]
    T=[T cookie]
    cookievalidfor=86400 [a day in seconds]
    SSL=[SSL cookie]
    ```
if the token is correct.

6. The client retrieves the `Y`, `T`, and `B` cookies, along with the crumb, and then does the following: concatenates the crumb and challenge string sent; MD5 hashes the concatenation; and finally Yahoo64 encodes the hash. Let's call the resulting data "response".

7. The client finally responds with this humongous packet:

    ```
    Service: \x00\x54(T) (84; AUTHRESP)
    ```

    ```
    Payload:
      
      '1' -> [YahooId]
      '0' -> [YahooId]
      '277' -> [Contents of the "Y" cookie]
      '278' -> [Contents of the "T" cookie]
      '307' -> [The "response" variable the client had crafted]
      '244' -> [The client's build ID]
      '2' -> [YahooId]
      '2' -> '1'
      '59' -> [Contents of the "B" cookie]
      '98' -> [Locale]
      '135' -> [Client version]
    ```

[WIP]

More to come soon...