Transport Layer:Transmission Control Protocol (TCP)
Application Layer:Battle.net v1 TCP Messages (SID)
Message Id:0x3E
Direction:Server to Client
Options: In Research
Used By:Diablo II, Diablo II Lord of Destruction
Message Format:
(does not include protocol header)
 (UINT32)     MCP Cookie
 (UINT32)     MCP Status
 (UINT32)[2]  MCP Chunk 1
 (UINT32)     IP
 (UINT32)     Port
 (UINT32)[12] MCP Chunk 2
 (STRING)     Battle.net unique name

NEW (as of D2 1.14d):
UINT8[16]       Unknown 1 (MD5?)
UINT32          IP (Big Endian)
UINT32          Port (Big endian)
UINT8[40]       Unknown 2 (SHA-1?)
UINT8[9]        Null padding


This packet supplies the data necessary to connect to an MCP server. The cookie value is defined in the first UINT32 of SID_LOGONREALMEX and is simply returned by Battle.net. If the length of the message (not including header) is greater than 8, the client should continue to logon to the MCP. Otherwise, the Status field is interpreted as an error code:

  • 0x80000001: Realm is unavailable
  • 0x80000002: Realm logon failed

Any other value indicates failure.

The first part of the MCP Chunk that must be sent to the MCP server includes the Status and Cookie UINT32S, making MCP Chunk 1 the first 4 UINT32S of the packet.

There used to be a UINT16 at the end of this packet, which was apparently random.

As of D2 1.14d, the packet structure is different, but is backwards-compatible with the previous structure.

| Edited: Anonymous



Credit goes to l2k-Shadow for the following information:

To get the port # from the port DWORD, use the ntohs function.

Example code in C++:

#include <windows.h>
#include <winsock2.h>
#include <iostream.h>

#pragma comment(lib, "ws2_32")

void main()
    char s[] = "\x17\xe0\x00\x00";
    DWORD d = 0;
    memcpy(&d, s, 4);
    cout << ntohs(d) << endl;

Credits goes to Chriso.de for VB conversion:

Public Declare Function ntohs Lib "ws2_32" (ByVal netshort As Long) As Long

' Get the port
Port = ntohs(r.GetInt32())

Diablo 2 realms went down the morning of 9/7/08, and when they came back up, this packet had changed, breaking a few clientless bots.

The (WORD) Unknown from this packet HAS been removed entirely.


Hmm, strange. Guess we'll never know what it was.


How to get the port # from DWORD with Python:
socket.ntohs(struct.unpack("< i", "\x17\xe0\x00\x00")[0])

How to convert port to DWORD with Python:
struct.pack("< i", socket.htons(6112))

Remove the space between < and i, these docs dont parse code comments well.

How to get the IP from DWORD with Python:

How to convert IP to DWORD with Python:

Remove the extra quotation marks, these docs dont parse code comments well.


It's important to note that bot the IP and the Port are both in big endian byte order. It also appears that the port is a 2 byte value followed by and unknown of 2 bytes.


As of 1.14d, the packet structure is significantly different.

UINT8[16]       Unknown 1 (MD5?)
UINT32          IP (Big Endian)
UINT32          Port (Big endian)
UINT8[40]       Unknown 2 (SHA-1?)
UINT8[9]        Unknown 3 (Padding?)