diff --git a/Il2CppInspector.Common/Cpp/MangledNameBuilder.cs b/Il2CppInspector.Common/Cpp/MangledNameBuilder.cs index 0a3e87b3..2f0a8c1d 100644 --- a/Il2CppInspector.Common/Cpp/MangledNameBuilder.cs +++ b/Il2CppInspector.Common/Cpp/MangledNameBuilder.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Text; +using System.Text.RegularExpressions; using Il2CppInspector.Reflection; namespace Il2CppInspector.Cpp; @@ -174,6 +175,8 @@ private void WriteGenericParams(TypeInfo[] generics) private void WriteIdentifier(string identifier) { + identifier = MangledRegex.Gcc.Replace(identifier, "_"); + _sb.Append(identifier.Length); _sb.Append(identifier); } @@ -201,4 +204,21 @@ private void WriteEnd() { _sb.Append('E'); } +} + +internal static partial class MangledRegex +{ + public static Regex Gcc + { + get + { + _gcc ??= GccNameRegex(); + return _gcc; + } + } + + private static Regex? _gcc; + + [GeneratedRegex("[^a-zA-Z0-9_]")] + public static partial Regex GccNameRegex(); } \ No newline at end of file diff --git a/Il2CppInspector.Common/Outputs/JSONMetadata.cs b/Il2CppInspector.Common/Outputs/JSONMetadata.cs index 89f4ddc6..f777aeaa 100644 --- a/Il2CppInspector.Common/Outputs/JSONMetadata.cs +++ b/Il2CppInspector.Common/Outputs/JSONMetadata.cs @@ -74,6 +74,9 @@ private void writeMethods(IEnumerable methods) { writeObject(() => { writeTypedFunctionName(method.MethodCodeAddress, method.CppFnPtrType.ToSignatureString(), method.ToMangledString()); writeDotNetSignature(method.Method); + + var groupString = $"{method.Method.DeclaringType.Assembly.ShortName}/{method.Method.DeclaringType.FullName.Replace(".", "/")}"; + writer.WriteString("group", groupString); }); } } diff --git a/Il2CppInspector.Common/Outputs/ScriptResources/Targets/Ghidra.py b/Il2CppInspector.Common/Outputs/ScriptResources/Targets/Ghidra.py index 5759ea02..81d05ca8 100644 --- a/Il2CppInspector.Common/Outputs/ScriptResources/Targets/Ghidra.py +++ b/Il2CppInspector.Common/Outputs/ScriptResources/Targets/Ghidra.py @@ -64,10 +64,11 @@ def script_prologue(status): if currentProgram.getExecutableFormat().endswith('(ELF)'): currentProgram.setImageBase(toAddr(%IMAGE_BASE%), True) -def script_epilogue(status): - pass +def script_epilogue(status): pass def get_script_directory(): return getSourceFile().getParentFile().toString() +def add_function_to_group(addr, group): pass + class StatusWrapper(BaseStatusHandler): pass \ No newline at end of file diff --git a/Il2CppInspector.Common/Outputs/ScriptResources/Targets/IDA.py b/Il2CppInspector.Common/Outputs/ScriptResources/Targets/IDA.py index c09b01b2..d7d228b2 100644 --- a/Il2CppInspector.Common/Outputs/ScriptResources/Targets/IDA.py +++ b/Il2CppInspector.Common/Outputs/ScriptResources/Targets/IDA.py @@ -7,19 +7,77 @@ import ida_nalt import ida_ida import ida_ua +import ida_segment try: # 7.7+ import ida_srclang IDACLANG_AVAILABLE = True + print("IDACLANG available") except ImportError: IDACLANG_AVAILABLE = False -import datetime +try: + import ida_dirtree + FOLDERS_AVAILABLE = True + print("folders available") +except ImportError: + FOLDERS_AVAILABLE = False + +cached_genflags = 0 +skip_make_function = False +func_dirtree = None + +def script_prologue(status): + global cached_genflags, skip_make_function, func_dirtree + # Disable autoanalysis + cached_genflags = ida_ida.inf_get_genflags() + ida_ida.inf_set_genflags(cached_genflags & ~ida_ida.INFFL_AUTO) + + # Unload type libraries we know to cause issues - like the c++ linux one + PLATFORMS = ["x86", "x64", "arm", "arm64"] + PROBLEMATIC_TYPELIBS = ["gnulnx"] + + for lib in PROBLEMATIC_TYPELIBS: + for platform in PLATFORMS: + ida_typeinf.del_til(f"{lib}_{platform}") + + # Set name mangling to GCC 3.x and display demangled as default + ida_ida.inf_set_demnames(ida_ida.DEMNAM_GCC3 | ida_ida.DEMNAM_NAME) + + status.update_step('Processing Types') + + if IDACLANG_AVAILABLE: + header_path = os.path.join(get_script_directory(), "%TYPE_HEADER_RELATIVE_PATH%") + ida_srclang.set_parser_argv("clang", "-x c++ -D_IDACLANG_=1") + ida_srclang.parse_decls_with_parser("clang", None, header_path, True) + else: + original_macros = ida_typeinf.get_c_macros() + ida_typeinf.set_c_macros(original_macros + ";_IDA_=1") + ida_typeinf.idc_parse_types(os.path.join(get_script_directory(), "%TYPE_HEADER_RELATIVE_PATH%"), ida_typeinf.PT_FILE) + ida_typeinf.set_c_macros(original_macros) + + # Skip make_function on Windows GameAssembly.dll files due to them predefining all functions through pdata which makes the method very slow + skip_make_function = ida_segment.get_segm_by_name(".pdata") is not None + if skip_make_function: + print(".pdata section found, skipping function boundaries") + + if FOLDERS_AVAILABLE: + func_dirtree = ida_dirtree.get_std_dirtree(ida_dirtree.DIRTREE_FUNCS) + + +def script_epilogue(status): + # Reenable auto-analysis + global cached_genflags + ida_ida.inf_set_genflags(cached_genflags) def set_name(addr, name): ida_name.set_name(addr, name, ida_name.SN_NOWARN | ida_name.SN_NOCHECK | ida_name.SN_FORCE) def make_function(start, end = None): + global skip_make_function + if skip_make_function: + return + ida_bytes.del_items(start, ida_bytes.DELIT_SIMPLE, 12) # Undefine x bytes which should hopefully be enough for the first instruction ida_ua.create_insn(start) # Create instruction at start if not ida_funcs.add_func(start, end if end is not None else ida_idaapi.BADADDR): # This fails if the function doesn't start with an instruction @@ -81,44 +139,24 @@ def set_header_comment(addr, comment): ida_funcs.set_func_cmt(func, comment, True) -cached_genflags = 0 - -def script_prologue(status): - global cached_genflags - # Disable autoanalysis - cached_genflags = ida_ida.inf_get_genflags() - ida_ida.inf_set_genflags(cached_genflags & ~ida_ida.INFFL_AUTO) - - # Unload type libraries we know to cause issues - like the c++ linux one - PLATFORMS = ["x86", "x64", "arm", "arm64"] - PROBLEMATIC_TYPELIBS = ["gnulnx"] - - for lib in PROBLEMATIC_TYPELIBS: - for platform in PLATFORMS: - ida_typeinf.del_til(f"{lib}_{platform}") +def get_script_directory(): + return os.path.dirname(os.path.realpath(__file__)) - # Set name mangling to GCC 3.x and display demangled as default - ida_ida.inf_set_demnames(ida_ida.DEMNAM_GCC3 | ida_ida.DEMNAM_NAME) +folders = [] +def add_function_to_group(addr, group): + global func_dirtree, folders + return - status.update_step('Processing Types') + if not FOLDERS_AVAILABLE: + return - if IDACLANG_AVAILABLE: - header_path = os.path.join(get_script_directory(), "%TYPE_HEADER_RELATIVE_PATH%") - ida_srclang.set_parser_argv("clang", "-x c++ -D_IDACLANG_=1") - ida_srclang.parse_decls_with_parser("clang", None, header_path, True) - else: - original_macros = ida_typeinf.get_c_macros() - ida_typeinf.set_c_macros(original_macros + ";_IDA_=1") - ida_typeinf.idc_parse_types(os.path.join(get_script_directory(), "%TYPE_HEADER_RELATIVE_PATH%"), ida_typeinf.PT_FILE) - ida_typeinf.set_c_macros(original_macros) + if group not in folders: + folders.append(group) + func_dirtree.mkdir(group) -def script_epilogue(status): - # Reenable auto-analysis - global cached_genflags - ida_ida.inf_set_genflags(cached_genflags) + name = ida_funcs.get_func_name(addr) + func_dirtree.rename(name, f"{group}/{name}") -def get_script_directory(): - return os.path.dirname(os.path.realpath(__file__)) class StatusHandler(BaseStatusHandler): def __init__(self): diff --git a/Il2CppInspector.Common/Outputs/ScriptResources/shared-main.py b/Il2CppInspector.Common/Outputs/ScriptResources/shared-main.py index 20c44e4e..5bcc02e2 100644 --- a/Il2CppInspector.Common/Outputs/ScriptResources/shared-main.py +++ b/Il2CppInspector.Common/Outputs/ScriptResources/shared-main.py @@ -8,6 +8,7 @@ def define_il_method(jsonDef): set_name(addr, jsonDef['name']) set_function_type(addr, jsonDef['signature']) set_header_comment(addr, jsonDef['dotNetSignature']) + add_function_to_group(addr, jsonDef['group']) def define_il_method_info(jsonDef): addr = parse_address(jsonDef) @@ -161,6 +162,8 @@ def process_json(jsonData, status): status.initialize() try: + start_time = datetime.datetime.now() + status.update_step("Running script prologue") script_prologue(status) @@ -173,5 +176,6 @@ def process_json(jsonData, status): script_epilogue(status) status.update_step('Script execution complete.') + print(f"Took: {datetime.datetime.now() - start_time}") except RuntimeError: pass finally: status.close() diff --git a/Il2CppInspector.Common/Outputs/ScriptResources/shared-preamble.py b/Il2CppInspector.Common/Outputs/ScriptResources/shared-preamble.py index dba3b853..7310b918 100644 --- a/Il2CppInspector.Common/Outputs/ScriptResources/shared-preamble.py +++ b/Il2CppInspector.Common/Outputs/ScriptResources/shared-preamble.py @@ -4,6 +4,7 @@ import json import os import sys +import datetime class BaseStatusHandler: def initialize(self): pass