This repository has been archived by the owner on Dec 9, 2021. It is now read-only.
forked from clanner/cocdp
-
Notifications
You must be signed in to change notification settings - Fork 2
Protocol
* * * A S H * * * edited this page Feb 4, 2016
·
4 revisions
ofs | size | contents |
---|---|---|
+00 | 2byte | message id, see Protocol Messages |
+02 | 3byte | payload length |
+05 | 2byte | client version |
+07 | encrypted message |
-
The client initializes 2 rc4 streams, with key:
basekey
+"nonce"- len(+"nonce") bytes are skipped initially.
- One stream for client->server, and one stream for server->client communication.
- the
basekey
is:fhsd6f86f67rt8fw78fw789we78r9789wer6re
- XOR your payload against the key stream (see the old scramble function from cocutils.py for an example of how to do it).
-
The client connects to port 9339 ( or 843, 1863, 3724, 30000 ) of the server gamea.clashofclans.com
- note: the server seems to be hosted in the amazon cloud.
-
The client sends a Login packet
- When logging in for the first time, the uid fields are set to 0
- this packet contains the 'client seed' value
- NOTE: the masterhash has to be correct, if not you will get a LoginFailed message, containing the updated fingerprint.json contents.
-
The server responds with a Encryption packet, containing the server random value.
-
The client calculates a new nonce by scrambling the serverrandom with the client seed, using the algorithm listed below.
-
The client creates 2 new rc4 streams, now with key:
basekey
+servernonce -
Then the server continues with a LoginOk message
-
Followed by the gamestate in a OwnHomeData message.
import struct
def lshift(num, n):
return (num*(2**n))%(2**32)
def rshift(num, n):
highbits= 0
if num&(2**31):
highbits= (2**n-1)*(2**(32-n))
return (num/(2**n))|highbits
def isneg(num):
return num&(2**31)
def negate(num):
return (~num)+1
class prng:
def __init__(self, seed):
self.seed= seed
def next(self):
mask=0x100000000
v3= self.seed if self.seed else 0xffffffff
v3 ^= lshift(v3,13)
v3 ^= rshift(v3,17)
v3 ^= lshift(v3,5)
self.seed= v3
if isneg(v3):
v3= negate(v3)
return v3%0x100
def scramble(data, seed):
rng= prng(seed)
return "".join(chr(ord(c)^rng.next()) for c in data)
in version 7.x the scramble function has changed to:
class scramble7prng:
def __init__(self, seed):
self.ix= 0
self.buffer= [ 0 for i in range(624) ]
self.seedbuffer(seed)
def dumpbuffer(self):
for x in self.buffer:
print " %08x" % x,
print
def seedbuffer(self, seed):
for i in range(624):
self.buffer[i]= seed
seed= (1812433253 * ((seed ^ rshift(seed, 30)) + 1)) & 0xFFFFFFFF
def getbyte(self):
x= self.getint()
if isneg(x):
x= negate(x)
return x % 256
def getint(self):
if self.ix==0:
self.mixbuffer()
val= self.buffer[self.ix]
self.ix = (self.ix+1) % 624
val ^= rshift(val, 11) ^ lshift((val ^ rshift(val, 11)), 7) & 0x9D2C5680;
return rshift((val ^ lshift(val, 15) & 0xEFC60000), 18) ^ val ^ lshift(val, 15) & 0xEFC60000;
def mixbuffer(self):
i=0
j=0
while i<624:
i += 1
v4= (self.buffer[i%624] & 0x7FFFFFFF) + (self.buffer[j]&0x80000000)
v6 = rshift(v4,1) ^ self.buffer[(i+396)%624]
if v4&1:
v6 ^= 0x9908B0DF
self.buffer[j] = v6
j += 1
def newscramble(serverrandom, seed):
prng= scramble7prng(seed)
for i in range(100):
byte100= prng.getbyte()
return "".join(chr(ord(c)^(prng.getbyte()&byte100)) for c in serverrandom)