def unwrap_packet(data): if data.bytesAvailable() < 2: return flag = data.readByte() if flag & BIG_LENGTH_FLAG: if data.bytesAvailable() >= 3: compressed = False byte0 = (flag ^ BIG_LENGTH_FLAG) << 24 byte1 = (data.readByte() & 0xFF) << 16 byte2 = (data.readByte() & 0xFF) << 8 byte3 = data.readByte() & 0xFF length = byte0 + byte1 + byte2 + byte3 else: return else: compressed = flag & ZIPPED_FLAG byte0 = (flag & 63) << 8 byte1 = data.readByte() & 0xFF length = byte0 + byte1 if data.bytesAvailable() < length: return unwrapped = util.ByteArray(data.readBytes(length)) assert unwrapped.bytesAvailable() == length if compressed: compressed = unwrapped.readBytes() print('Compressed:', compressed.hex()) unzipped = zlib.decompress(compressed, -15) unwrapped = util.ByteArray(unzipped) return unwrapped
def dump_with_null_map(fname, optional): with open(fname, 'rb') as f: packet = util.ByteArray(f.read()) space = protocol.SpaceCommandDecoder() while packet.bytesAvailable(): command = space.decode(packet, optional) print(simplejson.dumps(command.__dict__, indent=4, ignore_nan=True))
def unwrapClient(self, data): output = util.ByteArray() for byte in data: if byte >= 128: byte -= 256 self.clientSequence[ self.clientSelector] = byte ^ self.clientSequence[ self.clientSelector] output.writeByte(self.clientSequence[self.clientSelector]) self.clientSelector ^= self.clientSequence[self.clientSelector] & 7 return output.data
def decode_null_map(data): flag = data.readByte() if ((flag & INPLACE_MASK_FLAG) != 0): length = flag & 0x3F if ((flag & INPLACE_MASK_2_BYTES) != 0): length = (length << 16) + ( (data.readByte() & 0xFF) << 8) + (data.readByte() & 0xFF) map = util.ByteArray(data.readBytes(length)) return OptionalMap(length << 3, map) length = (flag & 0x60) >> 5 flag = flag << 3 map = util.ByteArray() if length == 0: map.writeByte(flag) size = 5 elif length == 1: map1 = data.readByte() map.writeByte(flag + ((map1 & 0xFF) >> 5)) map.writeByte(map1 << 3) size = 13 elif length == 2: map1, map2 = data.readByte(), data.readByte() map.writeByte(flag + ((map1 & 0xFF) >> 5)) map.writeByte((map1 << 3) + ((map2 & 0xFF) >> 5)) map.writeByte(map2 << 3) size = 21 elif length == 3: map1, map2, map3 = data.readByte(), data.readByte(), data.readByte() map.writeByte(flag + ((map1 & 0xFF) >> 5)) map.writeByte((map1 << 3) + ((map2 & 0xFF) >> 5)) map.writeByte((map2 << 3) + ((map3 & 0xFF) >> 5)) map.writeByte(map3 << 3) size = 29 else: raise AssertionError() return OptionalMap(size, map)
def dump_bin(fname): os.makedirs('dump', exist_ok=True) with open(fname, 'rb') as f: i = 0 reader = PacketReader(f) for record in reader: if record.rec_type == RECORD_DATA: packet = util.ByteArray(record.data) protocol.decode_null_map(packet) length = packet.position packet.position = 0 raw_optional = list(packet.readBytes(length)) with open(f'dump/{i}.bin', 'wb') as f: f.write(packet.readBytes()) print(f'Saved {i}.bin, optional={raw_optional}') i += 1
def _next_record(self): record = super().__next__() if record.rec_type == RECORD_BEGIN: self.in_space[record.conn_id] = False self.queue.append(BeginEvent(record)) elif record.rec_type == RECORD_END: self.queue.append(EndEvent(record)) elif record.rec_type == RECORD_DATA: packet = util.ByteArray(record.data) optional_map = protocol.decode_null_map(packet) space_conn = self.in_space[record.conn_id] decoder = self.space if space_conn else self.control[ record.outgoing] while packet.bytesAvailable(): try: command = decoder.decode(packet, optional_map) except: traceback.print_exc() dummy = protocol.SpaceCommand(None, None) dummy.data = base64.b64encode(packet.data).decode() self.queue.append(CommandEvent(record, self.i, dummy)) return # upgrade connection if necessary if not space_conn and command.command_id == 3: self.in_space[record.conn_id] = True # prevent leaking sensitive information if space_conn and command.data[ 'codec'] == 'LoginModelServer_login': command.data['password'] = '******' * 12 self.queue.append(CommandEvent(record, self.i, command)) assert packet.bytesAvailable() == 0 self.i += 1
def main(): parser = argparse.ArgumentParser() parser.add_argument('filename') parser.add_argument('-j', '--json', action='store_true', help='output as json') parser.add_argument('-b', '--bin', action='store_true', help='output a binary file for each packet') parser.add_argument('-r', '--raw', action='store_true', help='read file as raw packet with embedded null map') parser.add_argument('-n', '--null', nargs=1, help='read file as raw packet with provided null map') args = parser.parse_args() if not os.path.isfile(args.filename): parser.error(f'{args.filename} not found') sys.exit(1) if args.json: dump_json(args.filename) elif args.bin: dump_bin(args.filename) elif args.raw: dump_raw(args.filename) elif args.null: nulls = bytearray([int(x, 0) for x in args.null[0].split(',')]) null_map = protocol.decode_null_map(util.ByteArray(nulls)) dump_with_null_map(args.filename, null_map) else: dump_contents(args.filename)