mirror of
https://github.com/nihilvux/bancho.py.git
synced 2025-09-16 18:48:38 -07:00
168 lines
4.7 KiB
Python
168 lines
4.7 KiB
Python
# like budget wireshark for osu! server stuff
|
|
# usage: enable http://localhost:8080 proxy in windows,
|
|
# (https://i.cmyui.xyz/DNnqifKHyBSA9X8NEHg.png)
|
|
# and run this with `mitmdump -qs tools/proxy.py`
|
|
from __future__ import annotations
|
|
|
|
domain = "cmyui.xyz" # XXX: put your domain here
|
|
|
|
import re
|
|
import struct
|
|
import sys
|
|
from enum import IntEnum
|
|
from enum import unique
|
|
|
|
from mitmproxy import http
|
|
|
|
|
|
@unique
|
|
class ServerPackets(IntEnum):
|
|
USER_ID = 5
|
|
SEND_MESSAGE = 7
|
|
PONG = 8
|
|
HANDLE_IRC_CHANGE_USERNAME = 9 # unused
|
|
HANDLE_IRC_QUIT = 10
|
|
USER_STATS = 11
|
|
USER_LOGOUT = 12
|
|
SPECTATOR_JOINED = 13
|
|
SPECTATOR_LEFT = 14
|
|
SPECTATE_FRAMES = 15
|
|
VERSION_UPDATE = 19
|
|
SPECTATOR_CANT_SPECTATE = 22
|
|
GET_ATTENTION = 23
|
|
NOTIFICATION = 24
|
|
UPDATE_MATCH = 26
|
|
NEW_MATCH = 27
|
|
DISPOSE_MATCH = 28
|
|
TOGGLE_BLOCK_NON_FRIEND_DMS = 34
|
|
MATCH_JOIN_SUCCESS = 36
|
|
MATCH_JOIN_FAIL = 37
|
|
FELLOW_SPECTATOR_JOINED = 42
|
|
FELLOW_SPECTATOR_LEFT = 43
|
|
ALL_PLAYERS_LOADED = 45
|
|
MATCH_START = 46
|
|
MATCH_SCORE_UPDATE = 48
|
|
MATCH_TRANSFER_HOST = 50
|
|
MATCH_ALL_PLAYERS_LOADED = 53
|
|
MATCH_PLAYER_FAILED = 57
|
|
MATCH_COMPLETE = 58
|
|
MATCH_SKIP = 61
|
|
UNAUTHORIZED = 62 # unused
|
|
CHANNEL_JOIN_SUCCESS = 64
|
|
CHANNEL_INFO = 65
|
|
CHANNEL_KICK = 66
|
|
CHANNEL_AUTO_JOIN = 67
|
|
BEATMAP_INFO_REPLY = 69
|
|
PRIVILEGES = 71
|
|
FRIENDS_LIST = 72
|
|
PROTOCOL_VERSION = 75
|
|
MAIN_MENU_ICON = 76
|
|
MONITOR = 80 # unused
|
|
MATCH_PLAYER_SKIPPED = 81
|
|
USER_PRESENCE = 83
|
|
RESTART = 86
|
|
MATCH_INVITE = 88
|
|
CHANNEL_INFO_END = 89
|
|
MATCH_CHANGE_PASSWORD = 91
|
|
SILENCE_END = 92
|
|
USER_SILENCED = 94
|
|
USER_PRESENCE_SINGLE = 95
|
|
USER_PRESENCE_BUNDLE = 96
|
|
USER_DM_BLOCKED = 100
|
|
TARGET_IS_SILENCED = 101
|
|
VERSION_UPDATE_FORCED = 102
|
|
SWITCH_SERVER = 103
|
|
ACCOUNT_RESTRICTED = 104
|
|
RTX = 105 # unused
|
|
MATCH_ABORT = 106
|
|
SWITCH_TOURNAMENT_SERVER = 107
|
|
|
|
def __repr__(self) -> str:
|
|
return f"<{self.name} ({self.value})>"
|
|
|
|
|
|
BYTE_ORDER_SUFFIXES = ["B", "KB", "MB", "GB"]
|
|
|
|
|
|
def fmt_bytes(n: int | float) -> str:
|
|
suffix = None
|
|
for suffix in BYTE_ORDER_SUFFIXES:
|
|
if n < 1024:
|
|
break
|
|
n /= 1024
|
|
return f"{n:,.2f}{suffix}"
|
|
|
|
|
|
DOMAIN_RGX = re.compile(
|
|
rf"^(?P<subdomain>osu|c[e4]?|a|s|b|assets)\.(?:ppy\.sh|{re.escape(domain)})$",
|
|
)
|
|
|
|
PACKET_HEADER_FMT = struct.Struct("<HxI") # header gives us packet id & data length
|
|
|
|
print(f"\x1b[0;92mListening (ppy.sh & {domain})\x1b[0m\n")
|
|
|
|
|
|
def response(flow: http.HTTPFlow) -> None:
|
|
r_match = DOMAIN_RGX.match(flow.request.host)
|
|
if not r_match:
|
|
return # unrelated request
|
|
|
|
body = flow.response.content
|
|
if not body:
|
|
return # empty resp
|
|
|
|
sys.stdout.write(f"\x1b[0;93m[{flow.request.method}] {flow.request.url}\x1b[0m\n")
|
|
body_view = memoryview(body)
|
|
body_len = len(body)
|
|
|
|
if r_match["subdomain"] in ("c", "ce", "c4", "c5", "c6"):
|
|
if flow.request.method == "POST":
|
|
packet_num = 1
|
|
while body_view:
|
|
# read header
|
|
_pid, plen = PACKET_HEADER_FMT.unpack_from(body_view)
|
|
pid = ServerPackets(_pid)
|
|
body_view = body_view[7:]
|
|
|
|
# read data
|
|
pdata = str(body_view[:plen].tobytes())[2:-1] # remove b''
|
|
body_view = body_view[plen:]
|
|
|
|
sys.stdout.write(f"[{packet_num}] \x1b[0;95m{pid!r}\x1b[0m {pdata}\n")
|
|
|
|
packet_num += 1
|
|
|
|
if packet_num % 5: # don't build up too much in ram
|
|
sys.stdout.flush()
|
|
sys.stdout.write("\n")
|
|
else: # format varies per request
|
|
if ( # todo check host
|
|
(
|
|
# jfif, jpe, jpeg, jpg graphics file
|
|
body_view[:4] == b"\xff\xd8\xff\xe0"
|
|
and body_view[6:11] == b"JFIF\x00"
|
|
)
|
|
or (
|
|
# exif digital jpg
|
|
body_view[:4] == b"\xff\xd8\xff\xe1"
|
|
and body_view[6:11] == b"Exif\x00"
|
|
)
|
|
or (
|
|
# spiff still picture jpg
|
|
body_view[:4] == b"\xff\xd8\xff\xe8"
|
|
and body_view[6:12] == b"SPIFF\x00"
|
|
)
|
|
):
|
|
sys.stdout.write(f"[{fmt_bytes(body_len)} jpeg file]\n\n")
|
|
elif (
|
|
body_view[:8] == b"\x89PNG\r\n\x1a\n"
|
|
and body_view[-8:] == b"\x49END\xae\x42\x60\x82"
|
|
):
|
|
sys.stdout.write(f"[{fmt_bytes(body_len)} png file]\n\n")
|
|
elif body_view[:6] in (b"GIF87a", b"GIF89a") and body_view[-2:] == b"\x00\x3b":
|
|
sys.stdout.write(f"[{fmt_bytes(body_len)} gif file]\n\n")
|
|
else:
|
|
sys.stdout.write(f"{str(body)[2:-1]}\n\n") # remove b''
|
|
|
|
sys.stdout.flush()
|