- 
                Notifications
    You must be signed in to change notification settings 
- Fork 62
Protocol
        clanner edited this page Feb 26, 2015 
        ·
        3 revisions
      
    | ofs | size | contents | 
|---|---|---|
| +00 | 2byte | message id, see Protocol Messages | 
| +02 | 3byte | payload length | 
| +05 | 2byte | unknown | 
| +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 basekeyis:fhsd6f86f67rt8fw78fw789we78r9789wer6re
 
- 
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)