Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save harlos0517/1b94b2bedb00e2ac6343036232c4e04f to your computer and use it in GitHub Desktop.
Save harlos0517/1b94b2bedb00e2ac6343036232c4e04f to your computer and use it in GitHub Desktop.
Cytus2 chart dump
#r "$NuGet\Newtonsoft.Json\11.0.2\lib\net45\Newtonsoft.Json.dll"
using Newtonsoft.Json;
using Newtonsoft.Json.Bson;
using Newtonsoft.Json.Linq;
string path = @"C:\Users\PC1\Desktop\wdir\20181031-cytus2_3\scripts\output.bson";
var br = new BsonReader(File.OpenRead(path));
var js = new JsonSerializer();
JObject obj = (JObject)js.Deserialize(br);
obj.ToString()
string path = @"C:\Users\PC1\Desktop\wdir\20181031-cytus2_3\text\charts";
byte[] key = Convert.FromBase64String("<key_here>");
foreach (string file in Directory.GetFiles(path)) {
string fileName = Path.GetFileNameWithoutExtension(file);
fileName = fileName.Split('-')[0];
var bytes = File.ReadAllBytes(file);
var bytes2 = System.Security.Cryptography.Aes
.Create()
.CreateDecryptor(key, bytes.Skip(4).Take(16).ToArray())
.TransformFinalBlock(bytes, 20, bytes.Length - 20);
File.WriteAllBytes(Path.Combine(path, fileName + ".json"), bytes2);
}

Cytus 2 chart dump

Explains how to dump decrypted chart data from Cytus 2.

Requirements

  • Rooted android device (or emulator) capable of running GameGuardian
  • IDA Pro 7.0+
  • RoslynPad or LinqPad for .csx scripts

Steps

  1. Dump libil2cpp from memory (.so in apk is packed) and open it in IDA Pro. You can use something like GameGuardian for this. Keep in mind that there are 2 memory regions that make up the full binary.
  2. Run Il2CppDumper on the original .so file to get a python script that fixes symbol names. Modify the methods like this shown below and run in IDA Pro.
def SetString(addr, comm):
	global index
	name = "StringLiteral_" + str(index);
	if (comm.startswith("Rayark.Cytus2.") and "." not in comm[14:]):
		name = "Symbol_" + comm[14:]
	ret = idc.MakeNameEx(addr, name, SN_NOWARN)
	idc.MakeComm(addr, comm)
	index += 1

def SetMethod(addr, name):
	i = 0
	MakeFunction(addr)

	ret = idc.MakeNameEx(addr, name, SN_NOWARN)
	if ret == 0:
		new_name = name + '_' + str(addr)
		ret = idc.MakeNameEx(addr, str(new_name), SN_NOWARN)

def MakeFunction(start):
	if GetFunctionAttr(start, FUNCATTR_START) == 0xFFFFFFFF:
		idc.MakeFunction(start)
	else:
		pass # idc.SetFunctionEnd(start)
  1. Edit IDAPythonFindKey.py to use your path and run in IDA Pro.
  2. Get the ASSET key from the extracted data using bsonKeyExtract.csx and fill in its base64 representation in chartDecrypt.csx.
  3. Run chartDecrypt.csx after filling in the correct path.
import idautils
prefixes = ['Accedo','Adhaero','Horum','Imperium','Incredibilis','Influo','Lacuna','Luctus','Menapiorum','Molior','Navigatio','Obstinatus','Ordo','Peracto','Pollen','Quaeso']
def isValidName(name):
split = name.split("$$")
if len(split) != 2: return False
if (split[1] == ".ctor" or split[1] == ".cctor"): return False
anyPrefix = False
for x in prefixes:
if name.startswith(x) and name[len(x)].isupper() and sum(1 for c in split[0] if c.isupper()) == 3:
return True
return False
def findInvocations(name, depth):
ea = get_name_ea(BADADDR, name)
foundRecursive = False
li = []
for instr in idautils.FuncItems(ea):
oper = idc.GetOpnd(instr, 1)
if oper.startswith("(Symbol_"): # looking for (Symbol_InfluoMemberSaver - 2277D34h)[ebx]
partSymbol = oper[8:oper.index(" - ")]
symbol = next(x for x in allMethods if x.startswith(partSymbol))
# print "["+name+"] found symbol name", symbol, "in", oper
# print '\t'*depth, symbol
val = findInvocations(symbol, depth+1)
li.extend(val)
foundRecursive = True
if not foundRecursive:
# loop again, but look for last instance of 'mov byte ptr [esi+3Bh], 30h'-like
item = 0
for x in idautils.FuncItems(ea):
if idc.GetMnem(x) == "mov" and idc.GetOpType(x, 0) == o_displ and idc.GetOpType(x, 1) == o_imm:
item = x
# print "found", idc.GetDisasm(item), "in", name, "with depth", depth
# print '{0:03d}-{1:02x}'.format(idc.GetOperandValue(item, 0), idc.GetOperandValue(item, 1))
li.append((idc.GetOperandValue(item, 0), idc.GetOperandValue(item, 1)))
if len(li) == 0:
print "didn't find anything in", name
return li
## main code
print "getting all matching functions"
allMethods = list(GetFunctionName(x) for x in Functions() if isValidName(GetFunctionName(x))) # get all methods
print "found", len(allMethods)
li = []
toScan = ["ApplicationContext$$.cctor", "ApplicationContext$$_Init", "KeyStoreFactory$$Create"]
for scan in toScan:
print "starting to look for invocations in", scan
inv = findInvocations(scan, 0)
print "found", len(inv), "invocations"
li.extend(inv)
li = sorted(li)
# for x in li: print '{0:03d}-{1:02x}'.format(x[0], x[1])
y = bytearray([chr(x[1]) for x in li])
f = open('C:/Users/PC1/Desktop/wdir/20181031-cytus2_3/scripts/output.bson', 'wb')
f.write(y)
f.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment