diff --git a/Cpp2IL/Analysis/ASMAnalyzer.cs b/Cpp2IL/Analysis/ASMAnalyzer.cs index c0470e31..2aff8d43 100644 --- a/Cpp2IL/Analysis/ASMAnalyzer.cs +++ b/Cpp2IL/Analysis/ASMAnalyzer.cs @@ -107,7 +107,7 @@ internal AsmDumper(MethodDefinition methodDefinition, CppMethodData method, ulon _cppAssembly = cppAssembly; //Pass 0: Disassemble - _instructions = LibCpp2ILUtils.DisassembleBytes(LibCpp2IlMain.ThePe.is32Bit, method.MethodBytes); + _instructions = Utils.DisassembleBytes(LibCpp2IlMain.ThePe.is32Bit, method.MethodBytes); } private void TaintMethod(TaintReason reason) @@ -154,7 +154,7 @@ private List TrimOutIl2CppCrap(List instructions) try { //Single call to the bailout, sometimes left over at the end of a function - if (insn.Mnemonic == ud_mnemonic_code.UD_Icall && LibCpp2ILUtils.GetJumpTarget(insn, _methodStart + insn.PC) == _keyFunctionAddresses.AddrBailOutFunction) + if (insn.Mnemonic == ud_mnemonic_code.UD_Icall && Utils.GetJumpTarget(insn, _methodStart + insn.PC) == _keyFunctionAddresses.AddrBailOutFunction) continue; } catch (Exception) @@ -431,7 +431,7 @@ private void CheckForSingleOpInstruction(Instruction instruction) break; case ud_mnemonic_code.UD_Ijmp when instruction.Operands[0].Type != ud_type.UD_OP_REG: { - var jumpTarget = LibCpp2ILUtils.GetJumpTarget(instruction, instruction.PC + _methodStart); + var jumpTarget = Utils.GetJumpTarget(instruction, instruction.PC + _methodStart); if (SharedState.MethodsByAddress.ContainsKey(jumpTarget)) { //Call a managed function @@ -440,13 +440,13 @@ private void CheckForSingleOpInstruction(Instruction instruction) } break; } - case ud_mnemonic_code.UD_Icall when instruction.Operands[0].Type == ud_type.UD_OP_MEM && LibCpp2ILUtils.GetOperandMemoryOffset(instruction.Operands[0]) > 0x128 && operand is ConstantDefinition checkForVirtualCallCons && checkForVirtualCallCons.Value is Il2CppClassIdentifier checkForVirtualCallClassPtr: + case ud_mnemonic_code.UD_Icall when instruction.Operands[0].Type == ud_type.UD_OP_MEM && Utils.GetOperandMemoryOffset(instruction.Operands[0]) > 0x128 && operand is ConstantDefinition checkForVirtualCallCons && checkForVirtualCallCons.Value is Il2CppClassIdentifier checkForVirtualCallClassPtr: //Virtual method call _analysis.Actions.Add(new CallVirtualMethodAction(_analysis, instruction)); break; case ud_mnemonic_code.UD_Icall when instruction.Operands[0].Type != ud_type.UD_OP_REG: { - var jumpTarget = LibCpp2ILUtils.GetJumpTarget(instruction, instruction.PC + _methodStart); + var jumpTarget = Utils.GetJumpTarget(instruction, instruction.PC + _methodStart); if (jumpTarget == _keyFunctionAddresses.AddrBailOutFunction) { @@ -533,8 +533,8 @@ private void CheckForTwoOpInstruction(Instruction instruction) var op0 = _analysis.GetOperandInRegister(r0); var op1 = _analysis.GetOperandInRegister(r1); - var offset0 = LibCpp2ILUtils.GetOffsetFromMemoryAccess(instruction, instruction.Operands[0]); - var offset1 = LibCpp2ILUtils.GetOffsetFromMemoryAccess(instruction, instruction.Operands[1]); + var offset0 = Utils.GetOffsetFromMemoryAccess(instruction, instruction.Operands[0]); + var offset1 = Utils.GetOffsetFromMemoryAccess(instruction, instruction.Operands[1]); var type0 = instruction.Operands[0].Type; var type1 = instruction.Operands[1].Type; @@ -565,7 +565,7 @@ private void CheckForTwoOpInstruction(Instruction instruction) case ud_mnemonic_code.UD_Imov when type1 == ud_type.UD_OP_MEM && (offset0 == 0 || r0 == "rsp") && r1 == "rip": { //Global to stack or reg. Could be metadata literal, non-metadata literal, metadata type, or metadata method. - var globalAddress = _methodStart + LibCpp2ILUtils.GetOffsetFromMemoryAccess(instruction, instruction.Operands[1]); + var globalAddress = _methodStart + Utils.GetOffsetFromMemoryAccess(instruction, instruction.Operands[1]); if (SharedState.GlobalsByOffset.TryGetValue(globalAddress, out var global)) { //Have a global here. @@ -587,7 +587,7 @@ private void CheckForTwoOpInstruction(Instruction instruction) } else { - var potentialLiteral = Utils.TryGetLiteralAt(LibCpp2IlMain.ThePe, LibCpp2ILUtils.GetOffsetFromMemoryAccess(instruction, instruction.Operands[1])); + var potentialLiteral = Utils.TryGetLiteralAt(LibCpp2IlMain.ThePe, Utils.GetOffsetFromMemoryAccess(instruction, instruction.Operands[1])); if (potentialLiteral != null) { if (r0 == "rsp") @@ -970,7 +970,7 @@ private List DetectPotentialLoops(List instructions) switch (operand.Type) { case ud_type.UD_OP_REG: - case ud_type.UD_OP_MEM when LibCpp2ILUtils.GetOperandMemoryOffset(operand) == 0: + case ud_type.UD_OP_MEM when Utils.GetOperandMemoryOffset(operand) == 0: _registerAliases.TryGetValue(sourceReg, out var alias); _registerContents.TryGetValue(sourceReg, out constant); _registerTypes.TryGetValue(sourceReg, out objectType); @@ -997,7 +997,7 @@ private List DetectPotentialLoops(List instructions) if (_registerContents.ContainsKey(sourceReg) && (_registerTypes.ContainsKey(sourceReg) && _registerTypes[sourceReg]?.IsArray == true || _registerAliases.ContainsKey(sourceReg) && _registerContents[sourceReg] is ArrayData)) { _typeDump.Append($" ; - probably an array read as we have an aliased arraydata in reg {sourceReg}"); - var offset = LibCpp2ILUtils.GetOperandMemoryOffset(operand); + var offset = Utils.GetOperandMemoryOffset(operand); _typeDump.Append($" ; offset is 0x{offset:X}"); @@ -1041,7 +1041,7 @@ private List DetectPotentialLoops(List instructions) //Check for global if (operand.Base == ud_type.UD_R_RIP) { - var globalAddr = LibCpp2ILUtils.GetOffsetFromMemoryAccess(i, operand) + _methodStart; + var globalAddr = Utils.GetOffsetFromMemoryAccess(i, operand) + _methodStart; if (SharedState.GlobalsByOffset.TryGetValue(globalAddr, out var glob)) { if (glob.IdentifierType == GlobalIdentifier.Type.LITERAL) @@ -1071,13 +1071,13 @@ private List DetectPotentialLoops(List instructions) constant = operand.LvalUDWord; break; case ud_type.UD_OP_IMM: - var imm = LibCpp2ILUtils.GetImmediateValue(i, operand); + var imm = Utils.GetImmediateValue(i, operand); if (imm == 0) objectName = "0 / null"; else objectName = $"0x{imm:X}"; objectType = LongReference; - constant = LibCpp2ILUtils.GetImmediateValue(i, operand); + constant = Utils.GetImmediateValue(i, operand); break; } @@ -1089,7 +1089,7 @@ private List DetectPotentialLoops(List instructions) if (operand.Type != ud_type.UD_OP_MEM || operand.Base == ud_type.UD_R_RIP) return null; var sourceReg = Utils.GetRegisterName(operand); - var offset = LibCpp2ILUtils.GetOperandMemoryOffset(operand); + var offset = Utils.GetOperandMemoryOffset(operand); if (offset == 0 && _registerContents.ContainsKey(sourceReg) && _registerContents[sourceReg] is FieldDefinition fld) //This register contains a field definition. Return it @@ -1315,13 +1315,13 @@ private void CheckForArithmeticOperations(Instruction instruction) if (instruction.Operands[1].Type == ud_type.UD_OP_IMM) { - var immediateValue = LibCpp2ILUtils.GetImmediateValue(instruction, instruction.Operands[1]); + var immediateValue = Utils.GetImmediateValue(instruction, instruction.Operands[1]); constantValue = immediateValue; _typeDump.Append($" ; - Arithmetic operation between {name} and constant value: {constantValue}"); } else { - var virtualAddress = LibCpp2ILUtils.GetOffsetFromMemoryAccess(instruction, instruction.Operands[1]) + _methodStart; + var virtualAddress = Utils.GetOffsetFromMemoryAccess(instruction, instruction.Operands[1]) + _methodStart; _typeDump.Append($" ; - Arithmetic operation between {name} and constant stored at 0x{virtualAddress:X}"); long rawAddr = _cppAssembly.MapVirtualAddressToRaw(virtualAddress); var constantBytes = _cppAssembly.raw.SubArray((int) rawAddr, 4); @@ -1495,7 +1495,7 @@ private void CheckForFieldArrayAndStackReads(Instruction instruction) //Special case: Stack pointers if (instruction.Operands[1].Base == ud_type.UD_R_RSP) { - var stackOffset = LibCpp2ILUtils.GetOperandMemoryOffset(instruction.Operands[1]); + var stackOffset = Utils.GetOperandMemoryOffset(instruction.Operands[1]); if (instruction.Mnemonic == ud_mnemonic_code.UD_Ilea) { //Register now has a pointer to the stack @@ -1541,7 +1541,7 @@ private void CheckForFieldArrayAndStackReads(Instruction instruction) } //Klass pointers - if (LibCpp2ILUtils.GetOperandMemoryOffset(instruction.Operands[1]) is {} offset && offset != 0 && _registerContents.TryGetValue(Utils.GetRegisterName(instruction.Operands[1]), out var con) && con is Il2CppClassIdentifier klass) + if (Utils.GetOperandMemoryOffset(instruction.Operands[1]) is {} offset && offset != 0 && _registerContents.TryGetValue(Utils.GetRegisterName(instruction.Operands[1]), out var con) && con is Il2CppClassIdentifier klass) { if (offset >= 0x128) { @@ -1595,7 +1595,7 @@ private void CheckForFieldArrayAndStackReads(Instruction instruction) var sourceReg = Utils.GetRegisterName(instruction.Operands[1]); - if (LibCpp2ILUtils.GetOperandMemoryOffset(instruction.Operands[1]) == 0) + if (Utils.GetOperandMemoryOffset(instruction.Operands[1]) == 0) { //Probably a klass pointer read, handled in checkformoveintoregister return; @@ -1614,7 +1614,7 @@ private void CheckForFieldArrayAndStackReads(Instruction instruction) } else if (field == null) { - var fieldReadOffset = LibCpp2ILUtils.GetOperandMemoryOffset(instruction.Operands[1]); + var fieldReadOffset = Utils.GetOperandMemoryOffset(instruction.Operands[1]); //For some godawful reason the compiler sometimes uses LEA on a known constant and offset to get another constant (e.g. LEA dest, [regWithNullPtr+0x01] => put 1 into dest; LEA dest, [regWith1-0x02] => put -1 into dest; etc) if (instruction.Mnemonic == ud_mnemonic_code.UD_Ilea && _registerContents.ContainsKey(sourceReg) && _registerContents[sourceReg].GetType().IsPrimitive) @@ -1684,7 +1684,7 @@ private void CheckForFieldArrayAndStackReads(Instruction instruction) } else if (_registerContents.TryGetValue(sourceReg, out var cons) && (cons is ArrayData || typeInReg.IsArray)) { - var arrayOffset = LibCpp2ILUtils.GetOperandMemoryOffset(instruction.Operands[1]); + var arrayOffset = Utils.GetOperandMemoryOffset(instruction.Operands[1]); _typeDump.Append(" ; - array operation"); @@ -1902,12 +1902,12 @@ private void CheckForMoveIntoRegister(Instruction instruction) var destReg = Utils.GetRegisterName(instruction.Operands[0]); var sourceReg = Utils.GetRegisterName(instruction.Operands[1]); var isStack = destReg == "rsp"; - var stackAddr = isStack ? LibCpp2ILUtils.GetOperandMemoryOffset(instruction.Operands[0]) : -1; + var stackAddr = isStack ? Utils.GetOperandMemoryOffset(instruction.Operands[0]) : -1; //Ok now decide what to do based on what the second register is switch (instruction.Operands[1].Type) { - case ud_type.UD_OP_MEM when LibCpp2ILUtils.GetOperandMemoryOffset(instruction.Operands[1]) == 0: + case ud_type.UD_OP_MEM when Utils.GetOperandMemoryOffset(instruction.Operands[1]) == 0: var (alias, type, constantVal) = GetDetailsOfReferencedObject(instruction.Operands[1], instruction); if (type != null && !type.IsPrimitive) @@ -2008,7 +2008,7 @@ private void CheckForMoveIntoRegister(Instruction instruction) //TODO: do we ever actually do this with the stack? Might do via a reg first //Reading either a global or a string literal into the register - var offset = LibCpp2ILUtils.GetOffsetFromMemoryAccess(instruction, instruction.Operands[1]); + var offset = Utils.GetOffsetFromMemoryAccess(instruction, instruction.Operands[1]); if (offset == 0) break; var addr = _methodStart + offset; _typeDump.Append($"; - Read on memory location 0x{addr:X}"); @@ -2112,7 +2112,7 @@ private void CheckForMoveIntoRegister(Instruction instruction) break; case ud_type.UD_OP_IMM: //Immediate - constant - _registerContents[destReg] = LibCpp2ILUtils.GetImmediateValue(instruction, instruction.Operands[1]); + _registerContents[destReg] = Utils.GetImmediateValue(instruction, instruction.Operands[1]); _registerAliases[destReg] = _registerContents[destReg].ToString(); _registerTypes[destReg] = LongReference; _typeDump.Append($" ; - Moves constant of value {_registerContents[destReg]} into {destReg}"); @@ -2165,7 +2165,7 @@ private void CheckForCallAddress(Instruction instruction) //Call to a Vtable func on a klass pointer //Valid for offset > 0x128 - var vTableMethod = GetMethodFromReadKlassOffset(LibCpp2ILUtils.GetOperandMemoryOffset(instruction.Operands[0])); + var vTableMethod = GetMethodFromReadKlassOffset(Utils.GetOperandMemoryOffset(instruction.Operands[0])); if (vTableMethod != null) { @@ -2183,7 +2183,7 @@ private void CheckForCallAddress(Instruction instruction) ulong jumpAddress; try { - jumpAddress = LibCpp2ILUtils.GetJumpTarget(instruction, _methodStart + instruction.PC); + jumpAddress = Utils.GetJumpTarget(instruction, _methodStart + instruction.PC); _typeDump.Append($" ; jump to 0x{jumpAddress:X}"); } catch (Exception) @@ -2576,11 +2576,11 @@ private void CheckForCallAddress(Instruction instruction) // Console.WriteLine($"Trying to find an exception throwing function at unknown function address 0x{jumpAddress:X}..."); var actualAddr = _cppAssembly.MapVirtualAddressToRaw(jumpAddress); - var body = LibCpp2ILUtils.GetMethodBodyAtRawAddress(_cppAssembly, actualAddr, peek: true); + var body = Utils.GetMethodBodyAtRawAddress(_cppAssembly, actualAddr, peek: true); var leas = body.Where(i => i.Mnemonic == ud_mnemonic_code.UD_Ilea).ToList(); - var offsets = leas.Select(i => LibCpp2ILUtils.GetOffsetFromMemoryAccess(i, i.Operands[1])).ToList(); + var offsets = leas.Select(i => Utils.GetOffsetFromMemoryAccess(i, i.Operands[1])).ToList(); var targets = offsets.Select(offset => offset + jumpAddress).ToList(); @@ -2696,7 +2696,7 @@ private void CheckForConditions(Instruction instruction) } //Compiler generated weirdness. I hate it. - if (instruction.Mnemonic == ud_mnemonic_code.UD_Icmp && instruction.Operands[1].Type == ud_type.UD_OP_IMM && GetFieldReferencedByOperand(instruction.Operands[0]) is { } field && LibCpp2ILUtils.GetImmediateValue(instruction, instruction.Operands[1]) == 0) + if (instruction.Mnemonic == ud_mnemonic_code.UD_Icmp && instruction.Operands[1].Type == ud_type.UD_OP_IMM && GetFieldReferencedByOperand(instruction.Operands[0]) is { } field && Utils.GetImmediateValue(instruction, instruction.Operands[1]) == 0) { var registerName = Utils.GetRegisterName(instruction.Operands[0]); if (_registerAliases.ContainsKey(registerName)) @@ -2744,7 +2744,7 @@ private void CheckForConditionalJumps(Instruction instruction) if (comparisonItemA.Contains("unknown global") || comparisonItemB.Contains("unknown global") || comparisonItemA.Contains("value in") || comparisonItemB.Contains("value in") || comparisonItemA.Contains("unknown refobject") || comparisonItemB.Contains("unknown refobject")) TaintMethod(TaintReason.BAD_CONDITION); - var dest = LibCpp2ILUtils.GetJumpTarget(instruction, _methodStart + instruction.PC); + var dest = Utils.GetJumpTarget(instruction, _methodStart + instruction.PC); //Going back to an earlier point in the function - loop. //This works because the compiler puts all if statements *after* the main function body for jumping forward to, then back. diff --git a/Cpp2IL/Analysis/Actions/CallManagedFunctionAction.cs b/Cpp2IL/Analysis/Actions/CallManagedFunctionAction.cs index 44cb2174..5efd42a1 100644 --- a/Cpp2IL/Analysis/Actions/CallManagedFunctionAction.cs +++ b/Cpp2IL/Analysis/Actions/CallManagedFunctionAction.cs @@ -44,7 +44,7 @@ private bool CheckParameters(Il2CppMethodDefinition method, MethodAnalysis conte public CallManagedFunctionAction(MethodAnalysis context, Instruction instruction) : base(context, instruction) { - var jumpTarget = LibCpp2ILUtils.GetJumpTarget(instruction, context.MethodStart + instruction.PC); + var jumpTarget = Utils.GetJumpTarget(instruction, context.MethodStart + instruction.PC); var objectMethodBeingCalledOn = context.GetLocalInReg("rcx"); var listOfCallableMethods = LibCpp2IlMain.GetManagedMethodImplementationsAtAddress(jumpTarget); diff --git a/Cpp2IL/Analysis/Actions/CallVirtualMethodAction.cs b/Cpp2IL/Analysis/Actions/CallVirtualMethodAction.cs index 8c1e1e9f..b600ef1a 100644 --- a/Cpp2IL/Analysis/Actions/CallVirtualMethodAction.cs +++ b/Cpp2IL/Analysis/Actions/CallVirtualMethodAction.cs @@ -21,7 +21,7 @@ public CallVirtualMethodAction(MethodAnalysis context, Instruction instruction) var classReadFrom = klass.backingType; - var readOffset = LibCpp2ILUtils.GetOperandMemoryOffset(instruction.Operands[0]); + var readOffset = Utils.GetOperandMemoryOffset(instruction.Operands[0]); Called = Utils.GetMethodFromReadKlassOffset(readOffset); if (Called == null) return; diff --git a/Cpp2IL/Analysis/Actions/GlobalMethodRefToConstantAction.cs b/Cpp2IL/Analysis/Actions/GlobalMethodRefToConstantAction.cs index 39d67244..9557b11d 100644 --- a/Cpp2IL/Analysis/Actions/GlobalMethodRefToConstantAction.cs +++ b/Cpp2IL/Analysis/Actions/GlobalMethodRefToConstantAction.cs @@ -17,7 +17,7 @@ public class GlobalMethodRefToConstantAction : BaseAction public GlobalMethodRefToConstantAction(MethodAnalysis context, Instruction instruction) : base(context, instruction) { - var globalAddress = context.MethodStart + LibCpp2ILUtils.GetOffsetFromMemoryAccess(instruction, instruction.Operands[1]); + var globalAddress = context.MethodStart + Utils.GetOffsetFromMemoryAccess(instruction, instruction.Operands[1]); MethodData = LibCpp2IlMain.GetMethodDefinitionByGlobalAddress(globalAddress); var (type, genericParams) = Utils.TryLookupTypeDefByName(MethodData!.DeclaringType.FullName); diff --git a/Cpp2IL/Analysis/Actions/GlobalStringRefToConstantAction.cs b/Cpp2IL/Analysis/Actions/GlobalStringRefToConstantAction.cs index b72bdb83..20e7740a 100644 --- a/Cpp2IL/Analysis/Actions/GlobalStringRefToConstantAction.cs +++ b/Cpp2IL/Analysis/Actions/GlobalStringRefToConstantAction.cs @@ -13,7 +13,7 @@ public class GlobalStringRefToConstantAction : BaseAction public GlobalStringRefToConstantAction(MethodAnalysis context, Instruction instruction) : base(context, instruction) { - var globalAddress = context.MethodStart + LibCpp2ILUtils.GetOffsetFromMemoryAccess(instruction, instruction.Operands[1]); + var globalAddress = context.MethodStart + Utils.GetOffsetFromMemoryAccess(instruction, instruction.Operands[1]); ResolvedString = LibCpp2IlMain.GetLiteralByAddress(globalAddress); if (ResolvedString == null) return; diff --git a/Cpp2IL/Analysis/Actions/GlobalTypeRefToConstantAction.cs b/Cpp2IL/Analysis/Actions/GlobalTypeRefToConstantAction.cs index c683c881..11a01759 100644 --- a/Cpp2IL/Analysis/Actions/GlobalTypeRefToConstantAction.cs +++ b/Cpp2IL/Analysis/Actions/GlobalTypeRefToConstantAction.cs @@ -15,7 +15,7 @@ public class GlobalTypeRefToConstantAction : BaseAction public GlobalTypeRefToConstantAction(MethodAnalysis context, Instruction instruction) : base(context, instruction) { - var globalAddress = context.MethodStart + LibCpp2ILUtils.GetOffsetFromMemoryAccess(instruction, instruction.Operands[1]); + var globalAddress = context.MethodStart + Utils.GetOffsetFromMemoryAccess(instruction, instruction.Operands[1]); var typeData = LibCpp2IlMain.GetTypeGlobalByAddress(globalAddress); var (type, genericParams) = Utils.TryLookupTypeDefByName(typeData!.ToString()); ResolvedType = type; diff --git a/Cpp2IL/Analysis/Actions/JumpIfNonZeroOrNonNullAction.cs b/Cpp2IL/Analysis/Actions/JumpIfNonZeroOrNonNullAction.cs index 7704a00a..1909c950 100644 --- a/Cpp2IL/Analysis/Actions/JumpIfNonZeroOrNonNullAction.cs +++ b/Cpp2IL/Analysis/Actions/JumpIfNonZeroOrNonNullAction.cs @@ -14,7 +14,7 @@ public class JumpIfNonZeroOrNonNullAction : BaseAction public JumpIfNonZeroOrNonNullAction(MethodAnalysis context, Instruction instruction) : base(context, instruction) { - jumpTarget = LibCpp2ILUtils.GetJumpTarget(instruction, instruction.PC + context.MethodStart); + jumpTarget = Utils.GetJumpTarget(instruction, instruction.PC + context.MethodStart); if (jumpTarget > context.MethodStart + instruction.PC && jumpTarget < context.AbsoluteMethodEnd) { diff --git a/Cpp2IL/Analysis/Actions/JumpIfZeroOrNullAction.cs b/Cpp2IL/Analysis/Actions/JumpIfZeroOrNullAction.cs index 40240bf0..63d9cd54 100644 --- a/Cpp2IL/Analysis/Actions/JumpIfZeroOrNullAction.cs +++ b/Cpp2IL/Analysis/Actions/JumpIfZeroOrNullAction.cs @@ -14,7 +14,7 @@ public class JumpIfZeroOrNullAction : BaseAction public JumpIfZeroOrNullAction(MethodAnalysis context, Instruction instruction) : base(context, instruction) { - jumpTarget = LibCpp2ILUtils.GetJumpTarget(instruction, instruction.PC + context.MethodStart); + jumpTarget = Utils.GetJumpTarget(instruction, instruction.PC + context.MethodStart); if (jumpTarget > context.MethodStart + instruction.PC && jumpTarget < context.AbsoluteMethodEnd) { diff --git a/Cpp2IL/Analysis/Actions/LoadVirtualFunctionPointerAction.cs b/Cpp2IL/Analysis/Actions/LoadVirtualFunctionPointerAction.cs index 1cb155af..135caf12 100644 --- a/Cpp2IL/Analysis/Actions/LoadVirtualFunctionPointerAction.cs +++ b/Cpp2IL/Analysis/Actions/LoadVirtualFunctionPointerAction.cs @@ -23,7 +23,7 @@ public LoadVirtualFunctionPointerAction(MethodAnalysis context, Instruction inst classReadFrom = klass.backingType; - var readOffset = LibCpp2ILUtils.GetOperandMemoryOffset(instruction.Operands[1]); + var readOffset = Utils.GetOperandMemoryOffset(instruction.Operands[1]); methodPointerRead = Utils.GetMethodFromReadKlassOffset(readOffset); if (methodPointerRead == null) return; @@ -31,7 +31,7 @@ public LoadVirtualFunctionPointerAction(MethodAnalysis context, Instruction inst var regPutInto = Utils.GetRegisterName(instruction.Operands[0]); if (regPutInto == "rsp") { - var stackOffset = LibCpp2ILUtils.GetOperandMemoryOffset(instruction.Operands[0]); + var stackOffset = Utils.GetOperandMemoryOffset(instruction.Operands[0]); context.PushToStack(context.MakeConstant(typeof(MethodDefinition), methodPointerRead), stackOffset); } else diff --git a/Cpp2IL/KeyFunctionAddresses.cs b/Cpp2IL/KeyFunctionAddresses.cs index d9be392b..e4bf1428 100644 --- a/Cpp2IL/KeyFunctionAddresses.cs +++ b/Cpp2IL/KeyFunctionAddresses.cs @@ -58,13 +58,13 @@ public static KeyFunctionAddresses Find(List<(TypeDefinition type, List insn.Mnemonic == ud_mnemonic_code.UD_Icall); if (targetCall != null) { - addr = LibCpp2ILUtils.GetJumpTarget(targetCall, tatn.MethodOffsetRam + targetCall.PC); + addr = Utils.GetJumpTarget(targetCall, tatn.MethodOffsetRam + targetCall.PC); Console.WriteLine($"\t\t\t\tLocated il2cpp_codegen_initialize_method function at 0x{addr:X}"); ret.il2cpp_codegen_initialize_method = addr; @@ -90,14 +90,14 @@ public static KeyFunctionAddresses Find(List<(TypeDefinition type, List insn.Mnemonic == ud_mnemonic_code.UD_Icall).ToArray(); if (calls.Length > 1) { - addr = LibCpp2ILUtils.GetJumpTarget(calls[1], method.MethodOffsetRam + calls[1].PC); + addr = Utils.GetJumpTarget(calls[1], method.MethodOffsetRam + calls[1].PC); Console.WriteLine($"\t\t\t\tLocated il2cpp_codegen_object_new at 0x{addr:X}"); ret.il2cpp_codegen_object_new = addr; @@ -176,10 +176,10 @@ public static KeyFunctionAddresses Find(List<(TypeDefinition type, List m.MethodName == ".ctor"); Console.WriteLine($"\t\t\t\tSearching for class instantiation function near offset 0x{ctor.MethodOffsetRam:X}..."); - instructions = LibCpp2ILUtils.DisassembleBytes(LibCpp2IlMain.ThePe.is32Bit, ctor.MethodBytes); + instructions = Utils.DisassembleBytes(LibCpp2IlMain.ThePe.is32Bit, ctor.MethodBytes); calls = instructions.Where(insn => insn.Mnemonic == ud_mnemonic_code.UD_Icall).ToArray(); - addr = LibCpp2ILUtils.GetJumpTarget(calls[1], ctor.MethodOffsetRam + calls[1].PC); + addr = Utils.GetJumpTarget(calls[1], ctor.MethodOffsetRam + calls[1].PC); Console.WriteLine($"\t\t\t\tLocated Class Instantiation (`new`) function at 0x{addr:X}"); ret.il2cpp_codegen_object_new = addr; } @@ -198,12 +198,12 @@ public static KeyFunctionAddresses Find(List<(TypeDefinition type, List insn.Mnemonic == ud_mnemonic_code.UD_Icall).ToArray(); - addr = LibCpp2ILUtils.GetJumpTarget(calls[1], method.MethodOffsetRam + calls[1].PC); + addr = Utils.GetJumpTarget(calls[1], method.MethodOffsetRam + calls[1].PC); Console.WriteLine($"\t\t\t\tLocated il2cpp_array_new_specific (`new[]`) function at 0x{addr:X}"); ret.il2cpp_array_new_specific = addr; @@ -214,21 +214,21 @@ public static KeyFunctionAddresses Find(List<(TypeDefinition type, List insn.Mnemonic == ud_mnemonic_code.UD_Icall).ToArray(); - if (calls.Length > 3 && LibCpp2IlMain.GetManagedMethodImplementationsAtAddress(LibCpp2ILUtils.GetJumpTarget(calls[2], method.MethodOffsetRam + calls[2].PC)) is { } methodImplementations) + if (calls.Length > 3 && LibCpp2IlMain.GetManagedMethodImplementationsAtAddress(Utils.GetJumpTarget(calls[2], method.MethodOffsetRam + calls[2].PC)) is { } methodImplementations) { Console.WriteLine($"\t\t\t\tWARNING: Couldn't use native method lookup function detection because the located address defines {methodImplementations.Count} managed methods."); } else if (calls.Length > 3) { - addr = LibCpp2ILUtils.GetJumpTarget(calls[2], method.MethodOffsetRam + calls[2].PC); + addr = Utils.GetJumpTarget(calls[2], method.MethodOffsetRam + calls[2].PC); Console.WriteLine($"\t\t\t\tLocated Native Method Lookup function at 0x{addr:X}"); ret.AddrNativeLookup = addr; - addr = LibCpp2ILUtils.GetJumpTarget(calls[3], method.MethodOffsetRam + calls[3].PC); + addr = Utils.GetJumpTarget(calls[3], method.MethodOffsetRam + calls[3].PC); Console.WriteLine($"\t\t\t\tLocated Native Method Bailout function at 0x{addr:X}"); ret.AddrNativeLookupGenMissingMethod = addr; } @@ -241,9 +241,9 @@ public static KeyFunctionAddresses Find(List<(TypeDefinition type, List t.type.Name == "Int16Converter" && t.type.Namespace == "System.ComponentModel").methods; @@ -283,11 +283,11 @@ public static KeyFunctionAddresses Find(List<(TypeDefinition type, List insn.Mnemonic == ud_mnemonic_code.UD_Icall).ToArray(); - addr = LibCpp2ILUtils.GetJumpTarget(calls[3], method.MethodOffsetRam + calls[3].PC); + addr = Utils.GetJumpTarget(calls[3], method.MethodOffsetRam + calls[3].PC); Console.WriteLine($"\t\t\t\tLocated il2cpp_object_is_inst function at 0x{addr:X}"); ret.il2cpp_object_is_inst = addr; @@ -299,13 +299,13 @@ public static KeyFunctionAddresses Find(List<(TypeDefinition type, List insn.Mnemonic == ud_mnemonic_code.UD_Icall).ToArray(); var indexBefore = calls.ToList().FindIndex(call => { - var targetAddrVirt = LibCpp2ILUtils.GetJumpTarget(call, method.MethodOffsetRam + call.PC); + var targetAddrVirt = Utils.GetJumpTarget(call, method.MethodOffsetRam + call.PC); if (SharedState.MethodsByAddress.TryGetValue(targetAddrVirt, out var methodDef)) { if (methodDef.Name == ".ctor" && methodDef.DeclaringType.Name == "ArgumentException") @@ -318,7 +318,7 @@ public static KeyFunctionAddresses Find(List<(TypeDefinition type, List= 0) { Console.WriteLine($"\t\t\t\tUsing ArgumentException construction to find il2cpp_raise_managed_exception, expecting it to be call ${indexBefore + 1}..."); - addr = LibCpp2ILUtils.GetJumpTarget(calls[indexBefore + 1], method.MethodOffsetRam + calls[indexBefore + 1].PC); + addr = Utils.GetJumpTarget(calls[indexBefore + 1], method.MethodOffsetRam + calls[indexBefore + 1].PC); Console.WriteLine($"\t\t\t\tLocated il2cpp_raise_managed_exception at 0x{addr:X}"); ret.il2cpp_raise_managed_exception = addr; } @@ -336,13 +336,13 @@ public static KeyFunctionAddresses Find(List<(TypeDefinition type, List i.Mnemonic == ud_mnemonic_code.UD_Icall).ToList(); - var callTargets = callInstructions.Select(i => LibCpp2ILUtils.GetJumpTarget(i, targetMethod.MethodOffsetRam + i.PC)).ToList(); + var callTargets = callInstructions.Select(i => Utils.GetJumpTarget(i, targetMethod.MethodOffsetRam + i.PC)).ToList(); //First call is codegen_init_method //Second is, or should be, a call to Object$GetType @@ -407,11 +407,11 @@ private static void TryUseArrayEnumerator(List<(TypeDefinition type, List i try { - var callAddr = LibCpp2ILUtils.GetJumpTarget(instructionsInRange[2], offsetInRam + instructionsInRange[2].PC); + var callAddr = GetJumpTarget(instructionsInRange[2], offsetInRam + instructionsInRange[2].PC); return callAddr == kfe.il2cpp_codegen_initialize_method ? 3 : 0; } catch (Exception) @@ -319,7 +320,7 @@ public static int CheckForStaticClassInitAtIndex(ulong offsetInRam, List i.Mnemonic).ToArray(); if (requiredPattern.SequenceEqual(actualPattern)) { - var callAddr = LibCpp2ILUtils.GetJumpTarget(instructionsInRange[4], offsetInRam + instructionsInRange[4].PC); + var callAddr = GetJumpTarget(instructionsInRange[4], offsetInRam + instructionsInRange[4].PC); //If this is true then we have an il2cpp-generated initialization call. return callAddr == kfe.il2cpp_runtime_class_init_actual || callAddr == kfe.il2cpp_runtime_class_init_export ? 4 : 0; @@ -333,7 +334,7 @@ public static int CheckForStaticClassInitAtIndex(ulong offsetInRam, List GetMethodBodyAtRawAddress(PE theDll, long addr, bool peek) + { + var ret = new List(); + var con = true; + var buff = new List(); + while (con) + { + buff.Add(theDll.raw[addr]); + + ret = DisassembleBytes(theDll.is32Bit, buff.ToArray()); + + if (ret.All(i => !i.Error) && ret.Any(i => i.Mnemonic == ud_mnemonic_code.UD_Iint3)) + con = false; + + if (peek && buff.Count > 50) + con = false; + else if (buff.Count > 1000) + con = false; //Sanity breakout. + + addr++; + } + + return ret /*.Where(i => !i.Error).ToList()*/; + } + + public static List DisassembleBytes(bool is32Bit, byte[] bytes) + { + return new List(new Disassembler(bytes, is32Bit ? ArchitectureMode.x86_32 : ArchitectureMode.x86_64, 0, true).Disassemble()); + } + + public static ulong GetJumpTarget(Instruction insn, ulong start) + { + var opr = insn.Operands[0]; + + var mode = GetOprMode(insn); + + var num = UInt64.MaxValue >> 64 - mode; + return opr.Size switch + { + 8 => (start + (ulong) opr.LvalSByte & num), + 16 => (start + (ulong) opr.LvalSWord & num), + 32 => (start + (ulong) opr.LvalSDWord & num), + 64 => (start + (ulong) opr.LvalSQWord & num), + _ => throw new InvalidOperationException($"invalid relative offset size {opr.Size}.") + }; + } + + public static byte GetOprMode(Instruction instruction) + { + return (byte) oprMode.GetValue(instruction); + } + + public static ulong GetImmediateValue(Instruction insn, Operand op) + { + ulong num; + if (op.Opcode == ud_operand_code.OP_sI && op.Size != GetOprMode(insn)) + { + if (op.Size == 8) + { + num = (ulong) op.LvalSByte; + } + else + { + if (op.Size != 32) + throw new InvalidOperationException("Operand size must be 32"); + num = (ulong) op.LvalSDWord; + } + + if (GetOprMode(insn) < 64) + num &= (ulong) ((1L << GetOprMode(insn)) - 1L); + } + else + { + switch (op.Size) + { + case 8: + num = op.LvalByte; + break; + case 16: + num = op.LvalUWord; + break; + case 32: + num = op.LvalUDWord; + break; + case 64: + num = op.LvalUQWord; + break; + default: + throw new InvalidOperationException($"Invalid size for operand: {op.Size}"); + } + } + + return num; + } + + public static FieldInfo oprMode = typeof(Instruction).GetField("opr_mode", BindingFlags.Instance | BindingFlags.NonPublic)!; + + public static ulong GetOffsetFromMemoryAccess(Instruction insn, Operand op) + { + var num1 = (ulong) GetOperandMemoryOffset(op); + + if (num1 == 0) return 0; + + return num1 + insn.PC; + } + + public static int GetOperandMemoryOffset(Operand op) + { + if (op.Type != ud_type.UD_OP_MEM) return 0; + var num1 = op.Offset switch + { + 8 => op.LvalSByte, + 16 => op.LvalSWord, + 32 => op.LvalSDWord, + _ => 0 + }; + return num1; + } } } \ No newline at end of file diff --git a/LibCpp2IL/Extensions.cs b/LibCpp2IL/Extensions.cs index aeaabd9e..8c1fea33 100644 --- a/LibCpp2IL/Extensions.cs +++ b/LibCpp2IL/Extensions.cs @@ -1,11 +1,14 @@ using System; using System.Collections.Generic; using System.Text; +using Iced.Intel; namespace LibCpp2IL { public static class Extensions { + public static ulong GetInstructionMemoryAddress(this Instruction instruction) => instruction.NextIP + instruction.MemoryDisplacement64; + public static T[] SubArray(this T[] data, int index, int length) { var result = new T[length]; diff --git a/LibCpp2IL/LibCpp2IL.csproj b/LibCpp2IL/LibCpp2IL.csproj index 370848b9..53bca87f 100644 --- a/LibCpp2IL/LibCpp2IL.csproj +++ b/LibCpp2IL/LibCpp2IL.csproj @@ -7,7 +7,7 @@ - + diff --git a/LibCpp2IL/LibCpp2IlUtils.cs b/LibCpp2IL/LibCpp2IlUtils.cs index 568d9bf7..927f7625 100644 --- a/LibCpp2IL/LibCpp2IlUtils.cs +++ b/LibCpp2IL/LibCpp2IlUtils.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; using System.Text; +using Iced.Intel; using LibCpp2IL.Metadata; using LibCpp2IL.PE; using LibCpp2IL.Reflection; -using SharpDisasm; -using SharpDisasm.Udis86; +using Decoder = Iced.Intel.Decoder; namespace LibCpp2IL { @@ -35,123 +34,19 @@ public static class LibCpp2ILUtils {28, "object"} }; - public static List DisassembleBytes(bool is32Bit, byte[] bytes) - { - return new List(new Disassembler(bytes, is32Bit ? ArchitectureMode.x86_32 : ArchitectureMode.x86_64, 0, true).Disassemble()); - } - - public static List GetMethodBodyAtRawAddress(PE.PE theDll, long addr, bool peek) - { - var ret = new List(); - var con = true; - var buff = new List(); - while (con) - { - buff.Add(theDll.raw[addr]); - - ret = DisassembleBytes(theDll.is32Bit, buff.ToArray()); - - if (ret.All(i => !i.Error) && ret.Any(i => i.Mnemonic == ud_mnemonic_code.UD_Iint3)) - con = false; - - if (peek && buff.Count > 50) - con = false; - else if (buff.Count > 1000) - con = false; //Sanity breakout. - - addr++; - } - - return ret /*.Where(i => !i.Error).ToList()*/; - } - public static ulong GetJumpTarget(Instruction insn, ulong start) + public static InstructionList DisassembleBytesNew(bool is32Bit, byte[] bytes, ulong methodBase) { - var opr = insn.Operands[0]; - - var mode = GetOprMode(insn); - - var num = UInt64.MaxValue >> 64 - mode; - return opr.Size switch - { - 8 => (start + (ulong) opr.LvalSByte & num), - 16 => (start + (ulong) opr.LvalSWord & num), - 32 => (start + (ulong) opr.LvalSDWord & num), - 64 => (start + (ulong) opr.LvalSQWord & num), - _ => throw new InvalidOperationException($"invalid relative offset size {opr.Size}.") - }; - } + var codeReader = new ByteArrayCodeReader(bytes); + var decoder = Decoder.Create(is32Bit ? 32 : 64, codeReader); + decoder.IP = methodBase; + var instructions = new InstructionList(); + var endRip = decoder.IP + (uint) bytes.Length; - private static FieldInfo oprMode = typeof(Instruction).GetField("opr_mode", BindingFlags.Instance | BindingFlags.NonPublic)!; + while (decoder.IP < endRip) + decoder.Decode(out instructions.AllocUninitializedElement()); - private static byte GetOprMode(Instruction instruction) - { - return (byte) oprMode.GetValue(instruction); - } - - public static ulong GetImmediateValue(Instruction insn, Operand op) - { - ulong num; - if (op.Opcode == ud_operand_code.OP_sI && op.Size != GetOprMode(insn)) - { - if (op.Size == 8) - { - num = (ulong) op.LvalSByte; - } - else - { - if (op.Size != 32) - throw new InvalidOperationException("Operand size must be 32"); - num = (ulong) op.LvalSDWord; - } - - if (GetOprMode(insn) < 64) - num &= (ulong) ((1L << GetOprMode(insn)) - 1L); - } - else - { - switch (op.Size) - { - case 8: - num = op.LvalByte; - break; - case 16: - num = op.LvalUWord; - break; - case 32: - num = op.LvalUDWord; - break; - case 64: - num = op.LvalUQWord; - break; - default: - throw new InvalidOperationException($"Invalid size for operand: {op.Size}"); - } - } - - return num; - } - - public static ulong GetOffsetFromMemoryAccess(Instruction insn, Operand op) - { - var num1 = (ulong) GetOperandMemoryOffset(op); - - if (num1 == 0) return 0; - - return num1 + insn.PC; - } - - public static int GetOperandMemoryOffset(Operand op) - { - if (op.Type != ud_type.UD_OP_MEM) return 0; - var num1 = op.Offset switch - { - 8 => op.LvalSByte, - 16 => op.LvalSWord, - 32 => op.LvalSDWord, - _ => 0 - }; - return num1; + return instructions; } internal static string GetTypeName(Il2CppMetadata metadata, PE.PE cppAssembly, Il2CppTypeDefinition typeDef, bool fullName = false) diff --git a/LibCpp2IL/PE/PE.cs b/LibCpp2IL/PE/PE.cs index 6434345f..ad36714a 100644 --- a/LibCpp2IL/PE/PE.cs +++ b/LibCpp2IL/PE/PE.cs @@ -6,8 +6,6 @@ using System.Text; using LibCpp2IL.Metadata; using LibCpp2IL.Reflection; -using SharpDisasm; -using SharpDisasm.Udis86; namespace LibCpp2IL.PE { @@ -133,8 +131,9 @@ public long MapVirtualAddressToRaw(ulong uiAddr) return addr - (section.VirtualAddress - section.PointerToRawData); } - - public ulong MapRawAddressToVirtual(uint offset) { + + public ulong MapRawAddressToVirtual(uint offset) + { var section = sections.First(x => offset >= x.PointerToRawData && offset < x.PointerToRawData + x.SizeOfRawData); return imageBase + section.VirtualAddress + offset - section.PointerToRawData; @@ -284,8 +283,8 @@ public void Init(ulong codeRegistration, ulong metadataRegistration) if (!ConcreteGenericMethods.ContainsKey(baseMethod)) ConcreteGenericMethods[baseMethod] = new List(); - - if(!ConcreteGenericImplementationsByAddress.ContainsKey(concreteMethodPtr)) + + if (!ConcreteGenericImplementationsByAddress.ContainsKey(concreteMethodPtr)) ConcreteGenericImplementationsByAddress[concreteMethodPtr] = new List(); var concreteMethod = new Il2CppConcreteGenericMethod @@ -294,7 +293,7 @@ public void Init(ulong codeRegistration, ulong metadataRegistration) GenericParams = genericParamData, GenericVariantPtr = concreteMethodPtr }; - + ConcreteGenericMethods[baseMethod].Add(concreteMethod); ConcreteGenericImplementationsByAddress[concreteMethodPtr].Add(concreteMethod); } @@ -358,263 +357,273 @@ public bool PlusSearch(int methodCount, int typeDefinitionsCount) metadataRegistration = plusSearch.FindMetadataRegistration64Bit(); } +#if ALLOW_CODEREG_FALLBACK if (codeRegistration == 0 || metadataRegistration == 0) + (codeRegistration, metadataRegistration) = UseDecompilationBasedFallback(); +#endif + + if (codeRegistration == 0 && LibCpp2IlMain.Settings.AllowManualMetadataAndCodeRegInput) { - Console.WriteLine("\tFailed to find code and metadata registration functions using primary location method (probably because we're post-2019), checking if we can use the fallback..."); - //Get export for il2cpp_init function - var virtualAddrInit = GetVirtualAddressOfUnmanagedExportByName("il2cpp_init"); - if (virtualAddrInit <= 0) - { - Console.WriteLine("\tCould not find exported il2cpp_init function! Fallback method failed, execution will fail!"); - goto bailout; - } + Console.Write("Couldn't identify a CodeRegistration address. If you know it, enter it now, otherwise enter nothing or zero to fail: "); + var crInput = Console.ReadLine(); + ulong.TryParse(crInput, NumberStyles.HexNumber, null, out codeRegistration); + } - Console.WriteLine($"\tFound il2cpp_init export (resolves to virtual addr 0x{virtualAddrInit:X}), using fallback method to find Code and Metadata registration..."); - List initMethodBody = LibCpp2ILUtils.GetMethodBodyAtRawAddress(this, MapVirtualAddressToRaw(virtualAddrInit), false); + if (metadataRegistration == 0 && LibCpp2IlMain.Settings.AllowManualMetadataAndCodeRegInput) + { + Console.Write("Couldn't identify a MetadataRegistration address. If you know it, enter it now, otherwise enter nothing or zero to fail: "); + var mrInput = Console.ReadLine(); + ulong.TryParse(mrInput, NumberStyles.HexNumber, null, out metadataRegistration); + } - //Look for a JMP for older il2cpp versions, on newer ones it appears to be a CALL - var callToRuntimeInit = initMethodBody.Find(i => i.Mnemonic == ud_mnemonic_code.UD_Ijmp); + Console.WriteLine("Initializing with located addresses:"); + return AutoInit(codeRegistration, metadataRegistration); + } - if (callToRuntimeInit == null) - callToRuntimeInit = initMethodBody.FindLast(i => i.Mnemonic == ud_mnemonic_code.UD_Icall); +#if ALLOW_CODEREG_FALLBACK + private (ulong codeRegistration, ulong metadataRegistration) UseDecompilationBasedFallback() + { + ulong codeRegistration; + ulong metadataRegistration; + Console.WriteLine("\tFailed to find code and metadata registration functions using primary location method (probably because we're post-2019), checking if we can use the fallback..."); + //Get export for il2cpp_init function + var virtualAddrInit = GetVirtualAddressOfUnmanagedExportByName("il2cpp_init"); + if (virtualAddrInit <= 0) + { + Console.WriteLine("\tCould not find exported il2cpp_init function! Fallback method failed, execution will fail!"); + return (0, 0); + } - if (callToRuntimeInit == null) - { - Console.WriteLine("\tCould not find a call to Runtime::Init! Fallback method failed!"); - goto bailout; - } + Console.WriteLine($"\tFound il2cpp_init export (resolves to virtual addr 0x{virtualAddrInit:X}), using fallback method to find Code and Metadata registration..."); + List initMethodBody = LibCpp2ILUtils.GetMethodBodyAtRawAddress(this, MapVirtualAddressToRaw(virtualAddrInit), false); - var virtualAddressRuntimeInit = LibCpp2ILUtils.GetJumpTarget(callToRuntimeInit, callToRuntimeInit.PC + virtualAddrInit); - Console.WriteLine($"\tLocated probable Runtime::Init function at virtual addr 0x{virtualAddressRuntimeInit:X}"); + //Look for a JMP for older il2cpp versions, on newer ones it appears to be a CALL + var callToRuntimeInit = initMethodBody.Find(i => i.Mnemonic == ud_mnemonic_code.UD_Ijmp); - List methodBodyRuntimeInit = LibCpp2ILUtils.GetMethodBodyAtRawAddress(this, MapVirtualAddressToRaw(virtualAddressRuntimeInit), false); + if (callToRuntimeInit == null) + callToRuntimeInit = initMethodBody.FindLast(i => i.Mnemonic == ud_mnemonic_code.UD_Icall); - Disassembler.Translator.IncludeBinary = true; - // File.WriteAllText(Path.Combine("cpp2il_out", "runtime_init_dump.txt"), string.Join('\n', methodBodyRuntimeInit.Select(i => i.ToString()))); + if (callToRuntimeInit == null) + { + Console.WriteLine("\tCould not find a call to Runtime::Init! Fallback method failed!"); + return (0, 0); + } - //This is kind of sketchy, but look for a global read (i.e an LEA where the second base is RIP), as that's the framework version read, then there's a MOV, then 4 calls, the third of which is our target. - //So as to ensure compat with 2018, ensure we have a call before this LEA. - var minimumIndex = methodBodyRuntimeInit.FindIndex(i => i.Mnemonic == ud_mnemonic_code.UD_Icall); + var virtualAddressRuntimeInit = LibCpp2ILUtils.GetJumpTarget(callToRuntimeInit, callToRuntimeInit.PC + virtualAddrInit); + Console.WriteLine($"\tLocated probable Runtime::Init function at virtual addr 0x{virtualAddressRuntimeInit:X}"); - var idx = -1; - var indexOfFrameworkVersionLoad = methodBodyRuntimeInit.FindIndex(i => idx++ > minimumIndex && i.Mnemonic == ud_mnemonic_code.UD_Ilea && i.Operands.Last().Base == ud_type.UD_R_RIP); + List methodBodyRuntimeInit = LibCpp2ILUtils.GetMethodBodyAtRawAddress(this, MapVirtualAddressToRaw(virtualAddressRuntimeInit), false); - if (indexOfFrameworkVersionLoad < 0) - { - idx = -1; - //Could be a mov in optimised code - mov of hard coded address to hard coded address - indexOfFrameworkVersionLoad = methodBodyRuntimeInit.FindIndex(i => idx++ > minimumIndex && i.Mnemonic == ud_mnemonic_code.UD_Imov && i.Bytes.Length == 10 /*&& i.Operands.First().Type == ud_type.UD_OP_MEM && LibCpp2ILUtils.GetImmediateValue(i, i.Operands.Last()) != 0*/); + Disassembler.Translator.IncludeBinary = true; + // File.WriteAllText(Path.Combine("cpp2il_out", "runtime_init_dump.txt"), string.Join('\n', methodBodyRuntimeInit.Select(i => i.ToString()))); - if (indexOfFrameworkVersionLoad < 0) - { - Console.WriteLine("\tCouldn't find framework load index, abort!"); - goto bailout; - } - } - - Console.WriteLine("\tLocated probable framework version set, instruction bytes are " + methodBodyRuntimeInit[indexOfFrameworkVersionLoad].ToString()); + //This is kind of sketchy, but look for a global read (i.e an LEA where the second base is RIP), as that's the framework version read, then there's a MOV, then 4 calls, the third of which is our target. + //So as to ensure compat with 2018, ensure we have a call before this LEA. + var minimumIndex = methodBodyRuntimeInit.FindIndex(i => i.Mnemonic == ud_mnemonic_code.UD_Icall); - var instructionsFromThatPoint = methodBodyRuntimeInit.Skip(indexOfFrameworkVersionLoad + 1).TakeWhile(i => true).ToList(); - var calls = instructionsFromThatPoint.Where(i => i.Mnemonic == ud_mnemonic_code.UD_Icall).ToList(); + var idx = -1; + var indexOfFrameworkVersionLoad = methodBodyRuntimeInit.FindIndex(i => idx++ > minimumIndex && i.Mnemonic == ud_mnemonic_code.UD_Ilea && i.Operands.Last().Base == ud_type.UD_R_RIP); - if (calls.Count < 3) - { - Console.WriteLine("\tRuntime::Init does not call enough methods for us to locate ExecuteInitializations! Fallback failed!"); - goto bailout; - } - - var thirdCall = calls[2]; - - //Now we have the address of the ExecuteInitializations function - var virtAddrExecuteInit = LibCpp2ILUtils.GetJumpTarget(thirdCall, thirdCall.PC + virtualAddressRuntimeInit); - Console.WriteLine($"\tLocated probable ExecuteInitializations function at virt addr 0x{virtAddrExecuteInit:X}"); - - //Could be first or second instruction - List execInitMethodBody = LibCpp2ILUtils.GetMethodBodyAtRawAddress(this, MapVirtualAddressToRaw(virtAddrExecuteInit), true); + if (indexOfFrameworkVersionLoad < 0) + { + idx = -1; + //Could be a mov in optimised code - mov of hard coded address to hard coded address + indexOfFrameworkVersionLoad = methodBodyRuntimeInit.FindIndex(i => idx++ > minimumIndex && i.Mnemonic == ud_mnemonic_code.UD_Imov && i.Bytes.Length == 10 /*&& i.Operands.First().Type == ud_type.UD_OP_MEM && LibCpp2ILUtils.GetImmediateValue(i, i.Operands.Last()) != 0*/); - if (execInitMethodBody.Count < 2) - { - Console.WriteLine("\tToo few instructions in ExecuteInitializations! Aborting..."); - goto bailout; - } - - var movOfGlobalCallbackList = execInitMethodBody[0].Mnemonic == ud_mnemonic_code.UD_Imov ? execInitMethodBody[0] : execInitMethodBody[1]; - - if (movOfGlobalCallbackList.Error || movOfGlobalCallbackList.Mnemonic != ud_mnemonic_code.UD_Imov) + if (indexOfFrameworkVersionLoad < 0) { - Console.WriteLine("\tInvalid MOV instruction in ExecuteInitializations, fallback failed!"); - goto bailout; + Console.WriteLine("\tCouldn't find framework load index, abort!"); + return (0, 0); } + } - ulong addrGlobalCallbackList; - if (!is32Bit) - { - var offset = LibCpp2ILUtils.GetOffsetFromMemoryAccess(movOfGlobalCallbackList, movOfGlobalCallbackList.Operands[1]); + Console.WriteLine("\tLocated probable framework version set, instruction bytes are " + methodBodyRuntimeInit[indexOfFrameworkVersionLoad].ToString()); - //This SHOULD be the address of the global list of callbacks il2cpp executes on boot, which should only contain one item, that being the function which invokes the code + metadata registration - addrGlobalCallbackList = virtAddrExecuteInit + offset; - } - else - { - //32-bit, address is literal scalar. - addrGlobalCallbackList = LibCpp2ILUtils.GetImmediateValue(movOfGlobalCallbackList, movOfGlobalCallbackList.Operands.Last()); - } + var instructionsFromThatPoint = methodBodyRuntimeInit.Skip(indexOfFrameworkVersionLoad + 1).TakeWhile(i => true).ToList(); + var calls = instructionsFromThatPoint.Where(i => i.Mnemonic == ud_mnemonic_code.UD_Icall).ToList(); - var bytesNotToCheck = execInitMethodBody[1].Bytes; - Console.WriteLine($"\tGot what we believe is the address of the global callback list - 0x{addrGlobalCallbackList:X}. Searching for another MOV instruction that references it within the .text segment..."); + if (calls.Count < 3) + { + Console.WriteLine("\tRuntime::Init does not call enough methods for us to locate ExecuteInitializations! Fallback failed!"); + return (0, 0); + } - var textSection = sections.First(s => s.Name == ".text"); - var toDisasm = raw.SubArray((int) textSection.PointerToRawData, (int) textSection.SizeOfRawData); - var allInstructionsInTextSection = LibCpp2ILUtils.DisassembleBytes(is32Bit, toDisasm); - - Console.WriteLine($"\tDisassembled entire .text section, into {allInstructionsInTextSection.Count} instructions."); + var thirdCall = calls[2]; - Instruction callbackListWrite; - //if 32-bit, first param is a literal address (LibCpp2ILUtils.GetImmediateValue), else it's an rip offset. - if (!is32Bit) - { - var allMOVs = allInstructionsInTextSection.Where(i => i.Mnemonic == ud_mnemonic_code.UD_Imov && i.Operands[0].Base == ud_type.UD_R_RIP).ToList(); + //Now we have the address of the ExecuteInitializations function + var virtAddrExecuteInit = LibCpp2ILUtils.GetJumpTarget(thirdCall, thirdCall.PC + virtualAddressRuntimeInit); + Console.WriteLine($"\tLocated probable ExecuteInitializations function at virt addr 0x{virtAddrExecuteInit:X}"); - Console.WriteLine($"\t\t...of which {allMOVs.Count} are MOV instructions with a global/Rip first base"); + //Could be first or second instruction + List execInitMethodBody = LibCpp2ILUtils.GetMethodBodyAtRawAddress(this, MapVirtualAddressToRaw(virtAddrExecuteInit), true); - var references = allMOVs.AsParallel().Where(mov => - { - var rawMemoryRead = LibCpp2ILUtils.GetOffsetFromMemoryAccess(mov, mov.Operands[0]); - var virtMemoryRead = rawMemoryRead + textSection.VirtualAddress + imageBase; - return virtMemoryRead == addrGlobalCallbackList; - }).ToList(); + if (execInitMethodBody.Count < 2) + { + Console.WriteLine("\tToo few instructions in ExecuteInitializations! Aborting..."); + return (0, 0); + } - Console.WriteLine($"\t\t...of which {references.Count} have a first parameter as that callback list."); + var movOfGlobalCallbackList = execInitMethodBody[0].Mnemonic == ud_mnemonic_code.UD_Imov ? execInitMethodBody[0] : execInitMethodBody[1]; - if (references.Count != 1) - { - Console.WriteLine("\tExpected only one reference, but didn't get that, fallback failed!"); - goto bailout; - } + if (movOfGlobalCallbackList.Error || movOfGlobalCallbackList.Mnemonic != ud_mnemonic_code.UD_Imov) + { + Console.WriteLine("\tInvalid MOV instruction in ExecuteInitializations, fallback failed!"); + return (0, 0); + } - callbackListWrite = references[0]; - } - else - { - var allMOVsWhichReferenceAddress = allInstructionsInTextSection.Where(i => i.Mnemonic == ud_mnemonic_code.UD_Imov && LibCpp2ILUtils.GetImmediateValue(i, i.Operands[0]) == addrGlobalCallbackList).ToList(); - - Console.WriteLine($"Found {allMOVsWhichReferenceAddress.Count} MOV instructions which reference address 0x{addrGlobalCallbackList:X} as the first operand."); + ulong addrGlobalCallbackList; + if (!is32Bit) + { + var offset = LibCpp2ILUtils.GetOffsetFromMemoryAccess(movOfGlobalCallbackList, movOfGlobalCallbackList.Operands[1]); - if (allMOVsWhichReferenceAddress.Count != 1) - { - Console.WriteLine("\tExpected only one reference, but didn't get that, fallback failed!"); - goto bailout; - } + //This SHOULD be the address of the global list of callbacks il2cpp executes on boot, which should only contain one item, that being the function which invokes the code + metadata registration + addrGlobalCallbackList = virtAddrExecuteInit + offset; + } + else + { + //32-bit, address is literal scalar. + addrGlobalCallbackList = LibCpp2ILUtils.GetImmediateValue(movOfGlobalCallbackList, movOfGlobalCallbackList.Operands.Last()); + } - callbackListWrite = allMOVsWhichReferenceAddress.First(); - } - - var virtualAddressOfInstruction = callbackListWrite.PC + imageBase + textSection.VirtualAddress - (ulong) callbackListWrite.Length; - Console.WriteLine($"\tLocated a single write reference to callback list, therefore identified callback registration function, which must contain the instruction at virt address 0x{virtualAddressOfInstruction:X}"); + var bytesNotToCheck = execInitMethodBody[1].Bytes; + Console.WriteLine($"\tGot what we believe is the address of the global callback list - 0x{addrGlobalCallbackList:X}. Searching for another MOV instruction that references it within the .text segment..."); - var instructionIdx = allInstructionsInTextSection.IndexOf(callbackListWrite); - var instructionsUpToCallbackListWrite = allInstructionsInTextSection.Take(instructionIdx).ToList(); - instructionsUpToCallbackListWrite.Reverse(); + var textSection = sections.First(s => s.Name == ".text"); + var toDisasm = raw.SubArray((int) textSection.PointerToRawData, (int) textSection.SizeOfRawData); + var allInstructionsInTextSection = LibCpp2ILUtils.DisassembleBytes(is32Bit, toDisasm); - var indexOfFirstInt3 = instructionsUpToCallbackListWrite.FindIndex(i => i.Mnemonic == ud_mnemonic_code.UD_Iint3); - var firstInstructionInRegisterCallback = instructionsUpToCallbackListWrite[indexOfFirstInt3 - 1]; + Console.WriteLine($"\tDisassembled entire .text section, into {allInstructionsInTextSection.Count} instructions."); - var virtAddrRegisterCallback = firstInstructionInRegisterCallback.PC + imageBase + textSection.VirtualAddress - (ulong) firstInstructionInRegisterCallback.Length; - - Console.WriteLine($"\tGot address of register callback function to be 0x{virtAddrRegisterCallback:X}"); + Instruction callbackListWrite; + //if 32-bit, first param is a literal address (LibCpp2ILUtils.GetImmediateValue), else it's an rip offset. + if (!is32Bit) + { + var allMOVs = allInstructionsInTextSection.Where(i => i.Mnemonic == ud_mnemonic_code.UD_Imov && i.Operands[0].Base == ud_type.UD_R_RIP).ToList(); - var callToRegisterCallback = allInstructionsInTextSection.Find(i => (i.Mnemonic == ud_mnemonic_code.UD_Icall || i.Mnemonic == ud_mnemonic_code.UD_Ijmp) && LibCpp2ILUtils.GetJumpTarget(i, imageBase + textSection.VirtualAddress + i.PC) == virtAddrRegisterCallback); + Console.WriteLine($"\t\t...of which {allMOVs.Count} are MOV instructions with a global/Rip first base"); - var addrCallToRegCallback = callToRegisterCallback.PC + imageBase + textSection.VirtualAddress - (ulong) callToRegisterCallback.Length; - Console.WriteLine($"\tFound a call to that function at 0x{addrCallToRegCallback:X}"); + var references = allMOVs.AsParallel().Where(mov => + { + var rawMemoryRead = LibCpp2ILUtils.GetOffsetFromMemoryAccess(mov, mov.Operands[0]); + var virtMemoryRead = rawMemoryRead + textSection.VirtualAddress + imageBase; + return virtMemoryRead == addrGlobalCallbackList; + }).ToList(); - var indexOfCallToRegisterCallback = allInstructionsInTextSection.IndexOf(callToRegisterCallback); - Instruction? loadOfAddressToCodegenRegistrationFunction = null; - for (var i = indexOfCallToRegisterCallback; i > 0; i--) + Console.WriteLine($"\t\t...of which {references.Count} have a first parameter as that callback list."); + + if (references.Count != 1) { - if (!is32Bit) - { - if (allInstructionsInTextSection[i].Mnemonic == ud_mnemonic_code.UD_Ilea && allInstructionsInTextSection[i].Operands[0].Base == ud_type.UD_R_RDX) - { - loadOfAddressToCodegenRegistrationFunction = allInstructionsInTextSection[i]; - break; - } - } - else - { - //32-bit, look for a PUSH with a non-zero addr - if (allInstructionsInTextSection[i].Mnemonic == ud_mnemonic_code.UD_Ipush && LibCpp2ILUtils.GetImmediateValue(allInstructionsInTextSection[i], allInstructionsInTextSection[i].Operands.First()) != 0) - { - loadOfAddressToCodegenRegistrationFunction = allInstructionsInTextSection[i]; - break; - } - } + Console.WriteLine("\tExpected only one reference, but didn't get that, fallback failed!"); + return (0, 0); } - if (loadOfAddressToCodegenRegistrationFunction == null) + callbackListWrite = references[0]; + } + else + { + var allMOVsWhichReferenceAddress = allInstructionsInTextSection.Where(i => i.Mnemonic == ud_mnemonic_code.UD_Imov && LibCpp2ILUtils.GetImmediateValue(i, i.Operands[0]) == addrGlobalCallbackList).ToList(); + + Console.WriteLine($"Found {allMOVsWhichReferenceAddress.Count} MOV instructions which reference address 0x{addrGlobalCallbackList:X} as the first operand."); + + if (allMOVsWhichReferenceAddress.Count != 1) { - Console.WriteLine("Failed to find an instruction loading the address of the codegen reg function. Fallback failed."); - goto bailout; + Console.WriteLine("\tExpected only one reference, but didn't get that, fallback failed!"); + return (0, 0); } - - Console.WriteLine("\tGot instruction containing the address of the codegen registration function: " + loadOfAddressToCodegenRegistrationFunction); - - ulong virtAddrS_Il2CppCodegenRegistration; - if (!is32Bit) - virtAddrS_Il2CppCodegenRegistration = LibCpp2ILUtils.GetOffsetFromMemoryAccess(loadOfAddressToCodegenRegistrationFunction, loadOfAddressToCodegenRegistrationFunction.Operands[1]) + imageBase + textSection.VirtualAddress; - else - virtAddrS_Il2CppCodegenRegistration = LibCpp2ILUtils.GetImmediateValue(loadOfAddressToCodegenRegistrationFunction, loadOfAddressToCodegenRegistrationFunction.Operands.First()); - - Console.WriteLine($"\tWhich means s_Il2CppCodegenRegistration is in-binary at 0x{virtAddrS_Il2CppCodegenRegistration:X}"); - //This should consist of LEA, LEA, LEA, JMP on x64, or PUSH PUSH PUSH CALL on 32-bit - List methodBodyS_Il2CppCodegenRegistration = LibCpp2ILUtils.GetMethodBodyAtRawAddress(this, MapVirtualAddressToRaw(virtAddrS_Il2CppCodegenRegistration), false); + callbackListWrite = allMOVsWhichReferenceAddress.First(); + } + + var virtualAddressOfInstruction = callbackListWrite.PC + imageBase + textSection.VirtualAddress - (ulong) callbackListWrite.Length; + Console.WriteLine($"\tLocated a single write reference to callback list, therefore identified callback registration function, which must contain the instruction at virt address 0x{virtualAddressOfInstruction:X}"); + + var instructionIdx = allInstructionsInTextSection.IndexOf(callbackListWrite); + var instructionsUpToCallbackListWrite = allInstructionsInTextSection.Take(instructionIdx).ToList(); + instructionsUpToCallbackListWrite.Reverse(); + + var indexOfFirstInt3 = instructionsUpToCallbackListWrite.FindIndex(i => i.Mnemonic == ud_mnemonic_code.UD_Iint3); + var firstInstructionInRegisterCallback = instructionsUpToCallbackListWrite[indexOfFirstInt3 - 1]; + var virtAddrRegisterCallback = firstInstructionInRegisterCallback.PC + imageBase + textSection.VirtualAddress - (ulong) firstInstructionInRegisterCallback.Length; + + Console.WriteLine($"\tGot address of register callback function to be 0x{virtAddrRegisterCallback:X}"); + + var callToRegisterCallback = allInstructionsInTextSection.Find(i => (i.Mnemonic == ud_mnemonic_code.UD_Icall || i.Mnemonic == ud_mnemonic_code.UD_Ijmp) && LibCpp2ILUtils.GetJumpTarget(i, imageBase + textSection.VirtualAddress + i.PC) == virtAddrRegisterCallback); + + var addrCallToRegCallback = callToRegisterCallback.PC + imageBase + textSection.VirtualAddress - (ulong) callToRegisterCallback.Length; + Console.WriteLine($"\tFound a call to that function at 0x{addrCallToRegCallback:X}"); + + var indexOfCallToRegisterCallback = allInstructionsInTextSection.IndexOf(callToRegisterCallback); + Instruction? loadOfAddressToCodegenRegistrationFunction = null; + for (var i = indexOfCallToRegisterCallback; i > 0; i--) + { if (!is32Bit) { - var loadMetadataRegistration = methodBodyS_Il2CppCodegenRegistration.Find(i => i.Mnemonic == ud_mnemonic_code.UD_Ilea && i.Operands[0].Base == ud_type.UD_R_RDX); - var loadCodeRegistration = methodBodyS_Il2CppCodegenRegistration.Find(i => i.Mnemonic == ud_mnemonic_code.UD_Ilea && i.Operands[0].Base == ud_type.UD_R_RCX); //This one's RCX not RDX - - metadataRegistration = LibCpp2ILUtils.GetOffsetFromMemoryAccess(loadMetadataRegistration, loadMetadataRegistration.Operands[1]) + virtAddrS_Il2CppCodegenRegistration; - codeRegistration = LibCpp2ILUtils.GetOffsetFromMemoryAccess(loadCodeRegistration, loadCodeRegistration.Operands[1]) + virtAddrS_Il2CppCodegenRegistration; + if (allInstructionsInTextSection[i].Mnemonic == ud_mnemonic_code.UD_Ilea && allInstructionsInTextSection[i].Operands[0].Base == ud_type.UD_R_RDX) + { + loadOfAddressToCodegenRegistrationFunction = allInstructionsInTextSection[i]; + break; + } } else { - //Three pushes. First is the config, so is irrelevant - //Second is metadata reg. - //Third is code reg - var pushes = methodBodyS_Il2CppCodegenRegistration.Where(i => i.Mnemonic == ud_mnemonic_code.UD_Ipush).ToList(); - - if (pushes.Count < 3) + //32-bit, look for a PUSH with a non-zero addr + if (allInstructionsInTextSection[i].Mnemonic == ud_mnemonic_code.UD_Ipush && LibCpp2ILUtils.GetImmediateValue(allInstructionsInTextSection[i], allInstructionsInTextSection[i].Operands.First()) != 0) { - Console.WriteLine($"\tExpecting 3 pushes, only got {pushes.Count}, aborting!"); - goto bailout; + loadOfAddressToCodegenRegistrationFunction = allInstructionsInTextSection[i]; + break; } - - metadataRegistration = LibCpp2ILUtils.GetImmediateValue(pushes[1], pushes[1].Operands.First()); - codeRegistration = LibCpp2ILUtils.GetImmediateValue(pushes[2], pushes[2].Operands.First()); } } - bailout: + if (loadOfAddressToCodegenRegistrationFunction == null) + { + Console.WriteLine("Failed to find an instruction loading the address of the codegen reg function. Fallback failed."); + return (0, 0); + } + + Console.WriteLine("\tGot instruction containing the address of the codegen registration function: " + loadOfAddressToCodegenRegistrationFunction); - if (codeRegistration == 0 && LibCpp2IlMain.Settings.AllowManualMetadataAndCodeRegInput) + ulong virtAddrS_Il2CppCodegenRegistration; + if (!is32Bit) + virtAddrS_Il2CppCodegenRegistration = LibCpp2ILUtils.GetOffsetFromMemoryAccess(loadOfAddressToCodegenRegistrationFunction, loadOfAddressToCodegenRegistrationFunction.Operands[1]) + imageBase + textSection.VirtualAddress; + else + virtAddrS_Il2CppCodegenRegistration = LibCpp2ILUtils.GetImmediateValue(loadOfAddressToCodegenRegistrationFunction, loadOfAddressToCodegenRegistrationFunction.Operands.First()); + + Console.WriteLine($"\tWhich means s_Il2CppCodegenRegistration is in-binary at 0x{virtAddrS_Il2CppCodegenRegistration:X}"); + + //This should consist of LEA, LEA, LEA, JMP on x64, or PUSH PUSH PUSH CALL on 32-bit + List methodBodyS_Il2CppCodegenRegistration = LibCpp2ILUtils.GetMethodBodyAtRawAddress(this, MapVirtualAddressToRaw(virtAddrS_Il2CppCodegenRegistration), false); + + if (!is32Bit) { - Console.Write("Couldn't identify a CodeRegistration address. If you know it, enter it now, otherwise enter nothing or zero to fail: "); - var crInput = Console.ReadLine(); - ulong.TryParse(crInput, NumberStyles.HexNumber, null, out codeRegistration); + //64-bit + var loadMetadataRegistration = methodBodyS_Il2CppCodegenRegistration.Find(i => i.Mnemonic == ud_mnemonic_code.UD_Ilea && i.Operands[0].Base == ud_type.UD_R_RDX); + var loadCodeRegistration = methodBodyS_Il2CppCodegenRegistration.Find(i => i.Mnemonic == ud_mnemonic_code.UD_Ilea && i.Operands[0].Base == ud_type.UD_R_RCX); //This one's RCX not RDX + + metadataRegistration = LibCpp2ILUtils.GetOffsetFromMemoryAccess(loadMetadataRegistration, loadMetadataRegistration.Operands[1]) + virtAddrS_Il2CppCodegenRegistration; + codeRegistration = LibCpp2ILUtils.GetOffsetFromMemoryAccess(loadCodeRegistration, loadCodeRegistration.Operands[1]) + virtAddrS_Il2CppCodegenRegistration; + + return (metadataRegistration, codeRegistration); } - if (metadataRegistration == 0 && LibCpp2IlMain.Settings.AllowManualMetadataAndCodeRegInput) + //Three pushes. First is the config, so is irrelevant + //Second is metadata reg. + //Third is code reg + var pushes = methodBodyS_Il2CppCodegenRegistration.Where(i => i.Mnemonic == ud_mnemonic_code.UD_Ipush).ToList(); + + if (pushes.Count < 3) { - Console.Write("Couldn't identify a MetadataRegistration address. If you know it, enter it now, otherwise enter nothing or zero to fail: "); - var mrInput = Console.ReadLine(); - ulong.TryParse(mrInput, NumberStyles.HexNumber, null, out metadataRegistration); + Console.WriteLine($"\tExpecting 3 pushes, only got {pushes.Count}, aborting!"); + return (0, 0); } - - Console.WriteLine("Initializing with located addresses:"); - return AutoInit(codeRegistration, metadataRegistration); + + metadataRegistration = LibCpp2ILUtils.GetImmediateValue(pushes[1], pushes[1].Operands.First()); + codeRegistration = LibCpp2ILUtils.GetImmediateValue(pushes[2], pushes[2].Operands.First()); + + return (metadataRegistration, codeRegistration); } +#endif public Il2CppType GetIl2CppTypeFromPointer(ulong pointer) { diff --git a/LibCpp2IL/PlusSearch.cs b/LibCpp2IL/PlusSearch.cs index 585b7d1d..ac736a94 100644 --- a/LibCpp2IL/PlusSearch.cs +++ b/LibCpp2IL/PlusSearch.cs @@ -1,10 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net.Mime; using System.Text; +using Iced.Intel; using LibCpp2IL.PE; -using SharpDisasm.Udis86; namespace LibCpp2IL { @@ -166,22 +165,23 @@ internal ulong FindCodeRegistrationUsingMscorlib() var codeGenAddr = endOfCodeGenRegAddr.First(); var textSection = _pe.sections.First(s => s.Name == ".text"); var toDisasm = _pe.raw.SubArray((int) textSection.PointerToRawData, (int) textSection.SizeOfRawData); - var allInstructionsInTextSection = LibCpp2ILUtils.DisassembleBytes(_pe.is32Bit, toDisasm); + var allInstructions = LibCpp2ILUtils.DisassembleBytesNew(_pe.is32Bit, toDisasm, textSection.VirtualAddress + _pe.imageBase); - var allSensibleInstructions = allInstructionsInTextSection.AsParallel().Where(i => - i.Mnemonic == ud_mnemonic_code.UD_Ilea - && !i.Error - && i.Operands.Length == 2 - && i.Operands[0].Base == ud_type.UD_R_RCX).ToList(); + var allSensibleInstructions = allInstructions.Where(i => + i.Mnemonic == Mnemonic.Lea + && i.OpCount == 2 + && i.Op0Kind == OpKind.Register + && i.Op1Kind == OpKind.Memory + && i.Op0Register == Register.RCX).ToList(); var sanity = 0; while (sanity++ < 500) { - var instruction = allSensibleInstructions.AsParallel().FirstOrDefault(i => - textSection.VirtualAddress + _pe.imageBase + LibCpp2ILUtils.GetOffsetFromMemoryAccess(i, i.Operands[1]) == codeGenAddr + var instruction = allSensibleInstructions.FirstOrDefault(i => + i.GetInstructionMemoryAddress() == codeGenAddr ); - if (instruction != null) return codeGenAddr; + if (instruction != default) return codeGenAddr; codeGenAddr -= 8; //Always 64-bit here so IntPtr is 8 } diff --git a/LibCpp2IL/README.md b/LibCpp2IL/README.md index 6917c0ba..eedf14f0 100644 --- a/LibCpp2IL/README.md +++ b/LibCpp2IL/README.md @@ -8,7 +8,7 @@ You can obtain a copy of LibCpp2IL from the [actions page](https://github.com/Sa ## Dependencies -LibCpp2IL requires you to have SharpDiasm available for it to use. +LibCpp2IL requires you to have 0xd4d's ICED disassembler available for it to use. ## Setting up