Skip to content

Allow Frida connection over network + custom adb path #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions Main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__))
Expand Down Expand Up @@ -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()

Expand All @@ -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'])
Expand Down
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
25 changes: 17 additions & 8 deletions TCP/Client/protocol.py
Original file line number Diff line number Diff line change
@@ -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):

Expand All @@ -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()
Expand All @@ -24,24 +29,28 @@ 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:
client_host = self.server.transport.getPeer().host

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)
21 changes: 14 additions & 7 deletions TCP/Server/protocol.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
# -*- 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):
self.factory = 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()
Expand All @@ -33,19 +37,22 @@ 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))

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)
3 changes: 2 additions & 1 deletion UDP/packetEnum.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-


packet_enum = {
udp_packet_enum = {
# Client Packet
15620: 'KeepAlive',
17187: 'SectorCommand',
17104: 'EndClientTurnMessage',

# Server Packet
26301: 'SectorHearbeat'
Expand Down
4 changes: 2 additions & 2 deletions UDP/packetProcessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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,
Expand Down