Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save azliabdullah/5bf3d645b7d3fd4a9fc6632c51521d33 to your computer and use it in GitHub Desktop.
Save azliabdullah/5bf3d645b7d3fd4a9fc6632c51521d33 to your computer and use it in GitHub Desktop.
Gen Proto BanG Dream

This is the code rewritten in Python 3 from the original code of esterTion's PHP version. Only works with blobs from Android apk!!!

genProto.py for old versions, genProto.py for new versions with arm64 blobs.

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)
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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment