diff --git a/Cpp2IL.Core.Tests/Cpp2IL.Core.Tests.csproj b/Cpp2IL.Core.Tests/Cpp2IL.Core.Tests.csproj
index 41efa2ed..46e3de6c 100644
--- a/Cpp2IL.Core.Tests/Cpp2IL.Core.Tests.csproj
+++ b/Cpp2IL.Core.Tests/Cpp2IL.Core.Tests.csproj
@@ -1,22 +1,20 @@
-
net7.0
- enable
false
+
-
+
-
-
+
+
-
diff --git a/Cpp2IL.Core.Tests/GameLoader.cs b/Cpp2IL.Core.Tests/GameLoader.cs
index 947e0416..839f2724 100644
--- a/Cpp2IL.Core.Tests/GameLoader.cs
+++ b/Cpp2IL.Core.Tests/GameLoader.cs
@@ -1,7 +1,6 @@
using AssetRipper.VersionUtilities;
-using Cpp2IL.Core.Api;
-using Cpp2IL.Core.InstructionSets;
using Cpp2IL.Core.Model.Contexts;
+using Cpp2IL.InstructionSets.All;
using LibCpp2IL;
namespace Cpp2IL.Core.Tests;
@@ -10,20 +9,7 @@ public static class GameLoader
{
static GameLoader()
{
- InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.X86_32);
- InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.X86_64);
- InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.WASM);
- InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.ARM_V7);
- var useNewArm64 = true;
- if (useNewArm64)
- {
- InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.ARM_V8);
- }
- else
- {
- InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.ARM_V8);
- }
-
+ AllInstructionSets.Register();
LibCpp2IlBinaryRegistry.RegisterBuiltInBinarySupport();
}
diff --git a/Cpp2IL.Core/Cpp2IL.Core.csproj b/Cpp2IL.Core/Cpp2IL.Core.csproj
index 8c67f80d..fd0281f3 100644
--- a/Cpp2IL.Core/Cpp2IL.Core.csproj
+++ b/Cpp2IL.Core/Cpp2IL.Core.csproj
@@ -1,56 +1,27 @@
- Sam Byass (Samboy063)
- N/A
- Copyright © Samboy063 2019-2023
- embedded
+ net7.0;net6.0;netstandard2.0
+ true
+
Reverses Unity's IL2CPP Build Process
true
true
- 10
- enable
- true
- Samboy063.Cpp2IL.Core
- MIT
- https://github.com/SamboyCoding/Cpp2IL
- true
- git
- https://github.com/SamboyCoding/Cpp2IL.git
- net7.0;net6.0;netstandard2.0
- Cpp2IL.Core
- 2022.1.0
-
- true
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
diff --git a/Cpp2IL.Core/Cpp2IlCorePlugin.cs b/Cpp2IL.Core/Cpp2IlCorePlugin.cs
index 8cb89a1b..26243301 100644
--- a/Cpp2IL.Core/Cpp2IlCorePlugin.cs
+++ b/Cpp2IL.Core/Cpp2IlCorePlugin.cs
@@ -1,8 +1,6 @@
-using System;
using Cpp2IL.Core;
using Cpp2IL.Core.Api;
using Cpp2IL.Core.Attributes;
-using Cpp2IL.Core.InstructionSets;
using Cpp2IL.Core.OutputFormats;
using Cpp2IL.Core.ProcessingLayers;
//Need this for the assembly attribute definition below.
@@ -22,19 +20,7 @@ public override void OnLoad()
{
Logger.VerboseNewline("Initializing...", "Core Plugin");
var start = DateTime.Now;
-
- Logger.VerboseNewline("\tRegistering built-in instruction set handlers...", "Core Plugin");
-
- InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.X86_32);
- InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.X86_64);
- InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.WASM);
- InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.ARM_V7);
-
- if(Environment.GetEnvironmentVariable("CPP2IL_NEW_ARM64") != null)
- InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.ARM_V8);
- else
- InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.ARM_V8);
-
+
Logger.VerboseNewline("\tRegistering built-in binary parsers...", "Core Plugin");
LibCpp2IlBinaryRegistry.RegisterBuiltInBinarySupport();
@@ -44,7 +30,6 @@ public override void OnLoad()
OutputFormatRegistry.Register();
OutputFormatRegistry.Register();
OutputFormatRegistry.Register();
- OutputFormatRegistry.Register();
Logger.VerboseNewline("\tRegistering built-in processing layers", "Core Plugin");
diff --git a/Cpp2IL.Core/Extensions/MiscExtensions.cs b/Cpp2IL.Core/Extensions/MiscExtensions.cs
index cd183d35..868da94d 100644
--- a/Cpp2IL.Core/Extensions/MiscExtensions.cs
+++ b/Cpp2IL.Core/Extensions/MiscExtensions.cs
@@ -1,66 +1,9 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
using System.Text;
-using Cpp2IL.Core.ISIL;
-using Gee.External.Capstone.Arm;
-using Gee.External.Capstone.Arm64;
-using Iced.Intel;
-using Instruction = Iced.Intel.Instruction;
namespace Cpp2IL.Core.Extensions
{
public static class MiscExtensions
{
- public static InstructionSetIndependentOperand MakeIndependent(this Register reg) => InstructionSetIndependentOperand.MakeRegister(reg.ToString().ToLower());
-
- public static ulong GetImmediateSafe(this Instruction instruction, int op) => instruction.GetOpKind(op).IsImmediate() ? instruction.GetImmediate(op) : 0;
-
- public static bool IsJump(this Mnemonic mnemonic) => mnemonic is Mnemonic.Call or >= Mnemonic.Ja and <= Mnemonic.Js;
- public static bool IsConditionalJump(this Mnemonic mnemonic) => mnemonic.IsJump() && mnemonic != Mnemonic.Jmp && mnemonic != Mnemonic.Call;
-
- //Arm Extensions
- public static ArmRegister? RegisterSafe(this ArmOperand operand) => operand.Type != ArmOperandType.Register ? null : operand.Register;
- public static bool IsImmediate(this ArmOperand operand) => operand.Type is ArmOperandType.CImmediate or ArmOperandType.Immediate or ArmOperandType.PImmediate;
- public static int ImmediateSafe(this ArmOperand operand) => operand.IsImmediate() ? operand.Immediate : 0;
- private static ArmOperand? MemoryOperand(ArmInstruction instruction) => instruction.Details.Operands.FirstOrDefault(a => a.Type == ArmOperandType.Memory);
-
- public static ArmRegister? MemoryBase(this ArmInstruction instruction) => MemoryOperand(instruction)?.Memory.Base;
- public static ArmRegister? MemoryIndex(this ArmInstruction instruction) => MemoryOperand(instruction)?.Memory.Index;
- public static int MemoryOffset(this ArmInstruction instruction) => MemoryOperand(instruction)?.Memory.Displacement ?? 0;
-
- //Arm64 Extensions
- public static Arm64Register? RegisterSafe(this Arm64Operand operand) => operand.Type != Arm64OperandType.Register ? null : operand.Register;
- public static bool IsImmediate(this Arm64Operand operand) => operand.Type is Arm64OperandType.CImmediate or Arm64OperandType.Immediate;
- public static long ImmediateSafe(this Arm64Operand operand) => operand.IsImmediate() ? operand.Immediate : 0;
- internal static Arm64Operand? MemoryOperand(this Arm64Instruction instruction) => instruction.Details.Operands.FirstOrDefault(a => a.Type == Arm64OperandType.Memory);
-
- public static Arm64Register? MemoryBase(this Arm64Instruction instruction) => instruction.MemoryOperand()?.Memory.Base;
- public static Arm64Register? MemoryIndex(this Arm64Instruction instruction) => instruction.MemoryOperand()?.Memory.Index;
- public static int MemoryOffset(this Arm64Instruction instruction) => instruction.MemoryOperand()?.Memory.Displacement ?? 0;
-
- public static bool IsConditionalMove(this Instruction instruction)
- {
- switch (instruction.Mnemonic)
- {
- case Mnemonic.Cmove:
- case Mnemonic.Cmovne:
- case Mnemonic.Cmovs:
- case Mnemonic.Cmovns:
- case Mnemonic.Cmovg:
- case Mnemonic.Cmovge:
- case Mnemonic.Cmovl:
- case Mnemonic.Cmovle:
- case Mnemonic.Cmova:
- case Mnemonic.Cmovae:
- case Mnemonic.Cmovb:
- case Mnemonic.Cmovbe:
- return true;
- default:
- return false;
- }
- }
public static Stack Clone(this Stack original)
{
var arr = new T[original.Count];
@@ -130,9 +73,6 @@ public static T[] SubArray(this T[] source, Range range)
return arr[i];
}
- public static bool IsImmediate(this OpKind opKind) => opKind is >= OpKind.Immediate8 and <= OpKind.Immediate32to64;
-
-
public static void TrimEndWhile(this List instructions, Func predicate)
{
var i = instructions.Count - 1;
diff --git a/Cpp2IL.Core/Il2CppApiFunctions/Arm64KeyFunctionAddresses.cs b/Cpp2IL.Core/Il2CppApiFunctions/Arm64KeyFunctionAddresses.cs
deleted file mode 100644
index 09fa75bd..00000000
--- a/Cpp2IL.Core/Il2CppApiFunctions/Arm64KeyFunctionAddresses.cs
+++ /dev/null
@@ -1,314 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Cpp2IL.Core.Extensions;
-using Cpp2IL.Core.Logging;
-using Cpp2IL.Core.Model.Contexts;
-using Cpp2IL.Core.Utils;
-using Gee.External.Capstone;
-using Gee.External.Capstone.Arm64;
-using LibCpp2IL;
-using LibCpp2IL.NintendoSwitch;
-using LibCpp2IL.Reflection;
-
-namespace Cpp2IL.Core.Il2CppApiFunctions
-{
- public class Arm64KeyFunctionAddresses : BaseKeyFunctionAddresses
- {
- private List _allInstructions = new();
-
- protected override void Init(ApplicationAnalysisContext context)
- {
- var disassembler = CapstoneDisassembler.CreateArm64Disassembler(context.Binary.IsBigEndian ? Arm64DisassembleMode.BigEndian : Arm64DisassembleMode.LittleEndian);
- disassembler.EnableInstructionDetails = true;
- disassembler.DisassembleSyntax = DisassembleSyntax.Intel;
- disassembler.EnableSkipDataMode = true;
-
- var primaryExecutableSection = context.Binary.GetEntirePrimaryExecutableSection();
- var primaryExecutableSectionVa = context.Binary.GetVirtualAddressOfPrimaryExecutableSection();
- var endOfTextSection = primaryExecutableSectionVa + (ulong) primaryExecutableSection.Length;
-
- Logger.InfoNewline("\tRunning entire .text section through Arm64 disassembler, this might take up to several minutes for large games, and may fail on large games if you have <16GB ram...");
-
- Logger.VerboseNewline($"\tPrimary executable section is {primaryExecutableSection.Length} bytes, starting at 0x{primaryExecutableSectionVa:X} and extending to 0x{endOfTextSection:X}");
- var attributeGeneratorList = SharedState.AttributeGeneratorStarts.ToList();
- attributeGeneratorList.SortByExtractedKey(a => a);
-
- if (attributeGeneratorList.Count > 0)
- {
- if (context.Binary is not NsoFile)
- {
- Logger.VerboseNewline($"\tLast attribute generator function is at address 0x{attributeGeneratorList[^1]:X}. Skipping everything before that.");
-
- //Optimisation: We can skip all bytes up to and including the last attribute restoration function
- //However we don't know how long the last restoration function is, so just skip up to it, we'd only be saving a further 100 instructions or so
- //These come at the beginning of the .text section usually and the only thing that comes before them is unmanaged finalizers and initializers.
- //This may not be correct on v29 which uses the Bee compiler, which may do things differently
- var oldLength = primaryExecutableSection.Length;
-
- var toRemove = (int) (attributeGeneratorList[^1] - primaryExecutableSectionVa);
- primaryExecutableSection = primaryExecutableSection.Skip(toRemove).ToArray();
-
- primaryExecutableSectionVa = attributeGeneratorList[^1];
-
- Logger.VerboseNewline($"\tBy trimming out attribute generator functions, reduced decompilation work by {toRemove} of {oldLength} bytes (a {toRemove * 100 / (float) oldLength:f1}% saving)");
-
- //Some games (e.g. Muse Dash APK) contain the il2cpp-ified methods in the .text section instead of their own dedicated one.
- //That makes this very slow
- //Try and detect the first function
- var methodAddresses = new List();
- methodAddresses.SortByExtractedKey(a => a);
-
- if (methodAddresses[0] < endOfTextSection && context.Binary.GetVirtualAddressOfExportedFunctionByName("il2cpp_object_new") != 0)
- {
- var exportAddresses = new[]
- {
- "il2cpp_object_new", "il2cpp_value_box", "il2cpp_runtime_class_init", "il2cpp_array_new_specific",
- "il2cpp_type_get_object", "il2cpp_resolve_icall", "il2cpp_string_new", "il2cpp_string_new_wrapper",
- "il2cpp_raise_exception"
- }.Select(context.Binary.GetVirtualAddressOfExportedFunctionByName).Where(a => a > 0).ToArray();
-
- var lastExport = exportAddresses.Max();
- var firstExport = exportAddresses.Min();
-
- Logger.VerboseNewline($"\tDetected that the il2cpp-ified managed methods are in the .text section and api functions are available. Attempting to trim out managed methods for KFA scanning - the first managed method is at 0x{methodAddresses[0]:X} and the last at 0x{methodAddresses[^1]:X}, " +
- $"the first export function is at 0x{firstExport:X} and the last at 0x{lastExport:X}");
-
- //I am assuming, arbitrarily, that the exports are always towards the end of the managed methods, in this case.
- var startFrom = Math.Min(firstExport, methodAddresses[^1]);
-
- //Just in case we didn't get the first export, let's subtract a little
- if (startFrom > 0x100_0000)
- startFrom -= 0x10_0000;
-
- Logger.VerboseNewline($"\tTrimming everything before 0x{startFrom:X}.");
- oldLength = primaryExecutableSection.Length;
-
- toRemove = (int) (startFrom - primaryExecutableSectionVa);
- primaryExecutableSection = primaryExecutableSection.Skip(toRemove).ToArray();
-
- primaryExecutableSectionVa = startFrom;
-
- Logger.VerboseNewline($"\tBy trimming out most of the il2cpp-ified managed methods, reduced decompilation work by {toRemove} of {oldLength} bytes (a {toRemove * 100L / (float) oldLength:f1}% saving)");
- }
- else if (methodAddresses[0] < endOfTextSection)
- {
- Logger.VerboseNewline($"\tDetected that the il2cpp-ified managed methods are in the .text section, but api functions are not available. Attempting to (conservatively) trim out managed methods for KFA scanning - the first managed method is at 0x{methodAddresses[0]:X} and the last at 0x{methodAddresses[^1]:X}");
-
- var startFrom = methodAddresses[^1];
-
- //Just in case the exports are mixed in with the end of the managed methods, let's subtract a little
- if (startFrom > 0x100_0000)
- startFrom -= 0x10_0000;
-
- Logger.VerboseNewline($"\tTrimming everything before 0x{startFrom:X}.");
- oldLength = primaryExecutableSection.Length;
-
- toRemove = (int) (startFrom - primaryExecutableSectionVa);
- primaryExecutableSection = primaryExecutableSection.Skip(toRemove).ToArray();
-
- primaryExecutableSectionVa = startFrom;
-
- Logger.VerboseNewline($"\tBy trimming out most of the il2cpp-ified managed methods, reduced decompilation work by {toRemove} of {oldLength} bytes (a {toRemove * 100L / (float) oldLength:f1}% saving)");
- }
- }
- else
- {
- //For now we skip everything after the last attribute generator. Not sure this is always reliable but in test binaries it works.
- //We choose last not first to include all the generators, so that we hopefully have some context for api function detection.
- Logger.VerboseNewline($"\tNSO: Last attribute generator function is at address 0x{attributeGeneratorList[^1]:X}. Skipping everything after that.");
-
- var oldLength = primaryExecutableSection.Length;
-
- var toKeep = (int) (attributeGeneratorList[^1] - primaryExecutableSectionVa);
- primaryExecutableSection = primaryExecutableSection.SubArray(..toKeep);
-
- //This doesn't change, we've trimmed the end, not the beginning
- // primaryExecutableSectionVa = primaryExecutableSectionVa;
-
- Logger.VerboseNewline($"\tBy trimming out everything after and including attribute generator functions, reduced decompilation work by {oldLength - toKeep} of {oldLength} bytes (a {(oldLength - toKeep) * 100L / (float) oldLength:f1}% saving)");
- }
- }
-
- _allInstructions = disassembler.Disassemble(primaryExecutableSection, (long) primaryExecutableSectionVa).ToList();
- }
-
- protected override IEnumerable FindAllThunkFunctions(ulong addr, uint maxBytesBack = 0, params ulong[] addressesToIgnore)
- {
- var allBranchesToAddr = _allInstructions.Where(i => i.Mnemonic is "b" or "bl")
- .Where(i => i.Details.Operands[0].IsImmediate() && i.Details.Operands[0].Immediate == (long) addr)
- .ToList();
-
- foreach (var potentialBranch in allBranchesToAddr)
- {
- if (addressesToIgnore.Contains((ulong) potentialBranch.Address))
- continue;
-
- var backtrack = 0;
- var idx = _allInstructions.IndexOf(potentialBranch);
-
- do
- {
- //About the only way we have of checking for a thunk is if there is an all-zero instruction or another unconditional branch just before this
- //Or a ret, but that's less reliable.
- //if so, this is probably a thunk.
- if (idx - backtrack > 0)
- {
- var prevInstruction = _allInstructions[idx - backtrack - 1];
-
- if (addressesToIgnore.Contains((ulong) prevInstruction.Address))
- {
- backtrack++;
- continue;
- }
-
- if (prevInstruction.IsSkippedData && prevInstruction.Bytes.All(b => b == 0))
- {
- //All-zero instructions are a match
- yield return (ulong) potentialBranch.Address - (ulong) (backtrack * 4);
- break;
- }
-
- if (prevInstruction.Mnemonic is "adrp" or "str")
- {
- //ADRP instructions are a deal breaker - this means we're loading something from memory, so it's not a simple thunk
- break;
- }
-
- if (prevInstruction.Mnemonic is "b" or "bl")
- {
- //Previous branches are a match
- yield return (ulong) potentialBranch.Address - (ulong) (backtrack * 4);
- break;
- }
-
- if (prevInstruction.Mnemonic is "ret")
- {
- //Previous rets are a match
- yield return (ulong) potentialBranch.Address - (ulong) (backtrack * 4);
- break;
- }
- }
-
- //We're working in the .text section here so we have few symbols, so there's no point looking for the previous one.
-
- backtrack++;
- } while (backtrack * 4 < maxBytesBack);
- }
- }
-
- protected override ulong GetObjectIsInstFromSystemType()
- {
- Logger.Verbose("\tTrying to use System.Type::IsInstanceOfType to find il2cpp::vm::Object::IsInst...");
- var typeIsInstanceOfType = LibCpp2IlReflection.GetType("Type", "System")?.Methods?.FirstOrDefault(m => m.Name == "IsInstanceOfType");
- if (typeIsInstanceOfType == null)
- {
- Logger.VerboseNewline("Type or method not found, aborting.");
- return 0;
- }
-
- //IsInstanceOfType is a very simple ICall, that looks like this:
- // Il2CppClass* klass = vm::Class::FromIl2CppType(type->type.type);
- // return il2cpp::vm::Object::IsInst(obj, klass) != NULL;
- //The last call is to Object::IsInst
-
- Logger.Verbose($"IsInstanceOfType found at 0x{typeIsInstanceOfType.MethodPointer:X}...");
- var instructions = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(typeIsInstanceOfType.MethodPointer, false);
-
- var lastCall = instructions.LastOrDefault(i => i.Mnemonic == "bl");
-
- if (lastCall == null)
- {
- Logger.VerboseNewline("Method does not match expected signature. Aborting.");
- return 0;
- }
-
- Logger.VerboseNewline($"Success. IsInst found at 0x{lastCall.Details.Operands[0].Immediate:X}");
- return (ulong) lastCall.Details.Operands[0].Immediate;
- }
-
- protected override ulong FindFunctionThisIsAThunkOf(ulong thunkPtr, bool prioritiseCall = false)
- {
- var idx = _allInstructions.FindIndex(i => i.Address == (long) thunkPtr);
-
- if (idx < 0)
- return 0;
-
- //Easy case, we have an unconditional jump at that address, just return what it points at
- if (_allInstructions[idx].Mnemonic is "b" or "bl")
- return (ulong) _allInstructions[idx].Details.Operands[0].Immediate;
-
- //Max number of instructions to check is 12. I use this because we check 50 bytes in x86 and 4 * 12 is 48.
-
- for (var i = 0; i <= 12; i++)
- {
- idx++;
- if (_allInstructions[idx].Mnemonic is "b" or "bl")
- return (ulong) _allInstructions[idx].Details.Operands[0].Immediate;
- }
-
- return 0;
- }
-
- protected override int GetCallerCount(ulong toWhere) => _allInstructions.Where(i => i.Mnemonic is "b" or "bl")
- .Count(i => i.Details.Operands[0].IsImmediate() && i.Details.Operands[0].Immediate == (long) toWhere);
-
- protected override void AttemptInstructionAnalysisToFillGaps()
- {
- if (il2cpp_object_new == 0)
- {
- Logger.Verbose("\tAttempting to use Array GetEnumerator to find il2cpp_codegen_object_new...");
- if (LibCpp2IlReflection.GetType("Array", "System") is { } arrayTypeDef)
- {
- if (arrayTypeDef.Methods!.FirstOrDefault(m => m.Name == "GetEnumerator") is { } methodDef)
- {
- var ptr = methodDef.MethodPointer;
- var body = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(ptr);
-
- //Looking for adrp, ldr, ldr, bl. Probably more than one - the first will be initializing the method, second will be the constructor call
- var probableResult = 0L;
- var numSeen = 0;
- for (var i = 0; i < body.Count - 4; i++)
- {
- if (body[i].Mnemonic != "adrp" || body[i + 1].Mnemonic != "ldr" || body[i + 2].Mnemonic != "ldr" || body[i + 3].Mnemonic != "bl")
- continue;
-
- if (numSeen++ < 2) //Only store first one or second one
- probableResult = body[i + 3].Details.Operands[0].Immediate;
- }
-
- if (probableResult > 0)
- {
- Logger.Verbose($"Probably found at 0x{probableResult:X}...");
-
- //This is *codegen*_object_new. Probably. Check it
- var thunk = FindFunctionThisIsAThunkOf((ulong) probableResult);
- long addrVmObjectNew;
- if (thunk > 0)
- //We've found codegen_object_new, map to vm::Object::New, then try and get back to object_new
- addrVmObjectNew = (long) thunk;
- else
- //Looks like we've been inlined and this is just vm::Object::New.
- addrVmObjectNew = probableResult;
-
- var allThunks = FindAllThunkFunctions((ulong) addrVmObjectNew, 16, (ulong) probableResult).ToList();
-
- allThunks.SortByExtractedKey(GetCallerCount); //Sort in ascending order by caller count
- allThunks.Reverse(); //Reverse to be in descending order
-
- il2cpp_object_new = allThunks.FirstOrDefault(); //Take first (i.e. most called)
-
- Logger.VerboseNewline($"Leaving il2cpp_object_new at 0x{il2cpp_object_new:X}");
- }
- else
- Logger.VerboseNewline("Couldn't find a matching instruction, can't be used.");
- }
- else
- Logger.VerboseNewline("Method stripped, can't be used.");
- }
- else
- Logger.VerboseNewline("Type stripped, can't be used.");
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Cpp2IL.Core/Il2CppApiFunctions/BaseKeyFunctionAddresses.cs b/Cpp2IL.Core/Il2CppApiFunctions/BaseKeyFunctionAddresses.cs
index 8ace55fc..65aed934 100644
--- a/Cpp2IL.Core/Il2CppApiFunctions/BaseKeyFunctionAddresses.cs
+++ b/Cpp2IL.Core/Il2CppApiFunctions/BaseKeyFunctionAddresses.cs
@@ -1,12 +1,7 @@
-using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
-using System.Linq;
using Cpp2IL.Core.Logging;
using Cpp2IL.Core.Model.Contexts;
-using Cpp2IL.Core.Utils;
-using Iced.Intel;
using LibCpp2IL;
-using LibCpp2IL.Reflection;
namespace Cpp2IL.Core.Il2CppApiFunctions
{
@@ -49,7 +44,7 @@ public abstract class BaseKeyFunctionAddresses
public ulong AddrPInvokeLookup; //TODO Re-find this and fix name
- private ApplicationAnalysisContext _appContext = null!; //Always initialized before used
+ protected ApplicationAnalysisContext _appContext = null!; //Always initialized before used
private readonly HashSet resolvedAddresses = new();
@@ -72,9 +67,7 @@ public void Find(ApplicationAnalysisContext applicationAnalysisContext)
Init(applicationAnalysisContext);
//Try to find System.Exception (should always be there)
- if (applicationAnalysisContext.Binary.InstructionSetId == DefaultInstructionSets.X86_32 || applicationAnalysisContext.Binary.InstructionSetId == DefaultInstructionSets.X86_64)
- //TODO make this abstract and implement in subclasses.
- TryGetInitMetadataFromException();
+ TryGetInitMetadataFromException();
//New Object
FindExport("il2cpp_object_new", out il2cpp_object_new);
@@ -115,38 +108,8 @@ public void Find(ApplicationAnalysisContext applicationAnalysisContext)
InitializeResolvedAddresses();
}
- protected void TryGetInitMetadataFromException()
+ protected virtual void TryGetInitMetadataFromException()
{
- //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 = LibCpp2IlReflection.GetType("Exception", "System")!;
- 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 at {targetMethod.MethodPointer}. Taking first CALL as the (version-specific) metadata initialization function...");
-
- var disasm = X86Utils.GetMethodBodyAtVirtAddressNew(targetMethod.MethodPointer, false);
- var calls = disasm.Where(i => i.Mnemonic == Mnemonic.Call).ToList();
-
- if (calls.Count == 0)
- {
- Logger.WarnNewline("Couldn't find any call instructions in the method body. This is not expected. Will not have metadata initialization function.");
- return;
- }
-
- if (_appContext.MetadataVersion < 27)
- {
- il2cpp_codegen_initialize_method = calls.First().NearBranchTarget;
- Logger.VerboseNewline($"\t\til2cpp_codegen_initialize_method => 0x{il2cpp_codegen_initialize_method:X}");
- }
- else
- {
- il2cpp_codegen_initialize_runtime_metadata = calls.First().NearBranchTarget;
- Logger.VerboseNewline($"\t\til2cpp_codegen_initialize_runtime_metadata => 0x{il2cpp_codegen_initialize_runtime_metadata:X}");
- }
- }
}
protected virtual void AttemptInstructionAnalysisToFillGaps()
@@ -183,7 +146,7 @@ private void FindThunks()
if (il2cpp_type_get_object != 0)
{
- Logger.Verbose("\t\tMapping il2cpp_resolve_icall to Reflection::GetTypeObject...");
+ Logger.Verbose("\t\tMapping il2cpp_type_get_object to Reflection::GetTypeObject...");
il2cpp_vm_reflection_get_type_object = FindFunctionThisIsAThunkOf(il2cpp_type_get_object);
Logger.VerboseNewline($"Found at 0x{il2cpp_vm_reflection_get_type_object:X}");
}
diff --git a/Cpp2IL.Core/Il2CppApiFunctions/NewArm64KeyFunctionAddresses.cs b/Cpp2IL.Core/Il2CppApiFunctions/NewArm64KeyFunctionAddresses.cs
deleted file mode 100644
index 8bbdd99a..00000000
--- a/Cpp2IL.Core/Il2CppApiFunctions/NewArm64KeyFunctionAddresses.cs
+++ /dev/null
@@ -1,135 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Disarm;
-using Cpp2IL.Core.Logging;
-using Cpp2IL.Core.Utils;
-using Iced.Intel;
-using LibCpp2IL;
-using LibCpp2IL.Reflection;
-
-namespace Cpp2IL.Core.Il2CppApiFunctions;
-
-public class NewArm64KeyFunctionAddresses : BaseKeyFunctionAddresses
-{
- private Arm64DisassemblyResult? _cachedDisassembledBytes;
-
- private Arm64DisassemblyResult DisassembleTextSection()
- {
- if (_cachedDisassembledBytes == null)
- {
- var toDisasm = LibCpp2IlMain.Binary!.GetEntirePrimaryExecutableSection();
- _cachedDisassembledBytes = Disassembler.Disassemble(toDisasm, LibCpp2IlMain.Binary.GetVirtualAddressOfPrimaryExecutableSection());
- }
-
- return _cachedDisassembledBytes.Value;
- }
-
- protected override IEnumerable FindAllThunkFunctions(ulong addr, uint maxBytesBack = 0, params ulong[] addressesToIgnore)
- {
- //Disassemble .text
- var disassembly = DisassembleTextSection();
-
- //Find all jumps to the target address
- var matchingJmps = disassembly.Instructions.Where(i => i.Mnemonic is Arm64Mnemonic.B or Arm64Mnemonic.BL && i.BranchTarget == addr).ToList();
-
- foreach (var matchingJmp in matchingJmps)
- {
- if (addressesToIgnore.Contains(matchingJmp.Address)) continue;
-
- //Find this instruction in the raw file
- var offsetInPe = (ulong) LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(matchingJmp.Address);
- 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 + 4);
-
- //Double-cc = thunk
- if (previousByte == 0xCC && nextByte == 0xCC)
- {
- yield return matchingJmp.Address;
- continue;
- }
-
- if (nextByte == 0xCC && maxBytesBack > 0)
- {
- for (ulong backtrack = 1; backtrack < maxBytesBack && offsetInPe - backtrack > 0; backtrack++)
- {
- if (addressesToIgnore.Contains(matchingJmp.Address - (backtrack - 1)))
- //Move to next jmp
- break;
-
- if (LibCpp2IlMain.Binary.GetByteAtRawAddress(offsetInPe - backtrack) == 0xCC)
- {
- yield return matchingJmp.Address - (backtrack - 1);
- break;
- }
- }
- }
- }
- }
-
- protected override ulong GetObjectIsInstFromSystemType()
- {
- Logger.Verbose("\tTrying to use System.Type::IsInstanceOfType to find il2cpp::vm::Object::IsInst...");
- var typeIsInstanceOfType = LibCpp2IlReflection.GetType("Type", "System")?.Methods?.FirstOrDefault(m => m.Name == "IsInstanceOfType");
- if (typeIsInstanceOfType == null)
- {
- Logger.VerboseNewline("Type or method not found, aborting.");
- return 0;
- }
-
- //IsInstanceOfType is a very simple ICall, that looks like this:
- // Il2CppClass* klass = vm::Class::FromIl2CppType(type->type.type);
- // return il2cpp::vm::Object::IsInst(obj, klass) != NULL;
- //The last call is to Object::IsInst
-
- Logger.Verbose($"IsInstanceOfType found at 0x{typeIsInstanceOfType.MethodPointer:X}...");
- var instructions = X86Utils.GetMethodBodyAtVirtAddressNew(typeIsInstanceOfType.MethodPointer, true);
-
- var lastCall = instructions.LastOrDefault(i => i.Mnemonic == Mnemonic.Call);
-
- if (lastCall.Mnemonic == Mnemonic.INVALID)
- {
- Logger.VerboseNewline("Method does not match expected signature. Aborting.");
- return 0;
- }
-
- Logger.VerboseNewline($"Success. IsInst found at 0x{lastCall.NearBranchTarget:X}");
- return lastCall.NearBranchTarget;
- }
-
- protected override ulong FindFunctionThisIsAThunkOf(ulong thunkPtr, bool prioritiseCall = false)
- {
- var instructions = X86Utils.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;
- }
- }
-
- protected override int GetCallerCount(ulong toWhere)
- {
- //Disassemble .text
- var disassembly = DisassembleTextSection();
-
- //Find all jumps to the target address
- return disassembly.Instructions.Count(i => i.Mnemonic is Arm64Mnemonic.B or Arm64Mnemonic.BL && i.BranchTarget == toWhere);
- }
-}
diff --git a/Cpp2IL.Core/InstructionSets/Arm64InstructionSet.cs b/Cpp2IL.Core/InstructionSets/Arm64InstructionSet.cs
deleted file mode 100644
index 6faee022..00000000
--- a/Cpp2IL.Core/InstructionSets/Arm64InstructionSet.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Cpp2IL.Core.Api;
-using Cpp2IL.Core.Graphs;
-using Cpp2IL.Core.Il2CppApiFunctions;
-using Cpp2IL.Core.ISIL;
-using Cpp2IL.Core.Model.Contexts;
-using Cpp2IL.Core.Utils;
-using LibCpp2IL;
-
-namespace Cpp2IL.Core.InstructionSets;
-
-public class Arm64InstructionSet : Cpp2IlInstructionSet
-{
- public virtual IControlFlowGraph BuildGraphForMethod(MethodAnalysisContext context)
- {
- return null!;
- }
-
- public override Memory GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator)
- {
- //Avoid use of capstone where possible
- if (true || context is not ConcreteGenericMethodAnalysisContext)
- {
- //Managed method or attr gen => grab raw byte range between a and b
- var startOfNextFunction = (int) MiscUtils.GetAddressOfNextFunctionStart(context.UnderlyingPointer) - 1;
- var ptrAsInt = (int) context.UnderlyingPointer;
- var count = startOfNextFunction - ptrAsInt;
-
- if (startOfNextFunction > 0)
- return LibCpp2IlMain.Binary!.GetRawBinaryContent().AsMemory(ptrAsInt, count);
- }
-
- var instructions = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(context.UnderlyingPointer);
-
- return instructions.SelectMany(i => i.Bytes).ToArray();
- }
-
- public override List GetIsilFromMethod(MethodAnalysisContext context)
- {
- return new();
- }
-
- public override BaseKeyFunctionAddresses CreateKeyFunctionAddressesInstance() => new Arm64KeyFunctionAddresses();
-
- public override string PrintAssembly(MethodAnalysisContext context)
- {
- var sb = new StringBuilder();
-
- var instructions = Arm64Utils.GetArm64MethodBodyAtVirtualAddress(context.UnderlyingPointer);
-
- var first = true;
- foreach (var instruction in instructions)
- {
- if (!first)
- sb.AppendLine();
-
- first = false;
- sb.Append("0x").Append(instruction.Address.ToString("X")).Append(" ").Append(instruction.Mnemonic).Append(" ").Append(instruction.Operand);
- }
-
- return sb.ToString();
- }
-}
diff --git a/Cpp2IL.Core/InstructionSets/ArmV7InstructionSet.cs b/Cpp2IL.Core/InstructionSets/ArmV7InstructionSet.cs
deleted file mode 100644
index a3208e57..00000000
--- a/Cpp2IL.Core/InstructionSets/ArmV7InstructionSet.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Cpp2IL.Core.Api;
-using Cpp2IL.Core.Graphs;
-using Cpp2IL.Core.Il2CppApiFunctions;
-using Cpp2IL.Core.ISIL;
-using Cpp2IL.Core.Model.Contexts;
-using Cpp2IL.Core.Utils;
-
-namespace Cpp2IL.Core.InstructionSets;
-
-public class ArmV7InstructionSet : Cpp2IlInstructionSet
-{
- public virtual IControlFlowGraph BuildGraphForMethod(MethodAnalysisContext context)
- {
- return null!;
- }
-
- public override Memory GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator)
- {
- if (ArmV7Utils.TryGetMethodBodyBytesFast(context.UnderlyingPointer, context is AttributeGeneratorMethodAnalysisContext) is { } ret)
- return ret;
-
- var instructions = ArmV7Utils.GetArmV7MethodBodyAtVirtualAddress(context.UnderlyingPointer);
-
- return instructions.SelectMany(i => i.Bytes).ToArray();
- }
-
- public override List GetIsilFromMethod(MethodAnalysisContext context)
- {
- return new();
- }
-
- public override BaseKeyFunctionAddresses CreateKeyFunctionAddressesInstance()
- {
- //TODO Fix
- return new Arm64KeyFunctionAddresses();
- }
-
- public override string PrintAssembly(MethodAnalysisContext context)
- {
- var sb = new StringBuilder();
-
- var instructions = ArmV7Utils.GetArmV7MethodBodyAtVirtualAddress(context.UnderlyingPointer);
-
- var first = true;
- foreach (var instruction in instructions)
- {
- if (!first)
- sb.AppendLine();
-
- first = false;
- sb.Append("0x").Append(instruction.Address.ToString("X")).Append(" ").Append(instruction.Mnemonic).Append(" ").Append(instruction.Operand);
- }
-
- return sb.ToString();
- }
-}
diff --git a/Cpp2IL.Core/OldGraphs/InstructionGraph.cs b/Cpp2IL.Core/OldGraphs/InstructionGraph.cs
index 2d22e4e0..5d8ae4e8 100644
--- a/Cpp2IL.Core/OldGraphs/InstructionGraph.cs
+++ b/Cpp2IL.Core/OldGraphs/InstructionGraph.cs
@@ -6,7 +6,6 @@
using System.Text;
using Cpp2IL.Core.Il2CppApiFunctions;
using Cpp2IL.Core.Utils;
-using Iced.Intel;
namespace Cpp2IL.Core.Graphs;
@@ -94,7 +93,7 @@ protected TNode SplitAndCreate(TNode target, int index)
return newNode;
}
- private static Dictionary UsageAndDefinitions = new();
+ // private static Dictionary UsageAndDefinitions = new();
public void Run(bool print = false)
@@ -509,4 +508,4 @@ public string Print(bool instructions = false)
public List INodes => Nodes.Cast().ToList();
public int Count => nodeSet.Count;
-}
\ No newline at end of file
+}
diff --git a/Cpp2IL.Core/SharedState.cs b/Cpp2IL.Core/SharedState.cs
index e036476d..d77adef1 100644
--- a/Cpp2IL.Core/SharedState.cs
+++ b/Cpp2IL.Core/SharedState.cs
@@ -9,7 +9,7 @@ public static class SharedState
{
internal static readonly Dictionary ConcreteImplementations = new();
- internal static readonly HashSet AttributeGeneratorStarts = new();
+ public static readonly HashSet AttributeGeneratorStarts = new();
internal static void Clear()
{
@@ -24,4 +24,4 @@ internal static void Clear()
AttributeGeneratorStarts.Clear();
}
}
-}
\ No newline at end of file
+}
diff --git a/Cpp2IL.Core/Utils/Arm64Utils.cs b/Cpp2IL.Core/Utils/Arm64Utils.cs
deleted file mode 100644
index 7fd82b1c..00000000
--- a/Cpp2IL.Core/Utils/Arm64Utils.cs
+++ /dev/null
@@ -1,149 +0,0 @@
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Linq;
-using Cpp2IL.Core.Extensions;
-using Gee.External.Capstone;
-using Gee.External.Capstone.Arm64;
-using LibCpp2IL;
-
-namespace Cpp2IL.Core.Utils
-{
- public static class Arm64Utils
- {
- private static readonly ConcurrentDictionary CachedArm64RegNamesNew = new();
- private static CapstoneArm64Disassembler? _arm64Disassembler;
-
- public static string GetRegisterNameNew(Arm64RegisterId registerId)
- {
- var key = registerId;
- if (registerId == Arm64RegisterId.Invalid)
- return "";
-
- if (CachedArm64RegNamesNew.TryGetValue(key, out var ret))
- return ret;
-
- //General purpose registers: X0-X30 are 64-bit registers. Can be accessed via W0-W30 to only take lower 32-bits. These need upscaling.
- //Vector registers: V0-V31 are 128-bit vector registers. Aliased to Q0-Q31. D0-D31 are the lower half (64 bits), S0-S31 are the lower half of that (32 bits)
- //H0-H31 are the lower half of *that* (16 bits), and b0-b31 are the lowest 8 bits of the vector registers. All of these should be upscaled to V registers.
-
- //Upscale W registers to X.
- if (registerId is >= Arm64RegisterId.ARM64_REG_W0 and <= Arm64RegisterId.ARM64_REG_W30)
- registerId = (registerId - Arm64RegisterId.ARM64_REG_W0) + Arm64RegisterId.ARM64_REG_X0;
-
- if (registerId is >= Arm64RegisterId.ARM64_REG_X0 and <= Arm64RegisterId.ARM64_REG_X28)
- {
- ret = $"x{registerId - Arm64RegisterId.ARM64_REG_X0}";
- CachedArm64RegNamesNew[key] = ret;
- return ret;
- }
-
- if (registerId is Arm64RegisterId.ARM64_REG_SP)
- {
- CachedArm64RegNamesNew[key] = "sp";
- return "sp";
- }
-
- if (registerId is Arm64RegisterId.ARM64_REG_FP)
- {
- CachedArm64RegNamesNew[key] = "fp";
- return "fp";
- }
-
- if (registerId is Arm64RegisterId.ARM64_REG_LR)
- {
- CachedArm64RegNamesNew[key] = "lr";
- return "lr";
- }
-
- if (registerId is Arm64RegisterId.ARM64_REG_WZR or Arm64RegisterId.ARM64_REG_XZR)
- {
- //Zero register - upscale to x variant
- CachedArm64RegNamesNew[key] = "xzr";
- return "xzr";
- }
-
- //Upscale vector registers.
- //One by one.
-
- //B to V
- if (registerId is >= Arm64RegisterId.ARM64_REG_B0 and <= Arm64RegisterId.ARM64_REG_B31)
- registerId = (registerId - Arm64RegisterId.ARM64_REG_B0) + Arm64RegisterId.ARM64_REG_V0;
-
- //H to V
- if (registerId is >= Arm64RegisterId.ARM64_REG_H0 and <= Arm64RegisterId.ARM64_REG_H31)
- registerId = (registerId - Arm64RegisterId.ARM64_REG_H0) + Arm64RegisterId.ARM64_REG_V0;
-
- //S to V
- if (registerId is >= Arm64RegisterId.ARM64_REG_B0 and <= Arm64RegisterId.ARM64_REG_B31)
- registerId = (registerId - Arm64RegisterId.ARM64_REG_B0) + Arm64RegisterId.ARM64_REG_V0;
-
- //D to V
- if (registerId is >= Arm64RegisterId.ARM64_REG_D0 and <= Arm64RegisterId.ARM64_REG_D31)
- registerId = (registerId - Arm64RegisterId.ARM64_REG_D0) + Arm64RegisterId.ARM64_REG_V0;
-
- //Q to V
- if (registerId is >= Arm64RegisterId.ARM64_REG_Q0 and <= Arm64RegisterId.ARM64_REG_Q31)
- registerId = (registerId - Arm64RegisterId.ARM64_REG_Q0) + Arm64RegisterId.ARM64_REG_V0;
-
- ret = $"v{registerId - Arm64RegisterId.ARM64_REG_V0}";
- CachedArm64RegNamesNew[key] = ret;
- return ret;
- }
-
- private static void InitArm64Decompilation()
- {
- var disassembler = CapstoneDisassembler.CreateArm64Disassembler(LibCpp2IlMain.Binary!.IsBigEndian ? Arm64DisassembleMode.BigEndian : Arm64DisassembleMode.LittleEndian);
- disassembler.EnableInstructionDetails = true;
- disassembler.EnableSkipDataMode = true;
- disassembler.DisassembleSyntax = DisassembleSyntax.Intel;
- _arm64Disassembler = disassembler;
- }
-
- public static List GetArm64MethodBodyAtVirtualAddress(ulong virtAddress, bool managed = true, int count = -1)
- {
- if(_arm64Disassembler == null)
- InitArm64Decompilation();
-
- //We can't use CppMethodBodyBytes to get the byte array, because ARMv7 doesn't have filler bytes like x86 does.
- //So we can't work out the end of the method.
- //But we can find the start of the next one! (If managed)
- if (managed)
- {
- var startOfNext = MiscUtils.GetAddressOfNextFunctionStart(virtAddress);
-
- //We have to fall through to default behavior for the last method because we cannot accurately pinpoint its end
- if (startOfNext > 0)
- {
- var rawStartOfNextMethod = LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(startOfNext);
-
- var rawStart = LibCpp2IlMain.Binary.MapVirtualAddressToRaw(virtAddress);
- if (rawStartOfNextMethod < rawStart)
- rawStartOfNextMethod = LibCpp2IlMain.Binary.RawLength;
-
- byte[] bytes = LibCpp2IlMain.Binary.GetRawBinaryContent().SubArray((int)rawStart..(int)rawStartOfNextMethod);
-
- var iter = _arm64Disassembler!.Iterate(bytes, (long)virtAddress);
- if (count > 0)
- iter = iter.Take(count);
-
- return iter.ToList();
- }
- }
-
- //Unmanaged function, look for first b or bl
- var pos = (int) LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(virtAddress);
- var allBytes = LibCpp2IlMain.Binary.GetRawBinaryContent();
- List ret = new();
-
- while (!ret.Any(i => i.Mnemonic is "b" or ".byte") && (count == -1 || ret.Count < count))
- {
- //All arm64 instructions are 4 bytes
- ret.AddRange(_arm64Disassembler!.Iterate(allBytes.SubArray(pos..(pos+4)), (long)virtAddress));
- virtAddress += 4;
- pos += 4;
- }
-
- return ret;
- }
- }
-}
\ No newline at end of file
diff --git a/Cpp2IL.Core/Utils/ArmV7Utils.cs b/Cpp2IL.Core/Utils/ArmV7Utils.cs
deleted file mode 100644
index d7b135d0..00000000
--- a/Cpp2IL.Core/Utils/ArmV7Utils.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using Cpp2IL.Core.Extensions;
-using Gee.External.Capstone;
-using Gee.External.Capstone.Arm;
-using LibCpp2IL;
-
-namespace Cpp2IL.Core.Utils;
-
-public static class ArmV7Utils
-{
- private static CapstoneArmDisassembler? _armDisassembler;
-
- private static void InitArmDecompilation()
- {
- var disassembler = CapstoneDisassembler.CreateArmDisassembler(ArmDisassembleMode.Arm);
- disassembler.EnableInstructionDetails = true;
- disassembler.EnableSkipDataMode = true;
- disassembler.DisassembleSyntax = DisassembleSyntax.Intel;
- _armDisassembler = disassembler;
- }
-
- public static byte[]? TryGetMethodBodyBytesFast(ulong virtAddress, bool isCAGen)
- {
- var startOfNext = MiscUtils.GetAddressOfNextFunctionStart(virtAddress);
-
- var length = (startOfNext - virtAddress);
- if (isCAGen && length > 50_000)
- return null;
-
- if (startOfNext <= 0)
- //We have to fall through to default behavior for the last method because we cannot accurately pinpoint its end
- return null;
-
- var rawStartOfNextMethod = LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(startOfNext);
-
- var rawStart = LibCpp2IlMain.Binary.MapVirtualAddressToRaw(virtAddress);
- if (rawStartOfNextMethod < rawStart)
- rawStartOfNextMethod = LibCpp2IlMain.Binary.RawLength;
-
- return LibCpp2IlMain.Binary.GetRawBinaryContent().SubArray((int) rawStart..(int) rawStartOfNextMethod);
- }
-
- public static List GetArmV7MethodBodyAtVirtualAddress(ulong virtAddress, bool managed = true, int count = -1)
- {
- if(_armDisassembler == null)
- InitArmDecompilation();
-
- //We can't use CppMethodBodyBytes to get the byte array, because ARMv7 doesn't have filler bytes like x86 does.
- //So we can't work out the end of the method.
- //But we can find the start of the next one! (If managed)
- if (managed)
- {
- var startOfNext = MiscUtils.GetAddressOfNextFunctionStart(virtAddress);
-
- //We have to fall through to default behavior for the last method because we cannot accurately pinpoint its end
- if (startOfNext > 0)
- {
- var rawStartOfNextMethod = LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(startOfNext);
-
- var rawStart = LibCpp2IlMain.Binary.MapVirtualAddressToRaw(virtAddress);
- if (rawStartOfNextMethod < rawStart)
- rawStartOfNextMethod = LibCpp2IlMain.Binary.RawLength;
-
- byte[] bytes = LibCpp2IlMain.Binary.GetRawBinaryContent().SubArray((int)rawStart..(int)rawStartOfNextMethod);
-
- var iter = _armDisassembler!.Iterate(bytes, (long)virtAddress);
- if (count > 0)
- iter = iter.Take(count);
-
- return iter.ToList();
- }
- }
-
- //Unmanaged function, look for first b or bl
- var pos = (int) LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(virtAddress);
- var allBytes = LibCpp2IlMain.Binary.GetRawBinaryContent();
- List ret = new();
-
- while (!ret.Any(i => i.Mnemonic is "b" or ".byte") && (count == -1 || ret.Count < count))
- {
- //All arm64 instructions are 4 bytes
- ret.AddRange(_armDisassembler!.Iterate(allBytes.SubArray(pos..(pos+4)), (long)virtAddress));
- virtAddress += 4;
- pos += 4;
- }
-
- return ret;
- }
-}
\ No newline at end of file
diff --git a/Cpp2IL.Core/Utils/NewArm64Utils.cs b/Cpp2IL.Core/Utils/NewArm64Utils.cs
deleted file mode 100644
index 3cf57d81..00000000
--- a/Cpp2IL.Core/Utils/NewArm64Utils.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using System;
-using System.Linq;
-using Disarm;
-using LibCpp2IL;
-
-namespace Cpp2IL.Core.Utils;
-
-public static class NewArm64Utils
-{
- public static Arm64DisassemblyResult GetArm64MethodBodyAtVirtualAddress(ulong virtAddress, bool managed = true, int count = -1)
- {
- if (managed)
- {
- var startOfNext = MiscUtils.GetAddressOfNextFunctionStart(virtAddress);
-
- //We have to fall through to default behavior for the last method because we cannot accurately pinpoint its end
- if (startOfNext > 0)
- {
- var rawStartOfNextMethod = LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(startOfNext);
-
- var rawStart = LibCpp2IlMain.Binary.MapVirtualAddressToRaw(virtAddress);
- if (rawStartOfNextMethod < rawStart)
- rawStartOfNextMethod = LibCpp2IlMain.Binary.RawLength;
-
- var bytes = LibCpp2IlMain.Binary.GetRawBinaryContent().AsSpan((int)rawStart, (int)(rawStartOfNextMethod - rawStart));
-
- return Disassemble(bytes, virtAddress);
- }
- }
-
- //Unmanaged function, look for first b
- var pos = (int)LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(virtAddress);
- var allBytes = LibCpp2IlMain.Binary.GetRawBinaryContent();
- var span = allBytes.AsSpan(pos, 4);
- Arm64DisassemblyResult ret = new();
-
- while ((count == -1 || ret.Instructions.Count < count) && !ret.Instructions.Any(i => i.Mnemonic is Arm64Mnemonic.B))
- {
- ret = Disassemble(span, virtAddress);
-
- //All arm64 instructions are 4 bytes
- span = allBytes.AsSpan(pos, span.Length + 4);
- }
-
- return ret;
- }
-
- private static Arm64DisassemblyResult Disassemble(Span bytes, ulong virtAddress)
- {
- try
- {
- return Disassembler.Disassemble(bytes, virtAddress);
- }
- catch (Exception e)
- {
- throw new($"Failed to disassemble method body: {string.Join(", ", bytes.ToArray().Select(b => "0x" + b.ToString("X2")))}", e);
- }
- }
-}
diff --git a/Cpp2IL.Gui/Cpp2IL.Gui.csproj b/Cpp2IL.Gui/Cpp2IL.Gui.csproj
index 32d21c6d..db8468fc 100644
--- a/Cpp2IL.Gui/Cpp2IL.Gui.csproj
+++ b/Cpp2IL.Gui/Cpp2IL.Gui.csproj
@@ -1,24 +1,21 @@
- true
- Debug;Release
- true
- true
- enable
+ net7.0
Exe
- AnyCPU;x64
+
true
true
- net7.0
+ true
+ true
+ true
link
- 2022.1.0
+
-
-
-
-
-
+
+
+
+
@@ -27,18 +24,9 @@
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
diff --git a/Cpp2IL.Gui/Program.cs b/Cpp2IL.Gui/Program.cs
index bfb913b3..69483ce0 100644
--- a/Cpp2IL.Gui/Program.cs
+++ b/Cpp2IL.Gui/Program.cs
@@ -3,6 +3,7 @@
using Avalonia.ReactiveUI;
using Cpp2IL.Core;
using Cpp2IL.Core.Logging;
+using Cpp2IL.InstructionSets.All;
namespace Cpp2IL.Gui
{
@@ -16,6 +17,7 @@ public static void Main(string[] args)
{
Console.WriteLine("Starting Cpp2IL GUI. Initializing Cpp2IL Core...");
+ AllInstructionSets.Register();
Cpp2IlApi.Init();
SimpleConsoleLogger.Initialize();
SimpleConsoleLogger.ShowVerbose = true;
diff --git a/Cpp2IL.InstructionSets.All/AllInstructionSets.cs b/Cpp2IL.InstructionSets.All/AllInstructionSets.cs
new file mode 100644
index 00000000..90056b9f
--- /dev/null
+++ b/Cpp2IL.InstructionSets.All/AllInstructionSets.cs
@@ -0,0 +1,19 @@
+using Cpp2IL.Core.Api;
+using Cpp2IL.InstructionSets.ArmV7;
+using Cpp2IL.InstructionSets.ArmV8;
+using Cpp2IL.InstructionSets.Wasm;
+using Cpp2IL.InstructionSets.X86;
+
+namespace Cpp2IL.InstructionSets.All;
+
+public static class AllInstructionSets
+{
+ public static void Register()
+ {
+ X86InstructionSet.RegisterInstructionSet();
+ ArmV7InstructionSet.RegisterInstructionSet();
+ ArmV8InstructionSet.RegisterInstructionSet();
+ WasmInstructionSet.RegisterInstructionSet();
+ OutputFormatRegistry.Register();
+ }
+}
diff --git a/Cpp2IL.InstructionSets.All/Cpp2IL.InstructionSets.All.csproj b/Cpp2IL.InstructionSets.All/Cpp2IL.InstructionSets.All.csproj
new file mode 100644
index 00000000..0d6a089f
--- /dev/null
+++ b/Cpp2IL.InstructionSets.All/Cpp2IL.InstructionSets.All.csproj
@@ -0,0 +1,14 @@
+
+
+ netstandard2.0
+ true
+
+
+
+
+
+
+
+
+
+
diff --git a/Cpp2IL.InstructionSets.ArmV7/ArmV7InstructionSet.cs b/Cpp2IL.InstructionSets.ArmV7/ArmV7InstructionSet.cs
new file mode 100644
index 00000000..abbf5092
--- /dev/null
+++ b/Cpp2IL.InstructionSets.ArmV7/ArmV7InstructionSet.cs
@@ -0,0 +1,66 @@
+using System.Text;
+using Cpp2IL.Core.Api;
+using Cpp2IL.Core.Il2CppApiFunctions;
+using Cpp2IL.Core.ISIL;
+using Cpp2IL.Core.Model.Contexts;
+using LibCpp2IL;
+
+namespace Cpp2IL.InstructionSets.ArmV7;
+
+public class ArmV7InstructionSet : Cpp2IlInstructionSet
+{
+ public static void RegisterInstructionSet()
+ {
+ InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.ARM_V7);
+ }
+
+ public override Memory GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator)
+ {
+ if (ArmV7Utils.TryGetMethodBodyBytesFast(context.UnderlyingPointer, context is AttributeGeneratorMethodAnalysisContext) is { } ret)
+ return ret;
+
+ ArmV7Utils.DisassembleManagedMethod(context.UnderlyingPointer, out var endVirtualAddress);
+
+ var start = (int)context.AppContext.Binary.MapVirtualAddressToRaw(context.UnderlyingPointer);
+ var end = (int)context.AppContext.Binary.MapVirtualAddressToRaw(endVirtualAddress);
+
+ return context.AppContext.Binary.GetRawBinaryContent().AsMemory(start, end - start);
+ }
+
+ public override List GetIsilFromMethod(MethodAnalysisContext context)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override BaseKeyFunctionAddresses CreateKeyFunctionAddressesInstance()
+ {
+ return new ArmV7KeyFunctionAddresses();
+ }
+
+ public override unsafe string PrintAssembly(MethodAnalysisContext context)
+ {
+ var sb = new StringBuilder();
+ var first = true;
+
+ using (ArmV7Utils.Disassembler.AllocInstruction(out var instruction))
+ {
+ fixed (byte* code = context.RawBytes.Span)
+ {
+ var size = (nuint)context.RawBytes.Length;
+ var address = context.UnderlyingPointer;
+ while (ArmV7Utils.Disassembler.UnsafeIterate(&code, &size, &address, instruction))
+ {
+ if (!first)
+ {
+ sb.AppendLine();
+ first = false;
+ }
+
+ sb.Append("0x").Append(address.ToString("X")).Append(" ").AppendLine(instruction->ToString());
+ }
+ }
+ }
+
+ return sb.ToString();
+ }
+}
diff --git a/Cpp2IL.InstructionSets.ArmV7/ArmV7KeyFunctionAddresses.cs b/Cpp2IL.InstructionSets.ArmV7/ArmV7KeyFunctionAddresses.cs
new file mode 100644
index 00000000..6f037962
--- /dev/null
+++ b/Cpp2IL.InstructionSets.ArmV7/ArmV7KeyFunctionAddresses.cs
@@ -0,0 +1,144 @@
+using CapstoneSharp.Arm;
+using Cpp2IL.Core.Il2CppApiFunctions;
+using Cpp2IL.Core.Logging;
+using LibCpp2IL;
+using LibCpp2IL.Reflection;
+
+namespace Cpp2IL.InstructionSets.ArmV7;
+
+public class ArmV7KeyFunctionAddresses : BaseKeyFunctionAddresses
+{
+ private List? _cachedDisassembledBytes;
+
+ private List DisassembleTextSection()
+ {
+ if (_cachedDisassembledBytes == null)
+ {
+ var toDisasm = LibCpp2IlMain.Binary!.GetEntirePrimaryExecutableSection();
+ _cachedDisassembledBytes = ArmV7Utils.Disassembler.Iterate(toDisasm, LibCpp2IlMain.Binary.GetVirtualAddressOfPrimaryExecutableSection()).ToList();
+ }
+
+ return _cachedDisassembledBytes;
+ }
+
+ protected override IEnumerable FindAllThunkFunctions(ulong addr, uint maxBytesBack = 0, params ulong[] addressesToIgnore)
+ {
+ //Disassemble .text
+ var disassembly = DisassembleTextSection();
+
+ //Find all jumps to the target address
+ var matchingJmps = disassembly.Where(i => i.IsBranchingTo((int)addr)).ToList();
+
+ foreach (var matchingJmp in matchingJmps)
+ {
+ if (addressesToIgnore.Contains(matchingJmp.Address)) continue;
+
+ var backtrack = 0;
+ var idx = disassembly.IndexOf(matchingJmp);
+
+ do
+ {
+ //About the only way we have of checking for a thunk is if there is an all-zero instruction or another unconditional branch just before this
+ //Or a ret, but that's less reliable.
+ //if so, this is probably a thunk.
+ if (idx - backtrack > 0)
+ {
+ var prevInstruction = disassembly[idx - backtrack - 1];
+
+ if (addressesToIgnore.Contains(prevInstruction.Address))
+ {
+ backtrack++;
+ continue;
+ }
+
+ if (prevInstruction.IsSkippedData && prevInstruction.Bytes.IsAllZero())
+ {
+ //All-zero instructions are a match
+ yield return matchingJmp.Address - (ulong)(backtrack * 4);
+ break;
+ }
+
+ if (prevInstruction.Id is CapstoneArmInstructionId.STR)
+ {
+ //ADRP instructions are a deal breaker - this means we're loading something from memory, so it's not a simple thunk
+ break;
+ }
+
+ if (prevInstruction.Id is CapstoneArmInstructionId.B or CapstoneArmInstructionId.BL)
+ {
+ //Previous branches are a match
+ yield return matchingJmp.Address - (ulong)(backtrack * 4);
+ break;
+ }
+ }
+
+ //We're working in the .text section here so we have few symbols, so there's no point looking for the previous one.
+
+ backtrack++;
+ } while (backtrack * 4 < maxBytesBack);
+ }
+ }
+
+ protected override ulong GetObjectIsInstFromSystemType()
+ {
+ Logger.Verbose("\tTrying to use System.Type::IsInstanceOfType to find il2cpp::vm::Object::IsInst...");
+ var typeIsInstanceOfType = LibCpp2IlReflection.GetType("Type", "System")?.Methods?.FirstOrDefault(m => m.Name == "IsInstanceOfType");
+ if (typeIsInstanceOfType == null)
+ {
+ Logger.VerboseNewline("Type or method not found, aborting.");
+ return 0;
+ }
+
+ //IsInstanceOfType is a very simple ICall, that looks like this:
+ // Il2CppClass* klass = vm::Class::FromIl2CppType(type->type.type);
+ // return il2cpp::vm::Object::IsInst(obj, klass) != NULL;
+ //The last call is to Object::IsInst
+
+ Logger.Verbose($"IsInstanceOfType found at 0x{typeIsInstanceOfType.MethodPointer:X}...");
+ var instructions = ArmV7Utils.DisassembleManagedMethod(typeIsInstanceOfType.MethodPointer);
+
+ var lastCall = instructions.LastOrDefault(i => i.Id == CapstoneArmInstructionId.BL);
+
+ if (lastCall == null)
+ {
+ Logger.VerboseNewline("Method does not match expected signature. Aborting.");
+ return 0;
+ }
+
+ var target = lastCall.GetBranchTarget();
+ Logger.VerboseNewline($"Success. IsInst found at 0x{target:X}");
+ return (ulong)target;
+ }
+
+ protected override ulong FindFunctionThisIsAThunkOf(ulong thunkPtr, bool prioritiseCall = false)
+ {
+ var instructions = ArmV7Utils.DisassembleFunction(thunkPtr);
+
+ try
+ {
+ var target = prioritiseCall ? CapstoneArmInstructionId.BL : CapstoneArmInstructionId.B;
+ var matchingCall = instructions.FirstOrDefault(i => i.Id == target);
+
+ if (matchingCall == null)
+ {
+ target = target == CapstoneArmInstructionId.BL ? CapstoneArmInstructionId.B : CapstoneArmInstructionId.BL;
+ matchingCall = instructions.First(i => i.Id == target);
+ }
+
+ return (ulong)matchingCall.GetBranchTarget();
+ }
+ catch (Exception)
+ {
+ return 0;
+ }
+ }
+
+ protected override int GetCallerCount(ulong toWhere)
+ {
+ //Disassemble .text
+ var disassembly = DisassembleTextSection();
+
+ //Find all jumps to the target address
+ return disassembly.Count(i => i.IsBranchingTo((int)toWhere));
+ }
+}
diff --git a/Cpp2IL.InstructionSets.ArmV7/ArmV7Utils.cs b/Cpp2IL.InstructionSets.ArmV7/ArmV7Utils.cs
new file mode 100644
index 00000000..5f922c1c
--- /dev/null
+++ b/Cpp2IL.InstructionSets.ArmV7/ArmV7Utils.cs
@@ -0,0 +1,125 @@
+using System.Runtime.InteropServices;
+using CapstoneSharp.Arm;
+using Cpp2IL.Core.Utils;
+using LibCpp2IL;
+
+namespace Cpp2IL.InstructionSets.ArmV7;
+
+internal static class ArmV7Utils
+{
+ private static CapstoneArmDisassembler? _disassembler;
+
+ // TODO dispose this
+ public static CapstoneArmDisassembler Disassembler => _disassembler ??= new CapstoneArmDisassembler
+ {
+ EnableInstructionDetails = true, EnableSkipData = true,
+ };
+
+ public static bool IsAllZero(this ReadOnlySpan span)
+ {
+ if (MemoryMarshal.TryRead(span, out var value))
+ {
+ return value == 0;
+ }
+
+ foreach (var b in span)
+ {
+ if (b != 0)
+ {
+ return true;
+ }
+ }
+
+ return true;
+ }
+
+ public static int GetBranchTarget(this CapstoneArmInstruction instruction)
+ {
+ if (instruction.Id is not (CapstoneArmInstructionId.B or CapstoneArmInstructionId.BL))
+ throw new InvalidOperationException("Branch target not available for this instruction, must be a B or BL");
+
+ return instruction.Details.ArchDetails.Operands[0].Immediate;
+ }
+
+ public static bool IsBranchingTo(this CapstoneArmInstruction instruction, int toWhere)
+ {
+ if (instruction.Id is not (CapstoneArmInstructionId.B or CapstoneArmInstructionId.BL))
+ return false;
+
+ return instruction.GetBranchTarget() == toWhere;
+ }
+
+ public static Memory? TryGetMethodBodyBytesFast(ulong virtualAddress, bool isCAGen)
+ {
+ var startOfNext = MiscUtils.GetAddressOfNextFunctionStart(virtualAddress);
+
+ var length = (startOfNext - virtualAddress);
+ if (isCAGen && length > 50_000)
+ return null;
+
+ if (startOfNext <= 0)
+ //We have to fall through to default behavior for the last method because we cannot accurately pinpoint its end
+ return null;
+
+ var rawStartOfNextMethod = LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(startOfNext);
+
+ var rawStart = LibCpp2IlMain.Binary.MapVirtualAddressToRaw(virtualAddress);
+ if (rawStartOfNextMethod < rawStart)
+ rawStartOfNextMethod = LibCpp2IlMain.Binary.RawLength;
+
+ return LibCpp2IlMain.Binary.GetRawBinaryContent().AsMemory((int)rawStart, (int)(rawStartOfNextMethod - rawStart));
+ }
+
+ public static List DisassembleFunction(ulong virtualAddress, int count = -1)
+ {
+ return DisassembleFunction(virtualAddress, out _, count);
+ }
+
+ public static List DisassembleFunction(ulong virtualAddress, out ulong endVirtualAddress, int count = -1)
+ {
+ // Unmanaged function, look for first b
+ var pos = (int)LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(virtualAddress);
+ var allBytes = LibCpp2IlMain.Binary.GetRawBinaryContent();
+
+ var instructions = new List();
+
+ endVirtualAddress = virtualAddress;
+ foreach (var instruction in Disassembler.Iterate(allBytes.AsSpan(pos), virtualAddress))
+ {
+ instructions.Add(instruction);
+ endVirtualAddress = instruction.Address;
+ if (instruction.Id == CapstoneArmInstructionId.B) break;
+ if (count != -1 && instructions.Count >= count) break;
+ }
+
+ return instructions;
+ }
+
+ public static IEnumerable DisassembleManagedMethod(ulong virtualAddress, int count = -1)
+ {
+ return DisassembleManagedMethod(virtualAddress, out _, count);
+ }
+
+ public static IEnumerable DisassembleManagedMethod(ulong virtualAddress, out ulong endVirtualAddress, int count = -1)
+ {
+ var startOfNext = MiscUtils.GetAddressOfNextFunctionStart(virtualAddress);
+
+ // We have to fall through to default behavior for the last method because we cannot accurately pinpoint its end
+ if (startOfNext > 0)
+ {
+ var rawStartOfNextMethod = LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(startOfNext);
+
+ var rawStart = LibCpp2IlMain.Binary.MapVirtualAddressToRaw(virtualAddress);
+ if (rawStartOfNextMethod < rawStart)
+ rawStartOfNextMethod = LibCpp2IlMain.Binary.RawLength;
+
+ var bytes = LibCpp2IlMain.Binary.GetRawBinaryContent().AsMemory((int)rawStart, (int)(rawStartOfNextMethod - rawStart));
+
+ endVirtualAddress = virtualAddress + (ulong)bytes.Length;
+ var instructions = Disassembler.Iterate(bytes, virtualAddress);
+ return count == -1 ? instructions : instructions.Take(count);
+ }
+
+ return DisassembleFunction(virtualAddress, out endVirtualAddress, count);
+ }
+}
diff --git a/Cpp2IL.InstructionSets.ArmV7/Cpp2IL.InstructionSets.ArmV7.csproj b/Cpp2IL.InstructionSets.ArmV7/Cpp2IL.InstructionSets.ArmV7.csproj
new file mode 100644
index 00000000..a034d770
--- /dev/null
+++ b/Cpp2IL.InstructionSets.ArmV7/Cpp2IL.InstructionSets.ArmV7.csproj
@@ -0,0 +1,12 @@
+
+
+ netstandard2.0
+ true
+ true
+
+
+
+
+
+
+
diff --git a/Cpp2IL.Core/InstructionSets/NewArmV8InstructionSet.cs b/Cpp2IL.InstructionSets.ArmV8/ArmV8InstructionSet.cs
similarity index 68%
rename from Cpp2IL.Core/InstructionSets/NewArmV8InstructionSet.cs
rename to Cpp2IL.InstructionSets.ArmV8/ArmV8InstructionSet.cs
index af28009d..f2bf24b9 100644
--- a/Cpp2IL.Core/InstructionSets/NewArmV8InstructionSet.cs
+++ b/Cpp2IL.InstructionSets.ArmV8/ArmV8InstructionSet.cs
@@ -1,17 +1,20 @@
-using System;
-using System.Collections.Generic;
-using Disarm;
using Cpp2IL.Core.Api;
using Cpp2IL.Core.Il2CppApiFunctions;
using Cpp2IL.Core.ISIL;
using Cpp2IL.Core.Model.Contexts;
using Cpp2IL.Core.Utils;
+using Disarm;
using LibCpp2IL;
-namespace Cpp2IL.Core.InstructionSets;
+namespace Cpp2IL.InstructionSets.ArmV8;
-public class NewArmV8InstructionSet : Cpp2IlInstructionSet
+public class ArmV8InstructionSet : Cpp2IlInstructionSet
{
+ public static void RegisterInstructionSet()
+ {
+ InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.ARM_V8);
+ }
+
public override Memory GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator)
{
if (context is not ConcreteGenericMethodAnalysisContext)
@@ -24,28 +27,26 @@ public override Memory GetRawBytesForMethod(MethodAnalysisContext context,
if (startOfNextFunction > 0)
return LibCpp2IlMain.Binary!.GetRawBinaryContent().AsMemory(ptrAsInt, count);
}
-
- var result = NewArm64Utils.GetArm64MethodBodyAtVirtualAddress(context.UnderlyingPointer);
- var endVa = result.EndVirtualAddress;
+
+ ArmV8Utils.GetArm64MethodBodyAtVirtualAddress(context.UnderlyingPointer, out var endVirtualAddress);
var start = (int) context.AppContext.Binary.MapVirtualAddressToRaw(context.UnderlyingPointer);
- var end = (int) context.AppContext.Binary.MapVirtualAddressToRaw(endVa);
-
+ var end = (int) context.AppContext.Binary.MapVirtualAddressToRaw(endVirtualAddress);
+
return context.AppContext.Binary.GetRawBinaryContent().AsMemory(start, end - start);
}
public override List GetIsilFromMethod(MethodAnalysisContext context)
{
- var result = NewArm64Utils.GetArm64MethodBodyAtVirtualAddress(context.UnderlyingPointer);
-
- //TODO
- return new();
+ var instructions = ArmV8Utils.GetArm64MethodBodyAtVirtualAddress(context.UnderlyingPointer, out var endVirtualAddress);
+
+ throw new NotImplementedException();
}
public override BaseKeyFunctionAddresses CreateKeyFunctionAddressesInstance()
{
- return new NewArm64KeyFunctionAddresses();
+ return new ArmV8KeyFunctionAddresses();
}
- public override string PrintAssembly(MethodAnalysisContext context) => string.Join("\n", Disassembler.Disassemble(context.RawBytes.Span, context.UnderlyingPointer).Instructions);
+ public override string PrintAssembly(MethodAnalysisContext context) => string.Join("\n", Disassembler.Disassemble(context.RawBytes, context.UnderlyingPointer));
}
diff --git a/Cpp2IL.InstructionSets.ArmV8/ArmV8KeyFunctionAddresses.cs b/Cpp2IL.InstructionSets.ArmV8/ArmV8KeyFunctionAddresses.cs
new file mode 100644
index 00000000..bfcbc162
--- /dev/null
+++ b/Cpp2IL.InstructionSets.ArmV8/ArmV8KeyFunctionAddresses.cs
@@ -0,0 +1,86 @@
+using Cpp2IL.Core.Il2CppApiFunctions;
+using Disarm;
+using LibCpp2IL;
+
+namespace Cpp2IL.InstructionSets.ArmV8;
+
+public class ArmV8KeyFunctionAddresses : BaseKeyFunctionAddresses
+{
+ private List? _cachedDisassembledBytes;
+
+ private List DisassembleTextSection()
+ {
+ if (_cachedDisassembledBytes == null)
+ {
+ var toDisasm = LibCpp2IlMain.Binary!.GetEntirePrimaryExecutableSection();
+ _cachedDisassembledBytes = Disassembler.Disassemble(toDisasm, LibCpp2IlMain.Binary.GetVirtualAddressOfPrimaryExecutableSection()).ToList();
+ }
+
+ return _cachedDisassembledBytes;
+ }
+
+ protected override IEnumerable FindAllThunkFunctions(ulong addr, uint maxBytesBack = 0, params ulong[] addressesToIgnore)
+ {
+ //Disassemble .text
+ var disassembly = DisassembleTextSection();
+
+ //Find all jumps to the target address
+ var matchingJmps = disassembly.Where(i => i.Mnemonic is Arm64Mnemonic.B or Arm64Mnemonic.BL && i.BranchTarget == addr).ToList();
+
+ foreach (var matchingJmp in matchingJmps)
+ {
+ if (addressesToIgnore.Contains(matchingJmp.Address)) continue;
+
+ //Find this instruction in the raw file
+ var offsetInPe = (ulong) LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(matchingJmp.Address);
+ 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 + 4);
+
+ //Double-cc = thunk
+ if (previousByte == 0xCC && nextByte == 0xCC)
+ {
+ yield return matchingJmp.Address;
+ continue;
+ }
+
+ if (nextByte == 0xCC && maxBytesBack > 0)
+ {
+ for (ulong backtrack = 1; backtrack < maxBytesBack && offsetInPe - backtrack > 0; backtrack++)
+ {
+ if (addressesToIgnore.Contains(matchingJmp.Address - (backtrack - 1)))
+ //Move to next jmp
+ break;
+
+ if (LibCpp2IlMain.Binary.GetByteAtRawAddress(offsetInPe - backtrack) == 0xCC)
+ {
+ yield return matchingJmp.Address - (backtrack - 1);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ protected override ulong GetObjectIsInstFromSystemType()
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override ulong FindFunctionThisIsAThunkOf(ulong thunkPtr, bool prioritiseCall = false)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override int GetCallerCount(ulong toWhere)
+ {
+ //Disassemble .text
+ var disassembly = DisassembleTextSection();
+
+ //Find all jumps to the target address
+ return disassembly.Count(i => i.Mnemonic is Arm64Mnemonic.B or Arm64Mnemonic.BL && i.BranchTarget == toWhere);
+ }
+}
diff --git a/Cpp2IL.InstructionSets.ArmV8/ArmV8Utils.cs b/Cpp2IL.InstructionSets.ArmV8/ArmV8Utils.cs
new file mode 100644
index 00000000..002fb3a2
--- /dev/null
+++ b/Cpp2IL.InstructionSets.ArmV8/ArmV8Utils.cs
@@ -0,0 +1,60 @@
+using Cpp2IL.Core.Utils;
+using Disarm;
+using LibCpp2IL;
+
+namespace Cpp2IL.InstructionSets.ArmV8;
+
+internal static class ArmV8Utils
+{
+ public static IEnumerable GetArm64MethodBodyAtVirtualAddress(ulong virtualAddress, out ulong endVirtualAddress, bool managed = true, int count = -1)
+ {
+ if (managed)
+ {
+ var startOfNext = MiscUtils.GetAddressOfNextFunctionStart(virtualAddress);
+
+ //We have to fall through to default behavior for the last method because we cannot accurately pinpoint its end
+ if (startOfNext > 0)
+ {
+ var rawStartOfNextMethod = LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(startOfNext);
+
+ var rawStart = LibCpp2IlMain.Binary.MapVirtualAddressToRaw(virtualAddress);
+ if (rawStartOfNextMethod < rawStart)
+ rawStartOfNextMethod = LibCpp2IlMain.Binary.RawLength;
+
+ var bytes = LibCpp2IlMain.Binary.GetRawBinaryContent().AsMemory((int)rawStart, (int)(rawStartOfNextMethod - rawStart));
+
+ return Disassemble(bytes, virtualAddress, out endVirtualAddress);
+ }
+ }
+
+ //Unmanaged function, look for first b
+ var pos = (int)LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(virtualAddress);
+ var allBytes = LibCpp2IlMain.Binary.GetRawBinaryContent();
+
+ var instructions = new List();
+
+ endVirtualAddress = virtualAddress;
+ foreach (var instruction in Disassembler.Disassemble(allBytes.AsSpan(pos), virtualAddress, Disassembler.Options.IgnoreErrors))
+ {
+ instructions.Add(instruction);
+ endVirtualAddress = instruction.Address;
+ if (instruction.Mnemonic == Arm64Mnemonic.B) break;
+ if (count != -1 && instructions.Count >= count) break;
+ }
+
+ return instructions;
+ }
+
+ private static IEnumerable Disassemble(ReadOnlyMemory bytes, ulong virtualAddress, out ulong endVirtualAddress)
+ {
+ try
+ {
+ endVirtualAddress = virtualAddress + (ulong)bytes.Length;
+ return Disassembler.Disassemble(bytes, virtualAddress, Disassembler.Options.IgnoreErrors);
+ }
+ catch (Exception e)
+ {
+ throw new($"Failed to disassemble method body: {string.Join(", ", bytes.ToArray().Select(b => "0x" + b.ToString("X2")))}", e);
+ }
+ }
+}
diff --git a/Cpp2IL.InstructionSets.ArmV8/Cpp2IL.InstructionSets.ArmV8.csproj b/Cpp2IL.InstructionSets.ArmV8/Cpp2IL.InstructionSets.ArmV8.csproj
new file mode 100644
index 00000000..90c1eb65
--- /dev/null
+++ b/Cpp2IL.InstructionSets.ArmV8/Cpp2IL.InstructionSets.ArmV8.csproj
@@ -0,0 +1,11 @@
+
+
+ netstandard2.0
+ true
+
+
+
+
+
+
+
diff --git a/Cpp2IL.InstructionSets.Wasm/Cpp2IL.InstructionSets.Wasm.csproj b/Cpp2IL.InstructionSets.Wasm/Cpp2IL.InstructionSets.Wasm.csproj
new file mode 100644
index 00000000..7f493c54
--- /dev/null
+++ b/Cpp2IL.InstructionSets.Wasm/Cpp2IL.InstructionSets.Wasm.csproj
@@ -0,0 +1,11 @@
+
+
+ netstandard2.0
+ true
+
+
+
+
+
+
+
diff --git a/Cpp2IL.Core/InstructionSets/WasmInstructionSet.cs b/Cpp2IL.InstructionSets.Wasm/WasmInstructionSet.cs
similarity index 89%
rename from Cpp2IL.Core/InstructionSets/WasmInstructionSet.cs
rename to Cpp2IL.InstructionSets.Wasm/WasmInstructionSet.cs
index 1fbedac2..2de6fd20 100644
--- a/Cpp2IL.Core/InstructionSets/WasmInstructionSet.cs
+++ b/Cpp2IL.InstructionSets.Wasm/WasmInstructionSet.cs
@@ -1,18 +1,21 @@
-using System;
-using System.Collections.Generic;
using Cpp2IL.Core.Api;
using Cpp2IL.Core.Graphs;
using Cpp2IL.Core.Il2CppApiFunctions;
using Cpp2IL.Core.ISIL;
using Cpp2IL.Core.Logging;
using Cpp2IL.Core.Model.Contexts;
-using Cpp2IL.Core.Utils;
+using LibCpp2IL;
using WasmDisassembler;
-namespace Cpp2IL.Core.InstructionSets;
+namespace Cpp2IL.InstructionSets.Wasm;
public class WasmInstructionSet : Cpp2IlInstructionSet
{
+ public static void RegisterInstructionSet()
+ {
+ InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.WASM);
+ }
+
public virtual IControlFlowGraph BuildGraphForMethod(MethodAnalysisContext context)
{
return null!;
diff --git a/Cpp2IL.Core/Il2CppApiFunctions/WasmKeyFunctionAddresses.cs b/Cpp2IL.InstructionSets.Wasm/WasmKeyFunctionAddresses.cs
similarity index 89%
rename from Cpp2IL.Core/Il2CppApiFunctions/WasmKeyFunctionAddresses.cs
rename to Cpp2IL.InstructionSets.Wasm/WasmKeyFunctionAddresses.cs
index ca784f11..656ba01c 100644
--- a/Cpp2IL.Core/Il2CppApiFunctions/WasmKeyFunctionAddresses.cs
+++ b/Cpp2IL.InstructionSets.Wasm/WasmKeyFunctionAddresses.cs
@@ -1,6 +1,6 @@
-using System.Collections.Generic;
+using Cpp2IL.Core.Il2CppApiFunctions;
-namespace Cpp2IL.Core.Il2CppApiFunctions
+namespace Cpp2IL.InstructionSets.Wasm
{
public class WasmKeyFunctionAddresses : BaseKeyFunctionAddresses
{
diff --git a/Cpp2IL.InstructionSets.Wasm/WasmMappingOutputFormat.cs b/Cpp2IL.InstructionSets.Wasm/WasmMappingOutputFormat.cs
new file mode 100644
index 00000000..de88b842
--- /dev/null
+++ b/Cpp2IL.InstructionSets.Wasm/WasmMappingOutputFormat.cs
@@ -0,0 +1,62 @@
+using System.Text;
+using Cpp2IL.Core.Api;
+using Cpp2IL.Core.Logging;
+using Cpp2IL.Core.Model.Contexts;
+using LibCpp2IL.Wasm;
+
+namespace Cpp2IL.InstructionSets.Wasm;
+
+public class WasmMappingOutputFormat : Cpp2IlOutputFormat
+{
+ public override string OutputFormatId => "wasmmappings";
+ public override string OutputFormatName => "WebAssembly Method Mappings";
+
+ public override void DoOutput(ApplicationAnalysisContext context, string outputRoot)
+ {
+ if (context.Binary is not WasmFile wasmFile)
+ throw new("This output format only works with WebAssembly files");
+
+ Logger.InfoNewline("Generating WebAssembly method mappings...This may take up to a minute...", "WasmMappingOutputFormat");
+ var output = new StringBuilder();
+
+ foreach (var assemblyAnalysisContext in context.Assemblies)
+ {
+ output.Append("// ").Append(assemblyAnalysisContext.Definition.AssemblyName.Name).Append(".dll").AppendLine().AppendLine();
+
+ foreach (var typeAnalysisContext in assemblyAnalysisContext.Types)
+ foreach (var methodAnalysisContext in typeAnalysisContext.Methods)
+ {
+ if (methodAnalysisContext is InjectedMethodAnalysisContext || methodAnalysisContext.Definition == null)
+ continue;
+
+ output.Append(methodAnalysisContext.Definition.ReturnType)
+ .Append(' ')
+ .Append(methodAnalysisContext.DeclaringType!.FullName)
+ .Append("::")
+ .Append(methodAnalysisContext.Definition.Name)
+ .Append('(')
+ .Append(string.Join(", ", methodAnalysisContext.Definition.Parameters!.Select(p => $"{p.Type} {p.ParameterName}")))
+ .Append(") -> ");
+
+ try
+ {
+ var wasmDef = WasmUtils.GetWasmDefinition(methodAnalysisContext.Definition);
+ var ghidraName = WasmUtils.GetGhidraFunctionName(wasmDef);
+
+ output.AppendLine(ghidraName);
+ }
+ catch (Exception)
+ {
+ output.AppendLine("");
+ }
+ }
+
+ output.AppendLine().AppendLine();
+ }
+
+ var outPath = Path.Combine(outputRoot, "wasm_mappings.txt");
+ File.WriteAllText(outPath, output.ToString());
+
+ Logger.InfoNewline("Wasm mappings written to: " + outPath, "WasmMappingOutputFormat");
+ }
+}
diff --git a/Cpp2IL.InstructionSets.Wasm/WasmUtils.cs b/Cpp2IL.InstructionSets.Wasm/WasmUtils.cs
new file mode 100644
index 00000000..3ff5552d
--- /dev/null
+++ b/Cpp2IL.InstructionSets.Wasm/WasmUtils.cs
@@ -0,0 +1,139 @@
+using System.Text.RegularExpressions;
+using LibCpp2IL;
+using LibCpp2IL.Metadata;
+using LibCpp2IL.Reflection;
+using LibCpp2IL.Wasm;
+
+namespace Cpp2IL.InstructionSets.Wasm
+{
+ public static class WasmUtils
+ {
+ internal static readonly Dictionary> MethodDefinitionIndices = new();
+ private static Regex DynCallRemappingRegex = new(@"Module\[\s*[""'](dynCall_[^""']+)[""']\s*\]\s*=\s*Module\[\s*[""']asm[""']\s*\]\[\s*[""']([^""']+)[""']\s*\]\s*\)\.apply", RegexOptions.Compiled);
+
+ public static string BuildSignature(Il2CppMethodDefinition definition)
+ {
+ var instanceParam = definition.IsStatic ? "" : "i";
+
+ //Something still off about p/invoke functions. They do have methodinfo args, but something is wrong somewhere.
+
+ return $"{GetSignatureLetter(definition.ReturnType!)}{instanceParam}{string.Join("", definition.Parameters!.Select(p => GetSignatureLetter(p.Type, p.IsRefOrOut)))}i"; //Add an extra i on the end for the method info param
+ }
+
+ private static char GetSignatureLetter(Il2CppTypeReflectionData type, bool isRefOrOut = false)
+ {
+ if(isRefOrOut)
+ //ref/out params are passed as pointers
+ return 'i';
+
+ if (type.isPointer)
+ //Pointers are ints
+ return 'i';
+
+ var typeDefinition = type.baseType ?? LibCpp2IlReflection.GetType("Int32", "System")!;
+
+ return typeDefinition.Name switch
+ {
+ "Void" => 'v',
+ "Int64" => 'j',
+ "Single" => 'f',
+ "Double" => 'd',
+ _ => 'i' //Including Int32
+ };
+ }
+
+ public static string GetGhidraFunctionName(WasmFunctionDefinition functionDefinition)
+ {
+ var index = functionDefinition.IsImport
+ ? ((WasmFile) LibCpp2IlMain.Binary!).FunctionTable.IndexOf(functionDefinition)
+ : functionDefinition.FunctionTableIndex;
+
+ return $"unnamed_function_{index}";
+ }
+
+ public static WasmFunctionDefinition? TryGetWasmDefinition(Il2CppMethodDefinition definition)
+ {
+ try
+ {
+ return GetWasmDefinition(definition);
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ public static WasmFunctionDefinition GetWasmDefinition(Il2CppMethodDefinition definition)
+ {
+ //First, we have to calculate the signature
+ var signature = BuildSignature(definition);
+ try
+ {
+ return ((WasmFile) LibCpp2IlMain.Binary!).GetFunctionFromIndexAndSignature(definition.MethodPointer, signature);
+ }
+ catch (Exception e)
+ {
+ throw new($"Failed to find wasm definition for {definition}\nwhich has params {definition.Parameters?.ToStringEnumerable()}", e);
+ }
+ }
+
+ private static void CalculateAllMethodDefinitionIndices()
+ {
+ foreach (var il2CppMethodDefinition in LibCpp2IlMain.TheMetadata!.methodDefs)
+ {
+ var methodDefinition = il2CppMethodDefinition;
+
+ try
+ {
+ var wasmDef = GetWasmDefinition(methodDefinition);
+ var index = ((WasmFile) LibCpp2IlMain.Binary!).FunctionTable.IndexOf(wasmDef);
+
+ if (!MethodDefinitionIndices.TryGetValue(index, out var mDefs))
+ MethodDefinitionIndices[index] = mDefs = new();
+
+ mDefs.Add(methodDefinition);
+ }
+ catch (Exception)
+ {
+ //Ignore
+ }
+ }
+ }
+
+ public static List? GetMethodDefinitionsAtIndex(int index)
+ {
+ if(MethodDefinitionIndices.Count == 0)
+ CalculateAllMethodDefinitionIndices();
+
+ if (MethodDefinitionIndices.TryGetValue(index, out var methodDefinitions))
+ return methodDefinitions;
+
+ return null;
+ }
+
+ public static Dictionary ExtractAndParseDynCallRemaps(string frameworkJsFile)
+ {
+ //At least one WASM binary found in the wild had the exported function names obfuscated.
+ //However, the framework.js file has mappings to the correct names.
+ /*e.g.
+ var dynCall_viffiiii = Module["dynCall_viffiiii"] = function() {
+ return (dynCall_viffiiii = Module["dynCall_viffiiii"] = Module["asm"]["Wo"]).apply(null, arguments)
+ }
+ */
+
+ var ret = new Dictionary();
+ var matches = DynCallRemappingRegex.Matches(frameworkJsFile);
+ foreach (Match match in matches)
+ {
+ //Group 1 is the original method name, e.g. dynCall_viffiiii
+ //Group 2 is the remapped name, e.g Wo
+ var origName = match.Groups[1];
+ var remappedName = match.Groups[2];
+
+ ret[remappedName.Value] = origName.Value;
+ }
+
+ return ret;
+ }
+ }
+}
diff --git a/Cpp2IL.InstructionSets.X86/Cpp2IL.InstructionSets.X86.csproj b/Cpp2IL.InstructionSets.X86/Cpp2IL.InstructionSets.X86.csproj
new file mode 100644
index 00000000..c2972006
--- /dev/null
+++ b/Cpp2IL.InstructionSets.X86/Cpp2IL.InstructionSets.X86.csproj
@@ -0,0 +1,11 @@
+
+
+ netstandard2.0
+ true
+
+
+
+
+
+
+
diff --git a/Cpp2IL.Core/OldGraphs/X86ControlFlowGraphCondition.cs b/Cpp2IL.InstructionSets.X86/OldGraphs/X86ControlFlowGraphCondition.cs
similarity index 98%
rename from Cpp2IL.Core/OldGraphs/X86ControlFlowGraphCondition.cs
rename to Cpp2IL.InstructionSets.X86/OldGraphs/X86ControlFlowGraphCondition.cs
index 6ed3e081..e049d502 100644
--- a/Cpp2IL.Core/OldGraphs/X86ControlFlowGraphCondition.cs
+++ b/Cpp2IL.InstructionSets.X86/OldGraphs/X86ControlFlowGraphCondition.cs
@@ -1,5 +1,4 @@
-using System;
-using Iced.Intel;
+using Iced.Intel;
namespace Cpp2IL.Core.Graphs;
diff --git a/Cpp2IL.Core/OldGraphs/x86ControlFlowGraph.cs b/Cpp2IL.InstructionSets.X86/OldGraphs/x86ControlFlowGraph.cs
similarity index 99%
rename from Cpp2IL.Core/OldGraphs/x86ControlFlowGraph.cs
rename to Cpp2IL.InstructionSets.X86/OldGraphs/x86ControlFlowGraph.cs
index 35c2a7dc..d2bf2e04 100644
--- a/Cpp2IL.Core/OldGraphs/x86ControlFlowGraph.cs
+++ b/Cpp2IL.InstructionSets.X86/OldGraphs/x86ControlFlowGraph.cs
@@ -1,7 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Cpp2IL.Core.Il2CppApiFunctions;
+using Cpp2IL.Core.Il2CppApiFunctions;
using Iced.Intel;
namespace Cpp2IL.Core.Graphs;
diff --git a/Cpp2IL.Core/OldGraphs/x86ControlFlowGraphNode.cs b/Cpp2IL.InstructionSets.X86/OldGraphs/x86ControlFlowGraphNode.cs
similarity index 97%
rename from Cpp2IL.Core/OldGraphs/x86ControlFlowGraphNode.cs
rename to Cpp2IL.InstructionSets.X86/OldGraphs/x86ControlFlowGraphNode.cs
index 810e3b3c..94fbda48 100644
--- a/Cpp2IL.Core/OldGraphs/x86ControlFlowGraphNode.cs
+++ b/Cpp2IL.InstructionSets.X86/OldGraphs/x86ControlFlowGraphNode.cs
@@ -1,4 +1,3 @@
-using System.Linq;
using Iced.Intel;
namespace Cpp2IL.Core.Graphs;
diff --git a/Cpp2IL.InstructionSets.X86/X86Extensions.cs b/Cpp2IL.InstructionSets.X86/X86Extensions.cs
new file mode 100644
index 00000000..66e7c8b4
--- /dev/null
+++ b/Cpp2IL.InstructionSets.X86/X86Extensions.cs
@@ -0,0 +1,39 @@
+using Cpp2IL.Core.ISIL;
+using Iced.Intel;
+using Instruction = Iced.Intel.Instruction;
+
+namespace Cpp2IL.InstructionSets.X86;
+
+public static class X86Extensions
+{
+ public static InstructionSetIndependentOperand MakeIndependent(this Register reg) => InstructionSetIndependentOperand.MakeRegister(reg.ToString().ToLower());
+
+ public static ulong GetImmediateSafe(this Instruction instruction, int op) => instruction.GetOpKind(op).IsImmediate() ? instruction.GetImmediate(op) : 0;
+
+ public static bool IsJump(this Mnemonic mnemonic) => mnemonic is Mnemonic.Call or >= Mnemonic.Ja and <= Mnemonic.Js;
+ public static bool IsConditionalJump(this Mnemonic mnemonic) => mnemonic.IsJump() && mnemonic != Mnemonic.Jmp && mnemonic != Mnemonic.Call;
+
+ public static bool IsConditionalMove(this Instruction instruction)
+ {
+ switch (instruction.Mnemonic)
+ {
+ case Mnemonic.Cmove:
+ case Mnemonic.Cmovne:
+ case Mnemonic.Cmovs:
+ case Mnemonic.Cmovns:
+ case Mnemonic.Cmovg:
+ case Mnemonic.Cmovge:
+ case Mnemonic.Cmovl:
+ case Mnemonic.Cmovle:
+ case Mnemonic.Cmova:
+ case Mnemonic.Cmovae:
+ case Mnemonic.Cmovb:
+ case Mnemonic.Cmovbe:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public static bool IsImmediate(this OpKind opKind) => opKind is >= OpKind.Immediate8 and <= OpKind.Immediate32to64;
+}
diff --git a/Cpp2IL.Core/InstructionSets/X86InstructionSet.cs b/Cpp2IL.InstructionSets.X86/X86InstructionSet.cs
similarity index 98%
rename from Cpp2IL.Core/InstructionSets/X86InstructionSet.cs
rename to Cpp2IL.InstructionSets.X86/X86InstructionSet.cs
index 87d6bd7f..cc7eb973 100644
--- a/Cpp2IL.Core/InstructionSets/X86InstructionSet.cs
+++ b/Cpp2IL.InstructionSets.X86/X86InstructionSet.cs
@@ -1,18 +1,19 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
using Cpp2IL.Core.Api;
-using Cpp2IL.Core.Extensions;
using Cpp2IL.Core.Il2CppApiFunctions;
using Cpp2IL.Core.ISIL;
using Cpp2IL.Core.Model.Contexts;
-using Cpp2IL.Core.Utils;
using Iced.Intel;
+using LibCpp2IL;
-namespace Cpp2IL.Core.InstructionSets;
+namespace Cpp2IL.InstructionSets.X86;
public class X86InstructionSet : Cpp2IlInstructionSet
{
+ public static void RegisterInstructionSet()
+ {
+ InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.X86_32);
+ InstructionSetRegistry.RegisterInstructionSet(DefaultInstructionSets.X86_64);
+ }
public override Memory GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator) => X86Utils.GetRawManagedOrCaCacheGenMethodBody(context.UnderlyingPointer, isAttributeGenerator);
diff --git a/Cpp2IL.Core/Il2CppApiFunctions/X86KeyFunctionAddresses.cs b/Cpp2IL.InstructionSets.X86/X86KeyFunctionAddresses.cs
similarity index 72%
rename from Cpp2IL.Core/Il2CppApiFunctions/X86KeyFunctionAddresses.cs
rename to Cpp2IL.InstructionSets.X86/X86KeyFunctionAddresses.cs
index a04e29b3..6fca5726 100644
--- a/Cpp2IL.Core/Il2CppApiFunctions/X86KeyFunctionAddresses.cs
+++ b/Cpp2IL.InstructionSets.X86/X86KeyFunctionAddresses.cs
@@ -1,13 +1,10 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
+using Cpp2IL.Core.Il2CppApiFunctions;
using Cpp2IL.Core.Logging;
-using Cpp2IL.Core.Utils;
using Iced.Intel;
using LibCpp2IL;
using LibCpp2IL.Reflection;
-namespace Cpp2IL.Core.Il2CppApiFunctions
+namespace Cpp2IL.InstructionSets.X86
{
public class X86KeyFunctionAddresses : BaseKeyFunctionAddresses
{
@@ -131,5 +128,39 @@ protected override int GetCallerCount(ulong toWhere)
//Find all jumps to the target address
return allInstructions.Count(i => i.Mnemonic == Mnemonic.Jmp || i.Mnemonic == Mnemonic.Call && i.NearBranchTarget == toWhere);
}
+
+ protected override void TryGetInitMetadataFromException()
+ {
+ //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 = LibCpp2IlReflection.GetType("Exception", "System")!;
+ 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 at {targetMethod.MethodPointer}. Taking first CALL as the (version-specific) metadata initialization function...");
+
+ var disasm = X86Utils.GetMethodBodyAtVirtAddressNew(targetMethod.MethodPointer, false);
+ var calls = disasm.Where(i => i.Mnemonic == Mnemonic.Call).ToList();
+
+ if (calls.Count == 0)
+ {
+ Logger.WarnNewline("Couldn't find any call instructions in the method body. This is not expected. Will not have metadata initialization function.");
+ return;
+ }
+
+ if (_appContext.MetadataVersion < 27)
+ {
+ il2cpp_codegen_initialize_method = calls.First().NearBranchTarget;
+ Logger.VerboseNewline($"\t\til2cpp_codegen_initialize_method => 0x{il2cpp_codegen_initialize_method:X}");
+ }
+ else
+ {
+ il2cpp_codegen_initialize_runtime_metadata = calls.First().NearBranchTarget;
+ Logger.VerboseNewline($"\t\til2cpp_codegen_initialize_runtime_metadata => 0x{il2cpp_codegen_initialize_runtime_metadata:X}");
+ }
+ }
+ }
}
-}
\ No newline at end of file
+}
diff --git a/Cpp2IL.Core/Utils/X86Utils.cs b/Cpp2IL.InstructionSets.X86/X86Utils.cs
similarity index 98%
rename from Cpp2IL.Core/Utils/X86Utils.cs
rename to Cpp2IL.InstructionSets.X86/X86Utils.cs
index 1e69cbab..2edc70ad 100644
--- a/Cpp2IL.Core/Utils/X86Utils.cs
+++ b/Cpp2IL.InstructionSets.X86/X86Utils.cs
@@ -1,14 +1,12 @@
-using System;
using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Linq;
using System.Text.RegularExpressions;
using Cpp2IL.Core.Extensions;
using Cpp2IL.Core.Logging;
+using Cpp2IL.Core.Utils;
using Iced.Intel;
using LibCpp2IL;
-namespace Cpp2IL.Core.Utils
+namespace Cpp2IL.InstructionSets.X86
{
public static class X86Utils
{
diff --git a/Cpp2IL.Plugin.BuildReport/Cpp2IL.Plugin.BuildReport.csproj b/Cpp2IL.Plugin.BuildReport/Cpp2IL.Plugin.BuildReport.csproj
index 6a85f9db..38d21c3c 100644
--- a/Cpp2IL.Plugin.BuildReport/Cpp2IL.Plugin.BuildReport.csproj
+++ b/Cpp2IL.Plugin.BuildReport/Cpp2IL.Plugin.BuildReport.csproj
@@ -1,22 +1,9 @@
-
net7.0
- enable
- enable
- embedded
-
- all
- compile; build
- runtime
-
+
-
-
-
-
-
diff --git a/Cpp2IL.Plugin.OrbisPkg/Cpp2IL.Plugin.OrbisPkg.csproj b/Cpp2IL.Plugin.OrbisPkg/Cpp2IL.Plugin.OrbisPkg.csproj
index befff947..87b92f53 100644
--- a/Cpp2IL.Plugin.OrbisPkg/Cpp2IL.Plugin.OrbisPkg.csproj
+++ b/Cpp2IL.Plugin.OrbisPkg/Cpp2IL.Plugin.OrbisPkg.csproj
@@ -1,21 +1,13 @@
-
-
-
- netstandard2.0
- enable
- enable
- 10
- embedded
-
-
-
-
-
-
-
-
- Deps\LibOrbisPkg.Core.dll
-
-
-
-
+
+
+ netstandard2.0
+
+
+
+
+
+
+ Deps\LibOrbisPkg.Core.dll
+
+
+
diff --git a/Cpp2IL.Plugin.StrippedCodeRegSupport/Cpp2IL.Plugin.StrippedCodeRegSupport.csproj b/Cpp2IL.Plugin.StrippedCodeRegSupport/Cpp2IL.Plugin.StrippedCodeRegSupport.csproj
index 30f47d37..38d21c3c 100644
--- a/Cpp2IL.Plugin.StrippedCodeRegSupport/Cpp2IL.Plugin.StrippedCodeRegSupport.csproj
+++ b/Cpp2IL.Plugin.StrippedCodeRegSupport/Cpp2IL.Plugin.StrippedCodeRegSupport.csproj
@@ -1,13 +1,9 @@
-
net7.0
- enable
- enable
-
+
-
diff --git a/Cpp2IL.sln b/Cpp2IL.sln
index d0d27607..a85acb1c 100644
--- a/Cpp2IL.sln
+++ b/Cpp2IL.sln
@@ -45,6 +45,18 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cpp2IL.Core.Tests", "Cpp2IL
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cpp2IL.Plugin.StrippedCodeRegSupport", "Cpp2IL.Plugin.StrippedCodeRegSupport\Cpp2IL.Plugin.StrippedCodeRegSupport.csproj", "{B2425628-0D69-44FA-AD34-997595512785}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "InstructionSets", "InstructionSets", "{6B0AAAA9-3C29-4AD9-84CB-47DB34134C82}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cpp2IL.InstructionSets.X86", "Cpp2IL.InstructionSets.X86\Cpp2IL.InstructionSets.X86.csproj", "{1BD3FC5D-A078-42BA-995D-E088EE799274}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cpp2IL.InstructionSets.All", "Cpp2IL.InstructionSets.All\Cpp2IL.InstructionSets.All.csproj", "{CC7781FC-1A0E-441D-9551-412509BE63DF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cpp2IL.InstructionSets.ArmV8", "Cpp2IL.InstructionSets.ArmV8\Cpp2IL.InstructionSets.ArmV8.csproj", "{6B9023CB-8EB3-4738-B079-FD7E7AE76023}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cpp2IL.InstructionSets.Wasm", "Cpp2IL.InstructionSets.Wasm\Cpp2IL.InstructionSets.Wasm.csproj", "{69F27003-2D6B-42E3-BB69-C542CA16E0D8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cpp2IL.InstructionSets.ArmV7", "Cpp2IL.InstructionSets.ArmV7\Cpp2IL.InstructionSets.ArmV7.csproj", "{BBBC3D51-9F2E-4AB3-B4BE-195A01568F1D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -99,12 +111,37 @@ Global
{B2425628-0D69-44FA-AD34-997595512785}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2425628-0D69-44FA-AD34-997595512785}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B2425628-0D69-44FA-AD34-997595512785}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1BD3FC5D-A078-42BA-995D-E088EE799274}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1BD3FC5D-A078-42BA-995D-E088EE799274}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1BD3FC5D-A078-42BA-995D-E088EE799274}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1BD3FC5D-A078-42BA-995D-E088EE799274}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CC7781FC-1A0E-441D-9551-412509BE63DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CC7781FC-1A0E-441D-9551-412509BE63DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CC7781FC-1A0E-441D-9551-412509BE63DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CC7781FC-1A0E-441D-9551-412509BE63DF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6B9023CB-8EB3-4738-B079-FD7E7AE76023}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6B9023CB-8EB3-4738-B079-FD7E7AE76023}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6B9023CB-8EB3-4738-B079-FD7E7AE76023}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6B9023CB-8EB3-4738-B079-FD7E7AE76023}.Release|Any CPU.Build.0 = Release|Any CPU
+ {69F27003-2D6B-42E3-BB69-C542CA16E0D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {69F27003-2D6B-42E3-BB69-C542CA16E0D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {69F27003-2D6B-42E3-BB69-C542CA16E0D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {69F27003-2D6B-42E3-BB69-C542CA16E0D8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BBBC3D51-9F2E-4AB3-B4BE-195A01568F1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BBBC3D51-9F2E-4AB3-B4BE-195A01568F1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BBBC3D51-9F2E-4AB3-B4BE-195A01568F1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BBBC3D51-9F2E-4AB3-B4BE-195A01568F1D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{51C7C919-3561-4630-827C-7FF8173AF665} = {46226B08-22BA-455E-8B99-F496E90EDCBC}
+ {1BD3FC5D-A078-42BA-995D-E088EE799274} = {6B0AAAA9-3C29-4AD9-84CB-47DB34134C82}
+ {CC7781FC-1A0E-441D-9551-412509BE63DF} = {6B0AAAA9-3C29-4AD9-84CB-47DB34134C82}
+ {6B9023CB-8EB3-4738-B079-FD7E7AE76023} = {6B0AAAA9-3C29-4AD9-84CB-47DB34134C82}
+ {69F27003-2D6B-42E3-BB69-C542CA16E0D8} = {6B0AAAA9-3C29-4AD9-84CB-47DB34134C82}
+ {BBBC3D51-9F2E-4AB3-B4BE-195A01568F1D} = {6B0AAAA9-3C29-4AD9-84CB-47DB34134C82}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E9A45B2C-AAEF-4D66-ADD6-7DD234DA3F39}
diff --git a/Cpp2IL/Cpp2IL.csproj b/Cpp2IL/Cpp2IL.csproj
index 9bb21c9d..71cfd8cb 100644
--- a/Cpp2IL/Cpp2IL.csproj
+++ b/Cpp2IL/Cpp2IL.csproj
@@ -1,26 +1,16 @@
-
+
- Samboy063
- Copyright © Samboy063 2019-2023
- Debug;Release
- embedded
- true
- true
- 10
- enable
+ net7.0;net472
Exe
+
true
true
- net7.0;net472
+ true
+ true
partial
- 2022.1.0
true
-
- bin\x64\Debug\
-
-
@@ -28,6 +18,7 @@
+
diff --git a/Cpp2IL/Program.cs b/Cpp2IL/Program.cs
index 2e44fe7c..2d1bb623 100644
--- a/Cpp2IL/Program.cs
+++ b/Cpp2IL/Program.cs
@@ -17,6 +17,7 @@
using LibCpp2IL.Wasm;
using AssetRipper.VersionUtilities;
using Cpp2IL.Core.Extensions;
+using Cpp2IL.InstructionSets.All;
using LibCpp2IL;
namespace Cpp2IL
@@ -346,6 +347,7 @@ private static Cpp2IlRuntimeArgs GetRuntimeOptionsFromCommandLine(string[] comma
ConsoleLogger.ShowVerbose = options.Verbose;
+ AllInstructionSets.Register();
Cpp2IlApi.Init();
if (options.ListProcessors)
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 00000000..dc4038f9
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,21 @@
+
+
+ enable
+ 10
+ enable
+ embedded
+
+ true
+
+ 2022.1.0
+ development
+ Sam Byass (Samboy063)
+ Copyright © Samboy063 2019-2023
+
+ https://github.com/SamboyCoding/Cpp2IL
+ MIT
+ git
+ https://github.com/SamboyCoding/Cpp2IL.git
+ true
+
+
diff --git a/Directory.Build.targets b/Directory.Build.targets
new file mode 100644
index 00000000..6a0a585e
--- /dev/null
+++ b/Directory.Build.targets
@@ -0,0 +1,5 @@
+
+
+ Samboy063.$(AssemblyName)
+
+
diff --git a/LibCpp2IL/LibCpp2IL.csproj b/LibCpp2IL/LibCpp2IL.csproj
index c814f56c..0450872e 100644
--- a/LibCpp2IL/LibCpp2IL.csproj
+++ b/LibCpp2IL/LibCpp2IL.csproj
@@ -1,28 +1,14 @@
-
- enable
- 9
- embedded
- Samboy063.LibCpp2IL
- LibCpp2IL
- Samboy063
- Copyright © Samboy063 2019-2023
- Samboy063
- 2022.1.0
- true
- MIT
- git
- https://github.com/SamboyCoding/Cpp2IL.git
- true
- Library for interacting with IL2CPP metadata and binaries
- Debug;Release
net7.0;net6.0;netstandard2.0
true
+
+ Library for interacting with IL2CPP metadata and binaries
+ true
-
+
@@ -34,5 +20,4 @@
-
diff --git a/LibCpp2ILTests/LibCpp2ILTests.csproj b/LibCpp2ILTests/LibCpp2ILTests.csproj
index 85a42ca6..f69a421e 100644
--- a/LibCpp2ILTests/LibCpp2ILTests.csproj
+++ b/LibCpp2ILTests/LibCpp2ILTests.csproj
@@ -1,17 +1,12 @@
-
net7.0
false
-
- Debug;Release
-
- AnyCPU;x64
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -24,5 +19,4 @@
-
diff --git a/StableNameDotNet/StableNameDotNet.csproj b/StableNameDotNet/StableNameDotNet.csproj
index ea12e058..4e53e870 100644
--- a/StableNameDotNet/StableNameDotNet.csproj
+++ b/StableNameDotNet/StableNameDotNet.csproj
@@ -1,21 +1,10 @@
-
- Samboy063
- Samboy063
- Copyright © Samboy063 2022-2023
- embedded
+ netstandard2.0
+ 0.1.0
+
+ StableNameDotNet
Library for generating somewhat stable names for obfuscated types, based on their content.
true
- enable
- StableNameDotNet
- true
- MIT
- git
- https://github.com/SamboyCoding/Cpp2IL.git
- netstandard2.0
- 10
- 0.1.0
-
diff --git a/WasmDisassembler/WasmDisassembler.csproj b/WasmDisassembler/WasmDisassembler.csproj
index 391329bd..dddf5c92 100644
--- a/WasmDisassembler/WasmDisassembler.csproj
+++ b/WasmDisassembler/WasmDisassembler.csproj
@@ -1,22 +1,9 @@
-
- Samboy063
- Samboy063
- embedded
- enable
- enable
- 10
- Samboy063.WasmDisassembler
- 2022.1.0
- true
- MIT
- git
- https://github.com/SamboyCoding/Cpp2IL.git
- true
- Simple, zero-dependency disassembler for WebAssembly bytecode
net7.0;net6.0;netstandard2.0
true
-
+ Simple, zero-dependency disassembler for WebAssembly bytecode
+ true
+