from struct import unpack import re import sys dumpcs = open('dump.cs').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') typeMap = { 'uint': 'uint32', 'string': 'string', 'ulong': 'uint64', 'float': 'float', 'int': 'int32', 'double': 'double', 'bool': 'bool', 'long': 'int64' } def getTag(address): offset = address & 0xFFFFFFFF prog.seek(offset) inst = prog.read(4) inst = int.from_bytes(inst, byteorder='little', signed=False) if inst == 0xe5900004: #0x080440f9: prog.seek(offset + 4) retnum = int.from_bytes(prog.read(2), 'little', signed=False) rotate_flag = int.from_bytes(prog.read(1), 'little', signed=False) if rotate_flag == 0xA0: # rotate tag number rotate_num = (retnum >> 8) & 0xF tag = retnum & 0xFF for i in range(rotate_num * 2): tag = rotr(tag, 32) return tag return retnum & 0xfff elif inst == 0xe92d4c10: prog.seek(offset + 12) return int.from_bytes(prog.read(2), 'little', signed=False) & 0xfff else: print(hex(inst), hex(address)) 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:11] == 'Dictionary`': subType = re.search('<(\w+), (\w+)>', typ) readClass(level + 1, subType[2]) # prefix = '{}_{}'.format(subType[1], subType[2]) # try: # definedClass.index(prefix) # shownClass = True # except ValueError: # definedClass.append(prefix) # shownClass = False # message = {} # sub = jumpTyp(level + 1, subType[1], '{}_key'.format(prefix)) # message[sub[0]] = [sub[1], 1, sub[2], sub[3]] # sub = jumpTyp(level + 1, subType[2], '{}_value'.format(prefix)) # message[sub[0]] = [sub[1], 2, sub[2], sub[3]] # if not shownClass: # writeMessage(prefix, message) return [name, '<{}, {}>'.format(typeMap.get(subType[1], subType[1]), typeMap.get(subType[2], subType[2])), 'map', 'dictionary'] elif typ[0:5] == 'List`': subType = re.search('<(\w+)>', typ) sub = jumpTyp(level + 1, subType[1], 'entry') return [name, sub[1], 'repeated', 'list'] elif typ[0:9] == '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)