* Initial commit of new UI c# component * Initial commit of new UI frontend component * target WinExe to hide console window in release mode, move ui exe into resources * force single file publishing and add initial gh workflow for publishing ui * fix workflow errors * update dependencies and remove cxxdemangler, as it was outdated * fix c# single file output due to invalid output path * smaller tweaks, hack around loops in cpp type layouting * process other queued exports even if one fails and show error message * add basic support for processing LC_DYLD_CHAINED_FIXUPS * ELF loading should not use the file offset for loading the dynamic section * fix symbol table loading in some modified elfs * add "start export" button on format selection screen, clear all toasts after selecting an export format * embed ui executable directly into c# assembly * only build tauri component in c# release builds * add il2cpp file (binary, metadata) export to advanced tab * fix and enable binary ninja fake string segment support * add support for metadata * unify logic for getting element type index * fix new ui not allowing script exports other than ida * new ui: clear out loaded binary if no IL2CPP images could be loaded * fix toAddr calls in ghidra script target * remove dependency on a section being named .text in loaded pe files * tweak symbol reading a bit and remove sht relocation reading * add initial support for required forward references in il2cpp types, also fix issues with type names clashing with il2cpp api types * reduce clang errors for header file, fix better array size struct, emit required forward definitions in header * expose forward definitions in AppModel, fix issue with method-only used types not being emitted * remove debug log line * fix spelling mistakes in gui outputs * fix il2cpp_array_size_t not being an actual type for later method definitions * change the default port for new ui dev to 5000 * show current version and hash in new ui footer * seperate redux ui impl into FrontendCore project * make inspector version a server api, split up output subtypes and tweak some option names * add redux CLI based on redux GUI output formats * replace all Console.WriteLine calls in core inspector with AnsiConsole calls * add workflow for new cli and add back old gui workflow * disable aot publish and enable single file for redux cli
128 lines
4.4 KiB
Python
128 lines
4.4 KiB
Python
# Ghidra-specific implementation
|
|
from ghidra.app.cmd.function import ApplyFunctionSignatureCmd
|
|
from ghidra.app.util.cparser.C import CParserUtils
|
|
from ghidra.program.model.data import ArrayDataType
|
|
from ghidra.program.model.symbol import SourceType
|
|
from ghidra.program.model.symbol import RefType
|
|
from ghidra.app.cmd.label import DemanglerCmd
|
|
from ghidra.app.services import DataTypeManagerService
|
|
from java.lang import Long
|
|
|
|
#try:
|
|
# from typing import TYPE_CHECKING
|
|
# if TYPE_CHECKING:
|
|
# from ..shared_base import BaseStatusHandler, BaseDisassemblerInterface, ScriptContext
|
|
# import json
|
|
# import os
|
|
# import sys
|
|
# from datetime import datetime
|
|
#except:
|
|
# pass
|
|
|
|
class GhidraDisassemblerInterface(BaseDisassemblerInterface):
|
|
supports_fake_string_segment = False
|
|
|
|
def _to_address(self, value):
|
|
return toAddr(Long(value))
|
|
|
|
def get_script_directory(self) -> str:
|
|
return getSourceFile().getParentFile().toString()
|
|
|
|
def on_start(self):
|
|
self.xrefs = currentProgram.getReferenceManager()
|
|
|
|
# Check that the user has parsed the C headers first
|
|
if len(getDataTypes('Il2CppObject')) == 0:
|
|
print('STOP! You must import the generated C header file (%TYPE_HEADER_RELATIVE_PATH%) before running this script.')
|
|
print('See https://github.com/djkaty/Il2CppInspector/blob/master/README.md#adding-metadata-to-your-ghidra-workflow for instructions.')
|
|
sys.exit()
|
|
|
|
# Ghidra sets the image base for ELF to 0x100000 for some reason
|
|
# https://github.com/NationalSecurityAgency/ghidra/issues/1020
|
|
# Make sure that the base address is 0
|
|
# Without this, Ghidra may not analyze the binary correctly and you will just waste your time
|
|
# If 0 doesn't work for you, replace it with the base address from the output of the CLI or GUI
|
|
if currentProgram.getExecutableFormat().endswith('(ELF)'):
|
|
currentProgram.setImageBase(self._to_address(0), True)
|
|
|
|
# Don't trigger decompiler
|
|
setAnalysisOption(currentProgram, "Call Convention ID", "false")
|
|
|
|
def on_finish(self):
|
|
pass
|
|
|
|
def define_function(self, address: int, end: int | None = None):
|
|
address = self._to_address(address)
|
|
# Don't override existing functions
|
|
fn = getFunctionAt(address)
|
|
if fn is None:
|
|
# Create new function if none exists
|
|
createFunction(address, None)
|
|
|
|
def define_data_array(self, address: int, type: str, count: int):
|
|
if type.startswith('struct '):
|
|
type = type[7:]
|
|
|
|
t = getDataTypes(type)[0]
|
|
a = ArrayDataType(t, count, t.getLength())
|
|
address = self._to_address(address)
|
|
removeDataAt(address)
|
|
createData(address, a)
|
|
|
|
def set_data_type(self, address: int, type: str):
|
|
if type.startswith('struct '):
|
|
type = type[7:]
|
|
|
|
try:
|
|
t = getDataTypes(type)[0]
|
|
address = self._to_address(address)
|
|
removeDataAt(address)
|
|
createData(address, t)
|
|
except:
|
|
print("Failed to set type: %s" % type)
|
|
|
|
def set_function_type(self, address: int, type: str):
|
|
typeSig = CParserUtils.parseSignature(DataTypeManagerService@None, currentProgram, type)
|
|
ApplyFunctionSignatureCmd(self._to_address(address), typeSig, SourceType.USER_DEFINED, False, True).applyTo(currentProgram)
|
|
|
|
def set_data_comment(self, address: int, cmt: str):
|
|
setEOLComment(self._to_address(address), cmt)
|
|
|
|
def set_function_comment(self, address: int, cmt: str):
|
|
setPlateComment(self._to_address(address), cmt)
|
|
|
|
def set_data_name(self, address: int, name: str):
|
|
address = self._to_address(address)
|
|
|
|
if len(name) > 2000:
|
|
print("Name length exceeds 2000 characters, skipping (%s)" % name)
|
|
return
|
|
|
|
if not name.startswith("_ZN"):
|
|
createLabel(address, name, True)
|
|
return
|
|
|
|
cmd = DemanglerCmd(address, name)
|
|
if not cmd.applyTo(currentProgram, monitor):
|
|
print(f"Failed to apply demangled name to {name} at {address} due {cmd.getStatusMsg()}, falling back to mangled")
|
|
createLabel(address, name, True)
|
|
|
|
def set_function_name(self, address: int, name: str):
|
|
return self.set_data_name(address, name)
|
|
|
|
def add_cross_reference(self, from_address: int, to_address: int):
|
|
self.xrefs.addMemoryReference(self._to_address(from_address), self._to_address(to_address), RefType.DATA, SourceType.USER_DEFINED, 0)
|
|
|
|
def import_c_typedef(self, type_def: str):
|
|
# 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
|
|
|
|
class GhidraStatusHandler(BaseStatusHandler):
|
|
pass
|
|
|
|
status = GhidraStatusHandler()
|
|
backend = GhidraDisassemblerInterface()
|
|
context = ScriptContext(backend, status)
|
|
context.process() |