├── .gitignore ├── .gitmodules ├── Client.cs ├── ClientCrypto.cs ├── ClientState.cs ├── PacketReceiver.cs ├── Protocol.cs ├── Proxy.cs ├── README.md ├── Server.cs ├── ServerCrypto.cs ├── ServerState.cs └── State.cs /.gitignore: -------------------------------------------------------------------------------- 1 | Properties 2 | bin 3 | obj 4 | *.config 5 | *.csproj -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "messages"] 2 | path = messages 3 | url = git@github.com:clugh/coc-messages-csharp.git 4 | -------------------------------------------------------------------------------- /Client.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Sockets; 4 | 5 | namespace coc_proxy_csharp 6 | { 7 | public class Client : ClientCrypto 8 | { 9 | public ClientState state = new ClientState(); 10 | 11 | public Client(ServerState serverstate) 12 | { 13 | this.state.serverState = serverstate; 14 | this.state.clientKey = this.clientKey; 15 | this.state.serverKey = ClientCrypto.serverKey; 16 | } 17 | 18 | public void StartClient() 19 | { 20 | try 21 | { 22 | IPHostEntry ipHostInfo = Dns.GetHostEntry(Proxy.hostname); 23 | IPAddress ipAddress = ipHostInfo.AddressList[0]; 24 | IPEndPoint remoteEndPoint = new IPEndPoint(ipAddress, Proxy.port); 25 | 26 | Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 27 | this.state.socket = socket; 28 | socket.Connect(remoteEndPoint); 29 | socket.BeginReceive(this.state.buffer, 0, State.BufferSize, 0, new AsyncCallback(Protocol.ReceiveCallback), this.state); 30 | 31 | Console.WriteLine("Connected to {0} ...", socket.RemoteEndPoint.ToString()); 32 | } 33 | catch (Exception e) 34 | { 35 | Console.WriteLine(e.ToString()); 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ClientCrypto.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Net.Sockets; 4 | using Sodium; 5 | using Newtonsoft.Json.Linq; 6 | 7 | namespace coc_proxy_csharp 8 | { 9 | public class ClientCrypto : Protocol 10 | { 11 | protected KeyPair clientKey = PublicKeyBox.GenerateKeyPair(); 12 | protected static byte[] serverKey = Utilities.HexToBinary("01c98c143a840d92ee656996dad5af41de5d1b8ebb289081368b5cfda9bd4a30"); 13 | 14 | public static void DecryptPacket(Socket socket, ClientState state, byte[] packet) 15 | { 16 | int messageId = BitConverter.ToInt32(new byte[2].Concat(packet.Take(2)).Reverse().ToArray(), 0); 17 | int payloadLength = BitConverter.ToInt32(new byte[1].Concat(packet.Skip(2).Take(3)).Reverse().ToArray(), 0); 18 | int unknown = BitConverter.ToInt32(new byte[2].Concat(packet.Skip(2).Skip(3).Take(2)).Reverse().ToArray(), 0); 19 | byte[] cipherText = packet.Skip(2).Skip(3).Skip(2).ToArray(); 20 | byte[] plainText; 21 | 22 | if (messageId == 20100 || (messageId == 20103 && state.serverState.sharedKey == null)) 23 | { 24 | plainText = cipherText; 25 | } 26 | else if (messageId == 20103 || messageId == 20104) 27 | { 28 | byte[] nonce = GenericHash.Hash(state.nonce.Concat(state.clientKey.PublicKey).Concat(state.serverKey).ToArray(), null, 24); 29 | plainText = PublicKeyBox.Open(cipherText, nonce, state.clientKey.PrivateKey, state.serverKey); 30 | state.serverState.nonce = plainText.Take(24).ToArray(); 31 | state.serverState.sharedKey = plainText.Skip(24).Take(32).ToArray(); 32 | plainText = plainText.Skip(24).Skip(32).ToArray(); 33 | } 34 | else 35 | { 36 | state.serverState.nonce = Utilities.Increment(Utilities.Increment(state.serverState.nonce)); 37 | plainText = SecretBox.Open(new byte[16].Concat(cipherText).ToArray(), state.serverState.nonce, state.serverState.sharedKey); 38 | } 39 | try 40 | { 41 | JObject decoded = state.decoder.decode(messageId, unknown, plainText); 42 | Console.WriteLine("{0}: {1}", decoded["name"], decoded["fields"]); 43 | } 44 | catch (Exception e) 45 | { 46 | Console.WriteLine(e.Message); 47 | Console.WriteLine("{0} {1}", messageId, Utilities.BinaryToHex(BitConverter.GetBytes(messageId).Reverse().Skip(2).Concat(BitConverter.GetBytes(plainText.Length).Reverse().Skip(1)).Concat(BitConverter.GetBytes(unknown).Reverse().Skip(2)).Concat(plainText).ToArray())); 48 | } 49 | ServerCrypto.EncryptPacket(state.serverState.socket, state.serverState, messageId, unknown, plainText); 50 | } 51 | 52 | public static void EncryptPacket(Socket socket, ClientState state, int messageId, int unknown, byte[] plainText) 53 | { 54 | byte[] cipherText; 55 | if (messageId == 10100) 56 | { 57 | cipherText = plainText; 58 | } 59 | else if (messageId == 10101) 60 | { 61 | byte[] nonce = GenericHash.Hash(state.clientKey.PublicKey.Concat(state.serverKey).ToArray(), null, 24); 62 | plainText = state.serverState.sessionKey.Concat(state.nonce).Concat(plainText).ToArray(); 63 | cipherText = PublicKeyBox.Create(plainText, nonce, state.clientKey.PrivateKey, state.serverKey); 64 | cipherText = state.clientKey.PublicKey.Concat(cipherText).ToArray(); 65 | } 66 | else 67 | { 68 | // nonce was already incremented in ServerCrypto.DecryptPacket 69 | cipherText = SecretBox.Create(plainText, state.nonce, state.serverState.sharedKey).Skip(16).ToArray(); 70 | } 71 | byte[] packet = BitConverter.GetBytes(messageId).Reverse().Skip(2).Concat(BitConverter.GetBytes(cipherText.Length).Reverse().Skip(1)).Concat(BitConverter.GetBytes(unknown).Reverse().Skip(2)).Concat(cipherText).ToArray(); 72 | socket.BeginSend(packet, 0, packet.Length, 0, new AsyncCallback(SendCallback), state); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /ClientState.cs: -------------------------------------------------------------------------------- 1 | using Sodium; 2 | 3 | namespace coc_proxy_csharp 4 | { 5 | public class ClientState : State 6 | { 7 | public ServerState serverState; 8 | 9 | public KeyPair clientKey; 10 | public byte[] serverKey, nonce; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /PacketReceiver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Net.Sockets; 4 | 5 | namespace coc_proxy_csharp 6 | { 7 | class PacketReceiver 8 | { 9 | public static void receive(int bytesReceived, Socket socket, State state) 10 | { 11 | int bytesRead = 0; 12 | int payloadLength, bytesAvailable, bytesNeeded; 13 | while (bytesRead < bytesReceived) 14 | { 15 | bytesAvailable = bytesReceived - bytesRead; 16 | if (bytesReceived > 0) 17 | { 18 | if (state.packet.Length >= 7) 19 | { 20 | payloadLength = BitConverter.ToInt32(new byte[1].Concat(state.packet.Skip(2).Take(3)).Reverse().ToArray(), 0); 21 | bytesNeeded = payloadLength - (state.packet.Length - 7); 22 | if (bytesAvailable >= bytesNeeded) 23 | { 24 | state.packet = state.packet.Concat(state.buffer.Skip(bytesRead).Take(bytesNeeded)).ToArray(); 25 | bytesRead += bytesNeeded; 26 | bytesAvailable -= bytesNeeded; 27 | if (state.GetType() == typeof(ClientState)) 28 | { 29 | ClientCrypto.DecryptPacket(socket, (ClientState)state, state.packet); 30 | } 31 | else if (state.GetType() == typeof(ServerState)) 32 | { 33 | ServerCrypto.DecryptPacket(socket, (ServerState)state, state.packet); 34 | } 35 | state.packet = new byte[0]; 36 | } 37 | else 38 | { 39 | state.packet = state.packet.Concat(state.buffer.Skip(bytesRead).Take(bytesAvailable)).ToArray(); 40 | bytesRead = bytesReceived; 41 | bytesAvailable = 0; 42 | } 43 | } 44 | else if (bytesAvailable >= 7) 45 | { 46 | state.packet = state.packet.Concat(state.buffer.Skip(bytesRead).Take(7)).ToArray(); 47 | bytesRead += 7; 48 | bytesAvailable -= 7; 49 | } 50 | else 51 | { 52 | state.packet = state.packet.Concat(state.buffer.Skip(bytesRead).Take(bytesAvailable)).ToArray(); 53 | bytesRead = bytesReceived; 54 | bytesAvailable = 0; 55 | } 56 | } 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Protocol.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Sockets; 3 | 4 | namespace coc_proxy_csharp 5 | { 6 | public class Protocol 7 | { 8 | protected static void ReceiveCallback(IAsyncResult ar) 9 | { 10 | try 11 | { 12 | State state = (State)ar.AsyncState; 13 | Socket socket = state.socket; 14 | int bytesReceived = socket.EndReceive(ar); 15 | PacketReceiver.receive(bytesReceived, socket, state); 16 | socket.BeginReceive(state.buffer, 0, State.BufferSize, 0, new AsyncCallback(ReceiveCallback), state); 17 | } 18 | catch (Exception e) 19 | { 20 | Console.WriteLine(e.ToString()); 21 | } 22 | } 23 | 24 | protected static void SendCallback(IAsyncResult ar) 25 | { 26 | try 27 | { 28 | State state = (State)ar.AsyncState; 29 | Socket socket = state.socket; 30 | int bytesSent = socket.EndSend(ar); 31 | } 32 | catch (Exception e) 33 | { 34 | Console.WriteLine(e.ToString()); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Proxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace coc_proxy_csharp 4 | { 5 | class Proxy 6 | { 7 | public static string hostname = "gamea.clashofclans.com"; 8 | public static int port = 9339; 9 | 10 | private static void Main(string[] args) 11 | { 12 | try 13 | { 14 | Server server = new Server(Proxy.port); 15 | server.StartServer(); 16 | } 17 | catch (Exception e) 18 | { 19 | Console.WriteLine(e.ToString()); 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #coc-proxy-csharp 2 | 3 | Build, then run with: 4 | 5 | coc-proxy-csharp.exe 6 | 7 | **Warning**: This does not automatically close the other side when the connection to the client or server closes. In fact, it doesn't even detect that either side has closed and will still try to send data on the closed socket. 8 | 9 | ##Installation 10 | 11 | Install [libsodium-net](https://github.com/adamcaudill/libsodium-net) with NuGet. 12 | 13 | Patch `libg.so` with: 14 | 15 | adb pull /data/data/com.supercell.clashofclans/lib/libg.so 16 | dd if=libg.so of=key-backup.bin skip=$( key-new.bin 19 | xxd -p -c 32 key-new.bin 20 | dd if=key-new.bin of=libg.so seek=$(