Files
Cpp2IL/Cpp2IL.Core/KeyFunctionAddresses.cs

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;
}
}
}
}