Ok. At least hypnodok doesn't mind.
This is about finding the key that the wow server uses to encrypt the message headers. My algorithm is able to calculate the key without knowledge about the login process.
The message headers look like this:
Code:
struct CLIENTHEADER
{
WORD oplen;
DWORD opcode;
};
struct SERVERHEADER
{
WORD oplen;
WORD opcode;
};
The weakness with this definition is, that the clientheader contains a lot of zeros and known bits for small messages. For example the oplen is known, if we have a nonfragmented message. Test have shown, that this is true for all messages with a len smaller than 256 bytes. Under special conditions, it is also possible to assume the opcode. Such a special condition can be created by right clicking into the wow window and turning the character heavily with the mouse for some seconds. You will find the one command for updating player position repeated multiple times.
Due to the poor encryption sheme that wow uses, a single zero byte in the crypted area stopps the influence from former bytes in the stream. And because the client header contains multiple zeros, it is easy to see a repeating pattern after that in scenarios as described above. A manual analysis showed a pattern length of 40 bytes.
Of course the original leaked client code gave a hint about the encryption method, but the following class for retreiving the key is originally mine.
Code:
#define KEY_LENGTH 40
class CRYPT
{
public:
CRYPT();
void Reset();
BOOL HasKey() { return m_key_valid; }
BOOL FindKey(BYTE* pData,int len,BYTE* pSollData,BYTE* pSollMask);
void Decrypt(BYTE* pData,int len);
protected:
BOOL m_key_valid;
BYTE m_key [KEY_LENGTH];
BYTE m_key_mask[KEY_LENGTH];
BYTE m_last_recv;
int m_key_index;
int m_missing_keys;
};
CRYPT::CRYPT()
{
Reset();
}
void CRYPT::Reset()
{
memset(m_key ,0,KEY_LENGTH);
memset(m_key_mask,0,KEY_LENGTH);
m_key_valid =FALSE;
m_last_recv =0;
m_key_index =0;
m_missing_keys=KEY_LENGTH;
}
BOOL CRYPT::FindKey(BYTE* pData,int len,BYTE* pSollData,BYTE* pSollMask)
{
for (int t = 0; t < len; t++)
{
m_key_index %= KEY_LENGTH;
BYTE temp = pData[t] - m_last_recv;
if(pSollMask[t])
{
if(m_key_mask[m_key_index])
{
temp^=m_key[m_key_index];
if(temp!=pSollData[t])
{
Reset();
printf("Key not consistent in position %d. Starting new key\n",t);
}
}
else
{
m_key [m_key_index]=pSollData[t]^temp;
m_key_mask[m_key_index]=1;
m_missing_keys--;
if(!m_missing_keys)
{
m_key_valid=TRUE;
printf("Key found !\n");
}
}
}
m_key_index++;
m_last_recv = pData[t];
pData[t] = temp;
}
return m_key_valid;
}
void CRYPT::Decrypt(BYTE* pData,int len)
{
for (int t = 0; t < len; t++)
{
m_key_index %= KEY_LENGTH;
BYTE temp = (pData[t] - m_last_recv)^m_key[m_key_index++];
m_last_recv = pData[t];
pData[t] = temp;
}
}
Then you hook the outgoing traffic from your wow client and use the crypt class like this (the following is a code fragment from my wow proxy) :
Code:
void GAME_CONNECT::OnClientData (int size)
{
CLIENTHEADER header;
if(!m_crypt.HasKey())
{
memcpy(&header,m_buffer,sizeof(CLIENTHEADER));
if(size>sizeof(CLIENTHEADER) && (size<256) )
{
BYTE soll_data[6]={ 0x00,0x20,0xee,0x00,0x00,0x00 };
BYTE soll_mask[6]={ 0, 1, 0, 1, 1, 1 };
soll_data[1]=size-2;
m_crypt.FindKey((BYTE*)&header,6,soll_data,soll_mask);
}
send(m_server_sock,m_buffer,size,0);
}
else
{
int decoded=0;
do
{
memcpy(&header,&m_buffer[decoded],sizeof(CLIENTHEADER));
decoded+=sizeof(CLIENTHEADER);
m_crypt.Decrypt((BYTE*)&header,sizeof(CLIENTHEADER));
header.oplen=htons(header.oplen)-4;
int missing=(header.oplen+sizeof(CLIENTHEADER))-size;
while(m_connected && (missing>0))
{ // read in the missing data to have the complete command block
int r=recv(m_client_sock,&m_buffer[size],missing,0);
if(r>0)
{
missing-=r;
size+=r;
}
else m_connected=FALSE;
}
OnClientCommand(header,&m_buffer[decoded]);
decoded+=header.oplen;
}while(m_connected && (decoded<size));
send(m_server_sock,m_buffer,size,0);
// printf("CLIENT->SERVER %d bytes\n",size);
// hexdump("Out",m_buffer,size);
}
}
WoW uses the same key for incoming and outgoing traffic, so once you found the key in the outgoing traffic, you can use the same key for incoming traffic.
Greetings
Apollo