diff --git a/Il2CppInspector.Common/Il2CppInspector.csproj b/Il2CppInspector.Common/Il2CppInspector.csproj index 3d897c5..bc34099 100644 --- a/Il2CppInspector.Common/Il2CppInspector.csproj +++ b/Il2CppInspector.Common/Il2CppInspector.csproj @@ -16,6 +16,7 @@ + diff --git a/Il2CppInspector.Common/Outputs/ScriptResources/ghidra-api.py b/Il2CppInspector.Common/Outputs/ScriptResources/ghidra-api.py new file mode 100644 index 0000000..7a98459 --- /dev/null +++ b/Il2CppInspector.Common/Outputs/ScriptResources/ghidra-api.py @@ -0,0 +1,55 @@ +# Ghidra-specific implementation +from ghidra.app.cmd.function import ApplyFunctionSignatureCmd +from ghidra.app.script import GhidraScriptUtil +from ghidra.app.util.cparser.C import CParserUtils +from ghidra.program.model.symbol import SourceType + +def SetName(addr, name): + createLabel(toAddr(addr), name, True) + +def MakeFunction(start, name=None): + addr = toAddr(start) + # Don't override existing functions + fn = getFunctionAt(addr) + if fn is not None and name is not None: + # Set existing function name if name available + fn.setName(name, SourceType.USER_DEFINED) + elif fn is None: + # Create new function if none exists + createFunction(addr, name) + # Set header comment if name available + if name is not None: + setPlateComment(addr, name) + +def DefineCode(code): + # Code declarations are not supported in Ghidra + # This only affects string literals for metadata version < 19 + # TODO: Replace with creating a DataType for enums + pass + +def SetFunctionType(addr, sig): + typeSig = CParserUtils.parseSignature(None, currentProgram, sig) + ApplyFunctionSignatureCmd(toAddr(addr), typeSig, SourceType.USER_DEFINED).applyTo(currentProgram) + +def SetType(addr, type): + if type.startswith('struct '): + type = type[7:] + + t = getDataTypes(type)[0] + addr = toAddr(addr) + removeDataAt(addr) + createData(addr, t) + +def SetComment(addr, text): + setEOLComment(toAddr(addr), text) + +def SetHeaderComment(addr, text): + setPlateComment(toAddr(addr), text) + +def CustomInitializer(): + # Do we need this for PE/MachO? + currentProgram.setImageBase(toAddr(0), True) + +def GetScriptDirectory(): + # Ghidra doesn't define __file__ so we have to iterate all the scripts + return next(iter(filter(lambda x: x.getName() == '%SCRIPTFILENAME%', GhidraScriptUtil.getAllScripts())), None).getParentFile().toString() diff --git a/Il2CppInspector.Common/Outputs/ScriptResources/ida-api.py b/Il2CppInspector.Common/Outputs/ScriptResources/ida-api.py new file mode 100644 index 0000000..7d7ffbe --- /dev/null +++ b/Il2CppInspector.Common/Outputs/ScriptResources/ida-api.py @@ -0,0 +1,39 @@ +# IDA-specific implementation +import idaapi + +def SetName(addr, name): + ret = idc.set_name(addr, name, SN_NOWARN | SN_NOCHECK) + if ret == 0: + new_name = name + '_' + str(addr) + ret = idc.set_name(addr, new_name, SN_NOWARN | SN_NOCHECK) + +def MakeFunction(start): + ida_funcs.add_func(start) + +def DefineCode(code): + idc.parse_decls(code) + +def SetFunctionType(addr, sig): + SetType(addr, sig) + +def SetType(addr, type): + ret = idc.SetType(addr, type) + if ret is None: + print('SetType(0x%x, %r) failed!' % (addr, type)) + +def SetComment(addr, text): + idc.set_cmt(addr, text, 1) + +def SetHeaderComment(addr, text): + SetComment(addr, text) + +def CustomInitializer(): + print('Processing Types') + + original_macros = ida_typeinf.get_c_macros() + ida_typeinf.set_c_macros(original_macros + ";_IDA_=1") + idc.parse_decls("%TYPE_HEADER_RELATIVE_PATH%", idc.PT_FILE) + ida_typeinf.set_c_macros(original_macros) + +def GetScriptDirectory(): + return os.path.dirname(os.path.realpath(__file__)) diff --git a/Il2CppInspector.Common/Outputs/ScriptResources/shared-main.py b/Il2CppInspector.Common/Outputs/ScriptResources/shared-main.py new file mode 100644 index 0000000..0a63b1f --- /dev/null +++ b/Il2CppInspector.Common/Outputs/ScriptResources/shared-main.py @@ -0,0 +1,113 @@ +# Shared interface +def ParseAddress(d): + return int(d['virtualAddress'], 0) + +def DefineILMethod(jsonDef): + addr = ParseAddress(jsonDef) + SetName(addr, jsonDef['name'].encode('utf-8')) + SetFunctionType(addr, jsonDef['signature'].encode('utf-8')) + SetHeaderComment(addr, jsonDef['dotNetSignature'].encode('utf-8')) + +def DefineILMethodInfo(jsonDef): + addr = ParseAddress(jsonDef) + SetName(addr, jsonDef['name'].encode('utf-8')) + SetType(addr, r'struct MethodInfo *') + SetComment(addr, jsonDef['dotNetSignature'].encode('utf-8')) + +def DefineCppFunction(jsonDef): + addr = ParseAddress(jsonDef) + SetName(addr, jsonDef['name'].encode('utf-8')) + SetFunctionType(addr, jsonDef['signature'].encode('utf-8')) + +def DefineString(jsonDef): + addr = ParseAddress(jsonDef) + SetName(addr, jsonDef['name'].encode('utf-8')) + SetType(addr, r'struct String *') + SetComment(addr, jsonDef['string'].encode('utf-8')) + +def DefineFieldFromJson(jsonDef): + DefineField(jsonDef['virtualAddress'], jsonDef['name'], jsonDef['type'], jsonDef['dotNetType']) + +def DefineField(addr, name, type, ilType = None): + addr = int(addr, 0) + SetName(addr, name.encode('utf-8')) + SetType(addr, type.encode('utf-8')) + if (ilType is not None): + SetComment(addr, ilType.encode('utf-8')) + +# Process JSON +def ProcessJSON(jsonData): + + # Method definitions + print('Processing method definitions') + for d in jsonData['methodDefinitions']: + DefineILMethod(d) + + # Constructed generic methods + print('Processing constructed generic methods') + for d in jsonData['constructedGenericMethods']: + DefineILMethod(d) + + # Custom attributes generators + print('Processing custom attributes generators') + for d in jsonData['customAttributesGenerators']: + DefineCppFunction(d) + + # Method.Invoke thunks + print('Processing Method.Invoke thunks') + for d in jsonData['methodInvokers']: + DefineCppFunction(d) + + # String literals for version >= 19 + print('Processing string literals') + if 'virtualAddress' in jsonData['stringLiterals'][0]: + for d in jsonData['stringLiterals']: + DefineString(d) + + # String literals for version < 19 + else: + litDecl = 'enum StringLiteralIndex {\n' + for d in jsonData['stringLiterals']: + litDecl += " " + d['name'].encode('utf-8') + ",\n" + litDecl += '};\n' + DefineCode(litDecl) + + # Il2CppClass (TypeInfo) pointers + print('Processing Il2CppClass (TypeInfo) pointers') + for d in jsonData['typeInfoPointers']: + DefineFieldFromJson(d) + + # Il2CppType (TypeRef) pointers + print('Processing Il2CppType (TypeRef) pointers') + for d in jsonData['typeRefPointers']: + DefineField(d['virtualAddress'], d['name'], r'struct Il2CppType *', d['dotNetType']) + + # MethodInfo pointers + print('Processing MethodInfo pointers') + for d in jsonData['methodInfoPointers']: + DefineILMethodInfo(d) + + # Function boundaries + print('Processing function boundaries') + for d in jsonData['functionAddresses']: + MakeFunction(int(d, 0)) + + # IL2CPP type metadata + print('Processing IL2CPP type metadata') + for d in jsonData['typeMetadata']: + DefineField(d['virtualAddress'], d['name'], d['type']) + + # IL2CPP function metadata + print('Processing IL2CPP function metadata') + for d in jsonData['functionMetadata']: + DefineCppFunction(d) + +# Entry point +print('Generated script file by Il2CppInspector - http://www.djkaty.com - https://github.com/djkaty') +CustomInitializer() + +with open(os.path.join(GetScriptDirectory(), "%JSON_METADATA_RELATIVE_PATH%"), "r") as jsonFile: + jsonData = json.load(jsonFile)['addressMap'] + ProcessJSON(jsonData) + +print('Script execution complete.') diff --git a/Il2CppInspector.Common/Outputs/ScriptResources/shared-preamble.py b/Il2CppInspector.Common/Outputs/ScriptResources/shared-preamble.py new file mode 100644 index 0000000..a77c19d --- /dev/null +++ b/Il2CppInspector.Common/Outputs/ScriptResources/shared-preamble.py @@ -0,0 +1,3 @@ +# Generated script file by Il2CppInspector - http://www.djkaty.com - https://github.com/djkaty +import json +import os