mirror of
https://github.com/BepInEx/Cpp2IL.git
synced 2026-11-13 04:27:04 -10:00
276 lines
14 KiB
C#
276 lines
14 KiB
C#
using System;
|
|
using System.Linq;
|
|
using Cpp2IL.Core.Exceptions;
|
|
using Iced.Intel;
|
|
using LibCpp2IL;
|
|
using LibCpp2IL.PE;
|
|
|
|
namespace Cpp2IL.Core
|
|
{
|
|
public class KeyFunctionAddresses
|
|
{
|
|
public ulong il2cpp_codegen_initialize_method; //Either this
|
|
public ulong il2cpp_codegen_initialize_runtime_metadata; //Or this, are present, depending on metadata version, but not exported.
|
|
public ulong il2cpp_vm_metadatacache_initializemethodmetadata; //This is thunked from the above (but only pre-27?)
|
|
|
|
public ulong il2cpp_runtime_class_init_export; //Api function (exported)
|
|
public ulong il2cpp_runtime_class_init_actual; //Thunked from above
|
|
|
|
public ulong il2cpp_object_new; //Api Function (exported)
|
|
public ulong il2cpp_vm_object_new; //Thunked from above
|
|
public ulong il2cpp_codegen_object_new; //Thunked TO above
|
|
|
|
public ulong il2cpp_array_new_specific; //Api function (exported)
|
|
public ulong il2cpp_vm_array_new_specific; //Thunked from above
|
|
public ulong SzArrayNew; //Thunked TO above.
|
|
|
|
public ulong il2cpp_type_get_object; //Api function (exported)
|
|
public ulong il2cpp_vm_reflection_get_type_object; //Thunked from above
|
|
|
|
public ulong il2cpp_resolve_icall; //Api function (exported)
|
|
public ulong InternalCalls_Resolve; //Thunked from above.
|
|
|
|
public ulong il2cpp_string_new; //Api function (exported)
|
|
public ulong il2cpp_vm_string_new; //Thunked from above
|
|
|
|
public ulong il2cpp_value_box; //Api function (exported)
|
|
public ulong il2cpp_vm_object_box; //Thunked from above
|
|
|
|
public ulong il2cpp_raise_exception; //Api function (exported)
|
|
public ulong il2cpp_vm_exception_raise; //Thunked from above
|
|
public ulong il2cpp_codegen_raise_exception; //Thunked TO above. don't know real name.
|
|
|
|
public ulong il2cpp_object_is_inst; //TODO Re-find this and fix name
|
|
|
|
public ulong AddrPInvokeLookup; //TODO Re-find this and fix name
|
|
|
|
public static KeyFunctionAddresses Find()
|
|
{
|
|
if (LibCpp2IlMain.Binary?.InstructionSet != InstructionSet.X86_32 && LibCpp2IlMain.Binary?.InstructionSet != InstructionSet.X86_64)
|
|
throw new UnsupportedInstructionSetException();
|
|
|
|
var cppAssembly = LibCpp2IlMain.Binary!;
|
|
var ret = new KeyFunctionAddresses();
|
|
|
|
//Try to find System.Exception (should always be there)
|
|
TryGetInitMetadataFromException(ret);
|
|
|
|
//New Object
|
|
Logger.Verbose("\tLooking for Exported il2cpp_object_new function...");
|
|
ret.il2cpp_object_new = ((PE) LibCpp2IlMain.Binary!).GetVirtualAddressOfPeExportByName("il2cpp_object_new");
|
|
Logger.VerboseNewline($"Found at 0x{ret.il2cpp_object_new:X}");
|
|
|
|
if (ret.il2cpp_object_new != 0)
|
|
{
|
|
Logger.Verbose("\t\tMapping il2cpp_object_new to vm::Object::New...");
|
|
ret.il2cpp_vm_object_new = FindFunctionThisIsAThunkOf(ret.il2cpp_object_new, true);
|
|
Logger.VerboseNewline($"Found at 0x{ret.il2cpp_vm_object_new:X}");
|
|
}
|
|
|
|
if (ret.il2cpp_vm_object_new != 0)
|
|
{
|
|
Logger.Verbose("\t\tLooking for il2cpp_codegen_object_new as a thunk of vm::Object::New...");
|
|
ret.il2cpp_codegen_object_new = FindThunkFunction(ret.il2cpp_vm_object_new);
|
|
Logger.VerboseNewline($"Found at 0x{ret.il2cpp_codegen_object_new:X}");
|
|
}
|
|
|
|
//Type => Object
|
|
Logger.Verbose("\tLooking for Exported il2cpp_type_get_object function...");
|
|
ret.il2cpp_type_get_object = ((PE) LibCpp2IlMain.Binary!).GetVirtualAddressOfPeExportByName("il2cpp_type_get_object");
|
|
Logger.VerboseNewline($"Found at 0x{ret.il2cpp_type_get_object:X}");
|
|
|
|
if (ret.il2cpp_type_get_object != 0)
|
|
{
|
|
Logger.Verbose("\t\tMapping il2cpp_resolve_icall to Reflection::GetTypeObject...");
|
|
ret.il2cpp_vm_reflection_get_type_object = FindFunctionThisIsAThunkOf(ret.il2cpp_type_get_object);
|
|
Logger.VerboseNewline($"Found at 0x{ret.il2cpp_vm_reflection_get_type_object:X}");
|
|
}
|
|
|
|
//Resolve ICall
|
|
Logger.Verbose("\tLooking for Exported il2cpp_resolve_icall function...");
|
|
ret.il2cpp_resolve_icall = ((PE) LibCpp2IlMain.Binary!).GetVirtualAddressOfPeExportByName("il2cpp_resolve_icall");
|
|
Logger.VerboseNewline($"Found at 0x{ret.il2cpp_resolve_icall:X}");
|
|
|
|
if (ret.il2cpp_resolve_icall != 0)
|
|
{
|
|
Logger.Verbose("\t\tMapping il2cpp_resolve_icall to InternalCalls::Resolve...");
|
|
ret.InternalCalls_Resolve = FindFunctionThisIsAThunkOf(ret.il2cpp_resolve_icall);
|
|
Logger.VerboseNewline($"Found at 0x{ret.InternalCalls_Resolve:X}");
|
|
}
|
|
|
|
//New String
|
|
Logger.Verbose("\tLooking for Exported il2cpp_string_new function...");
|
|
ret.il2cpp_string_new = ((PE) LibCpp2IlMain.Binary!).GetVirtualAddressOfPeExportByName("il2cpp_string_new");
|
|
Logger.VerboseNewline($"Found at 0x{ret.il2cpp_string_new:X}");
|
|
|
|
if (ret.il2cpp_string_new != 0)
|
|
{
|
|
Logger.Verbose("\t\tMapping il2cpp_string_new to String::New...");
|
|
ret.il2cpp_vm_string_new = FindFunctionThisIsAThunkOf(ret.il2cpp_string_new);
|
|
Logger.VerboseNewline($"Found at 0x{ret.il2cpp_vm_string_new:X}");
|
|
}
|
|
|
|
//Box Value
|
|
Logger.Verbose("\tLooking for Exported il2cpp_value_box function...");
|
|
ret.il2cpp_value_box = ((PE) LibCpp2IlMain.Binary!).GetVirtualAddressOfPeExportByName("il2cpp_value_box");
|
|
Logger.VerboseNewline($"Found at 0x{ret.il2cpp_string_new:X}");
|
|
|
|
if (ret.il2cpp_value_box != 0)
|
|
{
|
|
Logger.Verbose("\t\tMapping il2cpp_value_box to Object::Box...");
|
|
ret.il2cpp_vm_object_box = FindFunctionThisIsAThunkOf(ret.il2cpp_value_box);
|
|
Logger.VerboseNewline($"Found at 0x{ret.il2cpp_vm_object_box:X}");
|
|
}
|
|
|
|
//Raise Exception
|
|
Logger.Verbose("\tLooking for exported il2cpp_raise_exception function...");
|
|
ret.il2cpp_raise_exception = ((PE) LibCpp2IlMain.Binary!).GetVirtualAddressOfPeExportByName("il2cpp_raise_exception");
|
|
Logger.VerboseNewline($"Found at 0x{ret.il2cpp_raise_exception:X}");
|
|
|
|
if (ret.il2cpp_raise_exception != 0)
|
|
{
|
|
Logger.Verbose("\t\tMapping il2cpp_raise_exception to il2cpp::vm::Exception::Raise...");
|
|
ret.il2cpp_vm_exception_raise = FindFunctionThisIsAThunkOf(ret.il2cpp_raise_exception, true);
|
|
Logger.VerboseNewline($"Found at 0x{ret.il2cpp_vm_exception_raise:X}");
|
|
}
|
|
|
|
if (ret.il2cpp_vm_exception_raise != 0)
|
|
{
|
|
Logger.Verbose("\t\tMapping il2cpp::vm::Exception::Raise to il2cpp_codegen_raise_exception...");
|
|
ret.il2cpp_codegen_raise_exception = FindThunkFunction(ret.il2cpp_vm_exception_raise, 4, ret.il2cpp_raise_exception);
|
|
Logger.VerboseNewline($"Found at 0x{ret.il2cpp_codegen_raise_exception:X}");
|
|
}
|
|
|
|
//Class Init
|
|
Logger.Verbose("\tLooking for exported il2cpp_runtime_class_init function...");
|
|
ret.il2cpp_runtime_class_init_export = ((PE) cppAssembly).GetVirtualAddressOfPeExportByName("il2cpp_runtime_class_init");
|
|
Logger.VerboseNewline($"Found at 0x{ret.il2cpp_runtime_class_init_export:X}");
|
|
|
|
if (ret.il2cpp_runtime_class_init_export != 0)
|
|
{
|
|
Logger.Verbose("\t\tMapping il2cpp_runtime_class_init to il2cpp:vm::Runtime::ClassInit...");
|
|
ret.il2cpp_runtime_class_init_actual = FindFunctionThisIsAThunkOf(ret.il2cpp_runtime_class_init_export);
|
|
Logger.VerboseNewline($"Found at 0x{ret.il2cpp_runtime_class_init_actual:X}");
|
|
}
|
|
|
|
//New array of fixed size
|
|
Logger.Verbose("\tLooking for exported il2cpp_array_new_specific function...");
|
|
ret.il2cpp_array_new_specific = ((PE) LibCpp2IlMain.Binary!).GetVirtualAddressOfPeExportByName("il2cpp_array_new_specific");
|
|
Logger.VerboseNewline($"Found at 0x{ret.il2cpp_array_new_specific:X}");
|
|
|
|
if (ret.il2cpp_array_new_specific != 0)
|
|
{
|
|
Logger.Verbose("\t\tMapping il2cpp_array_new_specific to vm::Array::NewSpecific...");
|
|
ret.il2cpp_vm_array_new_specific = FindFunctionThisIsAThunkOf(ret.il2cpp_array_new_specific);
|
|
Logger.VerboseNewline($"Found at 0x{ret.il2cpp_vm_array_new_specific:X}");
|
|
}
|
|
|
|
if (ret.il2cpp_vm_array_new_specific != 0)
|
|
{
|
|
Logger.Verbose("\t\tLooking for SzArrayNew as a thunk function proxying Array::NewSpecific...");
|
|
ret.SzArrayNew = FindThunkFunction(ret.il2cpp_vm_array_new_specific, 4, ret.il2cpp_array_new_specific);
|
|
Logger.VerboseNewline($"Found at 0x{ret.SzArrayNew:X}");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
private static void TryGetInitMetadataFromException(KeyFunctionAddresses ret)
|
|
{
|
|
//Exception.get_Message() - first call is either to codegen_initialize_method (< v27) or codegen_initialize_runtime_metadata
|
|
Logger.VerboseNewline("\tLooking for Type System.Exception, Method get_Message...");
|
|
|
|
var type = Utils.TryLookupTypeDefKnownNotGeneric("System.Exception");
|
|
if (type != null)
|
|
{
|
|
Logger.VerboseNewline("\t\tType Located. Ensuring method exists...");
|
|
var targetMethod = type.Methods.FirstOrDefault(m => m.Name == "get_Message");
|
|
if (targetMethod != null) //Check struct contains valid data
|
|
{
|
|
Logger.VerboseNewline("\t\tTarget Method Located. Taking first CALL as the (version-specific) metadata initialization function...");
|
|
|
|
var disasm = LibCpp2ILUtils.DisassembleBytesNew(LibCpp2IlMain.Binary!.is32Bit, targetMethod.AsUnmanaged().CppMethodBodyBytes, targetMethod.AsUnmanaged().MethodPointer);
|
|
var calls = disasm.Where(i => i.Mnemonic == Mnemonic.Call).ToList();
|
|
|
|
if (LibCpp2IlMain.MetadataVersion < 27)
|
|
{
|
|
ret.il2cpp_codegen_initialize_method = calls.First().NearBranchTarget;
|
|
Logger.VerboseNewline($"\t\til2cpp_codegen_initialize_method => 0x{ret.il2cpp_codegen_initialize_method:X}");
|
|
}
|
|
else
|
|
{
|
|
ret.il2cpp_codegen_initialize_runtime_metadata = calls.First().NearBranchTarget;
|
|
Logger.VerboseNewline($"\t\til2cpp_codegen_initialize_runtime_metadata => 0x{ret.il2cpp_codegen_initialize_runtime_metadata:X}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static ulong FindThunkFunction(ulong addr, uint maxBytesBack = 0, params ulong[] addressesToIgnore)
|
|
{
|
|
//Disassemble .text
|
|
var allInstructions = ((PE) LibCpp2IlMain.Binary!).DisassembleTextSection();
|
|
|
|
//Find all jumps to the target address
|
|
var matchingJmps = allInstructions.Where(i => i.Mnemonic == Mnemonic.Jmp || i.Mnemonic == Mnemonic.Call && i.NearBranchTarget == addr).ToList();
|
|
|
|
foreach (var matchingJmp in matchingJmps)
|
|
{
|
|
if (addressesToIgnore.Contains(matchingJmp.IP)) continue;
|
|
|
|
//Find this instruction in the raw file
|
|
var offsetInPe = (ulong) LibCpp2IlMain.Binary.MapVirtualAddressToRaw(matchingJmp.IP);
|
|
if (offsetInPe == 0 || offsetInPe == (ulong) (LibCpp2IlMain.Binary!.RawLength - 1))
|
|
continue;
|
|
|
|
//get next and previous bytes
|
|
var previousByte = LibCpp2IlMain.Binary!.GetByteAtRawAddress(offsetInPe - 1);
|
|
var nextByte = LibCpp2IlMain.Binary!.GetByteAtRawAddress(offsetInPe + (ulong) matchingJmp.Length);
|
|
|
|
//Double-cc = thunk
|
|
if (previousByte == 0xCC && nextByte == 0xCC)
|
|
{
|
|
return matchingJmp.IP;
|
|
}
|
|
|
|
if (nextByte == 0xCC && maxBytesBack > 0)
|
|
{
|
|
for (ulong backtrack = 1; backtrack < maxBytesBack && offsetInPe - backtrack > 0; backtrack++)
|
|
{
|
|
if (addressesToIgnore.Contains(matchingJmp.IP - (backtrack - 1)))
|
|
//Move to next jmp
|
|
break;
|
|
|
|
if (LibCpp2IlMain.Binary!.GetByteAtRawAddress(offsetInPe - backtrack) == 0xCC)
|
|
return matchingJmp.IP - (backtrack - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private static ulong FindFunctionThisIsAThunkOf(ulong thunkPtr, bool prioritiseCall = false)
|
|
{
|
|
var instructions = Utils.GetMethodBodyAtVirtAddressNew(thunkPtr, true);
|
|
|
|
try
|
|
{
|
|
var target = prioritiseCall ? Mnemonic.Call : Mnemonic.Jmp;
|
|
var matchingCall = instructions.FirstOrDefault(i => i.Mnemonic == target);
|
|
|
|
if (matchingCall.Mnemonic == Mnemonic.INVALID)
|
|
{
|
|
target = target == Mnemonic.Call ? Mnemonic.Jmp : Mnemonic.Call;
|
|
matchingCall = instructions.First(i => i.Mnemonic == target);
|
|
}
|
|
|
|
return matchingCall.NearBranchTarget;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
} |