In this paper I’m going to speak about what I’ve did to reverse engineer the new encryption, using Clash of Clans as base (The logic used to break CoC could be replicated as well on Boom Beach and HayDay that are already shipped with the new encryption).
I’ll skip whatever that’s already known (the previous encryption), as it can be read and understood here: https://github.com/clugh/cocdp/wiki/Protocol (SuperCell shares the same protocol on all the games) and I’m going to focus on what really changes on this new encryption and how the crack has been done and figured out.
The goal, of course, was to break the encryption. Meanings, beeing able to decrypt and encrypt both server and client side to understand and reproduce payloads. You can find the patcher and the proxy server on github.
- Unicorn emulator
- Python for the patcher
- Node JS for the proxy server
- ARM (ARM / Thumb) Assembly to reverse and understand the logic
What’s changed in details and why:
The old encryption was cracked by patching the public server key, that’s hardcoded in the libg and that’s used to feed blake2b update, to build the nonce, and crypto_box beforenm to build the shared key used later to encrypt/decrypt the payload.
First static analysis shown clearly that both the methods (blake2b update and cryptobox / cryptobox_open) was hard modified. Most of the edits done was only on purpose to obfuscate the whole encryption/decryption logic. What really metter can be found on top of what now are 2 big subroutines wrapping most of the encryption/decryption logic. The 2 subroutines involved are now generiting a new key during runtime (the function that build the key is reversed as well, and its mostly obfuscated with control flow flattening) but still feeded with the hardcoded public key as evidenced during debugging.
Hardcoded CoC key:
Key built int runtime:
Following, this new key built in runtime is used to actually generate the nonce and to build the shared key with cryptobox_beforenm.
What follow, was dumping that new key, still static, and use it to reproduce and encrypt valid requests but of course that wasn’t enough to decrypt what the client send, why?
To build the login message, the official client, build the shared key from the private key generated during encryption logic and that pair with the key generated in runtime. What we know server side is the public key, that’s sent during login and that we can’t use to build the shared key.
So TLDR, patching the hardcoded key as we were used to, will just result in nothing done.
The actual logic
Starting from saying that I’m aware of another very easy method, that has been used by other guys and kept private and that i never ever tought to check since the amount of attention made to keep the things hard, my logic will patch 2 bytes. These 2 offsets has been found by digging in the memory at the entrance of the 2 involed sodium encryption and decryption subs (crypto_box and crypto_box_open). The library is loadint into R11 a struct holding our keys (public and private + the public server key) In more simple words, what the patch is doing, is replacing what’s used to build the shared key and instead of using the private key (that we don’t like because we don’t have on our server side) is using the hardcoded server key, that’s the pointer in memory just after the private one. This way, the shared key would be static and used always for any server proxy encryption and decryption.
- Note please, if you read how the proxy server has been modded to made it work, you have noticed that is now using a “magic key” as shared key. This magic key was originally built in runtime while i was playing and messing with some values over there. Its basically the result of a beforenm with the 32 bytes key (the 0x72 one) that we were used to patch and the new server public key that’s build in runtime, that we already know.
This explain why my patcher is still patching the hardcoded public server key with the 0x72 one (that is now mandatory to let the client generate the magic key as well)
The question that will problably jump on the mind is why I’m still patching the hardcoded key. Simple. The 0x72 was patched inside the client during my works and the first magic key that was built was using it. I see no reason to skip patching 32 bytes that will make life harder to baby leechers. Let’s see that magic key as a little signature from me.
The runtime key generation
Some screenshots while working
More to come…
If you are interested in the function that pack the new public server key, you can find my java port here.
Nice work! Can you explain a bit more how you created the “magic key”. I was under the impression that it should not be possible for the crypto_box public key encryption.
Lets start by recap cryptobox flow
* generation of a random keypair (32 byte pub key and 32 byte priv key)
* beforenm (generate the 32 bytes shared key)
* afternm (encrypt the payload with the shared key)
Same flow for cryptobox open.
My patches target the beforenm method which take as input server public key and our generated private key. In more specific my patch prevent the beforenm method to use our generated privatekey and instead, to use another value, the public server key again, which can be read 4 bytes later our priv key in the memory. This would generate the magic key. Magic key is the result of beforenm of pub server key + pub server key. If you try this you will see a different magic key because i used another value as second param but should make things clair
Il tuo sapere su Clash, potresti sfruttarlo per guadagnarci, visto che è un gioco che ha ancora milioni di appassionati che spendono ancora per questo gioco.
Se interessato, contattami.
Ciao!! In realtà sono riuscito a farceli un po di soldini e sono stato assunto da Overwolf ltd per continuare a lavorare (con focus su clash royale). Su github SC-DevTeam in quasi tutti le repo, nel readme, ce il link ad un server discord dove lavoro sui game supercell! Quando vuoi venire a trovarmi mi trovi li 🙂
riesci a contattarmi su instagram giovanni?
hey bro, I need your help I m trying to figure out,
I have used an app to get the key and the value how can I be able to change the value of the resources I have in the game please help me out
It’s not clear in the article what can be done with a decrypted payload. What is the point of doing this in plain language?
I am interested in pursuing this work to ascertain the location of buildings in my village programmatically. Is that possible?