Client Server Cryptography?

06/08/2009 21:54 _Villain_#1
i was wondering, the communication between alefcient and the game server is encrypted, and alefclient binary seems to use Cryptography API (CNG) with most of its functions like:

BCryptCloseAlgorithmProvider
BCryptCreateHash
BCryptDecrypt
BCryptDeriveKey
BCryptDestroyHash
BCryptDestroyKey
BCryptDestroySecret
BCryptDuplicateHash
BCryptDuplicateKey
BCryptEncrypt
BCryptExportKey
BCryptFinalizeKeyPair
BCryptFinishHash
BCryptFreeBuffer
BCryptGenerateKeyPair
BCryptGenerateSymmetricKey
BCryptGenRandom
BCryptGetProperty
BCryptHashData
BCryptImportKey
BCryptImportKeyPair
BCryptOpenAlgorithmProvider
BCryptSecretAgreement
BCryptSetProperty
BCryptSignHash
BCryptVerifySignature

so do they use SSL with the login process only? to secure the user name - passoword? or do they actually use SSL with all communicated packets?

i used to think that since its an mmorg they wouldnt use SSL with all SEND-RECIVE operations, since that creates a bit of performance issue especially that its not that of a big deal.

its obvious that the packets are encrypted and that the encryption key changes with each (character login) but what kind of encryption is it? the algorithm used? r they using DES? or something else?

i saw that many times upon login i receive a whisper from an RMT site, which is C formatted (and since we receive packets from all players within a certain range that includes info of these players like name-armor ids... i thought that someone already decrypted this thing and uses this packet distribution to whisper the players....)

i thought that many of u who checked this game already would have an idea bout this, i stumbled upon it only yesterday and im curious abit to know.

tx.
06/09/2009 11:15 _Emme_#2
Quote:
byte[] addKey1 = new byte[4];
byte[] addKey2 = new byte[4];
byte[] addResult = new byte[4];
//addKey1.i = 0;
//addKey2.i = 0;
byte[] tempKey = new byte[4];

long LMULer;
// InKey1[0] = 0x20;
// InKey1[1] = 0x5c;
// InKey1[2] = 0x48;
// InKey1[3] = 0xf4;
// InKey2[0] = 0x00;
// InKey2[1] = 0x44;
// InKey2[2] = 0xa6;
// InKey2[3] = 0x2e;

//if (Key3) delete [] Key3;
//if (Key4) delete [] Key4;
Monitor.Enter(this);
m_Key3 = new Byte[256];
m_Key4 = new Byte[256];
for (int x = 0; x < 4; x++)
{
addKey1[x] = InKey1[3 - x];
addKey2[x] = InKey2[3 - x];
}
//cout << "Key1: " << addKey1.i << endl;
//cout << "Key2: " << addKey2.i << endl;
uint Adder1;
uint Adder2;
uint Adder3;
Adder1 = (uint)((addKey1[3] << 24) | (addKey1[2] << 16) | (addKey1[1] << 8) | (addKey1[0]));
Adder2 = (uint)((addKey2[3] << 24) | (addKey2[2] << 16) | (addKey2[1] << 8) | (addKey2[0]));
Adder3 = Adder1 + Adder2;
addResult[0] = (byte)(Adder3 & 0xff);
addResult[1] = (byte)((Adder3 >> 8) & 0xff);
addResult[2] = (byte)((Adder3 >> 16) & 0xff);
addResult[3] = (byte)((Adder3 >> 24) & 0xff);
for (int b = 3; b >= 0; b--)
{
// printf("%.2x ", addResult.c[b]);
tempKey[3 - b] = addResult[b];
}
tempKey[2] = (byte)(tempKey[2] ^ (byte)0x43);
tempKey[3] = (byte)(tempKey[3] ^ (byte)0x21);

for (int b = 0; b < 4; b++)
{
tempKey[b] = (byte)(tempKey[b] ^ InKey1[b]);
}

//Build the 3rd Key
for (int b = 0; b < 256; b++)
{
m_Key3[b] = (byte)(tempKey[3 - (b % 4)] ^ m_Key1[b]);
}


for (int x = 0; x < 4; x++)
{
addResult[x] = tempKey[3 - x];
}
Adder3 = (uint)((addResult[3] << 24) | (addResult[2] << 16) | (addResult[1] << 8) | (addResult[0]));
LMULer = Adder3 * Adder3;
LMULer = LMULer << 32;
LMULer = LMULer >> 32;

Adder3 = Convert.ToUInt32(LMULer & 0xffffffff);

addResult[0] = (byte)(Adder3 & 0xff);
addResult[1] = (byte)((Adder3 >> 8) & 0xff);
addResult[2] = (byte)((Adder3 >> 16) & 0xff);
addResult[3] = (byte)((Adder3 >> 24) & 0xff);

for (int b = 3; b >= 0; b--)
{
tempKey[3 - b] = addResult[b];
}
//Build the 4th Key
for (int b = 0; b < 256; b++)
{
m_Key4[b] = Convert.ToByte(tempKey[3 - (b % 4)] ^ m_Key2[b]);
}
Monitor.Exit(this);

//cout << "Int representation: " << charadd.i << endl;
}

public void ResetCounter()
{
//Monitor.Enter(this);
m_OutCounter = 0;
m_InCounter = 0;
//Monitor.Exit(this);
Found this while I was debugging, hope it helps in some way.
06/09/2009 17:33 _Villain_#3
Quote:
Originally Posted by EmmeTheCoder View Post
Found this while I was debugging, hope it helps in some way.
debugging what? i found this match for ur code!!!! (c#)! and it looks more like simple XOR decryption [m_key1-2 seems to be 256 bytes of fixed values] (i used to thinK that they should use XOR since its easier to implement and produces higher performance!!! but the packets in Archlord are encrypted upon each login with random keys, so...!

Code:
public void Encrypt(ref byte[] Data){
	try
            {
            //Monitor.Enter(this);
	for (int b = 0; b < Data.Length; b++)
	{
	        Data[b] = (byte)(Data[b] ^ 0xab);
	        Data[b] = (byte)(Data[b] << 4 | Data[b] >> 4);
	        Data[b] = (byte)(m_Key2[m_OutCounter >> 8] ^ Data[b]);
	        Data[b] = (byte)(m_Key1[m_OutCounter & 0x00ff] ^ Data[b]);
	        m_OutCounter++;
	}        
	//Monitor.Exit(this);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }

}

public void Decrypt(ref byte[] Data)
		{
			try
            {
            byte [] Key1;
			byte [] Key2;
			if (m_UseAlt)
			{
				Key1 = m_Key3;
				Key2 = m_Key4;
			}
			else
			{
				Key1 = m_Key1;
				Key2 = m_Key2;
			}
			//Monitor.Enter(this);
			for (int b = 0; b < Data.Length; b++)
			{
				Data[b] = (byte)(Data[b] ^ 0xab);
				Data[b] = (byte)(Data[b] << 4 | Data[b] >> 4);
				Data[b] = (byte)(Key2[m_InCounter >> 8] ^ Data[b]);
				Data[b] = (byte)(Key1[(m_InCounter & 0x00ff)] ^ Data[b]);
				m_InCounter++;
			}
			//Monitor.Exit(this);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
		}
06/10/2009 09:29 _Emme_#4
Quote:
public class Cryptographer : object
{
ushort m_InCounter = 0;
ushort m_OutCounter = 0;
bool m_UseAlt = false;
private byte[] m_Key1 = {
0x9D, 0x90, 0x83, 0x8A, 0xD1, 0x8C, 0xE7, 0xF6, 0x25, 0x28, 0xEB, 0x82, 0x99, 0x64, 0x8F, 0x2E,
0x2D, 0x40, 0xD3, 0xFA, 0xE1, 0xBC, 0xB7, 0xE6, 0xB5, 0xD8, 0x3B, 0xF2, 0xA9, 0x94, 0x5F, 0x1E,
0xBD, 0xF0, 0x23, 0x6A, 0xF1, 0xEC, 0x87, 0xD6, 0x45, 0x88, 0x8B, 0x62, 0xB9, 0xC4, 0x2F, 0x0E,
0x4D, 0xA0, 0x73, 0xDA, 0x01, 0x1C, 0x57, 0xC6, 0xD5, 0x38, 0xDB, 0xD2, 0xC9, 0xF4, 0xFF, 0xFE,
0xDD, 0x50, 0xC3, 0x4A, 0x11, 0x4C, 0x27, 0xB6, 0x65, 0xE8, 0x2B, 0x42, 0xD9, 0x24, 0xCF, 0xEE,
0x6D, 0x00, 0x13, 0xBA, 0x21, 0x7C, 0xF7, 0xA6, 0xF5, 0x98, 0x7B, 0xB2, 0xE9, 0x54, 0x9F, 0xDE,
0xFD, 0xB0, 0x63, 0x2A, 0x31, 0xAC, 0xC7, 0x96, 0x85, 0x48, 0xCB, 0x22, 0xF9, 0x84, 0x6F, 0xCE,
0x8D, 0x60, 0xB3, 0x9A, 0x41, 0xDC, 0x97, 0x86, 0x15, 0xF8, 0x1B, 0x92, 0x09, 0xB4, 0x3F, 0xBE,
0x1D, 0x10, 0x03, 0x0A, 0x51, 0x0C, 0x67, 0x76, 0xA5, 0xA8, 0x6B, 0x02, 0x19, 0xE4, 0x0F, 0xAE,
0xAD, 0xC0, 0x53, 0x7A, 0x61, 0x3C, 0x37, 0x66, 0x35, 0x58, 0xBB, 0x72, 0x29, 0x14, 0xDF, 0x9E,
0x3D, 0x70, 0xA3, 0xEA, 0x71, 0x6C, 0x07, 0x56, 0xC5, 0x08, 0x0B, 0xE2, 0x39, 0x44, 0xAF, 0x8E,
0xCD, 0x20, 0xF3, 0x5A, 0x81, 0x9C, 0xD7, 0x46, 0x55, 0xB8, 0x5B, 0x52, 0x49, 0x74, 0x7F, 0x7E,
0x5D, 0xD0, 0x43, 0xCA, 0x91, 0xCC, 0xA7, 0x36, 0xE5, 0x68, 0xAB, 0xC2, 0x59, 0xA4, 0x4F, 0x6E,
0xED, 0x80, 0x93, 0x3A, 0xA1, 0xFC, 0x77, 0x26, 0x75, 0x18, 0xFB, 0x32, 0x69, 0xD4, 0x1F, 0x5E,
0x7D, 0x30, 0xE3, 0xAA, 0xB1, 0x2C, 0x47, 0x16, 0x05, 0xC8, 0x4B, 0xA2, 0x79, 0x04, 0xEF, 0x4E,
0x0D, 0xE0, 0x33, 0x1A, 0xC1, 0x5C, 0x17, 0x06, 0x95, 0x78, 0x9B, 0x12, 0x89, 0x34, 0xBF, 0x3E};

private byte[] m_Key2 = {
0x62, 0x4F, 0xE8, 0x15, 0xDE, 0xEB, 0x04, 0x91, 0x1A, 0xC7, 0xE0, 0x4D, 0x16, 0xE3, 0x7C, 0x49,
0xD2, 0x3F, 0xD8, 0x85, 0x4E, 0xDB, 0xF4, 0x01, 0x8A, 0xB7, 0xD0, 0xBD, 0x86, 0xD3, 0x6C, 0xB9,
0x42, 0x2F, 0xC8, 0xF5, 0xBE, 0xCB, 0xE4, 0x71, 0xFA, 0xA7, 0xC0, 0x2D, 0xF6, 0xC3, 0x5C, 0x29,
0xB2, 0x1F, 0xB8, 0x65, 0x2E, 0xBB, 0xD4, 0xE1, 0x6A, 0x97, 0xB0, 0x9D, 0x66, 0xB3, 0x4C, 0x99,
0x22, 0x0F, 0xA8, 0xD5, 0x9E, 0xAB, 0xC4, 0x51, 0xDA, 0x87, 0xA0, 0x0D, 0xD6, 0xA3, 0x3C, 0x09,
0x92, 0xFF, 0x98, 0x45, 0x0E, 0x9B, 0xB4, 0xC1, 0x4A, 0x77, 0x90, 0x7D, 0x46, 0x93, 0x2C, 0x79,
0x02, 0xEF, 0x88, 0xB5, 0x7E, 0x8B, 0xA4, 0x31, 0xBA, 0x67, 0x80, 0xED, 0xB6, 0x83, 0x1C, 0xE9,
0x72, 0xDF, 0x78, 0x25, 0xEE, 0x7B, 0x94, 0xA1, 0x2A, 0x57, 0x70, 0x5D, 0x26, 0x73, 0x0C, 0x59,
0xE2, 0xCF, 0x68, 0x95, 0x5E, 0x6B, 0x84, 0x11, 0x9A, 0x47, 0x60, 0xCD, 0x96, 0x63, 0xFC, 0xC9,
0x52, 0xBF, 0x58, 0x05, 0xCE, 0x5B, 0x74, 0x81, 0x0A, 0x37, 0x50, 0x3D, 0x06, 0x53, 0xEC, 0x39,
0xC2, 0xAF, 0x48, 0x75, 0x3E, 0x4B, 0x64, 0xF1, 0x7A, 0x27, 0x40, 0xAD, 0x76, 0x43, 0xDC, 0xA9,
0x32, 0x9F, 0x38, 0xE5, 0xAE, 0x3B, 0x54, 0x61, 0xEA, 0x17, 0x30, 0x1D, 0xE6, 0x33, 0xCC, 0x19,
0xA2, 0x8F, 0x28, 0x55, 0x1E, 0x2B, 0x44, 0xD1, 0x5A, 0x07, 0x20, 0x8D, 0x56, 0x23, 0xBC, 0x89,
0x12, 0x7F, 0x18, 0xC5, 0x8E, 0x1B, 0x34, 0x41, 0xCA, 0xF7, 0x10, 0xFD, 0xC6, 0x13, 0xAC, 0xF9,
0x82, 0x6F, 0x08, 0x35, 0xFE, 0x0B, 0x24, 0xB1, 0x3A, 0xE7, 0x00, 0x6D, 0x36, 0x03, 0x9C, 0x69,
0xF2, 0x5F, 0xF8, 0xA5, 0x6E, 0xFB, 0x14, 0x21, 0xAA, 0xD7, 0xF0, 0xDD, 0xA6, 0xF3, 0x8C, 0xD9};
private byte[] m_Key3;
private byte[] m_Key4;

public Cryptographer()
{
}

public void SetKeys(byte[] InKey1, byte[] InKey2)
{
byte[] addKey1 = new byte[4];
byte[] addKey2 = new byte[4];
byte[] addResult = new byte[4];
//addKey1.i = 0;
//addKey2.i = 0;
byte[] tempKey = new byte[4];

long LMULer;
// InKey1[0] = 0x20;
// InKey1[1] = 0x5c;
// InKey1[2] = 0x48;
// InKey1[3] = 0xf4;
// InKey2[0] = 0x00;
// InKey2[1] = 0x44;
// InKey2[2] = 0xa6;
// InKey2[3] = 0x2e;

//if (Key3) delete [] Key3;
//if (Key4) delete [] Key4;
Monitor.Enter(this);
m_Key3 = new Byte[256];
m_Key4 = new Byte[256];
for (int x = 0; x < 4; x++)
{
addKey1[x] = InKey1[3 - x];
addKey2[x] = InKey2[3 - x];
}
//cout << "Key1: " << addKey1.i << endl;
//cout << "Key2: " << addKey2.i << endl;
uint Adder1;
uint Adder2;
uint Adder3;
Adder1 = (uint)((addKey1[3] << 24) | (addKey1[2] << 16) | (addKey1[1] << 8) | (addKey1[0]));
Adder2 = (uint)((addKey2[3] << 24) | (addKey2[2] << 16) | (addKey2[1] << 8) | (addKey2[0]));
Adder3 = Adder1 + Adder2;
addResult[0] = (byte)(Adder3 & 0xff);
addResult[1] = (byte)((Adder3 >> 8) & 0xff);
addResult[2] = (byte)((Adder3 >> 16) & 0xff);
addResult[3] = (byte)((Adder3 >> 24) & 0xff);
for (int b = 3; b >= 0; b--)
{
// printf("%.2x ", addResult.c[b]);
tempKey[3 - b] = addResult[b];
}
tempKey[2] = (byte)(tempKey[2] ^ (byte)0x43);
tempKey[3] = (byte)(tempKey[3] ^ (byte)0x21);

for (int b = 0; b < 4; b++)
{
tempKey[b] = (byte)(tempKey[b] ^ InKey1[b]);
}

//Build the 3rd Key
for (int b = 0; b < 256; b++)
{
m_Key3[b] = (byte)(tempKey[3 - (b % 4)] ^ m_Key1[b]);
}


for (int x = 0; x < 4; x++)
{
addResult[x] = tempKey[3 - x];
}
Adder3 = (uint)((addResult[3] << 24) | (addResult[2] << 16) | (addResult[1] << 8) | (addResult[0]));
LMULer = Adder3 * Adder3;
LMULer = LMULer << 32;
LMULer = LMULer >> 32;

Adder3 = Convert.ToUInt32(LMULer & 0xffffffff);

addResult[0] = (byte)(Adder3 & 0xff);
addResult[1] = (byte)((Adder3 >> 8) & 0xff);
addResult[2] = (byte)((Adder3 >> 16) & 0xff);
addResult[3] = (byte)((Adder3 >> 24) & 0xff);

for (int b = 3; b >= 0; b--)
{
tempKey[3 - b] = addResult[b];
}
//Build the 4th Key
for (int b = 0; b < 256; b++)
{
m_Key4[b] = Convert.ToByte(tempKey[3 - (b % 4)] ^ m_Key2[b]);
}
Monitor.Exit(this);

//cout << "Int representation: " << charadd.i << endl;
}

public void DisplayCounters()
{
General.WriteLine("InCounter: " + m_InCounter);
General.WriteLine("OutCounter: " + m_OutCounter);
}

public void ResetCounter()
{
//Monitor.Enter(this);
m_OutCounter = 0;
m_InCounter = 0;
//Monitor.Exit(this);
}

public void EnableAlternateKeys()
{
//Monitor.Enter(this);
m_UseAlt = true;
//m_InCounter = 0;
m_OutCounter = 0;
//Monitor.Exit(this);
}

public void DisableAlternateKeys()
{
//Monitor.Enter(this);
m_UseAlt = false;
m_InCounter = 0;
m_OutCounter = 0;
//Monitor.Exit(this);
}

public void Encrypt(ref byte[] Data)
{
try
{
//Monitor.Enter(this);
for (int b = 0; b < Data.Length; b++)
{
Data[b] = (byte)(Data[b] ^ 0xab);
Data[b] = (byte)(Data[b] << 4 | Data[b] >> 4);
Data[b] = (byte)(m_Key2[m_OutCounter >> 8] ^ Data[b]);
Data[b] = (byte)(m_Key1[m_OutCounter & 0x00ff] ^ Data[b]);
m_OutCounter++;
}
//General.WriteLine("OutCounter = " + m_OutCounter);
//Monitor.Exit(this);
}
catch (Exception e)
{
General.WriteLine(e.ToString());
}

}

public void Decrypt(ref byte[] Data)
{
try
{
byte[] Key1;
byte[] Key2;
if (m_UseAlt)
{
Key1 = m_Key3;
Key2 = m_Key4;
}
else
{
Key1 = m_Key1;
Key2 = m_Key2;
}
//Monitor.Enter(this);
for (int b = 0; b < Data.Length; b++)
{
Data[b] = (byte)(Data[b] ^ 0xab);
Data[b] = (byte)(Data[b] << 4 | Data[b] >> 4);
Data[b] = (byte)(Key2[m_InCounter >> 8] ^ Data[b]);
Data[b] = (byte)(Key1[(m_InCounter & 0x00ff)] ^ Data[b]);
m_InCounter++;
}
//Monitor.Exit(this);
}
catch (Exception e)
{
General.WriteLine(e.ToString());
}
}
public void PeekDecrypt(ref byte[] Data)
{
//Monitor.Enter(this);
for (int b = 0; b < Data.Length; b++)
{
Data[b] = (byte)(m_Key1[((m_InCounter + b) & 0x00ff)] ^ Data[b]);
Data[b] = (byte)(m_Key2[(m_InCounter + b) >> 8] ^ Data[b]);
Data[b] = (byte)(Data[b] << 4 | Data[b] >> 4);
Data[b] = (byte)(Data[b] ^ 0xab);
}
//Monitor.Exit(this);
}

}
There, try that.
06/10/2009 19:46 JPHcom#5
Hi, i was research logining data transfers between client and server. i discovered that server and client are sent to each other a keys that uses for crypt.

i was catch a packets using sniffer. and create a c# app, that accept data from client and return packets that i was captured.

results that it gives to me: client each time send different data arrays (but they not totally different), and my app send each time the same data, and client recognize it as a normal server.

so i think that in game process there are two keys. one for client and one for server, and they are transfered in login process (but i don't know what to look).

Your ideas about it?
06/10/2009 23:29 Newbb#6
interresting, ill keep my eye on this one, though i dont know annything about it..
07/13/2009 14:55 rona#7
possible to compile the file?
07/14/2009 13:32 Marveh#8
It's not DES or 3DES, The encryption algorithm is a 'broken' version, of a popular fishy (clue!) block cipher ;)