Skip to content

Instantly share code, notes, and snippets.

@holly-hacker
Last active February 2, 2021 03:09
Show Gist options
  • Save holly-hacker/84bda48aa565c6214d1803a49fd6f5b7 to your computer and use it in GitHub Desktop.
Save holly-hacker/84bda48aa565c6214d1803a49fd6f5b7 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()
@iFailatGitHub
Copy link

Apparently Cytus 2 (at least v2.7) has the global-metadata.dat file either obfuscated or encrypted
Luckily, the key to decrypt the charts are the same, but do you know how to make deobfuscate/decrypt global-metadata.dat so it could be used for Il2CppDumper?

@holly-hacker
Copy link
Author

Sadly no, I haven't looked at Cytus for quite a while. Reversing the game shouldn't be too hard though, so you can try to do that yourself.

@harlos0517
Copy link

Apparently Cytus 2 (at least v2.7) has the global-metadata.dat file either obfuscated or encrypted
Luckily, the key to decrypt the charts are the same, but do you know how to make deobfuscate/decrypt global-metadata.dat so it could be used for Il2CppDumper?

Same question here, I've been figuring out and running the steps for the past 16 hours.
Still, very thankful for this tutorial.

@harlos0517
Copy link

Apparently Cytus 2 (at least v2.7) has the global-metadata.dat file either obfuscated or encrypted
Luckily, the key to decrypt the charts are the same, but do you know how to make deobfuscate/decrypt global-metadata.dat so it could be used for Il2CppDumper?

Hey, I found that file in Android\data\com.rayark.cytus2\files\il2cpp, I wonder if it would work?

@ddf420
Copy link

ddf420 commented Apr 8, 2020

Apparently Cytus 2 (at least v2.7) has the global-metadata.dat file either obfuscated or encrypted
Luckily, the key to decrypt the charts are the same, but do you know how to make deobfuscate/decrypt global-metadata.dat so it could be used for Il2CppDumper?

Hey, I found that file in Android\data\com.rayark.cytus2\files\il2cpp, I wonder if it would work?

Nope, it is not a valid Global-metadata.dat according to IL2CPPInspector

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment