#!/usr/bin/env python3 import json from urllib.request import Request, urlopen from base64 import b64encode, b64decode from binascii import hexlify, unhexlify from random import randrange from os.path import isfile, isdir from os import mkdir from time import time charmap = { ' ': 0x00, 'é': 0x1B, '0': 0xA1, '1': 0xA2, '2': 0xA3, '3': 0xA4, '4': 0xA5, '5': 0xA6, '6': 0xA7, '7': 0xA8, '8': 0xA9, '9': 0xAA, '!': 0xAB, '?': 0xAC, '.': 0xAD, '-': 0xAE, '…': 0xB0, '“': 0xB1, '”': 0xB2, '‘': 0xB3, '’': 0xB4, '♂': 0xB5, '♀': 0xB6, ',': 0xB8, '/': 0xBA, 'A': 0xBB, 'B': 0xBC, 'C': 0xBD, 'D': 0xBE, 'E': 0xBF, 'F': 0xC0, 'G': 0xC1, 'H': 0xC2, 'I': 0xC3, 'J': 0xC4, 'K': 0xC5, 'L': 0xC6, 'M': 0xC7, 'N': 0xC8, 'O': 0xC9, 'P': 0xCA, 'Q': 0xCB, 'R': 0xCC, 'S': 0xCD, 'T': 0xCE, 'U': 0xCF, 'V': 0xD0, 'W': 0xD1, 'X': 0xD2, 'Y': 0xD3, 'Z': 0xD4, 'a': 0xD5, 'b': 0xD6, 'c': 0xD7, 'd': 0xD8, 'e': 0xD9, 'f': 0xDA, 'g': 0xDB, 'h': 0xDC, 'i': 0xDD, 'j': 0xDE, 'k': 0xDF, 'l': 0xE0, 'm': 0xE1, 'n': 0xE2, 'o': 0xE3, 'p': 0xE4, 'q': 0xE5, 'r': 0xE6, 's': 0xE7, 't': 0xE8, 'u': 0xE9, 'v': 0xEA, 'w': 0xEB, 'x': 0xEC, 'y': 0xED, 'z': 0xEE, '$': 0xFF, } charmap_rev = {} for x, y in charmap.items(): charmap_rev[y] = x def translate(str): d = bytearray() for x in str: d.append(charmap[x]) d.append(0xff) return d def translate_rev(bytes): s = "" for c in bytes: if c == 0xff: break s += charmap_rev[c] return s class BitstreamReader(): def __init__(self, bytes, bytesize=8): self.bytes = bytes self.bytesize = bytesize self.pos = 0 self.bits = 0 self.value = 0 def remaining(self): if self.pos < len(self.bytes): return True if self.bits > 0: return True return False def read(self, count): while self.bits < count: if self.pos >= len(self.bytes): break self.value |= (self.bytes[self.pos] & ((1 << self.bytesize) - 1)) << self.bits self.bits += self.bytesize self.pos += 1 ret = self.value & ((1 << count) - 1) self.value >>= count self.bits -= count return ret def bitpack(code, origbits, destbits): newcode = [] reader = BitstreamReader(code, origbits) while reader.remaining(): newcode.append(reader.read(destbits)) return newcode def ror(val, bits): return val >> bits | ((val << (32 - bits)) & (2 ** 32 - 1)) class Client: def __init__(self, user, pwd): self.token = None self.uid = None self.login(user, pwd) def makereq(self, endpoint, *args, **kwargs): req = Request("https://fools2022.online/%s" % endpoint, *args, **kwargs) req.add_header("User-Agent", "fools2022-client/1.1.0") req.add_header("Content-Type", "application/x-www-form-urlencoded") req.add_header("Accept-Encoding", "identity") if self.token: req.add_header("X-Foolssessiontoken", self.token) return req def makepkt(self, cmd, data): data = bitpack(data, 8, 32) rnd = randrange(256) pkt = [rnd << 8 | cmd] + data magic = 0xf0dbeb15 for x in pkt: magic = ror(magic, 5) magic ^= x magic += x * 2 pkt[0] = (magic & (2**16-1)) << 16 | pkt[0] pkt = b64encode(bytes(bitpack(pkt, 32, 8))) return self.makereq("packet/%s" % self.uid, data=pkt) def login(self, user, pwd): data = {"u": user, "p": pwd} data = json.dumps(data).encode() req = self.makereq("login", data=data) res = json.loads(urlopen(req).read().decode()) self.token = res["data"]["session"] self.uid = res["data"]["uid"] def getmap(self, mapid, pos=(0,0)): data = [mapid >> 0, mapid >> 8, pos[0], pos[1]] req = self.makepkt(0x01, data) rev = b64decode(urlopen(req).read().decode()) print(hexlify(rev).decode()) return rev def trendset(self, text): data = bytearray() for i, x in enumerate(text): data.append(x) data.append(0xff) req = self.makepkt(0x04, data) rev = b64decode(urlopen(req).read().decode()).decode() print(rev) def cave3(self, text): data = bytearray(unhexlify("ffffffffffffffffffff")) for i, x in enumerate(text): data[i] = x req = self.makepkt(0x06, data) print(urlopen(req).read().decode()) def cave4gen(self, text): data = bytearray() for i, x in enumerate(text): data.append(ord(x)) data.append(0xff) req = self.makepkt(0x07, data) rev = b64decode(urlopen(req).read().decode())[:-1].decode() print(rev) def cave4chk(self, cert): data = cert.encode() + b"\xff" req = self.makepkt(0x08, data) rev = b64decode(urlopen(req).read().decode()) print(rev) rev = rev[4:-1] for x in range(0, len(rev), 16): print(rev[x:x+16]) def lotto(self): data = b"" req = self.makepkt(0x05, data) rev = b64decode(urlopen(req).read().decode()) print(hexlify(rev).decode()) def complete(self): data = bytearray(unhexlify("ef7fffff7fe0ffff000000000000000000000000000000000000000000000000")) data = bytearray(unhexlify("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) req = self.makepkt(0x02, data) rev = b64decode(urlopen(req).read().decode()).decode() print(rev) maps = [ 0x0000, 0x0100, 0x0110, 0x0210, 0x0327, 0x0364, 0x043A, 0x0523, 0x0565, 0x0566, 0x062F, 0x0667, 0x0668, 0x0669, 0x066A, 0x0734, 0x0824, 0x0932, 0x096B, 0x098A, 0x0A32, 0x0B2D, 0x0C2C, 0x0D3E, 0x0E3C, 0x0E6C, 0x0F3B, 0x103B, 0x106D, 0x113D, 0x116E, 0x123D, 0x1321, 0x1337, 0x1432, 0x146F, 0x152D, 0x1631, 0x1639, 0x1670, 0x1671, 0x1672, 0x1720, 0x1730, 0x1731, 0x1732, 0x182A, 0x1927, 0x1A3C, 0x1A73, 0x1B37, 0x1C2C, 0x1D3B, 0x1E33, 0x1E92, 0x1F3A, 0x1F79, 0x1F7A, 0x2001, 0x202F, 0x207B, 0x2125, 0x2174, 0x223A, 0x2275, 0x2276, 0x2277, 0x232D, 0x2435, 0x2478, 0x2536, 0x2632, 0x2725, 0x2731, 0x2791, 0x2833, 0x2939, 0x2B29, 0x2B7C, 0x2B7D, 0x2B7E, 0x2C29, 0x2D27, 0x2E2B, 0x2F38, 0x2F7F, 0x2F80, 0x2F81, 0x302C, 0x3120, 0x318B, 0x318C, 0x318D, 0x318E, 0x318F, 0x3190, 0x3191, 0x323F, 0x3336, 0x3420, 0x3482, 0x353C, 0x3621, 0x3724, 0x3828, 0x3920, 0x3A3E, 0x3B22, 0x3B30, 0x3B31, 0x3B32, 0x3C36, 0x3D20, 0x3D83, 0x3E31, 0x3E90, 0x3F3D, 0x4026, 0x412E, 0x423A, 0x432A, 0x4384, 0x4430, 0x4528, 0x4530, 0x4585, 0x472B, 0x4786, 0x4787, 0x4788, 0x482B, 0x4889, 0x4933, 0x4A34, 0x4B3A, 0x4C21, 0x4C93, 0x4D3A, 0x4E22, 0x4F21, 0x506F, 0x5133, 0x5134, 0x5135, 0x5136, 0x5137, 0x5160, 0x5211, ] maps_skip = [ # YEET maps (not always accessible) 0x5133, 0x5134, 0x5135, 0x5136, 0x5137, 0x5160 ] if __name__ == "__main__": from time import sleep from sys import argv c = Client(open(".user").read().strip(), open(".pass").read().strip()) sleep(1) if argv[1] == "map": c.getmap(int(argv[2], 0)) if argv[1] == "maps": fun_map = c.getmap(0x1670) fun_value = int(translate_rev(fun_map[0x25b:0x260])) print("Fun value:", fun_value) fdir = "maps_%03d" % fun_value if not isdir(fdir): mkdir(fdir) sleep(1) # fdir = "maps" # for x in range(0x10000): for x in maps: fname = "%s/%04X.map" % (fdir, x) if isfile(fname): continue if x in maps_skip: continue print("Getting map %s..." % fname) m = c.getmap(x) open(fname, "wb").write(m) sleep(1) if argv[1] == "trend": c.trendset(translate(argv[2])) if argv[1] == "cave3": c.cave3(translate(argv[2])) if argv[1] == "cave4gen": c.cave4gen(argv[2]) if argv[1] == "cave4chk": c.cave4chk(argv[2]) if argv[1] == "complete": c.getmap(int(argv[2], 0)) sleep(1) c.complete() if argv[1] == "lotto": c.lotto() if argv[1] == "yeet": game_corner_map = c.getmap(0x1A73) sleep(1) yeet_map_id = game_corner_map[0x8B0] << 0 yeet_map_id |= game_corner_map[0x8B1] << 8 yeet_map = c.getmap(yeet_map_id) game_corner_map_fname = "maps_changing/1A73_%04X.map" % yeet_map_id if not isfile(game_corner_map_fname): open(game_corner_map_fname, "wb").write(game_corner_map) yeet_map_fname = "maps/%04X.map" % yeet_map_id if not isfile(yeet_map_fname): open(yeet_map_fname, "wb").write(yeet_map) sleep(1) results_map = c.getmap(0x5160) results_map_fname = "maps_changing/5160_%d.map" % time() open(results_map_fname, "wb").write(results_map) if argv[1] == "funroll": fun_map = c.getmap(0x472b, (15, 10)) if argv[1] == "funcheck": fun_map = c.getmap(0x1670) fun_value = int(translate_rev(fun_map[0x25b:0x260])) print(fun_value)