From a44d6d01110c7599a7b4def886aa7ae21a0297f8 Mon Sep 17 00:00:00 2001 From: js6pak Date: Mon, 1 May 2023 17:54:50 +0200 Subject: [PATCH 1/4] Refactor project files --- Cpp2IL.Core.Tests/Cpp2IL.Core.Tests.csproj | 9 ++--- Cpp2IL.Core/Cpp2IL.Core.csproj | 38 +++++-------------- Cpp2IL.Gui/Cpp2IL.Gui.csproj | 37 ++++++------------ .../Cpp2IL.Plugin.BuildReport.csproj | 15 +------- .../Cpp2IL.Plugin.OrbisPkg.csproj | 34 +++++++---------- ...pp2IL.Plugin.StrippedCodeRegSupport.csproj | 6 +-- Cpp2IL/Cpp2IL.csproj | 20 +++------- Directory.Build.props | 21 ++++++++++ Directory.Build.targets | 5 +++ LibCpp2IL/LibCpp2IL.csproj | 23 ++--------- LibCpp2ILTests/LibCpp2ILTests.csproj | 8 +--- StableNameDotNet/StableNameDotNet.csproj | 19 ++-------- WasmDisassembler/WasmDisassembler.csproj | 19 ++-------- 13 files changed, 82 insertions(+), 172 deletions(-) create mode 100644 Directory.Build.props create mode 100644 Directory.Build.targets diff --git a/Cpp2IL.Core.Tests/Cpp2IL.Core.Tests.csproj b/Cpp2IL.Core.Tests/Cpp2IL.Core.Tests.csproj index 41efa2ed..217e033f 100644 --- a/Cpp2IL.Core.Tests/Cpp2IL.Core.Tests.csproj +++ b/Cpp2IL.Core.Tests/Cpp2IL.Core.Tests.csproj @@ -1,8 +1,6 @@  - net7.0 - enable false @@ -12,11 +10,10 @@ - + - - + + - diff --git a/Cpp2IL.Core/Cpp2IL.Core.csproj b/Cpp2IL.Core/Cpp2IL.Core.csproj index 8c67f80d..317154d6 100644 --- a/Cpp2IL.Core/Cpp2IL.Core.csproj +++ b/Cpp2IL.Core/Cpp2IL.Core.csproj @@ -1,56 +1,36 @@ - 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.Gui/Cpp2IL.Gui.csproj b/Cpp2IL.Gui/Cpp2IL.Gui.csproj index 32d21c6d..d233b741 100644 --- a/Cpp2IL.Gui/Cpp2IL.Gui.csproj +++ b/Cpp2IL.Gui/Cpp2IL.Gui.csproj @@ -1,24 +1,20 @@  - true - Debug;Release - true - true - enable + net7.0 Exe - AnyCPU;x64 + true true - net7.0 + true + true + true link - 2022.1.0 + - - - - - + + + @@ -27,18 +23,9 @@ + - - - - - - - - - - - - + + 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/Cpp2IL.csproj b/Cpp2IL/Cpp2IL.csproj index 9bb21c9d..c8cd14f7 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\ - - 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 + From 9306f21fb6dd9152a4ee5b9e65922c309a6bbcf0 Mon Sep 17 00:00:00 2001 From: js6pak Date: Fri, 5 May 2023 17:52:01 +0200 Subject: [PATCH 2/4] Split instruction sets into their own packages --- Cpp2IL.Core.Tests/Cpp2IL.Core.Tests.csproj | 1 + Cpp2IL.Core.Tests/GameLoader.cs | 18 +- Cpp2IL.Core/Cpp2IL.Core.csproj | 9 - Cpp2IL.Core/Cpp2IlCorePlugin.cs | 17 +- Cpp2IL.Core/Extensions/MiscExtensions.cs | 60 ---- .../Arm64KeyFunctionAddresses.cs | 314 ------------------ .../BaseKeyFunctionAddresses.cs | 43 +-- .../InstructionSets/Arm64InstructionSet.cs | 66 ---- .../InstructionSets/ArmV7InstructionSet.cs | 60 ---- Cpp2IL.Core/OldGraphs/InstructionGraph.cs | 5 +- Cpp2IL.Core/SharedState.cs | 4 +- Cpp2IL.Core/Utils/Arm64Utils.cs | 149 --------- Cpp2IL.Core/Utils/ArmV7Utils.cs | 90 ----- Cpp2IL.Gui/Cpp2IL.Gui.csproj | 1 + Cpp2IL.Gui/Program.cs | 2 + .../AllInstructionSets.cs | 17 + .../Cpp2IL.InstructionSets.All.csproj | 13 + .../ArmV8InstructionSet.cs | 26 +- .../ArmV8KeyFunctionAddresses.cs | 59 +--- .../ArmV8Utils.cs | 7 +- .../Cpp2IL.InstructionSets.ArmV8.csproj | 11 + .../Cpp2IL.InstructionSets.Wasm.csproj | 11 + .../WasmInstructionSet.cs | 11 +- .../WasmKeyFunctionAddresses.cs | 4 +- .../WasmMappingOutputFormat.cs | 62 ++++ Cpp2IL.InstructionSets.Wasm/WasmUtils.cs | 139 ++++++++ .../Cpp2IL.InstructionSets.X86.csproj | 11 + .../OldGraphs/X86ControlFlowGraphCondition.cs | 3 +- .../OldGraphs/x86ControlFlowGraph.cs | 5 +- .../OldGraphs/x86ControlFlowGraphNode.cs | 1 - Cpp2IL.InstructionSets.X86/X86Extensions.cs | 39 +++ .../X86InstructionSet.cs | 13 +- .../X86KeyFunctionAddresses.cs | 43 ++- .../X86Utils.cs | 6 +- Cpp2IL.sln | 30 ++ Cpp2IL/Cpp2IL.csproj | 1 + Cpp2IL/Program.cs | 2 + 37 files changed, 429 insertions(+), 924 deletions(-) delete mode 100644 Cpp2IL.Core/Il2CppApiFunctions/Arm64KeyFunctionAddresses.cs delete mode 100644 Cpp2IL.Core/InstructionSets/Arm64InstructionSet.cs delete mode 100644 Cpp2IL.Core/InstructionSets/ArmV7InstructionSet.cs delete mode 100644 Cpp2IL.Core/Utils/Arm64Utils.cs delete mode 100644 Cpp2IL.Core/Utils/ArmV7Utils.cs create mode 100644 Cpp2IL.InstructionSets.All/AllInstructionSets.cs create mode 100644 Cpp2IL.InstructionSets.All/Cpp2IL.InstructionSets.All.csproj rename Cpp2IL.Core/InstructionSets/NewArmV8InstructionSet.cs => Cpp2IL.InstructionSets.ArmV8/ArmV8InstructionSet.cs (74%) rename Cpp2IL.Core/Il2CppApiFunctions/NewArm64KeyFunctionAddresses.cs => Cpp2IL.InstructionSets.ArmV8/ArmV8KeyFunctionAddresses.cs (56%) rename Cpp2IL.Core/Utils/NewArm64Utils.cs => Cpp2IL.InstructionSets.ArmV8/ArmV8Utils.cs (95%) create mode 100644 Cpp2IL.InstructionSets.ArmV8/Cpp2IL.InstructionSets.ArmV8.csproj create mode 100644 Cpp2IL.InstructionSets.Wasm/Cpp2IL.InstructionSets.Wasm.csproj rename {Cpp2IL.Core/InstructionSets => Cpp2IL.InstructionSets.Wasm}/WasmInstructionSet.cs (89%) rename {Cpp2IL.Core/Il2CppApiFunctions => Cpp2IL.InstructionSets.Wasm}/WasmKeyFunctionAddresses.cs (89%) create mode 100644 Cpp2IL.InstructionSets.Wasm/WasmMappingOutputFormat.cs create mode 100644 Cpp2IL.InstructionSets.Wasm/WasmUtils.cs create mode 100644 Cpp2IL.InstructionSets.X86/Cpp2IL.InstructionSets.X86.csproj rename {Cpp2IL.Core => Cpp2IL.InstructionSets.X86}/OldGraphs/X86ControlFlowGraphCondition.cs (98%) rename {Cpp2IL.Core => Cpp2IL.InstructionSets.X86}/OldGraphs/x86ControlFlowGraph.cs (99%) rename {Cpp2IL.Core => Cpp2IL.InstructionSets.X86}/OldGraphs/x86ControlFlowGraphNode.cs (97%) create mode 100644 Cpp2IL.InstructionSets.X86/X86Extensions.cs rename {Cpp2IL.Core/InstructionSets => Cpp2IL.InstructionSets.X86}/X86InstructionSet.cs (98%) rename {Cpp2IL.Core/Il2CppApiFunctions => Cpp2IL.InstructionSets.X86}/X86KeyFunctionAddresses.cs (72%) rename {Cpp2IL.Core/Utils => Cpp2IL.InstructionSets.X86}/X86Utils.cs (98%) diff --git a/Cpp2IL.Core.Tests/Cpp2IL.Core.Tests.csproj b/Cpp2IL.Core.Tests/Cpp2IL.Core.Tests.csproj index 217e033f..46e3de6c 100644 --- a/Cpp2IL.Core.Tests/Cpp2IL.Core.Tests.csproj +++ b/Cpp2IL.Core.Tests/Cpp2IL.Core.Tests.csproj @@ -7,6 +7,7 @@ + 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 317154d6..fd0281f3 100644 --- a/Cpp2IL.Core/Cpp2IL.Core.csproj +++ b/Cpp2IL.Core/Cpp2IL.Core.csproj @@ -13,15 +13,6 @@ - - - - - - - - - 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..bb5c7fa5 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() 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.Gui/Cpp2IL.Gui.csproj b/Cpp2IL.Gui/Cpp2IL.Gui.csproj index d233b741..db8468fc 100644 --- a/Cpp2IL.Gui/Cpp2IL.Gui.csproj +++ b/Cpp2IL.Gui/Cpp2IL.Gui.csproj @@ -13,6 +13,7 @@ + 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..7b26cd02 --- /dev/null +++ b/Cpp2IL.InstructionSets.All/AllInstructionSets.cs @@ -0,0 +1,17 @@ +using Cpp2IL.Core.Api; +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(); + 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..d87750f3 --- /dev/null +++ b/Cpp2IL.InstructionSets.All/Cpp2IL.InstructionSets.All.csproj @@ -0,0 +1,13 @@ + + + netstandard2.0 + true + + + + + + + + + diff --git a/Cpp2IL.Core/InstructionSets/NewArmV8InstructionSet.cs b/Cpp2IL.InstructionSets.ArmV8/ArmV8InstructionSet.cs similarity index 74% rename from Cpp2IL.Core/InstructionSets/NewArmV8InstructionSet.cs rename to Cpp2IL.InstructionSets.ArmV8/ArmV8InstructionSet.cs index af28009d..d911a6e5 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,8 +27,8 @@ public override Memory GetRawBytesForMethod(MethodAnalysisContext context, if (startOfNextFunction > 0) return LibCpp2IlMain.Binary!.GetRawBinaryContent().AsMemory(ptrAsInt, count); } - - var result = NewArm64Utils.GetArm64MethodBodyAtVirtualAddress(context.UnderlyingPointer); + + var result = ArmV8Utils.GetArm64MethodBodyAtVirtualAddress(context.UnderlyingPointer); var endVa = result.EndVirtualAddress; var start = (int) context.AppContext.Binary.MapVirtualAddressToRaw(context.UnderlyingPointer); @@ -36,15 +39,14 @@ public override Memory GetRawBytesForMethod(MethodAnalysisContext context, public override List GetIsilFromMethod(MethodAnalysisContext context) { - var result = NewArm64Utils.GetArm64MethodBodyAtVirtualAddress(context.UnderlyingPointer); - - //TODO - return new(); + var result = ArmV8Utils.GetArm64MethodBodyAtVirtualAddress(context.UnderlyingPointer); + + 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); diff --git a/Cpp2IL.Core/Il2CppApiFunctions/NewArm64KeyFunctionAddresses.cs b/Cpp2IL.InstructionSets.ArmV8/ArmV8KeyFunctionAddresses.cs similarity index 56% rename from Cpp2IL.Core/Il2CppApiFunctions/NewArm64KeyFunctionAddresses.cs rename to Cpp2IL.InstructionSets.ArmV8/ArmV8KeyFunctionAddresses.cs index 8bbdd99a..7249e6f7 100644 --- a/Cpp2IL.Core/Il2CppApiFunctions/NewArm64KeyFunctionAddresses.cs +++ b/Cpp2IL.InstructionSets.ArmV8/ArmV8KeyFunctionAddresses.cs @@ -1,16 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using Cpp2IL.Core.Il2CppApiFunctions; using Disarm; -using Cpp2IL.Core.Logging; -using Cpp2IL.Core.Utils; -using Iced.Intel; using LibCpp2IL; -using LibCpp2IL.Reflection; -namespace Cpp2IL.Core.Il2CppApiFunctions; +namespace Cpp2IL.InstructionSets.ArmV8; -public class NewArm64KeyFunctionAddresses : BaseKeyFunctionAddresses +public class ArmV8KeyFunctionAddresses : BaseKeyFunctionAddresses { private Arm64DisassemblyResult? _cachedDisassembledBytes; @@ -73,55 +67,12 @@ protected override IEnumerable FindAllThunkFunctions(ulong addr, uint max 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; + throw new NotImplementedException(); } 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; - } + throw new NotImplementedException(); } protected override int GetCallerCount(ulong toWhere) diff --git a/Cpp2IL.Core/Utils/NewArm64Utils.cs b/Cpp2IL.InstructionSets.ArmV8/ArmV8Utils.cs similarity index 95% rename from Cpp2IL.Core/Utils/NewArm64Utils.cs rename to Cpp2IL.InstructionSets.ArmV8/ArmV8Utils.cs index 3cf57d81..48eb0452 100644 --- a/Cpp2IL.Core/Utils/NewArm64Utils.cs +++ b/Cpp2IL.InstructionSets.ArmV8/ArmV8Utils.cs @@ -1,11 +1,10 @@ -using System; -using System.Linq; +using Cpp2IL.Core.Utils; using Disarm; using LibCpp2IL; -namespace Cpp2IL.Core.Utils; +namespace Cpp2IL.InstructionSets.ArmV8; -public static class NewArm64Utils +internal static class ArmV8Utils { public static Arm64DisassemblyResult GetArm64MethodBodyAtVirtualAddress(ulong virtAddress, bool managed = true, int count = -1) { diff --git a/Cpp2IL.InstructionSets.ArmV8/Cpp2IL.InstructionSets.ArmV8.csproj b/Cpp2IL.InstructionSets.ArmV8/Cpp2IL.InstructionSets.ArmV8.csproj new file mode 100644 index 00000000..0b3e0c4c --- /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.sln b/Cpp2IL.sln index d0d27607..b2102e57 100644 --- a/Cpp2IL.sln +++ b/Cpp2IL.sln @@ -45,6 +45,16 @@ 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 Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -99,12 +109,32 @@ 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 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} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E9A45B2C-AAEF-4D66-ADD6-7DD234DA3F39} diff --git a/Cpp2IL/Cpp2IL.csproj b/Cpp2IL/Cpp2IL.csproj index c8cd14f7..71cfd8cb 100644 --- a/Cpp2IL/Cpp2IL.csproj +++ b/Cpp2IL/Cpp2IL.csproj @@ -18,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) From d2199467491bdcf8efaba1f8d57d3868e6eaeefd Mon Sep 17 00:00:00 2001 From: js6pak Date: Fri, 5 May 2023 18:20:55 +0200 Subject: [PATCH 3/4] Update Disarm --- .../ArmV8InstructionSet.cs | 11 +++--- .../ArmV8KeyFunctionAddresses.cs | 12 +++---- Cpp2IL.InstructionSets.ArmV8/ArmV8Utils.cs | 34 ++++++++++--------- .../Cpp2IL.InstructionSets.ArmV8.csproj | 2 +- 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/Cpp2IL.InstructionSets.ArmV8/ArmV8InstructionSet.cs b/Cpp2IL.InstructionSets.ArmV8/ArmV8InstructionSet.cs index d911a6e5..f2bf24b9 100644 --- a/Cpp2IL.InstructionSets.ArmV8/ArmV8InstructionSet.cs +++ b/Cpp2IL.InstructionSets.ArmV8/ArmV8InstructionSet.cs @@ -28,18 +28,17 @@ public override Memory GetRawBytesForMethod(MethodAnalysisContext context, return LibCpp2IlMain.Binary!.GetRawBinaryContent().AsMemory(ptrAsInt, count); } - var result = ArmV8Utils.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 = ArmV8Utils.GetArm64MethodBodyAtVirtualAddress(context.UnderlyingPointer); + var instructions = ArmV8Utils.GetArm64MethodBodyAtVirtualAddress(context.UnderlyingPointer, out var endVirtualAddress); throw new NotImplementedException(); } @@ -49,5 +48,5 @@ public override BaseKeyFunctionAddresses CreateKeyFunctionAddressesInstance() 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 index 7249e6f7..bfcbc162 100644 --- a/Cpp2IL.InstructionSets.ArmV8/ArmV8KeyFunctionAddresses.cs +++ b/Cpp2IL.InstructionSets.ArmV8/ArmV8KeyFunctionAddresses.cs @@ -6,17 +6,17 @@ namespace Cpp2IL.InstructionSets.ArmV8; public class ArmV8KeyFunctionAddresses : BaseKeyFunctionAddresses { - private Arm64DisassemblyResult? _cachedDisassembledBytes; + private List? _cachedDisassembledBytes; - private Arm64DisassemblyResult DisassembleTextSection() + private List DisassembleTextSection() { if (_cachedDisassembledBytes == null) { var toDisasm = LibCpp2IlMain.Binary!.GetEntirePrimaryExecutableSection(); - _cachedDisassembledBytes = Disassembler.Disassemble(toDisasm, LibCpp2IlMain.Binary.GetVirtualAddressOfPrimaryExecutableSection()); + _cachedDisassembledBytes = Disassembler.Disassemble(toDisasm, LibCpp2IlMain.Binary.GetVirtualAddressOfPrimaryExecutableSection()).ToList(); } - return _cachedDisassembledBytes.Value; + return _cachedDisassembledBytes; } protected override IEnumerable FindAllThunkFunctions(ulong addr, uint maxBytesBack = 0, params ulong[] addressesToIgnore) @@ -25,7 +25,7 @@ protected override IEnumerable FindAllThunkFunctions(ulong addr, uint max 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(); + var matchingJmps = disassembly.Where(i => i.Mnemonic is Arm64Mnemonic.B or Arm64Mnemonic.BL && i.BranchTarget == addr).ToList(); foreach (var matchingJmp in matchingJmps) { @@ -81,6 +81,6 @@ protected override int GetCallerCount(ulong toWhere) 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); + 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 index 48eb0452..002fb3a2 100644 --- a/Cpp2IL.InstructionSets.ArmV8/ArmV8Utils.cs +++ b/Cpp2IL.InstructionSets.ArmV8/ArmV8Utils.cs @@ -6,49 +6,51 @@ namespace Cpp2IL.InstructionSets.ArmV8; internal static class ArmV8Utils { - public static Arm64DisassemblyResult GetArm64MethodBodyAtVirtualAddress(ulong virtAddress, bool managed = true, int count = -1) + public static IEnumerable GetArm64MethodBodyAtVirtualAddress(ulong virtualAddress, out ulong endVirtualAddress, bool managed = true, int count = -1) { if (managed) { - var startOfNext = MiscUtils.GetAddressOfNextFunctionStart(virtAddress); + 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(virtAddress); + var rawStart = LibCpp2IlMain.Binary.MapVirtualAddressToRaw(virtualAddress); if (rawStartOfNextMethod < rawStart) rawStartOfNextMethod = LibCpp2IlMain.Binary.RawLength; - var bytes = LibCpp2IlMain.Binary.GetRawBinaryContent().AsSpan((int)rawStart, (int)(rawStartOfNextMethod - rawStart)); + var bytes = LibCpp2IlMain.Binary.GetRawBinaryContent().AsMemory((int)rawStart, (int)(rawStartOfNextMethod - rawStart)); - return Disassemble(bytes, virtAddress); + return Disassemble(bytes, virtualAddress, out endVirtualAddress); } } //Unmanaged function, look for first b - var pos = (int)LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(virtAddress); + var pos = (int)LibCpp2IlMain.Binary!.MapVirtualAddressToRaw(virtualAddress); var allBytes = LibCpp2IlMain.Binary.GetRawBinaryContent(); - var span = allBytes.AsSpan(pos, 4); - Arm64DisassemblyResult ret = new(); + + var instructions = new List(); - while ((count == -1 || ret.Instructions.Count < count) && !ret.Instructions.Any(i => i.Mnemonic is Arm64Mnemonic.B)) + endVirtualAddress = virtualAddress; + foreach (var instruction in Disassembler.Disassemble(allBytes.AsSpan(pos), virtualAddress, Disassembler.Options.IgnoreErrors)) { - ret = Disassemble(span, virtAddress); - - //All arm64 instructions are 4 bytes - span = allBytes.AsSpan(pos, span.Length + 4); + instructions.Add(instruction); + endVirtualAddress = instruction.Address; + if (instruction.Mnemonic == Arm64Mnemonic.B) break; + if (count != -1 && instructions.Count >= count) break; } - return ret; + return instructions; } - private static Arm64DisassemblyResult Disassemble(Span bytes, ulong virtAddress) + private static IEnumerable Disassemble(ReadOnlyMemory bytes, ulong virtualAddress, out ulong endVirtualAddress) { try { - return Disassembler.Disassemble(bytes, virtAddress); + endVirtualAddress = virtualAddress + (ulong)bytes.Length; + return Disassembler.Disassemble(bytes, virtualAddress, Disassembler.Options.IgnoreErrors); } catch (Exception e) { diff --git a/Cpp2IL.InstructionSets.ArmV8/Cpp2IL.InstructionSets.ArmV8.csproj b/Cpp2IL.InstructionSets.ArmV8/Cpp2IL.InstructionSets.ArmV8.csproj index 0b3e0c4c..90c1eb65 100644 --- a/Cpp2IL.InstructionSets.ArmV8/Cpp2IL.InstructionSets.ArmV8.csproj +++ b/Cpp2IL.InstructionSets.ArmV8/Cpp2IL.InstructionSets.ArmV8.csproj @@ -6,6 +6,6 @@ - + From 1d54f1286a4fe4b9768eb752aebdd162eb52b39a Mon Sep 17 00:00:00 2001 From: js6pak Date: Fri, 5 May 2023 22:36:06 +0200 Subject: [PATCH 4/4] Implement ArmV7 using CapstoneSharp --- .../BaseKeyFunctionAddresses.cs | 2 +- .../AllInstructionSets.cs | 2 + .../Cpp2IL.InstructionSets.All.csproj | 1 + .../ArmV7InstructionSet.cs | 66 ++++++++ .../ArmV7KeyFunctionAddresses.cs | 144 ++++++++++++++++++ Cpp2IL.InstructionSets.ArmV7/ArmV7Utils.cs | 125 +++++++++++++++ .../Cpp2IL.InstructionSets.ArmV7.csproj | 12 ++ Cpp2IL.sln | 7 + 8 files changed, 358 insertions(+), 1 deletion(-) create mode 100644 Cpp2IL.InstructionSets.ArmV7/ArmV7InstructionSet.cs create mode 100644 Cpp2IL.InstructionSets.ArmV7/ArmV7KeyFunctionAddresses.cs create mode 100644 Cpp2IL.InstructionSets.ArmV7/ArmV7Utils.cs create mode 100644 Cpp2IL.InstructionSets.ArmV7/Cpp2IL.InstructionSets.ArmV7.csproj diff --git a/Cpp2IL.Core/Il2CppApiFunctions/BaseKeyFunctionAddresses.cs b/Cpp2IL.Core/Il2CppApiFunctions/BaseKeyFunctionAddresses.cs index bb5c7fa5..65aed934 100644 --- a/Cpp2IL.Core/Il2CppApiFunctions/BaseKeyFunctionAddresses.cs +++ b/Cpp2IL.Core/Il2CppApiFunctions/BaseKeyFunctionAddresses.cs @@ -146,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.InstructionSets.All/AllInstructionSets.cs b/Cpp2IL.InstructionSets.All/AllInstructionSets.cs index 7b26cd02..90056b9f 100644 --- a/Cpp2IL.InstructionSets.All/AllInstructionSets.cs +++ b/Cpp2IL.InstructionSets.All/AllInstructionSets.cs @@ -1,4 +1,5 @@ using Cpp2IL.Core.Api; +using Cpp2IL.InstructionSets.ArmV7; using Cpp2IL.InstructionSets.ArmV8; using Cpp2IL.InstructionSets.Wasm; using Cpp2IL.InstructionSets.X86; @@ -10,6 +11,7 @@ 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 index d87750f3..0d6a089f 100644 --- a/Cpp2IL.InstructionSets.All/Cpp2IL.InstructionSets.All.csproj +++ b/Cpp2IL.InstructionSets.All/Cpp2IL.InstructionSets.All.csproj @@ -6,6 +6,7 @@ + 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.sln b/Cpp2IL.sln index b2102e57..a85acb1c 100644 --- a/Cpp2IL.sln +++ b/Cpp2IL.sln @@ -55,6 +55,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cpp2IL.InstructionSets.ArmV 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 @@ -125,6 +127,10 @@ Global {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 @@ -135,6 +141,7 @@ Global {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}