diff --git a/Main.py b/Main.py index 86af93c..fee3212 100644 --- a/Main.py +++ b/Main.py @@ -26,14 +26,17 @@ def onClose(udp_protocol): udp_protocol.packetProcessor.stop() -def start_frida_script(): +def start_frida_script(network, adbpath): # Would be better to use frida.get_usb_device().spawn to spawn the app # But it seems that it is broken on some version so we use adb to spawn the game - os.system("adb shell monkey -p com.supercell.clashroyale -c android.intent.category.LAUNCHER 1") + os.system(adbpath + " shell monkey -p com.supercell.clashroyale -c android.intent.category.LAUNCHER 1") time.sleep(0.5) try: - device = frida.get_usb_device() + if network: + device = frida.get_remote_device() + else: + device = frida.get_usb_device() except Exception as exception: print('[*] Can\'t connect to your device ({}) !'.format(exception.__class__.__name__)) @@ -71,9 +74,11 @@ def start_frida_script(): if __name__ == '__main__': parser = argparse.ArgumentParser(description='Python proxy used to decrypt all clash royale game traffic') parser.add_argument('-f', '--frida', help='inject the frida script at the proxy runtime', action='store_true') + parser.add_argument('-n', '--network', help='connect to frida via network rather than USB', action='store_true') parser.add_argument('-v', '--verbose', help='print packet hexdump in console', action='store_true') parser.add_argument('-r', '--replay', help='save packets in replay folder', action='store_true') parser.add_argument('-u', '--udp', help='start the udp proxy', action='store_true') + parser.add_argument('-a', '--adbpath', help='path to adb', default='adb') args = parser.parse_args() @@ -85,7 +90,7 @@ def start_frida_script(): exit() if args.frida: - start_frida_script() + start_frida_script(args.network, args.adbpath) crypto = Crypto(config['ServerKey']) replay = Replay(config['ReplayDirectory']) diff --git a/README.md b/README.md index 4fcb3e3..b863192 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,12 @@ To start the proxy you will just have to run the following command: However the proxy accept some optionals arguments that are: -* `-f`: if specified, the game will be automatically spawned and the frida script will be injected at proxy runtime -* `-v`: if specified, the proxy will be run in verbose mode that basically output packets hexdump in terminal -* `-r`: if specified, all packets will be saved in the repository you've set in config.json (ReplayDirectory key) -* `-u`: if specified UDP proxy will be launched too +* `-f`: the game will be automatically spawned and the frida script will be injected at proxy runtime +* `-n`: connect to frida via network rather than USB +* `-v`: the proxy will be run in verbose mode that basically output packets hexdump in terminal +* `-r`: all packets will be saved in the repository you've set in config.json (ReplayDirectory key) +* `-u`: UDP proxy will be launched too +* `-a`: optional path to adb, useful if using specific adb/Android emulator ### UDP Proxy diff --git a/TCP/Client/protocol.py b/TCP/Client/protocol.py index 371527d..fc05064 100644 --- a/TCP/Client/protocol.py +++ b/TCP/Client/protocol.py @@ -1,11 +1,15 @@ # -*- coding: utf-8 -*- import hexdump +import socket from TCP.PacketReceiver import packetReceiver from TCP.Packet.packetEnum import packet_enum +from UDP.packetEnum import udp_packet_enum from twisted.internet.protocol import Protocol +UDP_IP = "127.0.0.1" +UDP_PORT = 9340 class ClientProtocol(packetReceiver, Protocol): @@ -14,6 +18,7 @@ def __init__(self, factory): self.factory.server.client = self self.server = self.factory.server self.crypto = self.server.crypto + self.udpsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) def connectionMade(self): self.peer = self.transport.getPeer() @@ -24,10 +29,6 @@ def connectionLost(self, reason): self.server.transport.loseConnection() def processPacket(self, packet_id, data): - packet_name = packet_enum.get(packet_id, packet_id) - - print('[*] {} received from server'.format(packet_name)) - decrypted = self.crypto.decrypt_server_packet(packet_id, data[7:]) if packet_id == 27579 and self.server.factory.args.udp: @@ -35,13 +36,21 @@ def processPacket(self, packet_id, data): decrypted = self.server.factory.udp_protocol.build_udp_info_packet(client_host, decrypted) + encrypted = self.crypto.encrypt_server_packet(packet_id, decrypted) + payload = packet_id.to_bytes(2, 'big') + len(encrypted).to_bytes(3, 'big') + data[5:7] + encrypted + + self.server.transport.write(payload) + bstr = (packet_id).to_bytes(2, byteorder='big') + if len(decrypted) < 40: + self.udpsock.sendto(bstr+decrypted, (UDP_IP, UDP_PORT)) + + packet_name = packet_enum.get(packet_id, udp_packet_enum.get(packet_id, packet_id)) + + print('[*] {} received from server'.format(packet_name)) + if self.server.factory.args.verbose and decrypted: print(hexdump.hexdump(decrypted)) if self.server.factory.args.replay: self.server.factory.replay.save_tcp_packet(packet_name, data[:7] + decrypted) - encrypted = self.crypto.encrypt_server_packet(packet_id, decrypted) - payload = packet_id.to_bytes(2, 'big') + len(encrypted).to_bytes(3, 'big') + data[5:7] + encrypted - - self.server.transport.write(payload) diff --git a/TCP/Server/protocol.py b/TCP/Server/protocol.py index ab11be4..e253c19 100644 --- a/TCP/Server/protocol.py +++ b/TCP/Server/protocol.py @@ -1,14 +1,17 @@ # -*- coding: utf-8 -*- import hexdump +import socket from twisted.internet import reactor from TCP.Client.factory import ClientFactory from TCP.PacketReceiver import packetReceiver from TCP.Packet.packetEnum import packet_enum +from UDP.packetEnum import udp_packet_enum from twisted.internet.protocol import Protocol - +UDP_IP = "127.0.0.1" +UDP_PORT = 9340 class ServerProtocol(packetReceiver, Protocol): def __init__(self, factory): @@ -16,6 +19,7 @@ def __init__(self, factory): self.factory.server = self self.crypto = self.factory.crypto self.client = None + self.udpsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) def connectionMade(self): self.peer = self.transport.getPeer() @@ -33,11 +37,18 @@ def processPacket(self, packet_id, data): reactor.callLater(0.25, self.processPacket, packet_id, data) return - packet_name = packet_enum.get(packet_id, packet_id) + decrypted = self.crypto.decrypt_client_packet(packet_id, data[7:]) + encrypted = self.crypto.encrypt_client_packet(packet_id, decrypted) + payload = packet_id.to_bytes(2, 'big') + len(encrypted).to_bytes(3, 'big') + data[5:7] + encrypted + + packet_name = packet_enum.get(packet_id, udp_packet_enum.get(packet_id, packet_id)) print('[*] {} received from client'.format(packet_name)) - decrypted = self.crypto.decrypt_client_packet(packet_id, data[7:]) + self.client.transport.write(payload) + bstr = (packet_id).to_bytes(2, byteorder='big') + if len(decrypted) < 40: + self.udpsock.sendto(bstr+decrypted, (UDP_IP, UDP_PORT)) if self.factory.args.verbose and decrypted: print(hexdump.hexdump(decrypted)) @@ -45,7 +56,3 @@ def processPacket(self, packet_id, data): if self.factory.args.replay: self.factory.replay.save_tcp_packet(packet_name, data[:7] + decrypted) - encrypted = self.crypto.encrypt_client_packet(packet_id, decrypted) - payload = packet_id.to_bytes(2, 'big') + len(encrypted).to_bytes(3, 'big') + data[5:7] + encrypted - - self.client.transport.write(payload) diff --git a/UDP/packetEnum.py b/UDP/packetEnum.py index c0d9af7..42c35b4 100644 --- a/UDP/packetEnum.py +++ b/UDP/packetEnum.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -packet_enum = { +udp_packet_enum = { # Client Packet 15620: 'KeepAlive', 17187: 'SectorCommand', + 17104: 'EndClientTurnMessage', # Server Packet 26301: 'SectorHearbeat' diff --git a/UDP/packetProcessor.py b/UDP/packetProcessor.py index 644deab..fddc354 100644 --- a/UDP/packetProcessor.py +++ b/UDP/packetProcessor.py @@ -3,7 +3,7 @@ from queue import Queue from threading import Thread from TCP.Packet.reader import Reader -from UDP.packetEnum import packet_enum +from UDP.packetEnum import udp_packet_enum class packetProcessor(Thread): @@ -51,7 +51,7 @@ def run(self): for packet in reversed(packet_list): if packet['sequence_id'] == host_dict['next_sequence_id']: - packet_name = packet_enum.get(packet['id'], packet['id']) + packet_name = udp_packet_enum.get(packet['id'], packet['id']) print('[*] Received UDP chunk {} from {}, chunk length: {}'.format( packet_name, host,