# @runtime PyGhidra # ^-- required for ghidra, ignored by all others # Generated script file by Il2CppInspectorRedux - https://github.com/LukeFZ (Original Il2CppInspector by http://www.djkaty.com - https://github.com/djkaty) # Target Unity version: %TARGET_UNITY_VERSION% import json import os from datetime import datetime from typing import Union import abc class BaseStatusHandler(abc.ABC): def initialize(self): pass def shutdown(self): pass def update_step(self, name: str, max_items: int = 0): print(name) def update_progress(self, progress: int = 1): pass def was_cancelled(self): return False class BaseDisassemblerInterface(abc.ABC): supports_fake_string_segment: bool = False @abc.abstractmethod def get_script_directory(self) -> str: return "" @abc.abstractmethod def on_start(self): pass @abc.abstractmethod def on_finish(self): pass @abc.abstractmethod def define_function(self, address: int, end: Union[int, None] = None): pass @abc.abstractmethod def define_data_array(self, address: int, type: str, count: int): pass @abc.abstractmethod def set_data_type(self, address: int, type: str): pass @abc.abstractmethod def set_function_type(self, address: int, type: str): pass @abc.abstractmethod def set_data_comment(self, address: int, cmt: str): pass @abc.abstractmethod def set_function_comment(self, address: int, cmt: str): pass @abc.abstractmethod def set_data_name(self, address: int, name: str): pass @abc.abstractmethod def set_function_name(self, address: int, name: str): pass @abc.abstractmethod def add_cross_reference(self, from_address: int, to_address: int): pass @abc.abstractmethod def import_c_typedef(self, type_def: str): pass # optional def add_function_to_group(self, address: int, group: str): pass def cache_function_types(self, function_types: list[str]): pass # only required if supports_fake_string_segment == True def create_fake_segment(self, name: str, size: int) -> int: return 0 def write_string(self, address: int, value: str) -> int: return 0 def write_address(self, address: int, value: int): pass class ScriptContext: _backend: BaseDisassemblerInterface _status: BaseStatusHandler def __init__( self, backend: BaseDisassemblerInterface, status: BaseStatusHandler ) -> None: self._backend = backend self._status = status def from_hex(self, addr: str): return int(addr, 0) def parse_address(self, d: dict): return self.from_hex(d["virtualAddress"]) def define_il_method(self, definition: dict): addr = self.parse_address(definition) self._backend.set_function_name(addr, definition["name"]) self._backend.set_function_type(addr, definition["signature"]) self._backend.set_function_comment(addr, definition["dotNetSignature"]) self._backend.add_function_to_group(addr, definition["group"]) def define_il_method_info(self, definition: dict): addr = self.parse_address(definition) self._backend.set_data_type(addr, r"struct MethodInfo *") self._backend.set_data_name(addr, definition["name"]) self._backend.set_data_comment(addr, definition["dotNetSignature"]) if "methodAddress" in definition: method_addr = self.from_hex(definition["methodAddress"]) self._backend.add_cross_reference(method_addr, addr) def define_cpp_function(self, definition: dict): addr = self.parse_address(definition) self._backend.set_function_name(addr, definition["name"]) self._backend.set_function_type(addr, definition["signature"]) def define_string(self, definition: dict): addr = self.parse_address(definition) self._backend.set_data_type(addr, r"struct String *") self._backend.set_data_name(addr, definition["name"]) self._backend.set_data_comment(addr, definition["string"]) def define_field( self, addr: str, name: str, type: str, il_type: Union[str, None] = None ): address = self.from_hex(addr) self._backend.set_data_type(address, type) self._backend.set_data_name(address, name) if il_type is not None: self._backend.set_data_comment(address, il_type) def define_field_from_json(self, definition: dict): self.define_field( definition["virtualAddress"], definition["name"], definition["type"], definition["dotNetType"], ) def define_array(self, definition: dict): addr = self.parse_address(definition) self._backend.define_data_array( addr, definition["type"], int(definition["count"]) ) self._backend.set_data_name(addr, definition["name"]) def define_field_with_value(self, definition: dict): addr = self.parse_address(definition) self._backend.set_data_name(addr, definition["name"]) self._backend.set_data_comment(addr, definition["value"]) def process_metadata(self, metadata: dict): # Function boundaries function_addresses = metadata["functionAddresses"] function_addresses.sort() count = len(function_addresses) self._status.update_step("Processing function boundaries", count) for i in range(count): start = self.from_hex(function_addresses[i]) if start == 0: self._status.update_progress() continue end = self.from_hex(function_addresses[i + 1]) if i + 1 != count else None self._backend.define_function(start, end) self._status.update_progress() # Method definitions self._status.update_step( "Processing method definitions", len(metadata["methodDefinitions"]) ) self._backend.cache_function_types( [x["signature"] for x in metadata["methodDefinitions"]] ) for d in metadata["methodDefinitions"]: self.define_il_method(d) self._status.update_progress() # Constructed generic methods self._status.update_step( "Processing constructed generic methods", len(metadata["constructedGenericMethods"]), ) self._backend.cache_function_types( [x["signature"] for x in metadata["constructedGenericMethods"]] ) for d in metadata["constructedGenericMethods"]: self.define_il_method(d) self._status.update_progress() # Custom attributes generators self._status.update_step( "Processing custom attributes generators", len(metadata["customAttributesGenerators"]), ) self._backend.cache_function_types( [x["signature"] for x in metadata["customAttributesGenerators"]] ) for d in metadata["customAttributesGenerators"]: self.define_cpp_function(d) self._status.update_progress() # Method.Invoke thunks self._status.update_step( "Processing Method.Invoke thunks", len(metadata["methodInvokers"]) ) self._backend.cache_function_types( [x["signature"] for x in metadata["methodInvokers"]] ) for d in metadata["methodInvokers"]: self.define_cpp_function(d) self._status.update_progress() # String literals for version >= 19 if "virtualAddress" in metadata["stringLiterals"][0]: self._status.update_step( "Processing string literals (V19+)", len(metadata["stringLiterals"]) ) if self._backend.supports_fake_string_segment: total_string_length = 0 for d in metadata["stringLiterals"]: total_string_length += len(d["string"]) + 1 aligned_length = total_string_length + ( 4096 - (total_string_length % 4096) ) segment_base = self._backend.create_fake_segment( ".fake_strings", aligned_length ) current_string_address = segment_base for d in metadata["stringLiterals"]: self.define_string(d) ref_addr = self.parse_address(d) written_string_length = self._backend.write_string( current_string_address, d["string"] ) self._backend.set_data_type(ref_addr, r"const char* const") self._backend.write_address(ref_addr, current_string_address) current_string_address += written_string_length self._status.update_progress() else: for d in metadata["stringLiterals"]: self.define_string(d) self._status.update_progress() # String literals for version < 19 else: self._status.update_step("Processing string literals (pre-V19)") litDecl = "enum StringLiteralIndex {\n" for d in metadata["stringLiterals"]: litDecl += " " + d["name"] + ",\n" litDecl += "};\n" self._backend.import_c_typedef(litDecl) # Il2CppClass (TypeInfo) pointers self._status.update_step( "Processing Il2CppClass (TypeInfo) pointers", len(metadata["typeInfoPointers"]), ) for d in metadata["typeInfoPointers"]: self.define_field_from_json(d) self._status.update_progress() # Il2CppType (TypeRef) pointers self._status.update_step( "Processing Il2CppType (TypeRef) pointers", len(metadata["typeRefPointers"]) ) for d in metadata["typeRefPointers"]: self.define_field( d["virtualAddress"], d["name"], r"struct Il2CppType *", d["dotNetType"] ) self._status.update_progress() # MethodInfo pointers self._status.update_step( "Processing MethodInfo pointers", len(metadata["methodInfoPointers"]) ) for d in metadata["methodInfoPointers"]: self.define_il_method_info(d) self._status.update_progress() # FieldInfo pointers, add the contents as a comment self._status.update_step( "Processing FieldInfo pointers", len(metadata["fields"]) ) for d in metadata["fields"]: self.define_field_with_value(d) self._status.update_progress() # FieldRva pointers, add the contents as a comment self._status.update_step( "Processing FieldRva pointers", len(metadata["fieldRvas"]) ) for d in metadata["fieldRvas"]: self.define_field_with_value(d) self._status.update_progress() # IL2CPP type metadata self._status.update_step( "Processing IL2CPP type metadata", len(metadata["typeMetadata"]) ) for d in metadata["typeMetadata"]: self.define_field(d["virtualAddress"], d["name"], d["type"]) # IL2CPP function metadata self._status.update_step( "Processing IL2CPP function metadata", len(metadata["functionMetadata"]) ) for d in metadata["functionMetadata"]: self.define_cpp_function(d) # IL2CPP array metadata self._status.update_step( "Processing IL2CPP array metadata", len(metadata["arrayMetadata"]) ) for d in metadata["arrayMetadata"]: self.define_array(d) # IL2CPP API functions self._status.update_step( "Processing IL2CPP API functions", len(metadata["apis"]) ) self._backend.cache_function_types([x["signature"] for x in metadata["apis"]]) for d in metadata["apis"]: self.define_cpp_function(d) def process(self): self._status.initialize() try: start_time = datetime.now() self._status.update_step("Running script prologue") self._backend.on_start() metadata_path = os.path.join( self._backend.get_script_directory(), "%JSON_METADATA_RELATIVE_PATH%" ) with open(metadata_path, "r") as f: self._status.update_step("Loading JSON metadata") metadata = json.load(f)["addressMap"] self.process_metadata(metadata) self._status.update_step("Running script epilogue") self._backend.on_finish() self._status.update_step("Script execution complete.") end_time = datetime.now() print(f"Took: {end_time - start_time}") except RuntimeError: pass finally: self._status.shutdown()