from struct import unpack import re import sys dumpcs = open('dump.cs', encoding="utf8").read() prog = open('libil2cpp.so', 'rb') definedClass = [] targetClass = sys.argv[1] #'SuiteMasterGetResponse' # change to get different classes outputPath = './{}.proto'.format(targetClass) outputFile = open(outputPath, 'w') # write first line outputFile.write('syntax = "proto2";\n') # outputFile.write('package bang;\n') typeMap = { 'uint': 'uint32', 'string': 'string', 'ulong': 'uint32', 'float': 'float', 'int': 'int32', 'double': 'double', 'bool': 'bool', 'long': 'int32' } def getTag(address): offset = address & 0xFFFFFFFF prog.seek(offset) inst = prog.read(4) inst = int.from_bytes(inst, byteorder='little', signed=False) if inst == 0xf9400408: prog.seek(offset + 4) inst = int.from_bytes(prog.read(4), 'little', signed=False) elif inst == 0xf81e0ff3: prog.seek(offset + 16) inst = int.from_bytes(prog.read(4), 'little', signed=False) else: print(hex(inst), hex(address)) return None if inst >> 24 == 0x52: return (inst >> 5) & 0xFFFF elif inst >> 24 == 0x32: retnum = (inst >> 8) & 0xFFFF immr = (retnum >> 8) & 0x3F imms = (retnum >> 2) & 0x3F clz = lambda x: "{:032b}".format(x).index("1") _len = 31 - clz((0 << 6) | (~imms & 0x3F)) _size = 1 << _len R = immr & (_size - 1) S = imms & (_size - 1) ret = (1 << (S+1)) - 1 for i in range(immr): ret = rotr(ret, 32) return ret def rotr(num, bits): num &= (2**bits-1) bit = num & 1 num >>= 1 if(bit): num |= (1 << (bits-1)) return num def writeMessage(target, message): outputFile.write('message {} {{\n'.format(target)) for item, info in message.items(): typ = info[0] if type(info[1]).__name__ == 'str': tag = getTag(int(info[1], 16)) else: tag = info[1] hint = info[2] comment = info[3] if hint == 'map': outputFile.write(' {}{} {} = {};\n'.format(hint, typ, item, tag)) else: outputFile.write(' {} {} {} = {};\n'.format(hint, typ, item, tag)) outputFile.write('}\n') def readClass(level, target): try: definedClass.index(target) shownClass = True except ValueError: definedClass.append(target) shownClass = False message = {} classDef = re.search('\[ProtoContractAttribute\].*?\n.*?class ' + target + ' [^\{\}]*?\{((.*\n)*?)?\s+(//\s+Properties(.*\n)*?)?\s+(//\s+Methods(.*\n)*?)?\}\s*?', dumpcs) if not classDef: print('{} not found'.format(target)) else: propList = re.findall('(\[ProtoMemberAttribute\] //.*Offset: 0x([0-9A-F]+).+?\n \w+ ([^\ \<]+(\<(.*?)\>)?) ([^\ ;]+))', classDef[0]) for prop in propList: typ = jumpTyp(level, prop[2], prop[5]) message[typ[0]] = [typ[1], prop[1], typ[2], typ[3]] if not shownClass: # print('{} \n'.format(target)) writeMessage(target, message) def jumpTyp(level, typ, name): if typ[-2:] == '[]': sub = jumpTyp(level + 2, typ[0:-2], 'entry') return [name, sub[1], 'repeated', 'array'] elif typ[0:10] == 'Dictionary': subType = re.search('<(\w+), (\w+)>', typ) readClass(level + 1, subType[2]) return [name, '<{}, {}>'.format(typeMap.get(subType[1], subType[1]), typeMap.get(subType[2], subType[2])), 'map', 'dictionary'] elif typ[0:4] == 'List': subType = re.search('<(\w+)>', typ) sub = jumpTyp(level + 1, subType[1], 'entry') return [name, sub[1], 'repeated', 'list'] elif typ[0:8] == 'Nullable': subType = re.search('<(\w+)>', typ) sub = jumpTyp(level, subType[1], name) sub[3] = 'nullable' return sub else: expectTyp = ['uint','string','ulong','float','int','double', 'bool','long'] try: expectTyp.index(typ) isType = True except ValueError: expectTyp.append(typ) isType = False if isType: return [name, typeMap[typ], 'optional', 'normal type'] else: readClass(level + 1, typ) return [name, typ, 'optional', 'sub class'] readClass(0, targetClass)