From a41ce2253948ac22bff171cc641fd516ad1625ff Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Wed, 1 Dec 2021 16:55:27 +0000 Subject: [PATCH 01/76] [skip ci] Readme: Add notice about rewrite --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index e82cd238..c0b70206 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,12 @@ [![NuGet](https://img.shields.io/nuget/v/Samboy063.Cpp2IL.Core)](https://www.nuget.org/packages/Samboy063.Cpp2IL.Core/) +# Important: Project is being rewritten + +Due to, well, very much forseen circumstances, we are in the process of rewriting most of cpp2il from the ground up over in the development branch. + +As such, bug fixes, and new updates, are likely to be sparse until the work is done, which I expect to be sometime in January 2022. + ### Need Help? Join [the discord](https://discord.gg/XdggT7XZXm)! WIP Tool to reverse Unity's IL2CPP build process back to the original managed DLLs. From a31226cc6d234c1d7f1d9d22eaa7ae802d663987 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Fri, 10 Dec 2021 12:49:42 +0000 Subject: [PATCH 02/76] Core: Revert generic changes and restore stable functionality. --- .../ARM64/Arm64ManagedFunctionCallAction.cs | 2 - .../Actions/Base/AbstractCallAction.cs | 67 ++++---- .../Actions/Base/AbstractFieldReadAction.cs | 31 +--- .../Actions/Base/AbstractFieldWriteAction.cs | 31 ---- .../Actions/Base/AbstractNewObjAction.cs | 42 +++-- .../Actions/Base/AbstractReturnAction.cs | 37 +++-- .../x86/GlobalMethodRefToConstantAction.cs | 4 +- .../Important/CallManagedFunctionAction.cs | 16 +- .../CallManagedFunctionInRegAction.cs | 1 - .../x86/Important/CallMethodSpecAction.cs | 3 +- .../x86/Important/CallVirtualMethodAction.cs | 7 - .../x86/Important/FieldToLocalAction.cs | 10 +- .../Actions/x86/Important/RegToFieldAction.cs | 2 +- Cpp2IL.Core/Analysis/AsmAnalyzerBase.cs | 10 +- .../ResultModels/AnalysedMethodReference.cs | 29 ---- .../ResultModels/ConstantDefinition.cs | 8 +- .../Analysis/ResultModels/MethodAnalysis.cs | 9 +- Cpp2IL.Core/AssemblyPopulator.cs | 26 ++- Cpp2IL.Core/AttributeRestorer.cs | 6 +- Cpp2IL.Core/CecilCodedIndex.cs | 20 --- Cpp2IL.Core/CecilEType.cs | 46 ------ Cpp2IL.Core/Cpp2IlApi.cs | 6 +- Cpp2IL.Core/Cpp2IlHarmonyPatches.cs | 36 ----- .../MetadataTokenCompressionException.cs | 11 -- .../TypeSignatureWriteFailedException.cs | 12 -- Cpp2IL.Core/StubAssemblyBuilder.cs | 38 ++--- Cpp2IL.Core/Utils/CecilUtils.cs | 149 ------------------ Cpp2IL.Core/Utils/FieldUtils.cs | 4 +- Cpp2IL.Core/Utils/GenericMethodUtils.cs | 31 ++++ Cpp2IL.Core/Utils/MiscUtils.cs | 10 +- Cpp2IL/ConsoleLogger.cs | 10 -- Cpp2IL/Program.cs | 7 +- LibCpp2IL/LibCpp2IlUtils.cs | 81 +++++----- .../Reflection/Il2CppTypeReflectionData.cs | 1 - 34 files changed, 207 insertions(+), 596 deletions(-) delete mode 100644 Cpp2IL.Core/Analysis/ResultModels/AnalysedMethodReference.cs delete mode 100644 Cpp2IL.Core/CecilCodedIndex.cs delete mode 100644 Cpp2IL.Core/CecilEType.cs delete mode 100644 Cpp2IL.Core/Exceptions/MetadataTokenCompressionException.cs delete mode 100644 Cpp2IL.Core/Exceptions/TypeSignatureWriteFailedException.cs delete mode 100644 Cpp2IL.Core/Utils/CecilUtils.cs create mode 100644 Cpp2IL.Core/Utils/GenericMethodUtils.cs diff --git a/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64ManagedFunctionCallAction.cs b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64ManagedFunctionCallAction.cs index 8ee5f47e..77331057 100644 --- a/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64ManagedFunctionCallAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64ManagedFunctionCallAction.cs @@ -52,8 +52,6 @@ public Arm64ManagedFunctionCallAction(MethodAnalysis context, ManagedMethodBeingCalled = possibleTarget.AsManaged(); else AddComment($"Failed to resolve any matching method (there are {listOfCallableMethods.Count} at this address)"); - - CacheMethodInfoArg(context); HandleReturnType(context); } diff --git a/Cpp2IL.Core/Analysis/Actions/Base/AbstractCallAction.cs b/Cpp2IL.Core/Analysis/Actions/Base/AbstractCallAction.cs index f0416c91..bb92cffe 100644 --- a/Cpp2IL.Core/Analysis/Actions/Base/AbstractCallAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/Base/AbstractCallAction.cs @@ -20,23 +20,16 @@ public abstract class AbstractCallAction : BaseAction protected bool IsCallToSuperclassMethod; protected bool ShouldUseCallvirt; protected TypeReference? StaticMethodGenericTypeOverride; - protected IAnalysedOperand? MethodInfoArg; protected AbstractCallAction(MethodAnalysis context, T instruction) : base(context, instruction) { } - protected void CacheMethodInfoArg(MethodAnalysis context) - { - if(ManagedMethodBeingCalled != null) - MethodInfoArg = MethodUtils.GetMethodInfoArg(ManagedMethodBeingCalled, context); - } - public override string? ToPsuedoCode() { - if (ManagedMethodBeingCalled == null) + if (ManagedMethodBeingCalled == null) return "[instruction error - managed method being called is null]"; - + var ret = new StringBuilder(); if (ReturnedLocal != null) @@ -48,7 +41,7 @@ protected void CacheMethodInfoArg(MethodAnalysis context) ret.Append(IsCallToSuperclassMethod ? "base" : "this"); else ret.Append(InstanceBeingCalledOn?.Name); - + if (ManagedMethodBeingCalled is MethodDefinition mDef) { if (mDef.Name.StartsWith("get_")) @@ -58,8 +51,7 @@ protected void CacheMethodInfoArg(MethodAnalysis context) if (prop != null) return ret.Append('.').Append(prop.Name).ToString(); - } - else if (mDef.Name.StartsWith("set_") && Arguments?.Count > 0) + } else if (mDef.Name.StartsWith("set_") && Arguments?.Count > 0) { var unmanaged = mDef.AsUnmanaged(); var prop = unmanaged.DeclaringType!.Properties!.FirstOrDefault(p => p.Setter == unmanaged); @@ -73,7 +65,7 @@ protected void CacheMethodInfoArg(MethodAnalysis context) if (ManagedMethodBeingCalled is GenericInstanceMethod gim) ret.Append('<').Append(string.Join(", ", gim.GenericArguments)).Append('>'); - + ret.Append('('); if (Arguments != null && Arguments.Count > 0) @@ -103,6 +95,9 @@ public override Mono.Cecil.Cil.Instruction[] ToILInstructions(MethodAnalysis if (InstanceBeingCalledOn == null && !ManagedMethodBeingCalled.Resolve().IsStatic) throw new TaintedInstructionException("Method is non-static but don't have an instance"); + + if (InstanceBeingCalledOn != null && !ManagedMethodBeingCalled.Resolve().IsStatic && !ManagedMethodBeingCalled.DeclaringType.Resolve().IsAssignableFrom(InstanceBeingCalledOn.Type?.Resolve())) + throw new TaintedInstructionException($"Mismatched instance parameter. Expecting an instance of {ManagedMethodBeingCalled.DeclaringType.FullName}, actually {InstanceBeingCalledOn}"); if (ManagedMethodBeingCalled.Name == ".ctor") { @@ -110,32 +105,28 @@ public override Mono.Cecil.Cil.Instruction[] ToILInstructions(MethodAnalysis if (!(IsCallToSuperclassMethod && InstanceBeingCalledOn?.Name == "this")) return Array.Empty(); //Ignore ctors that aren't super calls, because we're allocating a new instance. } - - if (InstanceBeingCalledOn != null && !ManagedMethodBeingCalled.Resolve().IsStatic && !ManagedMethodBeingCalled.DeclaringType.Resolve().IsAssignableFrom(InstanceBeingCalledOn.Type?.Resolve())) - throw new TaintedInstructionException($"Mismatched instance parameter. Expecting an instance of {ManagedMethodBeingCalled.DeclaringType.FullName}, actually {InstanceBeingCalledOn}"); - + var result = GetILToLoadParams(context, processor); var toCall = ManagedMethodBeingCalled; + + if(context.GetMethodDefinition() is {} contextMethod) + GenericMethodUtils.PrepareGenericMethodForEmissionToBody(toCall, toCall.DeclaringType, contextMethod.Module); + + if (ManagedMethodBeingCalled.HasGenericParameters && !ManagedMethodBeingCalled.IsGenericInstance) + toCall = ManagedMethodBeingCalled.Resolve(); + if (ManagedMethodBeingCalled.DeclaringType is GenericInstanceType git && git.HasAnyGenericParams()) + toCall = ManagedMethodBeingCalled.Resolve(); + if (ManagedMethodBeingCalled is GenericInstanceMethod gim && gim.GenericArguments.Any(g => g is GenericParameter || g is GenericInstanceType git2 && git2.HasAnyGenericParams())) + toCall = ManagedMethodBeingCalled.Resolve(); + + if(toCall is GenericInstanceMethod gim2) + toCall = processor.ImportRecursive(gim2); - if (context.GetMethodDefinition() is { } contextMethod) - //TODO Finish this method then re-enable - toCall = contextMethod.Module.ImportMethodButCleanly(toCall); - - // if (ManagedMethodBeingCalled.HasGenericParameters && !ManagedMethodBeingCalled.IsGenericInstance) - // toCall = ManagedMethodBeingCalled.Resolve(); - // if (ManagedMethodBeingCalled.DeclaringType is GenericInstanceType git && git.HasAnyGenericParams()) - // toCall = ManagedMethodBeingCalled.Resolve(); - // if (ManagedMethodBeingCalled is GenericInstanceMethod gim && gim.GenericArguments.Any(g => g is GenericParameter || g is GenericInstanceType git2 && git2.HasAnyGenericParams())) - // toCall = ManagedMethodBeingCalled.Resolve(); - // - // if (toCall is GenericInstanceMethod gim2) - // toCall = processor.ImportRecursive(gim2); - // - // if (toCall.DeclaringType is GenericInstanceType git2) - // toCall.DeclaringType = processor.ImportRecursive(git2); - // - // toCall = processor.ImportParameterTypes(toCall); + if (toCall.DeclaringType is GenericInstanceType git2) + toCall.DeclaringType = processor.ImportRecursive(git2); + + toCall = processor.ImportParameterTypes(toCall); result.Add(processor.Create(ShouldUseCallvirt ? OpCodes.Callvirt : OpCodes.Call, processor.ImportReference(toCall))); @@ -203,8 +194,8 @@ private TypeReference ResolveGenericReturnTypeIfNeeded(TypeReference returnType, { returnType = GenericInstanceUtils.ResolveGenericParameterType(gp, gmr.Type, gmr.Method) ?? returnType; StaticMethodGenericTypeOverride = gmr.Type; - - if (ManagedMethodBeingCalled.Resolve() == gmr.Method.Resolve()) + + if(ManagedMethodBeingCalled.Resolve() == gmr.Method.Resolve()) ManagedMethodBeingCalled = gmr.Method; } else @@ -249,7 +240,7 @@ protected void RegisterLocals(MethodAnalysis context) { if (operand == null) throw new TaintedInstructionException($"Found null operand in Arguments: {Arguments.ToStringEnumerable()}"); - + if (operand is LocalDefinition l) result.Add(context.GetIlToLoad(l, processor)); else if (operand is ConstantDefinition c) diff --git a/Cpp2IL.Core/Analysis/Actions/Base/AbstractFieldReadAction.cs b/Cpp2IL.Core/Analysis/Actions/Base/AbstractFieldReadAction.cs index 593b860e..abea4792 100644 --- a/Cpp2IL.Core/Analysis/Actions/Base/AbstractFieldReadAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/Base/AbstractFieldReadAction.cs @@ -1,10 +1,7 @@ using System.Collections.Generic; -using System.Linq; using Cpp2IL.Core.Analysis.ResultModels; using Cpp2IL.Core.Utils; -using Mono.Cecil; using Mono.Cecil.Cil; -using Mono.Cecil.Rocks; namespace Cpp2IL.Core.Analysis.Actions.Base { @@ -13,36 +10,10 @@ public abstract class AbstractFieldReadAction : BaseAction public FieldUtils.FieldBeingAccessedData? FieldRead; public LocalDefinition? LocalWritten; protected LocalDefinition? ReadFrom; - protected TypeReference? ReadFromType; - + protected AbstractFieldReadAction(MethodAnalysis context, T instruction) : base(context, instruction) { } - - protected void FixUpFieldRefForAnyPotentialGenericType(MethodAnalysis context) - { - if(context.GetMethodDefinition() is not {} contextMethod) - return; - - if(FieldRead == null) - return; - - if (ReadFromType is null or TypeDefinition {HasGenericParameters: false}) - return; - - if (ReadFromType is TypeDefinition) - ReadFromType = ReadFromType.MakeGenericInstanceType(ReadFromType.GenericParameters.Cast().ToArray()); - - if (FieldRead.ImpliedFieldLoad is { } impliedLoad) - { - var fieldRef = new FieldReference(impliedLoad.Name, impliedLoad.FieldType, ReadFromType); - FieldRead.ImpliedFieldLoad = contextMethod.Module.ImportFieldButCleanly(fieldRef); - } else if (FieldRead.FinalLoadInChain is { } finalLoad) - { - var fieldRef = new FieldReference(finalLoad.Name, finalLoad.FieldType, ReadFromType); - FieldRead.FinalLoadInChain = contextMethod.Module.ImportFieldButCleanly(fieldRef); - } - } public override string ToPsuedoCode() { diff --git a/Cpp2IL.Core/Analysis/Actions/Base/AbstractFieldWriteAction.cs b/Cpp2IL.Core/Analysis/Actions/Base/AbstractFieldWriteAction.cs index 7e03a804..92a2222d 100644 --- a/Cpp2IL.Core/Analysis/Actions/Base/AbstractFieldWriteAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/Base/AbstractFieldWriteAction.cs @@ -1,10 +1,7 @@ using System.Collections.Generic; -using System.Linq; using Cpp2IL.Core.Analysis.ResultModels; using Cpp2IL.Core.Utils; -using Mono.Cecil; using Mono.Cecil.Cil; -using Mono.Cecil.Rocks; namespace Cpp2IL.Core.Analysis.Actions.Base { @@ -20,34 +17,6 @@ protected AbstractFieldWriteAction(MethodAnalysis context, T instruction) : b protected abstract string? GetValuePseudocode(); protected abstract Instruction[] GetIlToLoadValue(MethodAnalysis context, ILProcessor processor); - protected void FixUpFieldRefForAnyPotentialGenericType(MethodAnalysis context) - { - if(context.GetMethodDefinition() is not {} contextMethod) - return; - - if(FieldWritten == null) - return; - - if(InstanceBeingSetOn?.Type is not {} writtenOnType) - return; - - if (writtenOnType is null or TypeDefinition {HasGenericParameters: false}) - return; - - if (writtenOnType is TypeDefinition) - writtenOnType = writtenOnType.MakeGenericInstanceType(writtenOnType.GenericParameters.Cast().ToArray()); - - if (FieldWritten.ImpliedFieldLoad is { } impliedLoad) - { - var fieldRef = new FieldReference(impliedLoad.Name, impliedLoad.FieldType, writtenOnType); - FieldWritten.ImpliedFieldLoad = contextMethod.Module.ImportFieldButCleanly(fieldRef); - } else if (FieldWritten.FinalLoadInChain is { } finalLoad) - { - var fieldRef = new FieldReference(finalLoad.Name, finalLoad.FieldType, writtenOnType); - FieldWritten.FinalLoadInChain = contextMethod.Module.ImportFieldButCleanly(fieldRef); - } - } - public override Instruction[] ToILInstructions(MethodAnalysis context, ILProcessor processor) { if (InstanceBeingSetOn == null || FieldWritten == null) diff --git a/Cpp2IL.Core/Analysis/Actions/Base/AbstractNewObjAction.cs b/Cpp2IL.Core/Analysis/Actions/Base/AbstractNewObjAction.cs index 598ad38a..ef5823e6 100644 --- a/Cpp2IL.Core/Analysis/Actions/Base/AbstractNewObjAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/Base/AbstractNewObjAction.cs @@ -37,28 +37,26 @@ public override Instruction[] ToILInstructions(MethodAnalysis context, ILProc var ctorToCall = managedConstructorCall.ManagedMethodBeingCalled!; - // if (ctorToCall.DeclaringType.ToString() != TypeCreated.ToString()) - // ctorToCall = TypeCreated?.Resolve()?.Methods.FirstOrDefault(m => m.Name == ".ctor" && m.Parameters.Count == ctorToCall.Parameters.Count) ?? throw new TaintedInstructionException($"Could not resolve a constructor with {ctorToCall.Parameters.Count} parameters."); - // - // if (ctorToCall.HasGenericParameters && TypeCreated is GenericInstanceType git) - // ctorToCall = ctorToCall.MakeMethodOnGenericType(git.GenericArguments.ToArray()); - // - // if (ctorToCall is GenericInstanceMethod gim2 && gim2.GenericArguments.Any(g => g is GenericParameter { Position: -1 })) - // ctorToCall = ctorToCall.Resolve(); - // if (ctorToCall is { DeclaringType: GenericInstanceType git2 } && git2.GenericArguments.Any(g => g is GenericParameter { Position: -1 })) - // ctorToCall = ctorToCall.Resolve(); - // - // if (ctorToCall is GenericInstanceMethod gim) - // ctorToCall = processor.ImportRecursive(gim); - // else - // ctorToCall = processor.ImportReference(ctorToCall); - // - // if (ctorToCall.DeclaringType is GenericInstanceType git3) - // ctorToCall.DeclaringType = processor.ImportRecursive(git3); - // - // ctorToCall = processor.ImportParameterTypes(ctorToCall); - if(context.GetMethodDefinition() is {} contextMethod) - ctorToCall = contextMethod.Module.ImportMethodButCleanly(ctorToCall); + if (ctorToCall.DeclaringType.ToString() != TypeCreated.ToString()) + ctorToCall = TypeCreated?.Resolve()?.Methods.FirstOrDefault(m => m.Name == ".ctor" && m.Parameters.Count == ctorToCall.Parameters.Count) ?? throw new TaintedInstructionException($"Could not resolve a constructor with {ctorToCall.Parameters.Count} parameters."); + + if (ctorToCall.HasGenericParameters && TypeCreated is GenericInstanceType git) + ctorToCall = ctorToCall.MakeMethodOnGenericType(git.GenericArguments.ToArray()); + + if (ctorToCall is GenericInstanceMethod gim2 && gim2.GenericArguments.Any(g => g is GenericParameter { Position: -1 })) + ctorToCall = ctorToCall.Resolve(); + if (ctorToCall is { DeclaringType: GenericInstanceType git2 } && git2.GenericArguments.Any(g => g is GenericParameter { Position: -1 })) + ctorToCall = ctorToCall.Resolve(); + + if (ctorToCall is GenericInstanceMethod gim) + ctorToCall = processor.ImportRecursive(gim); + else + ctorToCall = processor.ImportReference(ctorToCall); + + if (ctorToCall.DeclaringType is GenericInstanceType git3) + ctorToCall.DeclaringType = processor.ImportRecursive(git3); + + ctorToCall = processor.ImportParameterTypes(ctorToCall); result.Add(processor.Create(OpCodes.Newobj, ctorToCall)); diff --git a/Cpp2IL.Core/Analysis/Actions/Base/AbstractReturnAction.cs b/Cpp2IL.Core/Analysis/Actions/Base/AbstractReturnAction.cs index 12be18ab..c9de7008 100644 --- a/Cpp2IL.Core/Analysis/Actions/Base/AbstractReturnAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/Base/AbstractReturnAction.cs @@ -61,24 +61,27 @@ public override bool IsImportant() protected void TryCorrectConstant(MethodAnalysis context) { - if (_isVoid || returnValue is not ConstantDefinition constantDefinition || !typeof(IConvertible).IsAssignableFrom(constantDefinition.Type) || constantDefinition.Type == typeof(string)) - return; - - if (context.ReturnType.Resolve() is {IsEnum: true} returnTypeDefinition) - { - var underLyingType = typeof(int).Module.GetType(returnTypeDefinition.GetEnumUnderlyingType().FullName); - constantDefinition.Type = underLyingType; - constantDefinition.Value = MiscUtils.ReinterpretBytes((IConvertible) constantDefinition.Value, underLyingType); - } - else if (!string.IsNullOrEmpty(context.ReturnType?.FullName)) + if (!_isVoid && returnValue is ConstantDefinition constantDefinition && typeof(IConvertible).IsAssignableFrom(constantDefinition.Type) && constantDefinition.Type != typeof(string)) { - var returnValueType = typeof(int).Module.GetType(context.ReturnType!.FullName); - if (string.IsNullOrEmpty(returnValueType?.FullName) || returnValueType!.IsArray) - return; - if (!TypeDefinitions.IConvertible.IsAssignableFrom(context.ReturnType) || !context.ReturnType.IsPrimitive || context.ReturnType.Name == "String") - return; - constantDefinition.Value = MiscUtils.ReinterpretBytes((IConvertible) constantDefinition.Value, context.ReturnType); - constantDefinition.Type = returnValueType; + var returnTypeDefinition = context.ReturnType.Resolve(); + if (returnTypeDefinition.IsEnum) + { + var underLyingType = typeof(int).Module.GetType(returnTypeDefinition.GetEnumUnderlyingType().FullName); + constantDefinition.Type = underLyingType; + constantDefinition.Value = MiscUtils.ReinterpretBytes((IConvertible) constantDefinition.Value, underLyingType); + } + else if (!string.IsNullOrEmpty(context.ReturnType?.FullName)) + { + var returnValueType = typeof(int).Module.GetType(context.ReturnType!.FullName); + if (!string.IsNullOrEmpty(returnValueType?.FullName) && !returnValueType!.IsArray) + { + if (TypeDefinitions.IConvertible.IsAssignableFrom(context.ReturnType) && context.ReturnType.IsPrimitive && context.ReturnType.Name != "String") + { + constantDefinition.Value = MiscUtils.ReinterpretBytes((IConvertible) constantDefinition.Value, context.ReturnType); + constantDefinition.Type = returnValueType; + } + } + } } } } diff --git a/Cpp2IL.Core/Analysis/Actions/x86/GlobalMethodRefToConstantAction.cs b/Cpp2IL.Core/Analysis/Actions/x86/GlobalMethodRefToConstantAction.cs index f761e84a..d6d0229c 100644 --- a/Cpp2IL.Core/Analysis/Actions/x86/GlobalMethodRefToConstantAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/x86/GlobalMethodRefToConstantAction.cs @@ -54,7 +54,9 @@ public GlobalMethodRefToConstantAction(MethodAnalysis context, Inst if (_genericMethodParams.Count > 0) { - _method = _method.MakeGenericInstanceMethod(_genericMethodParams.ToArray()); + var gMethod = new GenericInstanceMethod(_method); + _genericMethodParams.ForEach(gMethod.GenericArguments.Add); + _method = gMethod; } if (instruction.Mnemonic != Mnemonic.Push) diff --git a/Cpp2IL.Core/Analysis/Actions/x86/Important/CallManagedFunctionAction.cs b/Cpp2IL.Core/Analysis/Actions/x86/Important/CallManagedFunctionAction.cs index aa7ba57b..705c329e 100644 --- a/Cpp2IL.Core/Analysis/Actions/x86/Important/CallManagedFunctionAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/x86/Important/CallManagedFunctionAction.cs @@ -48,20 +48,11 @@ public CallManagedFunctionAction(MethodAnalysis context, Instructio { MethodReference locatedMethod; if (matchingConstant.Value is MethodReference value) - { locatedMethod = value; - AddComment("Method resolved from concrete implementations at this address, with the help of a nongeneric constant value to identify which concrete implementation."); - } - else - { - var locatedGmr = (GenericMethodReference) matchingConstant.Value; - locatedMethod = locatedGmr.Method; + else + locatedMethod = ((GenericMethodReference) matchingConstant.Value).Method; - if (locatedGmr.Type is GenericInstanceType locatedGit) - locatedMethod = locatedMethod.MakeMethodOnGenericType(locatedGit.GenericArguments.ToArray()); - - AddComment("Method resolved from concrete implementations at this address, with the help of a generic constant value to identify which concrete implementation."); - } + AddComment("Method resolved from concrete implementations at this address, with the help of a constant value to identify which concrete implementation."); if (locatedMethod.HasThis && LibCpp2IlMain.Binary!.is32Bit && context.Stack.TryPeek(out var op) && op is LocalDefinition local) { @@ -237,7 +228,6 @@ public CallManagedFunctionAction(MethodAnalysis context, Instructio ManagedMethodBeingCalled = gmr.Method.MakeMethodOnGenericType(git.GenericArguments.ToArray()); } - CacheMethodInfoArg(context); HandleReturnType(context); } diff --git a/Cpp2IL.Core/Analysis/Actions/x86/Important/CallManagedFunctionInRegAction.cs b/Cpp2IL.Core/Analysis/Actions/x86/Important/CallManagedFunctionInRegAction.cs index b32fb338..caf38442 100644 --- a/Cpp2IL.Core/Analysis/Actions/x86/Important/CallManagedFunctionInRegAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/x86/Important/CallManagedFunctionInRegAction.cs @@ -45,7 +45,6 @@ public CallManagedFunctionInRegAction(MethodAnalysis context, Instr } CreateLocalForReturnType(context); - CacheMethodInfoArg(context); RegisterLocals(context); } diff --git a/Cpp2IL.Core/Analysis/Actions/x86/Important/CallMethodSpecAction.cs b/Cpp2IL.Core/Analysis/Actions/x86/Important/CallMethodSpecAction.cs index 77f0cb67..f58a5ad6 100644 --- a/Cpp2IL.Core/Analysis/Actions/x86/Important/CallMethodSpecAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/x86/Important/CallMethodSpecAction.cs @@ -30,10 +30,9 @@ public CallMethodSpecAction(MethodAnalysis context, Instruction ins ShouldUseCallvirt = true; if (methodSpec.classIndexIndex != -1) - ManagedMethodBeingCalled = ManagedMethodBeingCalled.MakeMethodOnGenericType(methodSpec.GenericClassParams.Select(p => MiscUtils.TryResolveTypeReflectionData(p, ManagedMethodBeingCalled, context.GetMethodDefinition())).ToArray()!); + ManagedMethodBeingCalled = ManagedMethodBeingCalled.MakeMethodOnGenericType(methodSpec.GenericClassParams.Select(p => MiscUtils.TryResolveTypeReflectionData(p, ManagedMethodBeingCalled)).ToArray()!); CreateLocalForReturnType(context); - CacheMethodInfoArg(context); RegisterLocals(context); } } diff --git a/Cpp2IL.Core/Analysis/Actions/x86/Important/CallVirtualMethodAction.cs b/Cpp2IL.Core/Analysis/Actions/x86/Important/CallVirtualMethodAction.cs index bd231cb3..c0c54448 100644 --- a/Cpp2IL.Core/Analysis/Actions/x86/Important/CallVirtualMethodAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/x86/Important/CallVirtualMethodAction.cs @@ -1,6 +1,5 @@ using Cpp2IL.Core.Analysis.ResultModels; using Cpp2IL.Core.Utils; -using Mono.Cecil; using Instruction = Iced.Intel.Instruction; namespace Cpp2IL.Core.Analysis.Actions.x86.Important @@ -23,17 +22,11 @@ public CallVirtualMethodAction(MethodAnalysis context, Instruction if (ManagedMethodBeingCalled == null) return; InstanceBeingCalledOn = ManagedMethodBeingCalled.HasThis ? context.GetLocalInReg("rcx") : null; - - if (ManagedMethodBeingCalled is MethodDefinition && InstanceBeingCalledOn?.Type is GenericInstanceType git) - { - ManagedMethodBeingCalled = ManagedMethodBeingCalled.MakeMethodOnGenericType(git.GenericArguments.ToArray()); - } if(!MethodUtils.CheckParameters(instruction, ManagedMethodBeingCalled, context, ManagedMethodBeingCalled.HasThis, out Arguments, InstanceBeingCalledOn?.Type, false)) AddComment("Arguments are incorrect?"); CreateLocalForReturnType(context); - CacheMethodInfoArg(context); RegisterLocals(context); } } diff --git a/Cpp2IL.Core/Analysis/Actions/x86/Important/FieldToLocalAction.cs b/Cpp2IL.Core/Analysis/Actions/x86/Important/FieldToLocalAction.cs index b38d5c9b..d93c1c2e 100644 --- a/Cpp2IL.Core/Analysis/Actions/x86/Important/FieldToLocalAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/x86/Important/FieldToLocalAction.cs @@ -14,20 +14,21 @@ public FieldToLocalAction(MethodAnalysis context, Instruction instr { var sourceRegName = X86Utils.GetRegisterNameNew(instruction.MemoryBase); _destRegName = X86Utils.GetRegisterNameNew(instruction.Op0Register); - var sourceFieldOffset = instruction.MemoryDisplacement32; + var sourceFieldOffset = instruction.MemoryDisplacement; var readFrom = context.GetOperandInRegister(sourceRegName); + TypeReference readFromType; if (readFrom is ConstantDefinition {Value: NewSafeCastResult result}) { - ReadFromType = result.castTo; + readFromType = result.castTo; ReadFrom = result.original; RegisterUsedLocal(ReadFrom, context); } else if(readFrom is LocalDefinition {IsMethodInfoParam: false} l && l.Type?.Resolve() != null) { ReadFrom = l; - ReadFromType = ReadFrom!.Type!; + readFromType = ReadFrom!.Type!; RegisterUsedLocal(ReadFrom, context); } else { @@ -35,12 +36,11 @@ public FieldToLocalAction(MethodAnalysis context, Instruction instr return; } - FieldRead = FieldUtils.GetFieldBeingAccessed(ReadFromType, sourceFieldOffset, false); + FieldRead = FieldUtils.GetFieldBeingAccessed(readFromType, sourceFieldOffset, false); if(FieldRead == null) return; LocalWritten = context.MakeLocal(FieldRead.GetFinalType(), reg: _destRegName); - FixUpFieldRefForAnyPotentialGenericType(context); RegisterDefinedLocalWithoutSideEffects(LocalWritten); } } diff --git a/Cpp2IL.Core/Analysis/Actions/x86/Important/RegToFieldAction.cs b/Cpp2IL.Core/Analysis/Actions/x86/Important/RegToFieldAction.cs index 3b729579..281a59df 100644 --- a/Cpp2IL.Core/Analysis/Actions/x86/Important/RegToFieldAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/x86/Important/RegToFieldAction.cs @@ -43,8 +43,8 @@ public RegToFieldAction(MethodAnalysis context, Instruction instruc } RegisterUsedLocal(InstanceBeingSetOn, context); + FieldWritten = FieldUtils.GetFieldBeingAccessed(InstanceBeingSetOn.Type, destFieldOffset, false); - FixUpFieldRefForAnyPotentialGenericType(context); } internal RegToFieldAction(MethodAnalysis context, Instruction instruction, FieldUtils.FieldBeingAccessedData fieldWritten, LocalDefinition instanceWrittenOn, LocalDefinition readFrom) : base(context, instruction) diff --git a/Cpp2IL.Core/Analysis/AsmAnalyzerBase.cs b/Cpp2IL.Core/Analysis/AsmAnalyzerBase.cs index b7de1f7f..0ee0d65b 100644 --- a/Cpp2IL.Core/Analysis/AsmAnalyzerBase.cs +++ b/Cpp2IL.Core/Analysis/AsmAnalyzerBase.cs @@ -123,14 +123,14 @@ public StringBuilder BuildILToString() try { - // if (varType is GenericInstanceType git2 && git2.HasAnyGenericParams()) - // varType = git2.Resolve(); - // if (varType is GenericInstanceType git) - // varType = processor.ImportRecursive(git, MethodDefinition); + if (varType is GenericInstanceType git2 && git2.HasAnyGenericParams()) + varType = git2.Resolve(); + if (varType is GenericInstanceType git) + varType = processor.ImportRecursive(git, MethodDefinition); if (varType is ArrayType arr && MiscUtils.GetUltimateElementType(arr).IsGenericParameter) throw new InvalidOperationException(); - localDefinition.Variable = new VariableDefinition(processor.Body.Method.Module.ImportTypeButCleanly(varType)); + localDefinition.Variable = new VariableDefinition(processor.ImportReference(varType, MethodDefinition)); body.Variables.Add(localDefinition.Variable); } catch (InvalidOperationException) diff --git a/Cpp2IL.Core/Analysis/ResultModels/AnalysedMethodReference.cs b/Cpp2IL.Core/Analysis/ResultModels/AnalysedMethodReference.cs deleted file mode 100644 index ff530cc3..00000000 --- a/Cpp2IL.Core/Analysis/ResultModels/AnalysedMethodReference.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Collections.Generic; -using Mono.Cecil; - -namespace Cpp2IL.Core.Analysis.ResultModels -{ - /// - /// Class to replace Cecil's MethodReference for actual analyzed actions, because it has so many shortcomings - /// - public class AnalysedMethodReference - { - public TypeReference ReturnType; - public TypeReference SpecificDeclaringType; - public MethodDefinition ActualMethodBeingCalled; - public List MethodGenericArguments = new(); - public List Parameters; - - public class AnalyzedMethodParameter - { - public TypeReference Type; - public string Name; - - public AnalyzedMethodParameter(TypeReference type, string name) - { - Type = type; - Name = name; - } - } - } -} \ No newline at end of file diff --git a/Cpp2IL.Core/Analysis/ResultModels/ConstantDefinition.cs b/Cpp2IL.Core/Analysis/ResultModels/ConstantDefinition.cs index 3c8f4e29..e94e9feb 100644 --- a/Cpp2IL.Core/Analysis/ResultModels/ConstantDefinition.cs +++ b/Cpp2IL.Core/Analysis/ResultModels/ConstantDefinition.cs @@ -106,20 +106,20 @@ public Instruction[] GetILToLoad(MethodAnalysis context, I }; if(Type == typeof(MethodReference) && Value is GenericInstanceMethod gim) - return new[] {ilProcessor.Create(OpCodes.Ldftn, ilProcessor.ImportMethodButCleanly(gim))}; + return new[] {ilProcessor.Create(OpCodes.Ldftn, ilProcessor.ImportRecursive(gim))}; if (Type == typeof(MethodReference) && Value is MethodReference reference) - return new[] {ilProcessor.Create(OpCodes.Ldftn, ilProcessor.ImportMethodButCleanly(reference))}; + return new[] {ilProcessor.Create(OpCodes.Ldftn, ilProcessor.ImportReference(reference))}; if(Type == typeof(TypeReference) && Value is GenericInstanceType git) - return new[] {ilProcessor.Create(OpCodes.Ldtoken, ilProcessor.ImportTypeButCleanly(git))}; + return new[] {ilProcessor.Create(OpCodes.Ldtoken, ilProcessor.ImportRecursive(git))}; if (Type == typeof(TypeReference) && Value is TypeReference typeReference) { if (typeReference is TypeSpecification {ElementType: GenericInstanceType} or GenericInstanceType or GenericParameter) throw new TaintedInstructionException("ConstantDefinition: TypeReference loading not supported for generic types because it's a mess."); - return new[] {ilProcessor.Create(OpCodes.Ldtoken, ilProcessor.ImportTypeButCleanly(typeReference))}; + return new[] {ilProcessor.Create(OpCodes.Ldtoken, ilProcessor.ImportReference(typeReference))}; } if (Type == typeof(FieldDefinition) && Value is FieldDefinition fieldDefinition) diff --git a/Cpp2IL.Core/Analysis/ResultModels/MethodAnalysis.cs b/Cpp2IL.Core/Analysis/ResultModels/MethodAnalysis.cs index 8d58f480..7f334c39 100644 --- a/Cpp2IL.Core/Analysis/ResultModels/MethodAnalysis.cs +++ b/Cpp2IL.Core/Analysis/ResultModels/MethodAnalysis.cs @@ -11,7 +11,6 @@ using LibCpp2IL; using Mono.Cecil; using Mono.Cecil.Cil; -using Mono.Cecil.Rocks; using Instruction = Mono.Cecil.Cil.Instruction; namespace Cpp2IL.Core.Analysis.ResultModels @@ -101,10 +100,6 @@ internal MethodAnalysis(MethodDefinition method, ulong methodStart, ulong initia _allInstructions = new(allInstructions); EmptyRegConstant = MakeConstant(typeof(int), 0, "0"); - var thisParamType = (TypeReference) method.DeclaringType; - if (thisParamType.HasGenericParameters) - thisParamType = thisParamType.MakeGenericInstanceType(thisParamType.GenericParameters.Cast().ToArray()); - var args = method.Parameters.ToList(); var haveHandledMethodInfoArg = false; //Set up parameters in registers & as locals. @@ -126,7 +121,7 @@ internal MethodAnalysis(MethodDefinition method, ulong methodStart, ulong initia if (!method.IsStatic) { method.Body.ThisParameter.Name = "this"; - FunctionArgumentLocals.Add(MakeLocal(thisParamType, "this", _parameterDestRegList.RemoveAndReturn(0)).WithParameter(method.Body.ThisParameter)); + FunctionArgumentLocals.Add(MakeLocal(method.DeclaringType, "this", _parameterDestRegList.RemoveAndReturn(0)).WithParameter(method.Body.ThisParameter)); } while (args.Count > 0) @@ -145,7 +140,7 @@ internal MethodAnalysis(MethodDefinition method, ulong methodStart, ulong initia { //x86_32 and wasm are stack based method.Body.ThisParameter.Name = "this"; - FunctionArgumentLocals.Add(MakeLocal(thisParamType, "this").WithParameter(method.Body.ThisParameter)); + FunctionArgumentLocals.Add(MakeLocal(method.DeclaringType, "this").WithParameter(method.Body.ThisParameter)); Stack.Push(FunctionArgumentLocals.First()); } diff --git a/Cpp2IL.Core/AssemblyPopulator.cs b/Cpp2IL.Core/AssemblyPopulator.cs index 952f54db..2e2ed627 100644 --- a/Cpp2IL.Core/AssemblyPopulator.cs +++ b/Cpp2IL.Core/AssemblyPopulator.cs @@ -33,7 +33,7 @@ public static void ConfigureHierarchy() foreach (var typeDefinition in SharedState.AllTypeDefinitions) { var il2cppTypeDef = SharedState.ManagedToUnmanagedTypes[typeDefinition]; - + //Type generic params. PopulateGenericParamsForType(il2cppTypeDef, typeDefinition); @@ -104,7 +104,7 @@ private static void InjectOurTypes(AssemblyDefinition imageDef, bool suppressAtt MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, imageDef.MainModule.ImportReference(TypeDefinitions.Void) ); - + defaultConstructor.Parameters.Add(new("message", ParameterAttributes.None, stringTypeReference)); var exceptionTypeDef = exceptionTypeReference.Resolve(); @@ -120,7 +120,7 @@ private static void InjectOurTypes(AssemblyDefinition imageDef, bool suppressAtt } analysisFailedExceptionType.Methods.Add(defaultConstructor); - + imageDef.MainModule.Types.Add(analysisFailedExceptionType); } @@ -128,8 +128,8 @@ public static void PopulateStubTypesInAssembly(Il2CppImageDefinition imageDef, b { var firstTypeDefinition = SharedState.TypeDefsByIndex[imageDef.firstTypeIndex]; var currentAssembly = firstTypeDefinition.Module.Assembly; - - InjectOurTypes(currentAssembly, suppressAttributes); + + InjectOurTypes(currentAssembly, suppressAttributes); foreach (var il2CppTypeDefinition in imageDef.Types!) { @@ -200,7 +200,7 @@ private static void FixupExplicitOverridesInType(TypeDefinition ilTypeDefinition TypeReference? ResolveGenericParameter(string name) { var (type, gParams) = MiscUtils.TryLookupTypeDefByName(name); - if (type == null) + if (type == null) return GenericInstanceUtils.ResolveGenericParameterType(new GenericParameter(name, baseType), ilTypeDefinition); if (gParams.Length > 0) @@ -209,10 +209,10 @@ private static void FixupExplicitOverridesInType(TypeDefinition ilTypeDefinition if (parameterRefs.Any(gp => gp == null)) return null; - + return ilTypeDefinition.Module.ImportRecursive(type.MakeGenericInstanceType(parameterRefs)); } - + return type; } @@ -276,10 +276,9 @@ private static void CopyIl2CppDataToManagedType(Il2CppTypeDefinition cppTypeDefi private static void PopulateGenericParamsForType(Il2CppTypeDefinition cppTypeDefinition, TypeDefinition ilTypeDefinition) { - if (cppTypeDefinition.GenericContainer == null) + if (cppTypeDefinition.GenericContainer == null) return; - - var position = 0; + foreach (var param in cppTypeDefinition.GenericContainer.GenericParameters) { if (!SharedState.GenericParamsByIndex.TryGetValue(param.Index, out var p)) @@ -428,7 +427,6 @@ private static void ProcessMethodsInType(Il2CppTypeDefinition cppTypeDefinition, } //Handle generic parameters. - var position = 0; methodDef.GenericContainer?.GenericParameters.ToList() .ForEach(p => { @@ -436,7 +434,7 @@ private static void ProcessMethodsInType(Il2CppTypeDefinition cppTypeDefinition, { if (!methodDefinition.GenericParameters.Contains(gp)) methodDefinition.GenericParameters.Add(gp); - + return; } @@ -450,7 +448,7 @@ private static void ProcessMethodsInType(Il2CppTypeDefinition cppTypeDefinition, .ToList() .ForEach(gp.Constraints.Add); }); - + if (methodDef.slot < ushort.MaxValue) SharedState.VirtualMethodsBySlot[methodDef.slot] = methodDefinition; } diff --git a/Cpp2IL.Core/AttributeRestorer.cs b/Cpp2IL.Core/AttributeRestorer.cs index 3b410f10..6cf1d2ce 100644 --- a/Cpp2IL.Core/AttributeRestorer.cs +++ b/Cpp2IL.Core/AttributeRestorer.cs @@ -311,7 +311,7 @@ public static List GetCustomAttributesByAttributeIndex(Il2Cp var propertyMappings = GetSimplePropertyFieldMappings(attr, keyFunctionAddresses); foreach (var fieldWriteAction in fieldSets) { - var field = fieldWriteAction.FieldWritten!.GetLast().FinalLoadInChain!.Resolve()!; + var field = fieldWriteAction.FieldWritten!.GetLast().FinalLoadInChain!; if(!propertyMappings.TryGetValue(field, out var prop)) //TODO: This may be an actual field set, not a property set - add support continue; @@ -646,7 +646,7 @@ private static object AllocateArray(AllocatedArray array) return null; } - ret = fieldWrites!.Select(f => new FieldToParameterMapping(f.FieldWritten!.FinalLoadInChain!.Resolve(), ((LocalDefinition) f.SourceOperand!).ParameterDefinition!)).ToArray(); + ret = fieldWrites!.Select(f => new FieldToParameterMapping(f.FieldWritten!.FinalLoadInChain!, ((LocalDefinition) f.SourceOperand!).ParameterDefinition!)).ToArray(); FieldToParameterMappings.TryAdd(constructor, ret); return ret; @@ -808,7 +808,7 @@ private static Dictionary GetSimpleProperty var fieldBeingWritten = fieldWriteAction.FieldWritten.GetLast(); - ret[fieldBeingWritten.FinalLoadInChain!.Resolve()] = propertyDefinition; + ret[fieldBeingWritten.FinalLoadInChain!] = propertyDefinition; } _simpleFieldToPropertyCache[type] = ret; diff --git a/Cpp2IL.Core/CecilCodedIndex.cs b/Cpp2IL.Core/CecilCodedIndex.cs deleted file mode 100644 index f721f162..00000000 --- a/Cpp2IL.Core/CecilCodedIndex.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Cpp2IL.Core -{ - public enum CecilCodedIndex - { - TypeDefOrRef, - HasConstant, - HasCustomAttribute, - HasFieldMarshal, - HasDeclSecurity, - MemberRefParent, - HasSemantics, - MethodDefOrRef, - MemberForwarded, - Implementation, - CustomAttributeType, - ResolutionScope, - TypeOrMethodDef, - HasCustomDebugInformation, - } -} \ No newline at end of file diff --git a/Cpp2IL.Core/CecilEType.cs b/Cpp2IL.Core/CecilEType.cs deleted file mode 100644 index 1ea39c54..00000000 --- a/Cpp2IL.Core/CecilEType.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace Cpp2IL.Core -{ - //Copied directly from Mono.Cecil.Metadata.ElementType - public enum CecilEType : byte { - None = 0x00, - Void = 0x01, - Boolean = 0x02, - Char = 0x03, - I1 = 0x04, - U1 = 0x05, - I2 = 0x06, - U2 = 0x07, - I4 = 0x08, - U4 = 0x09, - I8 = 0x0a, - U8 = 0x0b, - R4 = 0x0c, - R8 = 0x0d, - String = 0x0e, - Ptr = 0x0f, // Followed by token - ByRef = 0x10, // Followed by token - ValueType = 0x11, // Followed by token - Class = 0x12, // Followed by token - Var = 0x13, // Followed by generic parameter number - Array = 0x14, // - GenericInst = 0x15, // ... */ - TypedByRef = 0x16, - I = 0x18, // System.IntPtr - U = 0x19, // System.UIntPtr - FnPtr = 0x1b, // Followed by full method signature - Object = 0x1c, // System.Object - SzArray = 0x1d, // Single-dim array with 0 lower bound - MVar = 0x1e, // Followed by generic parameter number - CModReqD = 0x1f, // Required modifier : followed by a TypeDef or TypeRef token - CModOpt = 0x20, // Optional modifier : followed by a TypeDef or TypeRef token - Internal = 0x21, // Implemented within the CLI - Modifier = 0x40, // Or'd with following element types - Sentinel = 0x41, // Sentinel for varargs method signature - Pinned = 0x45, // Denotes a local variable that points at a pinned object - - // special undocumented constants - Type = 0x50, - Boxed = 0x51, - Enum = 0x55 - } -} \ No newline at end of file diff --git a/Cpp2IL.Core/Cpp2IlApi.cs b/Cpp2IL.Core/Cpp2IlApi.cs index 78b0c339..077eea44 100644 --- a/Cpp2IL.Core/Cpp2IlApi.cs +++ b/Cpp2IL.Core/Cpp2IlApi.cs @@ -412,18 +412,14 @@ public static void SaveAssemblies(string toWhere, List assem if (reference != null) assembly.MainModule.AssemblyReferences.Remove(reference); -#if !DEBUG try { -#endif - assembly.Write(dllPath); -#if !DEBUG + assembly.Write(dllPath); } catch (Exception e) { throw new DllSaveException(dllPath, e); } -#endif } } diff --git a/Cpp2IL.Core/Cpp2IlHarmonyPatches.cs b/Cpp2IL.Core/Cpp2IlHarmonyPatches.cs index 870f0d39..57e5cd18 100644 --- a/Cpp2IL.Core/Cpp2IlHarmonyPatches.cs +++ b/Cpp2IL.Core/Cpp2IlHarmonyPatches.cs @@ -34,14 +34,6 @@ internal static void Install() Logger.VerboseNewline("\tAdding finalizer to Mono.Cecil.MetadataBuilder:GetCustomAttributeSignature...", "Harmony"); harmony.Patch(AccessTools.Method("Mono.Cecil.MetadataBuilder:GetCustomAttributeSignature"), finalizer: new(typeof(Cpp2IlHarmonyPatches), nameof(FinalizeGetCustomAttributeSignature))); - - Logger.VerboseNewline("\tAdding finalizer to Mono.Cecil.SignatureWriter:WriteTypeSignature...", "Harmony"); - harmony.Patch(AccessTools.Method("Mono.Cecil.SignatureWriter:WriteTypeSignature"), finalizer: new(typeof(Cpp2IlHarmonyPatches), nameof(FinalizeWriteTypeSignature))); - - Logger.VerboseNewline("\tAdding finalizer to Mono.Cecil.Mixin:CompressMetadataToken...", "Harmony"); - harmony.Patch(AccessTools.Method("Mono.Cecil.Mixin:CompressMetadataToken"), new(typeof(Cpp2IlHarmonyPatches), nameof(PreCompressMetadataToken)), finalizer: new(typeof(Cpp2IlHarmonyPatches), nameof(FinalizeCompressMetadataToken))); - - harmony.Patch(AccessTools.Method("Mono.Cecil.MetadataBuilder+GenericParameterComparer:Compare"), finalizer: new(typeof(Cpp2IlHarmonyPatches), nameof(FinalizeCompareGenericParams))); Logger.VerboseNewline("\tDone", "Harmony"); } @@ -85,33 +77,5 @@ internal static void Install() return null; } - - public static Exception? FinalizeWriteTypeSignature(TypeReference type, Exception? __exception) - { - if (__exception != null) - return new TypeSignatureWriteFailedException(type, __exception); - - return null; - } - - public static void PreCompressMetadataToken(int self, MetadataToken token) - { - } - - public static Exception? FinalizeCompressMetadataToken(int self, MetadataToken token, Exception? __exception) - { - if (__exception != null) - return new MetadataTokenCompressionException((CecilCodedIndex) self, token, __exception); - - return null; - } - - public static Exception? FinalizeCompareGenericParams(GenericParameter a, GenericParameter b, Exception? __exception) - { - if (__exception != null) - return new Exception($"Failed to compare generic params {a} (owned by {a.Owner}) and {b} (owned by {b.Owner}) due to an exception", __exception); - - return null; - } } } \ No newline at end of file diff --git a/Cpp2IL.Core/Exceptions/MetadataTokenCompressionException.cs b/Cpp2IL.Core/Exceptions/MetadataTokenCompressionException.cs deleted file mode 100644 index 716dbdf0..00000000 --- a/Cpp2IL.Core/Exceptions/MetadataTokenCompressionException.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using Mono.Cecil; - -namespace Cpp2IL.Core.Exceptions -{ - public class MetadataTokenCompressionException : Exception - { - public MetadataTokenCompressionException(CecilCodedIndex codedIndex, MetadataToken token, Exception cause) : base($"Failed to compress metadata token {token} (of type {token.TokenType}, RID {token.RID}) into coded index of type {codedIndex}, due to an exception", cause) - { } - } -} \ No newline at end of file diff --git a/Cpp2IL.Core/Exceptions/TypeSignatureWriteFailedException.cs b/Cpp2IL.Core/Exceptions/TypeSignatureWriteFailedException.cs deleted file mode 100644 index b8a6e9c0..00000000 --- a/Cpp2IL.Core/Exceptions/TypeSignatureWriteFailedException.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using Cpp2IL.Core.Utils; -using Mono.Cecil; - -namespace Cpp2IL.Core.Exceptions -{ - public class TypeSignatureWriteFailedException : Exception - { - public TypeSignatureWriteFailedException(TypeReference type, Exception cause) : base($"Failed to write type {type} of etype {type.GetEType()} due to an exception", cause) - { } - } -} \ No newline at end of file diff --git a/Cpp2IL.Core/StubAssemblyBuilder.cs b/Cpp2IL.Core/StubAssemblyBuilder.cs index 6c8859a1..9942e563 100644 --- a/Cpp2IL.Core/StubAssemblyBuilder.cs +++ b/Cpp2IL.Core/StubAssemblyBuilder.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; -using Cpp2IL.Core.Utils; using LibCpp2IL.Metadata; using Mono.Cecil; using TypeAttributes = Mono.Cecil.TypeAttributes; @@ -11,6 +11,8 @@ namespace Cpp2IL.Core { internal static class StubAssemblyBuilder { + private static readonly FieldInfo _etypeField = typeof(TypeReference).GetField("etype", BindingFlags.NonPublic | BindingFlags.Instance)!; + /// /// Creates all the Assemblies defined in the provided metadata, along with (stub) definitions of all the types contained therein. /// @@ -96,28 +98,28 @@ private static void HandleTypeInAssembly(Il2CppTypeDefinition type, ModuleDefini var etype = name switch { //See ElementType in Mono.Cecil.Metadata - "Void" => CecilEType.Void, - nameof(Boolean) => CecilEType.Boolean, - nameof(Char) => CecilEType.Char, - nameof(SByte) => CecilEType.I1, //I1 - nameof(Byte) => CecilEType.U1, //U1 - nameof(Int16) => CecilEType.I2, //I2 - nameof(UInt16) => CecilEType.U2, //U2 - nameof(Int32) => CecilEType.I4, //I4 - nameof(UInt32) => CecilEType.U4, //U4 - nameof(Int64) => CecilEType.I8, //I8 - nameof(UInt64) => CecilEType.U8, //U8 - nameof(Single) => CecilEType.R4, //R4 - nameof(Double) => CecilEType.R8, //R8 - nameof(String) => CecilEType.String, - nameof(Object) => CecilEType.Object, //Object + "Void" => 1, + nameof(Boolean) => 2, + nameof(Char) => 3, + nameof(SByte) => 4, //I1 + nameof(Byte) => 5, //U1 + nameof(Int16) => 6, //I2 + nameof(UInt16) => 7, //U2 + nameof(Int32) => 8, //I4 + nameof(UInt32) => 9, //U4 + nameof(Int64) => 0xA, //I8 + nameof(UInt64) => 0xB, //U8 + nameof(Single) => 0xC, //R4 + nameof(Double) => 0xD, //R8 + nameof(String) => 0xE, + nameof(Object) => 0x1C, //Object // nameof(IntPtr) => 0xF, - _ => CecilEType.None, + _ => 0x0, }; if(etype != 0) //Fixup internaL cecil etypes for (among other things) attribute blobs. - definition.SetEType(etype); + _etypeField.SetValue(definition, (byte) etype); } if(!isNestedType) diff --git a/Cpp2IL.Core/Utils/CecilUtils.cs b/Cpp2IL.Core/Utils/CecilUtils.cs deleted file mode 100644 index 5100d276..00000000 --- a/Cpp2IL.Core/Utils/CecilUtils.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System; -using System.Linq; -using System.Reflection; -using LibCpp2IL; -using Mono.Cecil; -using Mono.Cecil.Cil; -using Mono.Cecil.Rocks; - -namespace Cpp2IL.Core.Utils -{ - public static class CecilUtils - { - private static readonly FieldInfo EtypeField = typeof(TypeReference).GetField("etype", BindingFlags.NonPublic | BindingFlags.Instance)!; - private static readonly FieldInfo PositionField = typeof(GenericParameter).GetField("position", BindingFlags.NonPublic | BindingFlags.Instance)!; - - public static bool HasAnyGenericCrapAnywhere(TypeReference reference) - { - if (reference is GenericParameter) - return true; - - if (reference is GenericInstanceType git) - { - //check for e.g. List> - return git.GenericArguments.Any(HasAnyGenericCrapAnywhere); - } - - if (reference is TypeSpecification typeSpec) - //Pointers, byrefs, etc - return HasAnyGenericCrapAnywhere(typeSpec.ElementType); - - return reference.HasGenericParameters; - } - - public static bool HasAnyGenericCrapAnywhere(MethodReference reference, bool checkDeclaringTypeParamsAndReturn = true) - { - if (checkDeclaringTypeParamsAndReturn && HasAnyGenericCrapAnywhere(reference.DeclaringType)) - return true; - - if (checkDeclaringTypeParamsAndReturn && HasAnyGenericCrapAnywhere(reference.ReturnType)) - return true; - - if (checkDeclaringTypeParamsAndReturn && reference.Parameters.Any(p => HasAnyGenericCrapAnywhere(p.ParameterType))) - return true; - - if (reference.HasGenericParameters) - return true; - - if (reference is GenericInstanceMethod gim) - return gim.GenericArguments.Any(HasAnyGenericCrapAnywhere); - - return false; - } - - public static TypeReference ImportTypeButCleanly(this ModuleDefinition module, TypeReference reference) - { - if (reference is GenericParameter) - //These two lines are what cecil is missing. It's so simple :/ - return reference; - - if (reference is GenericInstanceType git) - return module.ImportTypeButCleanly(git.ElementType).MakeGenericInstanceType(git.GenericArguments.Select(module.ImportTypeButCleanly).ToArray()); - - if (reference is ArrayType at) - return module.ImportTypeButCleanly(at.ElementType).MakeArrayType(); - - if (reference is ByReferenceType brt) - return module.ImportTypeButCleanly(brt.ElementType).MakeByReferenceType(); - - if (reference is PointerType pt) - return module.ImportTypeButCleanly(pt.ElementType).MakePointerType(); - - if (reference is TypeDefinition td) - return module.ImportReference(td); - - if (reference.GetType() == typeof(TypeReference)) - return module.ImportReference(reference); - - throw new NotSupportedException($"Support for importing {reference} of type {reference.GetType()} is not implemented"); - } - - public static ParameterDefinition ImportParameterButCleanly(this ModuleDefinition module, ParameterDefinition param) - { - param.ParameterType = HasAnyGenericCrapAnywhere(param.ParameterType) ? module.ImportTypeButCleanly(param.ParameterType) : module.ImportReference(param.ParameterType); - return param; - } - - public static MethodReference ImportMethodButCleanly(this ModuleDefinition module, MethodReference method) - { - var returnType = module.ImportTypeButCleanly(method.ReturnType); - var declaringType = module.ImportTypeButCleanly(method.DeclaringType); - var gParams = method.GenericParameters.ToList(); - var gArgs = (method as GenericInstanceMethod)?.GenericArguments.Select(module.ImportTypeButCleanly).ToList(); - var methodParams = method.Parameters.Select(module.ImportParameterButCleanly).ToList(); - - var ret = new MethodReference(method.Name, returnType, declaringType); - - gParams.ForEach(ret.GenericParameters.Add); - - if (gArgs != null) - { - var gMtd = new GenericInstanceMethod(ret); - gArgs.ForEach(gMtd.GenericArguments.Add); - ret = gMtd; - } - - methodParams.ForEach(ret.Parameters.Add); - - return ret; - } - - public static FieldReference ImportFieldButCleanly(this ModuleDefinition module, FieldReference field) - { - var declaringType = module.ImportTypeButCleanly(field.DeclaringType); - var fieldType = module.ImportTypeButCleanly(field.FieldType); - - return new(field.Name, fieldType, declaringType); - } - - public static TypeReference ImportTypeButCleanly(this ILProcessor processor, TypeReference reference) => processor.Body.Method.Module.ImportTypeButCleanly(reference); - - public static MethodReference ImportMethodButCleanly(this ILProcessor processor, MethodReference reference) => processor.Body.Method.Module.ImportMethodButCleanly(reference); - - public static void SetEType(this TypeReference tr, CecilEType eType) => EtypeField.SetValue(tr, (byte) eType); - - public static CecilEType GetEType(this TypeReference tr) => (CecilEType) (byte) EtypeField.GetValue(tr); - - public static GenericInstanceMethod MakeGenericInstanceMethod(this MethodReference methodReference, params TypeReference[] genericArguments) - { - if (methodReference == null) - throw new ArgumentNullException(nameof(methodReference), "Method to make generic cannot be null"); - - // if (genericArguments.Any(g => g is null) || genericArguments.Length != methodReference.GenericParameters.Count) - // throw new Exception($"Generic arguments {genericArguments.ToStringEnumerable()}, count {genericArguments.Length} are not suitable for use for generic parameters {methodReference.GenericParameters.ToStringEnumerable()}, length {methodReference.GenericParameters.Count}"); - - var gim = new GenericInstanceMethod(methodReference); - - //Cecil sucks major ass and doesn't do this in the constructor above for SOME FUCKING REASON - methodReference.GenericParameters.ToList().ForEach(gim.GenericParameters.Add); - - genericArguments.ToList().ForEach(gim.GenericArguments.Add); - - return gim; - } - - public static void SetPosition(this GenericParameter gp, int position) => PositionField.SetValue(gp, position); - - public static int GetPosition(this GenericParameter gp) => (int) PositionField.GetValue(gp); - } -} \ No newline at end of file diff --git a/Cpp2IL.Core/Utils/FieldUtils.cs b/Cpp2IL.Core/Utils/FieldUtils.cs index e4b89d5f..b7369b63 100644 --- a/Cpp2IL.Core/Utils/FieldUtils.cs +++ b/Cpp2IL.Core/Utils/FieldUtils.cs @@ -132,8 +132,8 @@ private static List RecalculateFieldOffsetsForGenericType(TypeRefer /// public class FieldBeingAccessedData { - public FieldReference? ImpliedFieldLoad; - public FieldReference? FinalLoadInChain; + public readonly FieldDefinition? ImpliedFieldLoad; + public readonly FieldDefinition? FinalLoadInChain; public readonly FieldBeingAccessedData? NextChainLink; private FieldBeingAccessedData(FieldDefinition? impliedFieldLoad, FieldDefinition? finalLoadInChain, FieldBeingAccessedData? nextChainLink) diff --git a/Cpp2IL.Core/Utils/GenericMethodUtils.cs b/Cpp2IL.Core/Utils/GenericMethodUtils.cs new file mode 100644 index 00000000..07c50143 --- /dev/null +++ b/Cpp2IL.Core/Utils/GenericMethodUtils.cs @@ -0,0 +1,31 @@ +using System.Diagnostics; +using Mono.Cecil; + +namespace Cpp2IL.Core.Utils +{ + public static class GenericMethodUtils + { + public static void PrepareGenericMethodForEmissionToBody(MethodReference theMethod, TypeReference declaringType, ModuleDefinition importInto) + { + //Naively resolving everything and its mother doesn't work. + //There are four key parts here. From left-to-right in the signature + // The return type, including generic arguments + // The declaring type, including generic arguments + // The method specification itself, including any generic arguments + // The parameters, with any generic parameters in the source method definition resolved to match those of method reference. + + //The key part is that generic *arguments* on the parameters and return type of the resulting method reference have to be the generic *parameters* of the method definition or type. + //E.g. Enumerable.Where(IEnumerable source, Func predicate), the 'T's in the parameters need to resolve to the T in the method Where, even if the method call itself + //has a generic argument. + //So this should look like Enumerable.Where(IEnumerable source, Func predicate) still, and NOT have the T in the parameters replaced with int. + + //And every single individual part has to be imported. + //And we have to return a unique new MethodReference for this call specifically. + + if(theMethod is MethodDefinition && declaringType is TypeDefinition) + return; + + //Debugger.Break(); + } + } +} \ No newline at end of file diff --git a/Cpp2IL.Core/Utils/MiscUtils.cs b/Cpp2IL.Core/Utils/MiscUtils.cs index 7cdcc620..cae3572b 100644 --- a/Cpp2IL.Core/Utils/MiscUtils.cs +++ b/Cpp2IL.Core/Utils/MiscUtils.cs @@ -584,16 +584,10 @@ public static object GetNumericConstant(ulong addr, TypeReference type) if (owner.GenericParameters.FirstOrDefault(a => a.Name == typeData.variableGenericParamName) is { } gp) return gp; - if (owner is MethodReference mRef && mRef.DeclaringType.GenericParameters.FirstOrDefault(g => g.Name == typeData.variableGenericParamName) is { } gp2) - return gp2; - foreach (var extraProvider in extra) { - if (extraProvider?.GenericParameters.FirstOrDefault(a => a.Name == typeData.variableGenericParamName) is { } gp3) - return gp3; - - if (extraProvider is MethodReference mRef2 && mRef2.DeclaringType.GenericParameters.FirstOrDefault(g => g.Name == typeData.variableGenericParamName) is { } gp4) - return gp4; + if (extraProvider?.GenericParameters.FirstOrDefault(a => a.Name == typeData.variableGenericParamName) is { } gp2) + return gp2; } //Generic parameter diff --git a/Cpp2IL/ConsoleLogger.cs b/Cpp2IL/ConsoleLogger.cs index a0646ab0..a6944082 100644 --- a/Cpp2IL/ConsoleLogger.cs +++ b/Cpp2IL/ConsoleLogger.cs @@ -31,16 +31,6 @@ public static void Initialize() Write("Verb", source, message, VERB); }; - HarmonyLib.Tools.Logger.MessageReceived += (sender, args) => - { - if (args.LogChannel is HarmonyLib.Tools.Logger.LogChannel.Warn) - Logger.WarnNewline(args.Message, "HarmonyInternal"); - if (args.LogChannel is HarmonyLib.Tools.Logger.LogChannel.Error) - Logger.ErrorNewline(args.Message, "HarmonyInternal"); - }; - - HarmonyLib.Tools.Logger.ChannelFilter = HarmonyLib.Tools.Logger.LogChannel.Warn | HarmonyLib.Tools.Logger.LogChannel.Error; - CheckColorSupport(); } diff --git a/Cpp2IL/Program.cs b/Cpp2IL/Program.cs index 9ca230a5..f0b75379 100644 --- a/Cpp2IL/Program.cs +++ b/Cpp2IL/Program.cs @@ -327,8 +327,8 @@ public static int MainWithArgs(Cpp2IlRuntimeArgs runtimeArgs) if (runtimeArgs.EnableAnalysis) Cpp2IlApi.PopulateConcreteImplementations(); - - // Cpp2IlApi.HarmonyPatchCecilForBetterExceptions(); + + Cpp2IlApi.HarmonyPatchCecilForBetterExceptions(); Cpp2IlApi.SaveAssemblies(runtimeArgs.OutputRootDirectory); @@ -376,10 +376,7 @@ private static void DoAnalysisForAssembly(string assemblyName, AnalysisLevel ana Cpp2IlApi.AnalyseAssembly(analysisLevel, targetAssembly, keyFunctionAddresses, skipDumps ? null : Path.Combine(rootDir, "types"), parallel, continueThroughErrors); if (doIlToAsm) - { - Cpp2IlApi.HarmonyPatchCecilForBetterExceptions(); Cpp2IlApi.SaveAssemblies(rootDir, new List {targetAssembly}); - } } diff --git a/LibCpp2IL/LibCpp2IlUtils.cs b/LibCpp2IL/LibCpp2IlUtils.cs index e17952c2..6b0df4fa 100644 --- a/LibCpp2IL/LibCpp2IlUtils.cs +++ b/LibCpp2IL/LibCpp2IlUtils.cs @@ -13,42 +13,42 @@ public static class LibCpp2ILUtils { private static readonly Dictionary TypeString = new Dictionary { - {1, "void"}, - {2, "bool"}, - {3, "char"}, - {4, "sbyte"}, - {5, "byte"}, - {6, "short"}, - {7, "ushort"}, - {8, "int"}, - {9, "uint"}, - {10, "long"}, - {11, "ulong"}, - {12, "float"}, - {13, "double"}, - {14, "string"}, - {22, "TypedReference"}, - {24, "IntPtr"}, - {25, "UIntPtr"}, - {28, "object"} + { 1, "void" }, + { 2, "bool" }, + { 3, "char" }, + { 4, "sbyte" }, + { 5, "byte" }, + { 6, "short" }, + { 7, "ushort" }, + { 8, "int" }, + { 9, "uint" }, + { 10, "long" }, + { 11, "ulong" }, + { 12, "float" }, + { 13, "double" }, + { 14, "string" }, + { 22, "TypedReference" }, + { 24, "IntPtr" }, + { 25, "UIntPtr" }, + { 28, "object" } }; private static readonly Dictionary PrimitiveSizes = new() { - {"Byte", 1}, - {"SByte", 1}, - {"Boolean", 1}, - {"Int16", 2}, - {"UInt16", 2}, - {"Char", 2}, - {"Int32", 4}, - {"UInt32", 4}, - {"Single", 4}, - {"Int64", 8}, - {"UInt64", 8}, - {"Double", 8}, - {"IntPtr", 8}, - {"UIntPtr", 8}, + { "Byte", 1 }, + { "SByte", 1 }, + { "Boolean", 1 }, + { "Int16", 2 }, + { "UInt16", 2 }, + { "Char", 2 }, + { "Int32", 4 }, + { "UInt32", 4 }, + { "Single", 4 }, + { "Int64", 8 }, + { "UInt64", 8 }, + { "Double", 8 }, + { "IntPtr", 8 }, + { "UIntPtr", 8}, }; private static Dictionary _cachedVersionAttributes = new(); @@ -98,7 +98,7 @@ internal static string GetTypeName(Il2CppMetadata metadata, Il2CppBinary cppAsse if (LibCpp2IlMain.Binary == null || LibCpp2IlMain.TheMetadata == null) return null; var types = new List(); - var pointers = LibCpp2IlMain.Binary.ReadClassArrayAtVirtualAddress(genericInst.pointerStart, (long) genericInst.pointerCount); + var pointers = LibCpp2IlMain.Binary.ReadClassArrayAtVirtualAddress(genericInst.pointerStart, (long)genericInst.pointerCount); for (uint i = 0; i < genericInst.pointerCount; ++i) { var oriType = LibCpp2IlMain.Binary.GetIl2CppTypeFromPointer(pointers[i]); @@ -111,7 +111,7 @@ internal static string GetTypeName(Il2CppMetadata metadata, Il2CppBinary cppAsse internal static string GetGenericTypeParamNames(Il2CppMetadata metadata, Il2CppBinary cppAssembly, Il2CppGenericInst genericInst) { var typeNames = new List(); - var pointers = cppAssembly.ReadClassArrayAtVirtualAddress(genericInst.pointerStart, (long) genericInst.pointerCount); + var pointers = cppAssembly.ReadClassArrayAtVirtualAddress(genericInst.pointerStart, (long)genericInst.pointerCount); for (uint i = 0; i < genericInst.pointerCount; ++i) { var oriType = cppAssembly.GetIl2CppTypeFromPointer(pointers[i]); @@ -172,7 +172,7 @@ public static string GetTypeName(Il2CppMetadata metadata, Il2CppBinary cppAssemb break; } default: - ret = TypeString[(int) type.type]; + ret = TypeString[(int)type.type]; break; } @@ -227,7 +227,7 @@ public static string GetTypeName(Il2CppMetadata metadata, Il2CppBinary cppAssemb if (LibCpp2IlMain.MetadataVersion < 29) len = metadata.ReadClassAtRawAddr(pointer); else - len = (int) metadata.ReadUnityCompressedIntAtRawAddr(pointer, out lenLen); + len = (int)metadata.ReadUnityCompressedIntAtRawAddr(pointer, out lenLen); if (len > 1024 * 32) throw new Exception($"Unreasonable string length {len}"); return Encoding.UTF8.GetString(metadata.ReadByteArrayAtRawAddress(pointer + lenLen, len)); @@ -312,13 +312,13 @@ public static Il2CppTypeReflectionData GetTypeReflectionData(Il2CppType forWhat) else { //This is slightly annoying, because we will have already read this type, but we have to re-read it. TODO FUTURE: Make a mapping of type definition addr => type def? - var type = LibCpp2IlMain.Binary.ReadClassAtVirtualAddress((ulong) genericClass.typeDefinitionIndex); + var type = LibCpp2IlMain.Binary.ReadClassAtVirtualAddress((ulong)genericClass.typeDefinitionIndex); type.Init(); typeDefinition = LibCpp2IlMain.TheMetadata!.typeDefs[type.data.classIndex]; } var genericInst = LibCpp2IlMain.Binary.ReadClassAtVirtualAddress(genericClass.context.class_inst); - var pointers = LibCpp2IlMain.Binary.GetPointers(genericInst.pointerStart, (long) genericInst.pointerCount); + var pointers = LibCpp2IlMain.Binary.GetPointers(genericInst.pointerStart, (long)genericInst.pointerCount); var genericParams = pointers .Select(pointer => LibCpp2IlMain.Binary.GetIl2CppTypeFromPointer(pointer)) .Select(type => GetTypeReflectionData(type)!) //Recursive call here @@ -345,7 +345,6 @@ public static Il2CppTypeReflectionData GetTypeReflectionData(Il2CppType forWhat) isType = false, isGenericType = false, variableGenericParamName = genericName, - variableGenericParamIndex = forWhat.data.genericParameterIndex, }; } case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY: @@ -395,7 +394,7 @@ public static int VersionAwareSizeOf(Type type, bool dontCheckVersionAttributes type = type.GetEnumUnderlyingType(); if (type.IsPrimitive) - return (int) PrimitiveSizes[type.Name]; + return (int)PrimitiveSizes[type.Name]; var shouldDownsize = downsize && LibCpp2IlMain.Binary!.is32Bit; @@ -430,7 +429,7 @@ public static int VersionAwareSizeOf(Type type, bool dontCheckVersionAttributes default: if (field.FieldType == type) throw new Exception($"Infinite recursion is not allowed. Field {field} of type {type} has the same type as its parent."); - + size += VersionAwareSizeOf(field.FieldType, dontCheckVersionAttributes, downsize); break; } diff --git a/LibCpp2IL/Reflection/Il2CppTypeReflectionData.cs b/LibCpp2IL/Reflection/Il2CppTypeReflectionData.cs index 7cb5f04b..8793be9f 100644 --- a/LibCpp2IL/Reflection/Il2CppTypeReflectionData.cs +++ b/LibCpp2IL/Reflection/Il2CppTypeReflectionData.cs @@ -24,7 +24,6 @@ public class Il2CppTypeReflectionData public Il2CppTypeReflectionData? arrayType; public byte arrayRank; public string variableGenericParamName; - public long variableGenericParamIndex; public bool isPointer; #pragma warning restore 8618 From 873a9eabd72d64477324c5b62586e91e6e57fbaa Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Fri, 10 Dec 2021 12:53:31 +0000 Subject: [PATCH 03/76] Lib: Fix wasm files creating too small memory blocks --- LibCpp2IL/Wasm/WasmMemoryBlock.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LibCpp2IL/Wasm/WasmMemoryBlock.cs b/LibCpp2IL/Wasm/WasmMemoryBlock.cs index abdb386e..ddfc4ccb 100644 --- a/LibCpp2IL/Wasm/WasmMemoryBlock.cs +++ b/LibCpp2IL/Wasm/WasmMemoryBlock.cs @@ -16,7 +16,7 @@ private static MemoryStream BuildStream(WasmFile file) .Max(); //Add an extra buffer beyond that just to be safe - var toAlloc = maxByte + 0x1000; + var toAlloc = (maxByte + 0x1000) * 2; var memoryBlock = new byte[toAlloc]; var stream = new MemoryStream(memoryBlock, 0, (int) toAlloc, true, true); From db3100b42fc6f5c00886e47662a59026801d7fb9 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Sat, 11 Dec 2021 14:41:42 +0000 Subject: [PATCH 04/76] NSO: Properly read MOD0 header, dynamic + symbol tables, and apply relocs --- LibCpp2IL/Elf/ElfDynamicType.cs | 16 +- LibCpp2IL/Elf/ElfRelaEntry.cs | 3 + LibCpp2IL/NintendoSwitch/NsoFile.cs | 289 +++++++++++++++-------- LibCpp2IL/NintendoSwitch/NsoHeader.cs | 4 +- LibCpp2IL/NintendoSwitch/NsoModHeader.cs | 14 ++ 5 files changed, 224 insertions(+), 102 deletions(-) create mode 100644 LibCpp2IL/NintendoSwitch/NsoModHeader.cs diff --git a/LibCpp2IL/Elf/ElfDynamicType.cs b/LibCpp2IL/Elf/ElfDynamicType.cs index 6502a98a..c803d93b 100644 --- a/LibCpp2IL/Elf/ElfDynamicType.cs +++ b/LibCpp2IL/Elf/ElfDynamicType.cs @@ -2,6 +2,9 @@ { public enum ElfDynamicType : long { + DT_NULL = 0, + DT_NEEDED = 1, + DT_PLTRELSZ = 2, DT_PLTGOT = 0x3, DT_HASH = 0x4, DT_STRTAB = 0x5, @@ -9,14 +12,25 @@ public enum ElfDynamicType : long DT_RELA = 0x7, DT_RELASZ = 0x8, DT_RELAENT = 0x9, + DT_STRSZ = 0xa, + DT_SYMENT = 0xb, DT_INIT = 0xC, DT_FINI = 0xD, DT_REL = 0x11, DT_RELSZ = 0x12, DT_RELENT = 0x13, + DT_PLTREL = 0x14, + DT_DEBUG = 0x15, + DT_TEXTREL = 0x16, DT_JMPREL = 0x17, + DT_BIND_NOW = 0x18, DT_INIT_ARRAY = 0x19, DT_FINI_ARRAY = 0x1A, - DT_INIT_ARRAYSZ = 0x1B + DT_INIT_ARRAYSZ = 0x1B, + DT_FINI_ARRAYSZ = 0x1C, + DT_RUNPATH = 0x1D, + DT_FLAGS = 0x1E, + DT_PREINIT_ARRAY = 0x20, + DT_PREINIT_ARRAYSZ = 0x21, } } \ No newline at end of file diff --git a/LibCpp2IL/Elf/ElfRelaEntry.cs b/LibCpp2IL/Elf/ElfRelaEntry.cs index f82a67a4..c3b2287c 100644 --- a/LibCpp2IL/Elf/ElfRelaEntry.cs +++ b/LibCpp2IL/Elf/ElfRelaEntry.cs @@ -5,5 +5,8 @@ public class ElfRelaEntry public ulong Offset; public ulong Info; public ulong Addend; + + public ElfRelocationType Type => (ElfRelocationType) (Info & 0xFFFF_FFFF); + public ulong Symbol => Info >> 32; } } \ No newline at end of file diff --git a/LibCpp2IL/NintendoSwitch/NsoFile.cs b/LibCpp2IL/NintendoSwitch/NsoFile.cs index e33c45c7..fa50b5f0 100644 --- a/LibCpp2IL/NintendoSwitch/NsoFile.cs +++ b/LibCpp2IL/NintendoSwitch/NsoFile.cs @@ -2,19 +2,25 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using LibCpp2IL.Elf; using LibCpp2IL.Logging; namespace LibCpp2IL.NintendoSwitch { - public class NsoFile : Il2CppBinary + public sealed class NsoFile : Il2CppBinary { + private const ulong NSO_GLOBAL_OFFSET = 0; + private byte[] _raw; private NsoHeader header; + private NsoModHeader _modHeader; private bool isTextCompressed; private bool isRoDataCompressed; private bool isDataCompressed; private List segments = new(); + private List dynamicEntries = new(); + public ElfDynamicSymbol64[] SymbolTable; private bool isCompressed => isTextCompressed || isRoDataCompressed || isDataCompressed; @@ -81,7 +87,7 @@ public NsoFile(MemoryStream input, long maxMetadataUsages) : base(input, maxMeta header.TextCompressedSize = ReadUInt32(); header.RoDataCompressedSize = ReadUInt32(); header.DataCompressedSize = ReadUInt32(); - header.Padding = ReadBytes(0x1C); + header.NsoHeaderReserved = ReadBytes(0x1C); LibLogger.VerboseNewline("\tRead post-segment fields OK. Reading Dynamic section and Api Info offsets..."); @@ -111,121 +117,208 @@ public NsoFile(MemoryStream input, long maxMetadataUsages) : base(input, maxMeta if (!isCompressed) { - LibLogger.VerboseNewline($"\tBinary is not compressed. Reading BSS segment header..."); - - Position = header.TextSegment.FileOffset + 4; - var modOffset = ReadUInt32(); - Position = header.TextSegment.FileOffset + modOffset + 4; - var dynamicOffset = ReadUInt32(); - var bssStart = ReadUInt32(); - var bssEnd = ReadUInt32(); - header.BssSegment = new() - { - FileOffset = bssStart, - MemoryOffset = bssStart, - DecompressedSize = bssEnd - bssStart - }; - var ehFrameHdrStart = ReadUInt32(); - var ehFrameHdrEnd = ReadUInt32(); + ReadModHeader(); + ReadDynamicSection(); + ReadSymbolTable(); + ApplyRelocations(); } LibLogger.VerboseNewline($"\tNSO Read completed OK."); } - public NsoFile Decompress() + private void ReadModHeader() { - LibLogger.InfoNewline("\tDecompressing NSO file..."); - if (isTextCompressed || isRoDataCompressed || isDataCompressed) + LibLogger.VerboseNewline($"\tNSO is decompressed. Reading MOD segment header..."); + + _modHeader = new(); + + //Location of real mod header must be at .text + 4 + //Which allows .text + 0 to be a jump over it + Position = header.TextSegment.FileOffset + 4; + _modHeader.ModOffset = ReadUInt32(); + + //Now we have the real mod header position, go to it and read + Position = header.TextSegment.FileOffset + _modHeader.ModOffset + 4; + _modHeader.DynamicOffset = ReadUInt32() + _modHeader.ModOffset; + _modHeader.BssStart = ReadUInt32(); + _modHeader.BssEnd = ReadUInt32(); + + //Construct the bss segment information from the fields we just read + _modHeader.BssSegment = new() { - var unCompressedStream = new MemoryStream(); - var writer = new BinaryWriter(unCompressedStream); - writer.Write(header.Magic); - writer.Write(header.Version); - writer.Write(header.Reserved); - writer.Write(0); //Flags - writer.Write(header.TextSegment.FileOffset); - writer.Write(header.TextSegment.MemoryOffset); - writer.Write(header.TextSegment.DecompressedSize); - writer.Write(header.ModuleOffset); - var roOffset = header.TextSegment.FileOffset + header.TextSegment.DecompressedSize; - writer.Write(roOffset); //header.RoDataSegment.FileOffset - writer.Write(header.RoDataSegment.MemoryOffset); - writer.Write(header.RoDataSegment.DecompressedSize); - writer.Write(header.ModuleFileSize); - writer.Write(roOffset + header.RoDataSegment.DecompressedSize); //header.DataSegment.FileOffset - writer.Write(header.DataSegment.MemoryOffset); - writer.Write(header.DataSegment.DecompressedSize); - writer.Write(header.BssSize); - writer.Write(header.DigestBuildID); - writer.Write(header.TextCompressedSize); - writer.Write(header.RoDataCompressedSize); - writer.Write(header.DataCompressedSize); - writer.Write(header.Padding); - writer.Write(header.APIInfo.RegionRoDataOffset); - writer.Write(header.APIInfo.RegionSize); - writer.Write(header.DynStr.RegionRoDataOffset); - writer.Write(header.DynStr.RegionSize); - writer.Write(header.DynSym.RegionRoDataOffset); - writer.Write(header.DynSym.RegionSize); - writer.Write(header.TextHash); - writer.Write(header.RoDataHash); - writer.Write(header.DataHash); - writer.BaseStream.Position = header.TextSegment.FileOffset; - Position = header.TextSegment.FileOffset; - var textBytes = ReadBytes((int)header.TextCompressedSize); - if (isTextCompressed) - { - var unCompressedData = new byte[header.TextSegment.DecompressedSize]; - using (var decoder = new Lz4DecodeStream(new MemoryStream(textBytes))) - { - decoder.Read(unCompressedData, 0, unCompressedData.Length); - } + FileOffset = _modHeader.BssStart, + MemoryOffset = _modHeader.BssStart, + DecompressedSize = _modHeader.BssEnd - _modHeader.BssStart + }; + + _modHeader.EhFrameHdrStart = ReadUInt32(); + _modHeader.EhFrameHdrEnd = ReadUInt32(); + } - writer.Write(unCompressedData); - } - else + private void ReadDynamicSection() + { + LibLogger.VerboseNewline($"\tReading NSO Dynamic section..."); + + Position = MapVirtualAddressToRaw(_modHeader.DynamicOffset); + + //This is mostly a sanity check so we don't read the entire damn file 16 bytes at a time + //This will be way more than we need in general (like, 100 times more) + var endOfData = header.DataSegment.MemoryOffset + header.DataSegment.DecompressedSize; + var maxPossibleDynSectionEntryCount = (endOfData - _modHeader.DynamicOffset) / 16; //16 being sizeof(ElfDynamicEntry) on 64-bit + + for (var i = 0; i < maxPossibleDynSectionEntryCount; i++) + { + var dynEntry = ReadClassAtRawAddr(-1); + if (dynEntry.Tag == ElfDynamicType.DT_NULL) + //End of dynamic section + break; + dynamicEntries.Add(dynEntry); + } + } + + private void ReadSymbolTable() + { + LibLogger.Verbose($"\tReading NSO symbol table..."); + + var hash = GetDynamicEntry(ElfDynamicType.DT_HASH); + Position = MapVirtualAddressToRaw(hash.Value); + ReadUInt32(); //Ignored + var symbolCount = ReadUInt32(); + + var symTab = GetDynamicEntry(ElfDynamicType.DT_SYMTAB); + SymbolTable = ReadClassArrayAtVirtualAddress((ulong) MapVirtualAddressToRaw(symTab.Value), symbolCount); + + LibLogger.VerboseNewline($"\tGot {SymbolTable.Length} symbols"); + } + + private void ApplyRelocations() + { + ElfRelaEntry[] relaEntries; + + try + { + var dtRela = GetDynamicEntry(ElfDynamicType.DT_RELA); + var dtRelaSize = GetDynamicEntry(ElfDynamicType.DT_RELASZ); + relaEntries = ReadClassArrayAtVirtualAddress(dtRela.Value, (long) (dtRelaSize.Value / 24)); //24 being sizeof(ElfRelaEntry) on 64-bit + } + catch + { + //If we don't have relocations, that's fine. + return; + } + + LibLogger.VerboseNewline($"\tApplying {relaEntries.Length} relocations from DT_RELA..."); + + foreach (var elfRelaEntry in relaEntries) + { + switch (elfRelaEntry.Type) { - writer.Write(textBytes); + case ElfRelocationType.R_AARCH64_ABS64: + var symbol = SymbolTable[elfRelaEntry.Symbol]; + WriteWord((int) MapVirtualAddressToRaw(elfRelaEntry.Offset), symbol.Value + elfRelaEntry.Addend); + break; + case ElfRelocationType.R_AARCH64_RELATIVE: + WriteWord((int) MapVirtualAddressToRaw(elfRelaEntry.Offset), elfRelaEntry.Addend); + break; } + } + } + + public ElfDynamicEntry GetDynamicEntry(ElfDynamicType tag) => dynamicEntries.Find(x => x.Tag == tag); - var roDataBytes = ReadBytes((int)header.RoDataCompressedSize); - if (isRoDataCompressed) - { - var unCompressedData = new byte[header.RoDataSegment.DecompressedSize]; - using (var decoder = new Lz4DecodeStream(new MemoryStream(roDataBytes))) - { - decoder.Read(unCompressedData, 0, unCompressedData.Length); - } + public NsoFile Decompress() + { + if (!isCompressed) + return this; + + LibLogger.InfoNewline("\tDecompressing NSO file..."); - writer.Write(unCompressedData); - } - else + var unCompressedStream = new MemoryStream(); + var writer = new BinaryWriter(unCompressedStream); + writer.Write(header.Magic); + writer.Write(header.Version); + writer.Write(header.Reserved); + writer.Write(0); //Flags + writer.Write(header.TextSegment.FileOffset); + writer.Write(header.TextSegment.MemoryOffset); + writer.Write(header.TextSegment.DecompressedSize); + writer.Write(header.ModuleOffset); + var roOffset = header.TextSegment.FileOffset + header.TextSegment.DecompressedSize; + writer.Write(roOffset); //header.RoDataSegment.FileOffset + writer.Write(header.RoDataSegment.MemoryOffset); + writer.Write(header.RoDataSegment.DecompressedSize); + writer.Write(header.ModuleFileSize); + writer.Write(roOffset + header.RoDataSegment.DecompressedSize); //header.DataSegment.FileOffset + writer.Write(header.DataSegment.MemoryOffset); + writer.Write(header.DataSegment.DecompressedSize); + writer.Write(header.BssSize); + writer.Write(header.DigestBuildID); + writer.Write(header.TextCompressedSize); + writer.Write(header.RoDataCompressedSize); + writer.Write(header.DataCompressedSize); + writer.Write(header.NsoHeaderReserved); + writer.Write(header.APIInfo.RegionRoDataOffset); + writer.Write(header.APIInfo.RegionSize); + writer.Write(header.DynStr.RegionRoDataOffset); + writer.Write(header.DynStr.RegionSize); + writer.Write(header.DynSym.RegionRoDataOffset); + writer.Write(header.DynSym.RegionSize); + writer.Write(header.TextHash); + writer.Write(header.RoDataHash); + writer.Write(header.DataHash); + writer.BaseStream.Position = header.TextSegment.FileOffset; + Position = header.TextSegment.FileOffset; + var textBytes = ReadBytes((int)header.TextCompressedSize); + if (isTextCompressed) + { + var unCompressedData = new byte[header.TextSegment.DecompressedSize]; + using (var decoder = new Lz4DecodeStream(new MemoryStream(textBytes))) { - writer.Write(roDataBytes); + decoder.Read(unCompressedData, 0, unCompressedData.Length); } - var dataBytes = ReadBytes((int)header.DataCompressedSize); - if (isDataCompressed) - { - var unCompressedData = new byte[header.DataSegment.DecompressedSize]; - using (var decoder = new Lz4DecodeStream(new MemoryStream(dataBytes))) - { - decoder.Read(unCompressedData, 0, unCompressedData.Length); - } + writer.Write(unCompressedData); + } + else + { + writer.Write(textBytes); + } - writer.Write(unCompressedData); + var roDataBytes = ReadBytes((int)header.RoDataCompressedSize); + if (isRoDataCompressed) + { + var unCompressedData = new byte[header.RoDataSegment.DecompressedSize]; + using (var decoder = new Lz4DecodeStream(new MemoryStream(roDataBytes))) + { + decoder.Read(unCompressedData, 0, unCompressedData.Length); } - else + + writer.Write(unCompressedData); + } + else + { + writer.Write(roDataBytes); + } + + var dataBytes = ReadBytes((int)header.DataCompressedSize); + if (isDataCompressed) + { + var unCompressedData = new byte[header.DataSegment.DecompressedSize]; + using (var decoder = new Lz4DecodeStream(new MemoryStream(dataBytes))) { - writer.Write(dataBytes); + decoder.Read(unCompressedData, 0, unCompressedData.Length); } - writer.Flush(); - unCompressedStream.Position = 0; - return new NsoFile(unCompressedStream, maxMetadataUsages); + writer.Write(unCompressedData); + } + else + { + writer.Write(dataBytes); } - return this; + writer.Flush(); + unCompressedStream.Position = 0; + return new(unCompressedStream, maxMetadataUsages); } public override long RawLength => _raw.Length; @@ -233,11 +326,11 @@ public NsoFile Decompress() public override long MapVirtualAddressToRaw(ulong addr) { - var segment = segments.FirstOrDefault(x => addr >= x.MemoryOffset && addr <= x.MemoryOffset + x.DecompressedSize); + var segment = segments.FirstOrDefault(x => addr - NSO_GLOBAL_OFFSET >= x.MemoryOffset && addr - NSO_GLOBAL_OFFSET <= x.MemoryOffset + x.DecompressedSize); if (segment == null) throw new InvalidOperationException($"NSO: Address 0x{addr:X} is not present in any of the segments. Known segment ends are (hex) {string.Join(", ", segments.Select(s => (s.MemoryOffset + s.DecompressedSize).ToString("X")))}"); - return (long)(addr - segment.MemoryOffset + segment.FileOffset); + return (long)(addr - (segment.MemoryOffset + NSO_GLOBAL_OFFSET) + segment.FileOffset); } public override ulong MapRawAddressToVirtual(uint offset) @@ -247,7 +340,7 @@ public override ulong MapRawAddressToVirtual(uint offset) { return 0; } - return offset - segment.FileOffset + segment.MemoryOffset; + return offset - segment.FileOffset + (NSO_GLOBAL_OFFSET + segment.MemoryOffset); } public override ulong GetRVA(ulong pointer) diff --git a/LibCpp2IL/NintendoSwitch/NsoHeader.cs b/LibCpp2IL/NintendoSwitch/NsoHeader.cs index 80185be9..13df8b7b 100644 --- a/LibCpp2IL/NintendoSwitch/NsoHeader.cs +++ b/LibCpp2IL/NintendoSwitch/NsoHeader.cs @@ -16,14 +16,12 @@ public class NsoHeader public uint TextCompressedSize; public uint RoDataCompressedSize; public uint DataCompressedSize; - public byte[] Padding; + public byte[] NsoHeaderReserved; public NsoRelativeExtent APIInfo; public NsoRelativeExtent DynStr; public NsoRelativeExtent DynSym; public byte[] TextHash; public byte[] RoDataHash; public byte[] DataHash; - - public NsoSegmentHeader BssSegment; } } \ No newline at end of file diff --git a/LibCpp2IL/NintendoSwitch/NsoModHeader.cs b/LibCpp2IL/NintendoSwitch/NsoModHeader.cs new file mode 100644 index 00000000..ddadb308 --- /dev/null +++ b/LibCpp2IL/NintendoSwitch/NsoModHeader.cs @@ -0,0 +1,14 @@ +namespace LibCpp2IL.NintendoSwitch +{ + public class NsoModHeader + { + public uint ModOffset; + public uint DynamicOffset; + public uint BssStart; + public uint BssEnd; + public uint EhFrameHdrStart; + public uint EhFrameHdrEnd; + + public NsoSegmentHeader BssSegment; + } +} \ No newline at end of file From b00e8bfa4841e751434965f02c282525a29b6403 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Sun, 12 Dec 2021 17:26:30 +0000 Subject: [PATCH 05/76] Lib: Change string length error to a warning + double limit --- LibCpp2IL/LibCpp2IlUtils.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/LibCpp2IL/LibCpp2IlUtils.cs b/LibCpp2IL/LibCpp2IlUtils.cs index 6b0df4fa..d946ba63 100644 --- a/LibCpp2IL/LibCpp2IlUtils.cs +++ b/LibCpp2IL/LibCpp2IlUtils.cs @@ -4,6 +4,7 @@ using System.Reflection; using System.Text; using LibCpp2IL.BinaryStructures; +using LibCpp2IL.Logging; using LibCpp2IL.Metadata; using LibCpp2IL.Reflection; @@ -227,9 +228,9 @@ public static string GetTypeName(Il2CppMetadata metadata, Il2CppBinary cppAssemb if (LibCpp2IlMain.MetadataVersion < 29) len = metadata.ReadClassAtRawAddr(pointer); else - len = (int)metadata.ReadUnityCompressedIntAtRawAddr(pointer, out lenLen); - if (len > 1024 * 32) - throw new Exception($"Unreasonable string length {len}"); + len = metadata.ReadUnityCompressedIntAtRawAddr(pointer, out lenLen); + if (len > 1024 * 64) + LibLogger.WarnNewline("[GetDefaultValue] String length is really large: " + len); return Encoding.UTF8.GetString(metadata.ReadByteArrayAtRawAddress(pointer + lenLen, len)); default: return null; From 639957e73c2a9b6a7ba542c99d87865474a5b881 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Mon, 20 Dec 2021 16:28:52 +0000 Subject: [PATCH 06/76] Cpp2IL: Print error when capstone hook fails --- Cpp2IL/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cpp2IL/Program.cs b/Cpp2IL/Program.cs index f0b75379..b5e15687 100644 --- a/Cpp2IL/Program.cs +++ b/Cpp2IL/Program.cs @@ -297,7 +297,7 @@ public static int MainWithArgs(Cpp2IlRuntimeArgs runtimeArgs) } catch (Exception e) { - Logger.WarnNewline("Unable to hook native library resolving for Capstone. If you're not on windows and analysing an ARM or ARM64 binary, expect this to crash!"); + Logger.WarnNewline("Unable to hook native library resolving for Capstone. If you're not on windows and analysing an ARM or ARM64 binary, expect this to crash! Caused by " + e); } #endif @@ -397,4 +397,4 @@ private static IntPtr DllImportResolver(string libraryName, Assembly assembly, D return IntPtr.Zero; } } -} \ No newline at end of file +} From bb5a5cfcd9f23c327327037343c574fb930e5f54 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Tue, 21 Dec 2021 14:37:13 +0000 Subject: [PATCH 07/76] Core: Don't die if Exception#message has no calls --- Cpp2IL.Core/BaseKeyFunctionAddresses.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Cpp2IL.Core/BaseKeyFunctionAddresses.cs b/Cpp2IL.Core/BaseKeyFunctionAddresses.cs index bcbc0d41..72f0d3d8 100644 --- a/Cpp2IL.Core/BaseKeyFunctionAddresses.cs +++ b/Cpp2IL.Core/BaseKeyFunctionAddresses.cs @@ -118,6 +118,12 @@ protected void TryGetInitMetadataFromException() var disasm = X86Utils.GetMethodBodyAtVirtAddressNew(targetMethod.AsUnmanaged().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 (LibCpp2IlMain.MetadataVersion < 27) { @@ -272,4 +278,4 @@ private void FindThunks() protected abstract int GetCallerCount(ulong toWhere); } -} \ No newline at end of file +} From 4ee07d1365a4f3308988eeeea745837441d7775a Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Tue, 4 Jan 2022 19:48:46 +0000 Subject: [PATCH 08/76] Core: Don't attempt to write UnknownGlobals to attribute params --- Cpp2IL.Core/Utils/AnalysisUtils.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cpp2IL.Core/Utils/AnalysisUtils.cs b/Cpp2IL.Core/Utils/AnalysisUtils.cs index 8d373edb..97841f6b 100644 --- a/Cpp2IL.Core/Utils/AnalysisUtils.cs +++ b/Cpp2IL.Core/Utils/AnalysisUtils.cs @@ -80,6 +80,9 @@ public static long[] ReadArrayInitializerForFieldDefinition(FieldDefinition fiel if (value is Il2CppString) throw new Exception("Cannot coerce an Il2CppString. Something has gone wrong here"); + + if (value is UnknownGlobalAddr) + throw new Exception("Cannot coerce an UnknownGlobal. Something has gone wrong here"); if (coerceToType.Resolve() is { IsEnum: true } enumType) coerceToType = enumType.GetEnumUnderlyingType(); From b0af36f33fd8be302585d4c5e5eb57c99d723675 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Tue, 4 Jan 2022 20:16:50 +0000 Subject: [PATCH 09/76] Core: Skip public key tokens for now bc they cause issues --- Cpp2IL.Core/StubAssemblyBuilder.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Cpp2IL.Core/StubAssemblyBuilder.cs b/Cpp2IL.Core/StubAssemblyBuilder.cs index 9942e563..d0fe9272 100644 --- a/Cpp2IL.Core/StubAssemblyBuilder.cs +++ b/Cpp2IL.Core/StubAssemblyBuilder.cs @@ -48,9 +48,11 @@ private static AssemblyDefinition BuildStubAssembly(ModuleParameters moduleParam asmName.HashAlgorithm = (AssemblyHashAlgorithm) assemblyDefinition.AssemblyName.hash_alg; asmName.Attributes = (AssemblyAttributes) assemblyDefinition.AssemblyName.flags; asmName.Culture = assemblyDefinition.AssemblyName.Culture; - asmName.PublicKeyToken = BitConverter.GetBytes(assemblyDefinition.AssemblyName.publicKeyToken); - if (assemblyDefinition.AssemblyName.publicKeyToken == 0) - asmName.PublicKeyToken = Array.Empty(); + + //This just causes more pain than it's worth, so we comment it out + // asmName.PublicKeyToken = BitConverter.GetBytes(assemblyDefinition.AssemblyName.publicKeyToken); + // if (assemblyDefinition.AssemblyName.publicKeyToken == 0) + asmName.PublicKeyToken = Array.Empty(); // asmName.PublicKey = Encoding.UTF8.GetBytes(assemblyDefinition.AssemblyName.PublicKey); //This seems to be garbage data, e.g. "\x0\x0\x0\x0\x0\x0\x0\x0\x4\x0\x0\x0\x0\x0\x0\x0", so we skip asmName.Hash = assemblyDefinition.AssemblyName.hash_len == 0 ? Array.Empty() : Encoding.UTF8.GetBytes(assemblyDefinition.AssemblyName.HashValue); From 6c43346e95bc04bfbeb4db43b929860120d3a49b Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Tue, 4 Jan 2022 20:19:58 +0000 Subject: [PATCH 10/76] Haha what if we did a sneaky 2022.0 --- Cpp2IL.Core/Cpp2IL.Core.csproj | 8 ++++---- Cpp2IL/Properties/AssemblyInfo.cs | 6 +++--- LibCpp2IL/LibCpp2IL.csproj | 2 +- LibCpp2IL/Properties/AssemblyInfo.cs | 6 +++--- WasmDisassembler/WasmDisassembler.csproj | 8 ++++++++ 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/Cpp2IL.Core/Cpp2IL.Core.csproj b/Cpp2IL.Core/Cpp2IL.Core.csproj index edd03c4d..60dee393 100644 --- a/Cpp2IL.Core/Cpp2IL.Core.csproj +++ b/Cpp2IL.Core/Cpp2IL.Core.csproj @@ -6,10 +6,10 @@ enable Samboy063.Cpp2IL.Core Samboy063 - 2021.6.1 - 2021.6.1 - 2021.6.1 - Copyright © Samboy063 2019-2021 + 2022.0.0 + 2022.0.0 + 2022.0.0 + Copyright © Samboy063 2019-2022 true MIT git diff --git a/Cpp2IL/Properties/AssemblyInfo.cs b/Cpp2IL/Properties/AssemblyInfo.cs index 7b12bff9..dbebd291 100644 --- a/Cpp2IL/Properties/AssemblyInfo.cs +++ b/Cpp2IL/Properties/AssemblyInfo.cs @@ -9,7 +9,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Samboy063")] [assembly: AssemblyProduct("Cpp2IL")] -[assembly: AssemblyCopyright("Copyright © Samboy063 2019-2021")] +[assembly: AssemblyCopyright("Copyright © Samboy063 2019-2022")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -31,5 +31,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2021.6.1")] -[assembly: AssemblyFileVersion("2021.6.1")] \ No newline at end of file +[assembly: AssemblyVersion("2022.0.0")] +[assembly: AssemblyFileVersion("2022.0.0")] \ No newline at end of file diff --git a/LibCpp2IL/LibCpp2IL.csproj b/LibCpp2IL/LibCpp2IL.csproj index b3e0a380..29288e59 100644 --- a/LibCpp2IL/LibCpp2IL.csproj +++ b/LibCpp2IL/LibCpp2IL.csproj @@ -6,7 +6,7 @@ 9 netstandard2.0 Samboy063.LibCpp2IL - 2021.6.1 + 2022.0.0 true MIT git diff --git a/LibCpp2IL/Properties/AssemblyInfo.cs b/LibCpp2IL/Properties/AssemblyInfo.cs index 72ca059e..30a2df7a 100644 --- a/LibCpp2IL/Properties/AssemblyInfo.cs +++ b/LibCpp2IL/Properties/AssemblyInfo.cs @@ -5,11 +5,11 @@ // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyDescription("Library for reversing Unity's il2cpp build process")] -[assembly: AssemblyCopyright("Copyright © Samboy063 2019-2020")] +[assembly: AssemblyCopyright("Copyright © Samboy063 2019-2022")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("2021.6.1")] -[assembly: AssemblyFileVersion("2021.6.1")] +[assembly: AssemblyVersion("2022.0.0")] +[assembly: AssemblyFileVersion("2022.0.0")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from diff --git a/WasmDisassembler/WasmDisassembler.csproj b/WasmDisassembler/WasmDisassembler.csproj index c7f89b48..eeec2de5 100644 --- a/WasmDisassembler/WasmDisassembler.csproj +++ b/WasmDisassembler/WasmDisassembler.csproj @@ -5,6 +5,14 @@ enable enable 10 + Samboy063.WasmDisassembler + 2022.0.0 + true + MIT + git + https://github.com/SamboyCoding/Cpp2IL.git + true + Simple, zero-dependency disassembler for WebAssembly bytecode From c98aefd414abfc0a1d4c0b193e165966742d3cae Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Tue, 4 Jan 2022 21:07:07 +0000 Subject: [PATCH 11/76] Cpp2Il: Do a little more logging about unity version --- Cpp2IL.Core/Cpp2IlApi.cs | 11 +++++++++-- Cpp2IL/Program.cs | 2 ++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Cpp2IL.Core/Cpp2IlApi.cs b/Cpp2IL.Core/Cpp2IlApi.cs index 077eea44..adbdc34b 100644 --- a/Cpp2IL.Core/Cpp2IlApi.cs +++ b/Cpp2IL.Core/Cpp2IlApi.cs @@ -59,6 +59,8 @@ public static class Cpp2IlApi if (Environment.OSVersion.Platform == PlatformID.Win32NT && !string.IsNullOrEmpty(unityPlayerPath)) { var unityVer = FileVersionInfo.GetVersionInfo(unityPlayerPath); + + Logger.VerboseNewline($"Running on windows and have unity player, so using file version: {unityVer.FileMajorPart}.{unityVer.FileMinorPart}.{unityVer.FileBuildPart}"); return new[] {unityVer.FileMajorPart, unityVer.FileMinorPart, unityVer.FileBuildPart}; } @@ -70,7 +72,9 @@ public static class Cpp2IlApi if (File.Exists(globalgamemanagersPath)) { var ggmBytes = File.ReadAllBytes(globalgamemanagersPath); - return GetVersionFromGlobalGameManagers(ggmBytes); + var ret = GetVersionFromGlobalGameManagers(ggmBytes); + Logger.VerboseNewline($"Got version {ret} from globalgamemanagers"); + return ret; } //Data.unity3d @@ -78,10 +82,13 @@ public static class Cpp2IlApi if (File.Exists(dataPath)) { using var dataStream = File.OpenRead(dataPath); - return GetVersionFromDataUnity3D(dataStream); + var ret = GetVersionFromDataUnity3D(dataStream); + Logger.VerboseNewline($"Got version {ret} from data.unity3d"); + return ret; } } + Logger.VerboseNewline($"Could not determine unity version, gameDataPath is {gameDataPath}, unityPlayerPath is {unityPlayerPath}"); return null; } diff --git a/Cpp2IL/Program.cs b/Cpp2IL/Program.cs index b5e15687..db3f2dc5 100644 --- a/Cpp2IL/Program.cs +++ b/Cpp2IL/Program.cs @@ -55,8 +55,10 @@ private static void ResolvePathsFromCommandLine(string gamePath, string? inputEx $"\t{unityPlayerPath}\n" + $"\t{args.PathToMetadata}\n"); + Logger.VerboseNewline($"Found probable windows game at path: {gamePath}. Attempting to get unity version..."); var gameDataPath = Path.Combine(gamePath, $"{exeName}_Data"); var uv = Cpp2IlApi.DetermineUnityVersion(unityPlayerPath, gameDataPath); + Logger.VerboseNewline($"First-attempt unity version detection gave: {uv?.ToString() ?? "null"}"); if (uv == null) { From ec5eec8ceff44c417b728deef0db16eb9b20859e Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Thu, 6 Jan 2022 16:49:44 +0000 Subject: [PATCH 12/76] Cpp2IL: Add option for WASM name remapping Lib: Make WASM dynCall calculation a little better --- Cpp2IL.Core/Utils/WasmUtils.cs | 27 +++++++++++++++++++++++++++ Cpp2IL/CommandLineArgs.cs | 3 +++ Cpp2IL/Cpp2IlRuntimeArgs.cs | 1 + Cpp2IL/Program.cs | 19 +++++++++++++++++++ LibCpp2IL/Wasm/WasmExportSection.cs | 7 ++++++- LibCpp2IL/Wasm/WasmFile.cs | 28 +++++++++++++++++++++++++++- README.md | 1 + 7 files changed, 84 insertions(+), 2 deletions(-) diff --git a/Cpp2IL.Core/Utils/WasmUtils.cs b/Cpp2IL.Core/Utils/WasmUtils.cs index 40abfa3d..ceb3e14e 100644 --- a/Cpp2IL.Core/Utils/WasmUtils.cs +++ b/Cpp2IL.Core/Utils/WasmUtils.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; using LibCpp2IL; using LibCpp2IL.Wasm; using Mono.Cecil; @@ -10,6 +11,7 @@ namespace Cpp2IL.Core.Utils 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(MethodDefinition definition) { @@ -81,5 +83,30 @@ private static void CalculateAllMethodDefinitionIndices() 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; + } } } \ No newline at end of file diff --git a/Cpp2IL/CommandLineArgs.cs b/Cpp2IL/CommandLineArgs.cs index acfa0d53..201fe428 100644 --- a/Cpp2IL/CommandLineArgs.cs +++ b/Cpp2IL/CommandLineArgs.cs @@ -67,6 +67,9 @@ public class CommandLineArgs [Option("simple-attribute-restoration", HelpText = "Don't use analysis to restore attributes, meaning any attributes with constructor parameters won't be recovered. Has no effect on metadata v29+")] public bool SimpleAttributeRestoration { get; set; } + + [Option("wasm-framework-file", HelpText = "Path to the wasm *.framework.js file. Only needed if your binary is a WASM file. If provided, it can be used to remap obfuscated dynCall function names in order to correct method pointers.")] + public string? WasmFrameworkFilePath { get; set; } internal bool AreForceOptionsValid { diff --git a/Cpp2IL/Cpp2IlRuntimeArgs.cs b/Cpp2IL/Cpp2IlRuntimeArgs.cs index 1f44727b..496c58b0 100644 --- a/Cpp2IL/Cpp2IlRuntimeArgs.cs +++ b/Cpp2IL/Cpp2IlRuntimeArgs.cs @@ -14,6 +14,7 @@ public struct Cpp2IlRuntimeArgs public string AssemblyToRunAnalysisFor; public bool AnalyzeAllAssemblies; + public string? WasmFrameworkJsFile; //Feature flags public bool EnableAnalysis; diff --git a/Cpp2IL/Program.cs b/Cpp2IL/Program.cs index db3f2dc5..7ba05e71 100644 --- a/Cpp2IL/Program.cs +++ b/Cpp2IL/Program.cs @@ -9,10 +9,12 @@ using System.Runtime.InteropServices; using CommandLine; using Cpp2IL.Core; +using Cpp2IL.Core.Utils; #if !DEBUG using Cpp2IL.Core.Exceptions; #endif using LibCpp2IL; +using LibCpp2IL.Wasm; using Mono.Cecil; namespace Cpp2IL @@ -203,6 +205,7 @@ private static Cpp2IlRuntimeArgs GetRuntimeOptionsFromCommandLine(string[] comma result.IlToAsmContinueThroughErrors = options.ThrowSafetyOutTheWindow; result.DisableMethodDumps = options.DisableMethodDumps; result.SimpleAttributeRestoration = options.SimpleAttributeRestoration; + result.WasmFrameworkJsFile = options.WasmFrameworkFilePath; if (options.UserIsImpatient) { @@ -284,6 +287,22 @@ public static int MainWithArgs(Cpp2IlRuntimeArgs runtimeArgs) ConsoleLogger.ShowVerbose = runtimeArgs.EnableVerboseLogging; + if (runtimeArgs.WasmFrameworkJsFile != null) + try + { + var frameworkJs = File.ReadAllText(runtimeArgs.WasmFrameworkJsFile); + var remaps = WasmUtils.ExtractAndParseDynCallRemaps(frameworkJs); + Logger.InfoNewline($"Parsed {remaps.Count} dynCall remaps from {runtimeArgs.WasmFrameworkJsFile}"); + WasmFile.RemappedDynCallFunctions = remaps; + } + catch (Exception e) + { + WasmFile.RemappedDynCallFunctions = null; + Logger.WarnNewline($"Failed to parse dynCall remaps from Wasm Framework Javascript File: {e}. They will not be used, so you probably won't get method bodies!"); + } + else + WasmFile.RemappedDynCallFunctions = null; + Cpp2IlApi.InitializeLibCpp2Il(runtimeArgs.PathToAssembly, runtimeArgs.PathToMetadata, runtimeArgs.UnityVersion, runtimeArgs.EnableRegistrationPrompts); Cpp2IlApi.MakeDummyDLLs(runtimeArgs.SuppressAttributes); diff --git a/LibCpp2IL/Wasm/WasmExportSection.cs b/LibCpp2IL/Wasm/WasmExportSection.cs index 43ab8847..2ca2c154 100644 --- a/LibCpp2IL/Wasm/WasmExportSection.cs +++ b/LibCpp2IL/Wasm/WasmExportSection.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using LibCpp2IL.Logging; namespace LibCpp2IL.Wasm { @@ -12,8 +13,12 @@ internal WasmExportSection(WasmSectionId type, long pointer, ulong size, WasmFil ExportCount = file.BaseStream.ReadLEB128Unsigned(); for (var i = 0UL; i < ExportCount; i++) { - Exports.Add(new(file)); + var export = new WasmExportEntry(file); + if(export.Kind == WasmExternalKind.EXT_FUNCTION) + LibLogger.VerboseNewline($"\t\t\t- Found exported function {export.Name}"); + Exports.Add(export); } + LibLogger.VerboseNewline($"\t\tRead {Exports.Count} exported functions"); } } } \ No newline at end of file diff --git a/LibCpp2IL/Wasm/WasmFile.cs b/LibCpp2IL/Wasm/WasmFile.cs index 6f4f5cfd..807dc13f 100644 --- a/LibCpp2IL/Wasm/WasmFile.cs +++ b/LibCpp2IL/Wasm/WasmFile.cs @@ -10,6 +10,8 @@ namespace LibCpp2IL.Wasm { public sealed class WasmFile : Il2CppBinary { + public static Dictionary? RemappedDynCallFunctions; + public readonly List FunctionTable = new(); internal readonly List Sections = new(); @@ -102,7 +104,20 @@ public WasmFunctionDefinition GetFunctionFromIndexAndSignature(ulong index, stri private void CalculateDynCallOffsets() { - var codeSec = CodeSection; + //Remap any exported functions we have remaps for + + if (RemappedDynCallFunctions != null) + { + foreach (var exportSectionExport in ExportSection.Exports.Where(e => e.Kind == WasmExternalKind.EXT_FUNCTION)) + { + if (!RemappedDynCallFunctions.TryGetValue(exportSectionExport.Name, out var remappedName)) + continue; + + LibLogger.VerboseNewline($"\t\tRemapped exported function {exportSectionExport.Name} to {remappedName}"); + exportSectionExport.Name.Value = remappedName; + } + } + foreach (var exportedDynCall in ExportSection.Exports.Where(e => e.Kind == WasmExternalKind.EXT_FUNCTION && e.Name.Value.StartsWith("dynCall_"))) { var signature = exportedDynCall.Name.Value["dynCall_".Length..]; @@ -144,6 +159,17 @@ private void CalculateDynCallOffsets() andWith = (ulong) relevantInstructions[0].Operands[0]; add = (ulong) relevantInstructions[2].Operands[0]; + } else if (disassembled.All(d => d.Mnemonic is WasmMnemonic.LocalGet or WasmMnemonic.CallIndirect or WasmMnemonic.End)) + { + //No remapping + andWith = int.MaxValue; + add = 0; + } else if (disassembled[^1].Mnemonic == WasmMnemonic.End && disassembled[^2].Mnemonic == WasmMnemonic.CallIndirect && disassembled[^3].Mnemonic == WasmMnemonic.LocalGet && (byte) disassembled[^3].Operands[0] == 0) + { + //Tentatively assume we're doing shenanigans only to the params and we don't touch the index + LibLogger.WarnNewline($"\t\tAssuming index is not touched, but couldn't get a proper calculation for dynCall_{signature}. Might cause issues later down the line."); + andWith = int.MaxValue; + add = 0; } else { diff --git a/README.md b/README.md index c0b70206..30eff7fe 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ same argument as above but pass in the path to the APK, and cpp2il will extract | --skip-method-dumps | <None> | Suppress creation of method_dumps folder and files, if you don't intend to use them. | | --just-give-me-dlls-asap-dammit | <None> | Shorthand for `--parallel --skip-method-dumps --experimental-enable-il-to-assembly-please --throw-safety-out-the-window --skip-metadata-txts`, if you don't want to type that much, but still want fast, complete, IL generation | | --simple-attribute-restoration | <None> | Don't use analysis to restore attributes, meaning any attributes with constructor parameters won't be recovered. Has no effect on metadata v29+ | +| --wasm-framework-file | C:\Path\To\webgl.framework.js | Only used in conjunction with WASM binaries. Some of these have obfuscated exports but they can be recovered via a framework.js file, which you can provide the path to using this argument. | ## Release Structure From e01aef1745de665d8231af0e0cace0d1d24483df Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Sun, 30 Jan 2022 20:08:39 +0000 Subject: [PATCH 13/76] Add XAPK support, fix potential IOE in populator --- Cpp2IL.Core/AssemblyPopulator.cs | 13 +- Cpp2IL.Core/Cpp2IL.Core.csproj | 2 +- Cpp2IL/Program.cs | 261 ++++++++++++++++++++----------- 3 files changed, 182 insertions(+), 94 deletions(-) diff --git a/Cpp2IL.Core/AssemblyPopulator.cs b/Cpp2IL.Core/AssemblyPopulator.cs index 2e2ed627..e39ae616 100644 --- a/Cpp2IL.Core/AssemblyPopulator.cs +++ b/Cpp2IL.Core/AssemblyPopulator.cs @@ -181,8 +181,17 @@ private static void FixupExplicitOverridesInType(TypeDefinition ilTypeDefinition var targetParameters = currentlyFixingUp.Parameters.Select(p => p.ParameterType.FullName).ToArray(); MethodReference? baseRef; if (genericParamNames.Length == 0) - baseRef = baseType.Methods.SingleOrDefault(m => - m.Name == baseMethodName && m.Parameters.Count == currentlyFixingUp.Parameters.Count && m.ReturnType.FullName == currentlyFixingUp.ReturnType.FullName && m.Parameters.Select(p => p.ParameterType.FullName).SequenceEqual(targetParameters)); + try + { + baseRef = baseType.Methods.SingleOrDefault(m => + m.Name == baseMethodName && m.Parameters.Count == currentlyFixingUp.Parameters.Count && m.ReturnType.FullName == currentlyFixingUp.ReturnType.FullName && m.Parameters.Select(p => p.ParameterType.FullName).SequenceEqual(targetParameters)); + } + catch (InvalidOperationException) + { + //More than one match - log a warning and skip + Logger.WarnNewline($"\tMore than one potential base method for base type \"{baseType.FullName}\", method name \"{baseMethodName}\", parameter types {targetParameters.ToStringEnumerable()}, while considering explicit override {currentlyFixingUp.FullName}"); + continue; + } else { MethodDefinition nonGenericRef; diff --git a/Cpp2IL.Core/Cpp2IL.Core.csproj b/Cpp2IL.Core/Cpp2IL.Core.csproj index 60dee393..75443e22 100644 --- a/Cpp2IL.Core/Cpp2IL.Core.csproj +++ b/Cpp2IL.Core/Cpp2IL.Core.csproj @@ -23,7 +23,7 @@ - + diff --git a/Cpp2IL/Program.cs b/Cpp2IL/Program.cs index 7ba05e71..8c3c8698 100644 --- a/Cpp2IL/Program.cs +++ b/Cpp2IL/Program.cs @@ -37,131 +37,210 @@ internal class Program private static void ResolvePathsFromCommandLine(string gamePath, string? inputExeName, ref Cpp2IlRuntimeArgs args) { if (Directory.Exists(gamePath)) - { - //Windows game. - args.PathToAssembly = Path.Combine(gamePath, "GameAssembly.dll"); - var exeName = Path.GetFileNameWithoutExtension(Directory.GetFiles(gamePath) - .FirstOrDefault(f => f.EndsWith(".exe") && !BlacklistedExecutableFilenames.Any(bl => f.EndsWith(bl)))); + HandleWindowsGamePath(gamePath, inputExeName, ref args); + else if (File.Exists(gamePath) && Path.GetExtension(gamePath).ToLowerInvariant() == ".apk") + HandleSingleApk(gamePath, ref args); + else if(File.Exists(gamePath) && Path.GetExtension(gamePath).ToLowerInvariant() == ".xapk") + HandleXapk(gamePath, ref args); + else + throw new SoftException($"Could not find a valid unity game at {gamePath}"); + } + + private static void HandleWindowsGamePath(string gamePath, string? inputExeName, ref Cpp2IlRuntimeArgs args) + { + //Windows game. + args.PathToAssembly = Path.Combine(gamePath, "GameAssembly.dll"); + var exeName = Path.GetFileNameWithoutExtension(Directory.GetFiles(gamePath) + .FirstOrDefault(f => f.EndsWith(".exe") && !BlacklistedExecutableFilenames.Any(f.EndsWith))); - exeName = inputExeName ?? exeName; + exeName = inputExeName ?? exeName; - if (exeName == null) - throw new SoftException("Failed to locate any executable in the provided game directory. Make sure the path is correct, and if you *really* know what you're doing (and know it's not supported), use the force options, documented if you provide --help."); + if (exeName == null) + throw new SoftException("Failed to locate any executable in the provided game directory. Make sure the path is correct, and if you *really* know what you're doing (and know it's not supported), use the force options, documented if you provide --help."); - var unityPlayerPath = Path.Combine(gamePath, $"{exeName}.exe"); - args.PathToMetadata = Path.Combine(gamePath, $"{exeName}_Data", "il2cpp_data", "Metadata", "global-metadata.dat"); + var unityPlayerPath = Path.Combine(gamePath, $"{exeName}.exe"); + args.PathToMetadata = Path.Combine(gamePath, $"{exeName}_Data", "il2cpp_data", "Metadata", "global-metadata.dat"); - if (!File.Exists(args.PathToAssembly) || !File.Exists(unityPlayerPath) || !File.Exists(args.PathToMetadata)) - throw new SoftException("Invalid game-path or exe-name specified. Failed to find one of the following:\n" + - $"\t{args.PathToAssembly}\n" + - $"\t{unityPlayerPath}\n" + - $"\t{args.PathToMetadata}\n"); + if (!File.Exists(args.PathToAssembly) || !File.Exists(unityPlayerPath) || !File.Exists(args.PathToMetadata)) + throw new SoftException("Invalid game-path or exe-name specified. Failed to find one of the following:\n" + + $"\t{args.PathToAssembly}\n" + + $"\t{unityPlayerPath}\n" + + $"\t{args.PathToMetadata}\n"); - Logger.VerboseNewline($"Found probable windows game at path: {gamePath}. Attempting to get unity version..."); - var gameDataPath = Path.Combine(gamePath, $"{exeName}_Data"); - var uv = Cpp2IlApi.DetermineUnityVersion(unityPlayerPath, gameDataPath); - Logger.VerboseNewline($"First-attempt unity version detection gave: {uv?.ToString() ?? "null"}"); + Logger.VerboseNewline($"Found probable windows game at path: {gamePath}. Attempting to get unity version..."); + var gameDataPath = Path.Combine(gamePath, $"{exeName}_Data"); + var uv = Cpp2IlApi.DetermineUnityVersion(unityPlayerPath, gameDataPath); + Logger.VerboseNewline($"First-attempt unity version detection gave: {uv?.ToString() ?? "null"}"); + + if (uv == null) + { + Logger.Warn("Could not determine unity version, probably due to not running on windows and not having any assets files to determine it from. Enter unity version, if known, in the format of (xxxx.x.x), else nothing to fail: "); + var userInputUv = Console.ReadLine(); + uv = userInputUv?.Split('.').Select(int.Parse).ToArray(); if (uv == null) + throw new SoftException("Failed to determine unity version. If you're not running on windows, I need a globalgamemanagers file or a data.unity3d file, or you need to use the force options."); + } + + args.UnityVersion = uv; + + if (args.UnityVersion[0] < 4) + { + Logger.WarnNewline($"Fail once: Unity version of provided executable is {args.UnityVersion.ToStringEnumerable()}. This is probably not the correct version. Retrying with alternative method..."); + + var readUnityVersionFrom = Path.Combine(gameDataPath, "globalgamemanagers"); + if (File.Exists(readUnityVersionFrom)) + args.UnityVersion = Cpp2IlApi.GetVersionFromGlobalGameManagers(File.ReadAllBytes(readUnityVersionFrom)); + else { - Logger.Warn("Could not determine unity version, probably due to not running on windows and not having any assets files to determine it from. Enter unity version, if known, in the format of (xxxx.x.x), else nothing to fail: "); - var userInputUv = Console.ReadLine(); - uv = userInputUv?.Split('.').Select(int.Parse).ToArray(); - - if(uv == null) - throw new SoftException("Failed to determine unity version. If you're not running on windows, I need a globalgamemanagers file or a data.unity3d file, or you need to use the force options."); + readUnityVersionFrom = Path.Combine(gameDataPath, "data.unity3d"); + using var stream = File.OpenRead(readUnityVersionFrom); + + args.UnityVersion = Cpp2IlApi.GetVersionFromDataUnity3D(stream); } + } - args.UnityVersion = uv; + Logger.InfoNewline($"Determined game's unity version to be {string.Join(".", args.UnityVersion)}"); - if (args.UnityVersion[0] < 4) - { - Logger.WarnNewline($"Fail once: Unity version of provided executable is {args.UnityVersion.ToStringEnumerable()}. This is probably not the correct version. Retrying with alternative method..."); + if (args.UnityVersion[0] <= 4) + throw new SoftException($"Unable to determine a valid unity version (got {args.UnityVersion.ToStringEnumerable()})"); - var readUnityVersionFrom = Path.Combine(gameDataPath, "globalgamemanagers"); - if (File.Exists(readUnityVersionFrom)) - args.UnityVersion = Cpp2IlApi.GetVersionFromGlobalGameManagers(File.ReadAllBytes(readUnityVersionFrom)); - else - { - readUnityVersionFrom = Path.Combine(gameDataPath, "data.unity3d"); - using var stream = File.OpenRead(readUnityVersionFrom); + args.Valid = true; + } - args.UnityVersion = Cpp2IlApi.GetVersionFromDataUnity3D(stream); - } - } + private static void HandleSingleApk(string gamePath, ref Cpp2IlRuntimeArgs args) + { + //APK + //Metadata: assets/bin/Data/Managed/Metadata + //Binary: lib/(armeabi-v7a)|(arm64-v8a)/libil2cpp.so - Logger.InfoNewline($"Determined game's unity version to be {string.Join(".", args.UnityVersion)}"); + Logger.InfoNewline($"Attempting to extract required files from APK {gamePath}", "APK"); - if (args.UnityVersion[0] <= 4) - throw new SoftException($"Unable to determine a valid unity version (got {args.UnityVersion.ToStringEnumerable()})"); + using var stream = File.OpenRead(gamePath); + using var zipArchive = new ZipArchive(stream); - args.Valid = true; - } - else if (File.Exists(gamePath) && Path.GetExtension(gamePath).ToLowerInvariant() == ".apk") - { - //APK - //Metadata: assets/bin/Data/Managed/Metadata - //Binary: lib/(armeabi-v7a)|(arm64-v8a)/libil2cpp.so + var globalMetadata = zipArchive.Entries.FirstOrDefault(e => e.FullName.EndsWith("assets/bin/Data/Managed/Metadata/global-metadata.dat")); + var binary = zipArchive.Entries.FirstOrDefault(e => e.FullName.EndsWith("lib/x86/libil2cpp.so")); + binary ??= zipArchive.Entries.FirstOrDefault(e => e.FullName.EndsWith("lib/arm64-v8a/libil2cpp.so")); + binary ??= zipArchive.Entries.FirstOrDefault(e => e.FullName.EndsWith("lib/armeabi-v7a/libil2cpp.so")); - Logger.InfoNewline($"Attempting to extract required files from APK {gamePath}", "APK"); + var globalgamemanagers = zipArchive.Entries.FirstOrDefault(e => e.FullName.EndsWith("assets/bin/Data/globalgamemanagers")); + var dataUnity3d = zipArchive.Entries.FirstOrDefault(e => e.FullName.EndsWith("assets/bin/Data/data.unity3d")); - using var stream = File.OpenRead(gamePath); - using var zipArchive = new ZipArchive(stream); + if (binary == null) + throw new SoftException("Could not find libil2cpp.so inside the apk."); + if (globalMetadata == null) + throw new SoftException("Could not find global-metadata.dat inside the apk"); + if (globalgamemanagers == null && dataUnity3d == null) + throw new SoftException("Could not find globalgamemanagers or data.unity3d inside the apk"); - var globalMetadata = zipArchive.Entries.FirstOrDefault(e => e.FullName.EndsWith("assets/bin/Data/Managed/Metadata/global-metadata.dat")); - var binary = zipArchive.Entries.FirstOrDefault(e => e.FullName.EndsWith("lib/x86/libil2cpp.so")); - binary ??= zipArchive.Entries.FirstOrDefault(e => e.FullName.EndsWith("lib/arm64-v8a/libil2cpp.so")); - binary ??= zipArchive.Entries.FirstOrDefault(e => e.FullName.EndsWith("lib/armeabi-v7a/libil2cpp.so")); + var tempFileBinary = Path.GetTempFileName(); + var tempFileMeta = Path.GetTempFileName(); - var globalgamemanagers = zipArchive.Entries.FirstOrDefault(e => e.FullName.EndsWith("assets/bin/Data/globalgamemanagers")); - var dataUnity3d = zipArchive.Entries.FirstOrDefault(e => e.FullName.EndsWith("assets/bin/Data/data.unity3d")); + _pathsToDeleteOnExit.Add(tempFileBinary); + _pathsToDeleteOnExit.Add(tempFileMeta); - if (binary == null) - throw new SoftException("Could not find libil2cpp.so inside the apk."); - if (globalMetadata == null) - throw new SoftException("Could not find global-metadata.dat inside the apk"); - if (globalgamemanagers == null && dataUnity3d == null) - throw new SoftException("Could not find globalgamemanagers or data.unity3d inside the apk"); + Logger.InfoNewline($"Extracting APK/{binary.FullName} to {tempFileBinary}", "APK"); + binary.ExtractToFile(tempFileBinary, true); + Logger.InfoNewline($"Extracting APK/{globalMetadata.FullName} to {tempFileMeta}", "APK"); + globalMetadata.ExtractToFile(tempFileMeta, true); - var tempFileBinary = Path.GetTempFileName(); - var tempFileMeta = Path.GetTempFileName(); + args.PathToAssembly = tempFileBinary; + args.PathToMetadata = tempFileMeta; - _pathsToDeleteOnExit.Add(tempFileBinary); - _pathsToDeleteOnExit.Add(tempFileMeta); + if (globalgamemanagers != null) + { + Logger.InfoNewline("Reading globalgamemanagers to determine unity version...", "APK"); + var ggmBytes = new byte[0x40]; + using var ggmStream = globalgamemanagers.Open(); + ggmStream.Read(ggmBytes, 0, 0x40); - Logger.InfoNewline($"Extracting APK/{binary.FullName} to {tempFileBinary}", "APK"); - binary.ExtractToFile(tempFileBinary, true); - Logger.InfoNewline($"Extracting APK/{globalMetadata.FullName} to {tempFileMeta}", "APK"); - globalMetadata.ExtractToFile(tempFileMeta, true); + args.UnityVersion = Cpp2IlApi.GetVersionFromGlobalGameManagers(ggmBytes); + } + else + { + Logger.InfoNewline("Reading data.unity3d to determine unity version...", "APK"); + using var du3dStream = dataUnity3d!.Open(); - args.PathToAssembly = tempFileBinary; - args.PathToMetadata = tempFileMeta; + args.UnityVersion = Cpp2IlApi.GetVersionFromDataUnity3D(du3dStream); + } - if (globalgamemanagers != null) - { - Logger.InfoNewline("Reading globalgamemanagers to determine unity version...", "APK"); - var ggmBytes = new byte[0x40]; - using var ggmStream = globalgamemanagers.Open(); - ggmStream.Read(ggmBytes, 0, 0x40); + Logger.InfoNewline($"Determined game's unity version to be {string.Join(".", args.UnityVersion)}", "APK"); - args.UnityVersion = Cpp2IlApi.GetVersionFromGlobalGameManagers(ggmBytes); - } - else - { - Logger.InfoNewline("Reading data.unity3d to determine unity version...", "APK"); - using var du3dStream = dataUnity3d!.Open(); + args.Valid = true; + } - args.UnityVersion = Cpp2IlApi.GetVersionFromDataUnity3D(du3dStream); - } + private static void HandleXapk(string gamePath, ref Cpp2IlRuntimeArgs args) + { + //XAPK file + //Contains two APKs - one starting with `config.` and one with the package name + //The config one is architecture-specific and so contains the binary + //The other contains the metadata + + Logger.InfoNewline($"Attempting to extract required files from XAPK {gamePath}", "XAPK"); + + using var xapkStream = File.OpenRead(gamePath); + using var xapkZip = new ZipArchive(xapkStream); + + var configApk = xapkZip.Entries.FirstOrDefault(e => e.FullName.Contains("config.") && e.FullName.EndsWith(".apk")); + var mainApk = xapkZip.Entries.FirstOrDefault(e => e != configApk && e.FullName.EndsWith(".apk")); + + Logger.InfoNewline($"Identified APKs inside XAPK - config: {configApk?.FullName}, mainPackage: {mainApk?.FullName}", "XAPK"); + + if (configApk == null) + throw new SoftException("Could not find a config apk inside the XAPK"); + if (mainApk == null) + throw new SoftException("Could not find a main apk inside the XAPK"); + + using var configZip = new ZipArchive(configApk.Open()); + using var mainZip = new ZipArchive(mainApk.Open()); + var binary = configZip.Entries.FirstOrDefault(e => e.FullName.EndsWith("libil2cpp.so")); + var globalMetadata = mainZip.Entries.FirstOrDefault(e => e.FullName.EndsWith("global-metadata.dat")); + + var globalgamemanagers = mainZip.Entries.FirstOrDefault(e => e.FullName.EndsWith("globalgamemanagers")); + var dataUnity3d = mainZip.Entries.FirstOrDefault(e => e.FullName.EndsWith("data.unity3d")); + + if (binary == null) + throw new SoftException("Could not find libil2cpp.so inside the config APK"); + if(globalMetadata == null) + throw new SoftException("Could not find global-metadata.dat inside the main APK"); + if(globalgamemanagers == null && dataUnity3d == null) + throw new SoftException("Could not find globalgamemanagers or data.unity3d inside the main APK"); + + var tempFileBinary = Path.GetTempFileName(); + var tempFileMeta = Path.GetTempFileName(); + + _pathsToDeleteOnExit.Add(tempFileBinary); + _pathsToDeleteOnExit.Add(tempFileMeta); + + Logger.InfoNewline($"Extracting XAPK/{configApk.Name}/{binary.FullName} to {tempFileBinary}", "XAPK"); + binary.ExtractToFile(tempFileBinary, true); + Logger.InfoNewline($"Extracting XAPK{mainApk.Name}/{globalMetadata.FullName} to {tempFileMeta}", "XAPK"); + globalMetadata.ExtractToFile(tempFileMeta, true); - Logger.InfoNewline($"Determined game's unity version to be {string.Join(".", args.UnityVersion)}", "APK"); + args.PathToAssembly = tempFileBinary; + args.PathToMetadata = tempFileMeta; - args.Valid = true; + if (globalgamemanagers != null) + { + Logger.InfoNewline("Reading globalgamemanagers to determine unity version...", "XAPK"); + var ggmBytes = new byte[0x40]; + using var ggmStream = globalgamemanagers.Open(); + ggmStream.Read(ggmBytes, 0, 0x40); + + args.UnityVersion = Cpp2IlApi.GetVersionFromGlobalGameManagers(ggmBytes); } else { - throw new SoftException($"Could not find a valid unity game at {gamePath}"); + Logger.InfoNewline("Reading data.unity3d to determine unity version...", "XAPK"); + using var du3dStream = dataUnity3d!.Open(); + + args.UnityVersion = Cpp2IlApi.GetVersionFromDataUnity3D(du3dStream); } + + Logger.InfoNewline($"Determined game's unity version to be {string.Join(".", args.UnityVersion)}", "XAPK"); + + args.Valid = true; } private static Cpp2IlRuntimeArgs GetRuntimeOptionsFromCommandLine(string[] commandLine) From 867c9dd1c16649b03b4b132c927b7f10afbf729a Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Sun, 30 Jan 2022 20:10:50 +0000 Subject: [PATCH 14/76] Fix stupid dependency error --- Cpp2IL.Core/Cpp2IL.Core.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cpp2IL.Core/Cpp2IL.Core.csproj b/Cpp2IL.Core/Cpp2IL.Core.csproj index 75443e22..60dee393 100644 --- a/Cpp2IL.Core/Cpp2IL.Core.csproj +++ b/Cpp2IL.Core/Cpp2IL.Core.csproj @@ -23,7 +23,7 @@ - + From 362d20c4c2111233980f019a42b8077f16b41451 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Fri, 4 Feb 2022 19:00:49 +0000 Subject: [PATCH 15/76] Core: Fix error saving custom attribute params that are object arrays --- Cpp2IL.Core/AttributeRestorer.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Cpp2IL.Core/AttributeRestorer.cs b/Cpp2IL.Core/AttributeRestorer.cs index 6cf1d2ce..92ecb3e8 100644 --- a/Cpp2IL.Core/AttributeRestorer.cs +++ b/Cpp2IL.Core/AttributeRestorer.cs @@ -143,6 +143,9 @@ private static void RestoreAttributesInType(Il2CppImageDefinition imageDef, T public static List GetCustomAttributesByAttributeIndex(Il2CppImageDefinition imageDef, int attributeIndex, uint token, ModuleDefinition module, BaseKeyFunctionAddresses? keyFunctionAddresses, string warningName) { var attributes = new List(); + + if(warningName == "RingOfPainTwitchCommon.RestEntities.DrawingModerationRequest") + Debugger.Break(); //Get attributes and look for the serialize field attribute. var attributeTypeRange = LibCpp2IlMain.TheMetadata!.GetCustomAttributeData(imageDef, attributeIndex, token); @@ -581,6 +584,10 @@ private static object AllocateArray(AllocatedArray array) try { var toSet = value == null ? null : AnalysisUtils.CoerceValue(value, typeForArrayToCreateNow); + + //Objects must be wrapped in a CustomAttributeArgument of the pre-casting type. + if(typeForArrayToCreateNow.FullName == "System.Object") + toSet = new CustomAttributeArgument(MiscUtils.TryLookupTypeDefKnownNotGeneric(value!.GetType().FullName), value); arr.SetValue(toSet, index); } From cc7551d79a4da25b0d5362ae28d0d23444a25055 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Fri, 4 Feb 2022 19:01:14 +0000 Subject: [PATCH 16/76] Core: Remove debugging code --- Cpp2IL.Core/AttributeRestorer.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Cpp2IL.Core/AttributeRestorer.cs b/Cpp2IL.Core/AttributeRestorer.cs index 92ecb3e8..a611047e 100644 --- a/Cpp2IL.Core/AttributeRestorer.cs +++ b/Cpp2IL.Core/AttributeRestorer.cs @@ -143,9 +143,6 @@ private static void RestoreAttributesInType(Il2CppImageDefinition imageDef, T public static List GetCustomAttributesByAttributeIndex(Il2CppImageDefinition imageDef, int attributeIndex, uint token, ModuleDefinition module, BaseKeyFunctionAddresses? keyFunctionAddresses, string warningName) { var attributes = new List(); - - if(warningName == "RingOfPainTwitchCommon.RestEntities.DrawingModerationRequest") - Debugger.Break(); //Get attributes and look for the serialize field attribute. var attributeTypeRange = LibCpp2IlMain.TheMetadata!.GetCustomAttributeData(imageDef, attributeIndex, token); From 21a45ea108b11296fd2f5a015d8f818b743c3c5d Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Fri, 4 Feb 2022 19:07:07 +0000 Subject: [PATCH 17/76] Bump to 2022.0.1 --- Cpp2IL.Core/Cpp2IL.Core.csproj | 6 +++--- Cpp2IL/Properties/AssemblyInfo.cs | 4 ++-- LibCpp2IL/LibCpp2IL.csproj | 2 +- LibCpp2IL/Properties/AssemblyInfo.cs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cpp2IL.Core/Cpp2IL.Core.csproj b/Cpp2IL.Core/Cpp2IL.Core.csproj index 60dee393..075367c3 100644 --- a/Cpp2IL.Core/Cpp2IL.Core.csproj +++ b/Cpp2IL.Core/Cpp2IL.Core.csproj @@ -6,9 +6,9 @@ enable Samboy063.Cpp2IL.Core Samboy063 - 2022.0.0 - 2022.0.0 - 2022.0.0 + 2022.0.1 + 2022.0.1 + 2022.0.1 Copyright © Samboy063 2019-2022 true MIT diff --git a/Cpp2IL/Properties/AssemblyInfo.cs b/Cpp2IL/Properties/AssemblyInfo.cs index dbebd291..4cd4571b 100644 --- a/Cpp2IL/Properties/AssemblyInfo.cs +++ b/Cpp2IL/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2022.0.0")] -[assembly: AssemblyFileVersion("2022.0.0")] \ No newline at end of file +[assembly: AssemblyVersion("2022.0.1")] +[assembly: AssemblyFileVersion("2022.0.1")] \ No newline at end of file diff --git a/LibCpp2IL/LibCpp2IL.csproj b/LibCpp2IL/LibCpp2IL.csproj index 29288e59..d24925a8 100644 --- a/LibCpp2IL/LibCpp2IL.csproj +++ b/LibCpp2IL/LibCpp2IL.csproj @@ -6,7 +6,7 @@ 9 netstandard2.0 Samboy063.LibCpp2IL - 2022.0.0 + 2022.0.1 true MIT git diff --git a/LibCpp2IL/Properties/AssemblyInfo.cs b/LibCpp2IL/Properties/AssemblyInfo.cs index 30a2df7a..f8a757b6 100644 --- a/LibCpp2IL/Properties/AssemblyInfo.cs +++ b/LibCpp2IL/Properties/AssemblyInfo.cs @@ -8,8 +8,8 @@ [assembly: AssemblyCopyright("Copyright © Samboy063 2019-2022")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("2022.0.0")] -[assembly: AssemblyFileVersion("2022.0.0")] +[assembly: AssemblyVersion("2022.0.1")] +[assembly: AssemblyFileVersion("2022.0.1")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from From 67ffa38de536249cd7c4438f9092eec8c938b17e Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Thu, 17 Feb 2022 16:32:14 +0000 Subject: [PATCH 18/76] Delete dotnet-core-pull.yml --- .github/workflows/dotnet-core-pull.yml | 40 -------------------------- 1 file changed, 40 deletions(-) delete mode 100644 .github/workflows/dotnet-core-pull.yml diff --git a/.github/workflows/dotnet-core-pull.yml b/.github/workflows/dotnet-core-pull.yml deleted file mode 100644 index 08d618b8..00000000 --- a/.github/workflows/dotnet-core-pull.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: .NET Core - Release - -on: - pull_request: - branches: [ master ] - -jobs: - release: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Setup .NET Core - uses: actions/setup-dotnet@v1 - with: - dotnet-version: 5.0.100 - - name: Install dependencies - run: dotnet restore - - name: Build - Windows x64 - run: dotnet publish --configuration Release --no-restore -r win-x64 /p:PublishSingleFile=true /p:IncludeNativeLibrariesForSelfExtract=true - - name: Upload Win x64 Artifact - uses: actions/upload-artifact@v2 - with: - name: Cpp2IL-Win - path: ./Cpp2IL/bin/Release/net5.0/win-x64/publish/Cpp2IL.exe - - name: Build - Ubuntu x64 - run: dotnet publish --configuration Release --no-restore -r ubuntu-x64 /p:PublishSingleFile=true /p:IncludeNativeLibrariesForSelfExtract=true - - name: Upload Ubuntu x64 Artifact - uses: actions/upload-artifact@v2 - with: - name: Cpp2IL-Linux - path: ./Cpp2IL/bin/Release/net5.0/ubuntu-x64/publish/Cpp2IL - - name: Build - OSX x64 - run: dotnet publish --configuration Release --no-restore -r osx-x64 /p:PublishSingleFile=true /p:IncludeNativeLibrariesForSelfExtract=true - - name: Upload OSX x64 Artifact - uses: actions/upload-artifact@v2 - with: - name: Cpp2IL-Mac - path: ./Cpp2IL/bin/Release/net5.0/osx-x64/publish/Cpp2IL From b31c1f8e74ae864bd011e8efecb7b37d76c533e9 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Thu, 17 Feb 2022 16:33:50 +0000 Subject: [PATCH 19/76] Update build to handle pull requests --- .github/workflows/dotnet-core.yml | 44 +++---------------------------- 1 file changed, 3 insertions(+), 41 deletions(-) diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index bf42f5d6..112c9718 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -2,7 +2,9 @@ name: .NET Core - Release on: push: - branches: [ master, new-analysis ] + branches: [ new-analysis ] + pull_request: + branches: [ new-analysis ] jobs: release: @@ -61,43 +63,3 @@ jobs: with: name: Cpp2IL-Mac path: ./Cpp2IL/bin/Release/net6.0/osx-x64/publish/Cpp2IL -# - name: Create Release -# id: create_release -# uses: actions/create-release@v1 -# env: -# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -# with: -# tag_name: commit_${{ github.sha }} -# release_name: Commit ${{ github.sha }} -# draft: false -# prerelease: true -# - name: Release Windows Build -# id: release-win -# uses: actions/upload-release-asset@v1 -# env: -# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -# with: -# upload_url: ${{ steps.create_release.outputs.upload_url }} -# asset_path: ./Cpp2IL/bin/Release/net5.0/win-x64/publish/Cpp2IL.exe -# asset_name: Cpp2IL-Win.exe -# asset_content_type: application/octet-stream -# - name: Release Linux Build -# id: release-linux -# uses: actions/upload-release-asset@v1 -# env: -# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -# with: -# upload_url: ${{ steps.create_release.outputs.upload_url }} -# asset_path: ./Cpp2IL/bin/Release/net5.0/ubuntu-x64/publish/Cpp2IL -# asset_name: Cpp2IL-Linux -# asset_content_type: application/octet-stream -# - name: Release OSX Build -# id: release-osx -# uses: actions/upload-release-asset@v1 -# env: -# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -# with: -# upload_url: ${{ steps.create_release.outputs.upload_url }} -# asset_path: ./Cpp2IL/bin/Release/net5.0/osx-x64/publish/Cpp2IL -# asset_name: Cpp2IL-OSX -# asset_content_type: application/octet-stream From 45ac3b2bdf416b0b08c7df262abaa2acb8293f81 Mon Sep 17 00:00:00 2001 From: js6pak Date: Thu, 17 Feb 2022 13:06:45 +0100 Subject: [PATCH 20/76] Use capstone fork with proper packaging --- Cpp2IL.Core/Cpp2IL.Core.csproj | 2 +- Cpp2IL.Core/Cpp2IlApi.cs | 37 ---------------------------------- Cpp2IL/Program.cs | 33 ------------------------------ 3 files changed, 1 insertion(+), 71 deletions(-) diff --git a/Cpp2IL.Core/Cpp2IL.Core.csproj b/Cpp2IL.Core/Cpp2IL.Core.csproj index 075367c3..7f2a44f7 100644 --- a/Cpp2IL.Core/Cpp2IL.Core.csproj +++ b/Cpp2IL.Core/Cpp2IL.Core.csproj @@ -23,7 +23,7 @@ - + diff --git a/Cpp2IL.Core/Cpp2IlApi.cs b/Cpp2IL.Core/Cpp2IlApi.cs index adbdc34b..0704819b 100644 --- a/Cpp2IL.Core/Cpp2IlApi.cs +++ b/Cpp2IL.Core/Cpp2IlApi.cs @@ -174,7 +174,6 @@ public static void InitializeLibCpp2Il(string assemblyPath, string metadataPath, ResetInternalState(); ConfigureLib(allowUserToInputAddresses); - FixCapstoneLib(); #if !DEBUG try @@ -203,7 +202,6 @@ public static void InitializeLibCpp2Il(byte[] assemblyData, byte[] metadataData, ResetInternalState(); ConfigureLib(allowUserToInputAddresses); - FixCapstoneLib(); try { @@ -700,40 +698,5 @@ private static void CheckLibInitialized() if (!IsLibInitialized()) throw new LibraryNotInitializedException(); } - - private static void FixCapstoneLib() - { - if (Environment.OSVersion.Platform == PlatformID.Win32NT) - return; - - //Capstone is super stupid and randomly fails to load on non-windows platforms. Fix it. - var runningFrom = AppContext.BaseDirectory; - var capstonePath = Path.Combine(runningFrom, "Gee.External.Capstone.dll"); - - if (!File.Exists(capstonePath)) - { - Logger.InfoNewline("Detected that Capstone's Managed assembly is missing. Attempting to copy the windows one..."); - var fallbackPath = Path.Combine(runningFrom, "runtimes", "win-x64", "lib", "netstandard2.0", "Gee.External.Capstone.dll"); - - if (!File.Exists(fallbackPath)) - { - Logger.WarnNewline($"Couldn't find it at {fallbackPath}. Your application will probably now throw an exception due to it being missing."); - return; - } - - File.Copy(fallbackPath, capstonePath); - } - - var loaded = Assembly.LoadFile(capstonePath); - Logger.InfoNewline("Loaded capstone: " + loaded.FullName); - - AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => - { - if (args.Name == loaded.FullName) - return loaded; - - return null; - }; - } } } \ No newline at end of file diff --git a/Cpp2IL/Program.cs b/Cpp2IL/Program.cs index 8c3c8698..ab5e11fb 100644 --- a/Cpp2IL/Program.cs +++ b/Cpp2IL/Program.cs @@ -386,21 +386,6 @@ public static int MainWithArgs(Cpp2IlRuntimeArgs runtimeArgs) Cpp2IlApi.MakeDummyDLLs(runtimeArgs.SuppressAttributes); -#if NET6_0 - //Fix capstone native library loading on non-windows - - try - { - var allInstructionsField = typeof(Arm64KeyFunctionAddresses).GetField("_allInstructions", BindingFlags.Instance | BindingFlags.NonPublic); - var arm64InstructionType = allInstructionsField!.FieldType.GenericTypeArguments.First(); - NativeLibrary.SetDllImportResolver(arm64InstructionType.Assembly, DllImportResolver); - } - catch (Exception e) - { - Logger.WarnNewline("Unable to hook native library resolving for Capstone. If you're not on windows and analysing an ARM or ARM64 binary, expect this to crash! Caused by " + e); - } -#endif - if (runtimeArgs.EnableMetadataGeneration) Cpp2IlApi.GenerateMetadataForAllAssemblies(runtimeArgs.OutputRootDirectory); @@ -478,23 +463,5 @@ private static void DoAnalysisForAssembly(string assemblyName, AnalysisLevel ana if (doIlToAsm) Cpp2IlApi.SaveAssemblies(rootDir, new List {targetAssembly}); } - - - private static IntPtr DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) - { - if (libraryName == "capstone") - { - // On linux, try .so.4 - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { -#if NET6_0 - return NativeLibrary.Load("lib" + libraryName + ".so.4", assembly, searchPath); -#endif - } - } - - // Otherwise, fallback to default import resolver. - return IntPtr.Zero; - } } } From fee377e6cf0eebf170b6f72eb895092165e68fc2 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Thu, 17 Feb 2022 16:44:43 +0000 Subject: [PATCH 21/76] Update WasmInstruction.cs --- WasmDisassembler/WasmInstruction.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/WasmDisassembler/WasmInstruction.cs b/WasmDisassembler/WasmInstruction.cs index 54d6daad..ac290047 100644 --- a/WasmDisassembler/WasmInstruction.cs +++ b/WasmDisassembler/WasmInstruction.cs @@ -6,6 +6,11 @@ public struct WasmInstruction public uint NextIp; public WasmMnemonic Mnemonic; public object[] Operands = Array.Empty(); + + public WasmInstruction() + { + //Required constructor bc microsoft silently broke structs + } public override string ToString() { @@ -14,4 +19,4 @@ public override string ToString() return $"0x{Ip:X} {Mnemonic} {string.Join(", ", Operands)}"; } -} \ No newline at end of file +} From 2cb5c3e7223be6a31374f321de55920e7190cff8 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Thu, 17 Feb 2022 16:50:02 +0000 Subject: [PATCH 22/76] wasm: fix stupid microsoft compiler breaking change --- WasmDisassembler/Disassembler.cs | 3 +++ WasmDisassembler/WasmInstruction.cs | 7 +------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/WasmDisassembler/Disassembler.cs b/WasmDisassembler/Disassembler.cs index 1a7f12b5..6633ec94 100644 --- a/WasmDisassembler/Disassembler.cs +++ b/WasmDisassembler/Disassembler.cs @@ -34,7 +34,10 @@ private static WasmInstruction ReadInstruction(this BinaryReader reader, WasmMne var opTypes = mnemonic.GetOperandTypes(); if (opTypes.Length == 0) + { + ret.Operands = Array.Empty(); return ret; + } ret.Operands = opTypes.Select(reader.ReadPrimitive).ToArray(); diff --git a/WasmDisassembler/WasmInstruction.cs b/WasmDisassembler/WasmInstruction.cs index ac290047..b3a34ab4 100644 --- a/WasmDisassembler/WasmInstruction.cs +++ b/WasmDisassembler/WasmInstruction.cs @@ -5,12 +5,7 @@ public struct WasmInstruction public uint Ip; public uint NextIp; public WasmMnemonic Mnemonic; - public object[] Operands = Array.Empty(); - - public WasmInstruction() - { - //Required constructor bc microsoft silently broke structs - } + public object[] Operands; public override string ToString() { From f27d45090d9c7411e7e3d9f66322e3941edecc4b Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Fri, 18 Feb 2022 18:14:01 +0000 Subject: [PATCH 23/76] Lib: Fix reading of unity 2018.4.34-2018.4.36 --- LibCpp2IL/BinaryStructures/Il2CppCodeRegistration.cs | 4 ++-- LibCpp2IL/Metadata/Il2CppAssemblyNameDefinition.cs | 4 +++- LibCpp2IL/Metadata/Il2CppGlobalMetadataHeader.cs | 4 ++-- LibCpp2IL/Metadata/Il2CppMetadata.cs | 2 ++ LibCpp2IL/Metadata/Il2CppMethodDefinition.cs | 10 +++++----- LibCpp2IL/Metadata/Il2CppTypeDefinition.cs | 4 ++-- 6 files changed, 16 insertions(+), 12 deletions(-) diff --git a/LibCpp2IL/BinaryStructures/Il2CppCodeRegistration.cs b/LibCpp2IL/BinaryStructures/Il2CppCodeRegistration.cs index 9b250575..f20dec27 100644 --- a/LibCpp2IL/BinaryStructures/Il2CppCodeRegistration.cs +++ b/LibCpp2IL/BinaryStructures/Il2CppCodeRegistration.cs @@ -3,8 +3,8 @@ namespace LibCpp2IL.BinaryStructures public class Il2CppCodeRegistration { - [Version(Max = 24.1f)] public ulong methodPointersCount; - [Version(Max = 24.1f)] public ulong methodPointers; + [Version(Max = 24.15f)] public ulong methodPointersCount; + [Version(Max = 24.15f)] public ulong methodPointers; public ulong reversePInvokeWrapperCount; public ulong reversePInvokeWrappers; diff --git a/LibCpp2IL/Metadata/Il2CppAssemblyNameDefinition.cs b/LibCpp2IL/Metadata/Il2CppAssemblyNameDefinition.cs index 4f29a2f4..38503d18 100644 --- a/LibCpp2IL/Metadata/Il2CppAssemblyNameDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppAssemblyNameDefinition.cs @@ -7,7 +7,9 @@ public class Il2CppAssemblyNameDefinition { public int nameIndex; public int cultureIndex; - [Version(Max = 24.3f)] public int hashValueIndex; + [Version(Max = 24.1f)] + [Version(Min = 24.2f, Max=24.3f)] //Not present in 24.15 + public int hashValueIndex; public int publicKeyIndex; public uint hash_alg; public int hash_len; diff --git a/LibCpp2IL/Metadata/Il2CppGlobalMetadataHeader.cs b/LibCpp2IL/Metadata/Il2CppGlobalMetadataHeader.cs index 00759418..dea6c0d7 100644 --- a/LibCpp2IL/Metadata/Il2CppGlobalMetadataHeader.cs +++ b/LibCpp2IL/Metadata/Il2CppGlobalMetadataHeader.cs @@ -44,8 +44,8 @@ public class Il2CppGlobalMetadataHeader public int interfaceOffsetsCount; public int typeDefinitionsOffset; // Il2CppTypeDefinition public int typeDefinitionsCount; - [Version(Max = 24.1f)] public int rgctxEntriesOffset; // Il2CppRGCTXDefinition - [Version(Max = 24.1f)] public int rgctxEntriesCount; + [Version(Max = 24.15f)] public int rgctxEntriesOffset; // Il2CppRGCTXDefinition + [Version(Max = 24.15f)] public int rgctxEntriesCount; public int imagesOffset; // Il2CppImageDefinition public int imagesCount; public int assembliesOffset; // Il2CppAssemblyDefinition diff --git a/LibCpp2IL/Metadata/Il2CppMetadata.cs b/LibCpp2IL/Metadata/Il2CppMetadata.cs index 1564d45b..967d3c83 100644 --- a/LibCpp2IL/Metadata/Il2CppMetadata.cs +++ b/LibCpp2IL/Metadata/Il2CppMetadata.cs @@ -91,6 +91,8 @@ public class Il2CppMetadata : ClassReadingBinaryReader actualVersion = 24.3f; //2019.3.7 introduces v24.3 else if (unityVersion.IsGreaterEqual(2019)) actualVersion = 24.2f; //2019.1.0 introduces v24.2 + else if (unityVersion.IsGreaterEqual(2018, 4, 34)) + actualVersion = 24.15f; //2018.4.34 made a tiny little change which just removes HashValueIndex from AssemblyNameDefinition else if (unityVersion.IsGreaterEqual(2018, 3)) actualVersion = 24.1f; //2018.3.0 introduces v24.1 else diff --git a/LibCpp2IL/Metadata/Il2CppMethodDefinition.cs b/LibCpp2IL/Metadata/Il2CppMethodDefinition.cs index e41ef3be..1f42deab 100644 --- a/LibCpp2IL/Metadata/Il2CppMethodDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppMethodDefinition.cs @@ -16,11 +16,11 @@ public class Il2CppMethodDefinition public int parameterStart; [Version(Max = 24)] public int customAttributeIndex; public int genericContainerIndex; - [Version(Max = 24.1f)] public int methodIndex; - [Version(Max = 24.1f)] public int invokerIndex; - [Version(Max = 24.1f)] public int delegateWrapperIndex; - [Version(Max = 24.1f)] public int rgctxStartIndex; - [Version(Max = 24.1f)] public int rgctxCount; + [Version(Max = 24.15f)] public int methodIndex; + [Version(Max = 24.15f)] public int invokerIndex; + [Version(Max = 24.15f)] public int delegateWrapperIndex; + [Version(Max = 24.15f)] public int rgctxStartIndex; + [Version(Max = 24.15f)] public int rgctxCount; public uint token; public ushort flags; public ushort iflags; diff --git a/LibCpp2IL/Metadata/Il2CppTypeDefinition.cs b/LibCpp2IL/Metadata/Il2CppTypeDefinition.cs index ae2d6163..9ca5aa01 100644 --- a/LibCpp2IL/Metadata/Il2CppTypeDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppTypeDefinition.cs @@ -20,8 +20,8 @@ public class Il2CppTypeDefinition public int parentIndex; public int elementTypeIndex; // we can probably remove this one. Only used for enums - [Version(Max = 24.1f)] public int rgctxStartIndex; - [Version(Max = 24.1f)] public int rgctxCount; + [Version(Max = 24.15f)] public int rgctxStartIndex; + [Version(Max = 24.15f)] public int rgctxCount; public int genericContainerIndex; From d703f93eeac0ea2f486dc825e0c7b82a6fa2d1d8 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Sun, 20 Feb 2022 21:28:04 +0000 Subject: [PATCH 24/76] Lib: Add an initial backtrack to v27 codegen modules list to increase perf --- LibCpp2IL/BinarySearcher.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/LibCpp2IL/BinarySearcher.cs b/LibCpp2IL/BinarySearcher.cs index 43743f13..5241035c 100644 --- a/LibCpp2IL/BinarySearcher.cs +++ b/LibCpp2IL/BinarySearcher.cs @@ -147,18 +147,24 @@ internal ulong FindCodeRegistrationPost2019() else { //but in v27 it's close to the LAST codegen module (winrt.dll is an exception), so we need to work back until we find an xref. - var sanityCheckNumberOfModules = 200; + var sanityCheckNumberOfModules = 200UL; var pSomewhereInCodegenModules = pMscorlibCodegenEntryInCodegenModulesList.AsEnumerable(); - for (var backtrack = 0; backtrack < sanityCheckNumberOfModules && (pCodegenModules?.Count() ?? 0) != 1; backtrack++) + var numModuleDefs = LibCpp2IlMain.TheMetadata!.imageDefinitions.Length; + var initialBacktrack = (ulong) numModuleDefs - 5L; + + pSomewhereInCodegenModules = pSomewhereInCodegenModules.Select(va => va - ptrSize * initialBacktrack); + + //Slightly experimental, but we're gonna try backtracking most of the way through the number of modules. Not all the way because we don't want to overshoot. + for (var backtrack = initialBacktrack; backtrack < sanityCheckNumberOfModules && (pCodegenModules?.Count() ?? 0) != 1; backtrack++) { pCodegenModules = FindAllMappedWords(pSomewhereInCodegenModules).ToList(); //Sanity check the count, which is one pointer back if (pCodegenModules.Count == 1) { - var moduleCount = _binary.ReadClassAtVirtualAddress(pCodegenModules.First() - ptrSize); + var moduleCount = _binary.ReadClassAtVirtualAddress(pCodegenModules.First() - ptrSize); - if (moduleCount < 0 || moduleCount > sanityCheckNumberOfModules) + if (moduleCount > sanityCheckNumberOfModules) pCodegenModules = new(); } From e4e29d9f1d694946da39b2deb7b1cf2e7cfccbd2 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Thu, 24 Feb 2022 22:40:26 +0000 Subject: [PATCH 25/76] Core: DisposeAndCleanupAll. Lib: Dispose of streams. Bump to .0.2 --- Cpp2IL.Core/Cpp2IL.Core.csproj | 6 +++--- Cpp2IL.Core/Cpp2IlApi.cs | 9 ++++++--- Cpp2IL/Properties/AssemblyInfo.cs | 4 ++-- LibCpp2IL/LibCpp2IL.csproj | 2 +- LibCpp2IL/LibCpp2IlMain.cs | 6 ++++++ LibCpp2IL/Properties/AssemblyInfo.cs | 4 ++-- WasmDisassembler/WasmDisassembler.csproj | 2 +- 7 files changed, 21 insertions(+), 12 deletions(-) diff --git a/Cpp2IL.Core/Cpp2IL.Core.csproj b/Cpp2IL.Core/Cpp2IL.Core.csproj index 7f2a44f7..fc6aae7c 100644 --- a/Cpp2IL.Core/Cpp2IL.Core.csproj +++ b/Cpp2IL.Core/Cpp2IL.Core.csproj @@ -6,9 +6,9 @@ enable Samboy063.Cpp2IL.Core Samboy063 - 2022.0.1 - 2022.0.1 - 2022.0.1 + 2022.0.2 + 2022.0.2 + 2022.0.2 Copyright © Samboy063 2019-2022 true MIT diff --git a/Cpp2IL.Core/Cpp2IlApi.cs b/Cpp2IL.Core/Cpp2IlApi.cs index 0704819b..5f47c1a0 100644 --- a/Cpp2IL.Core/Cpp2IlApi.cs +++ b/Cpp2IL.Core/Cpp2IlApi.cs @@ -171,7 +171,7 @@ public static void InitializeLibCpp2Il(string assemblyPath, string metadataPath, public static void InitializeLibCpp2Il(string assemblyPath, string metadataPath, int[] unityVersion, bool allowUserToInputAddresses = false) { if (IsLibInitialized()) - ResetInternalState(); + DisposeAndCleanupAll(); ConfigureLib(allowUserToInputAddresses); @@ -199,7 +199,7 @@ public static void InitializeLibCpp2Il(byte[] assemblyData, byte[] metadataData, public static void InitializeLibCpp2Il(byte[] assemblyData, byte[] metadataData, int[] unityVersion, bool allowUserToInputAddresses = false) { if (IsLibInitialized()) - ResetInternalState(); + DisposeAndCleanupAll(); ConfigureLib(allowUserToInputAddresses); @@ -216,7 +216,10 @@ public static void InitializeLibCpp2Il(byte[] assemblyData, byte[] metadataData, } } - private static void ResetInternalState() + /// + /// Clears all internal caches, lists, references, etc, disposes of the MemoryStream for the binary and metadata, and resets the state of the library. + /// + public static void DisposeAndCleanupAll() { SharedState.Clear(); diff --git a/Cpp2IL/Properties/AssemblyInfo.cs b/Cpp2IL/Properties/AssemblyInfo.cs index 4cd4571b..80422349 100644 --- a/Cpp2IL/Properties/AssemblyInfo.cs +++ b/Cpp2IL/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2022.0.1")] -[assembly: AssemblyFileVersion("2022.0.1")] \ No newline at end of file +[assembly: AssemblyVersion("2022.0.2")] +[assembly: AssemblyFileVersion("2022.0.2")] \ No newline at end of file diff --git a/LibCpp2IL/LibCpp2IL.csproj b/LibCpp2IL/LibCpp2IL.csproj index d24925a8..512ff35f 100644 --- a/LibCpp2IL/LibCpp2IL.csproj +++ b/LibCpp2IL/LibCpp2IL.csproj @@ -6,7 +6,7 @@ 9 netstandard2.0 Samboy063.LibCpp2IL - 2022.0.1 + 2022.0.2 true MIT git diff --git a/LibCpp2IL/LibCpp2IlMain.cs b/LibCpp2IL/LibCpp2IlMain.cs index 5fd8e51c..b7c3b1b2 100644 --- a/LibCpp2IL/LibCpp2IlMain.cs +++ b/LibCpp2IL/LibCpp2IlMain.cs @@ -33,6 +33,12 @@ public static void Reset() LibCpp2IlGlobalMapper.Reset(); LibCpp2ILUtils.Reset(); MethodsByPtr.Clear(); + + MetadataVersion = 0f; + Binary?.Dispose(); + TheMetadata?.Dispose(); + Binary = null; + TheMetadata = null; } public static List? GetManagedMethodImplementationsAtAddress(ulong addr) diff --git a/LibCpp2IL/Properties/AssemblyInfo.cs b/LibCpp2IL/Properties/AssemblyInfo.cs index f8a757b6..f4ffc1c1 100644 --- a/LibCpp2IL/Properties/AssemblyInfo.cs +++ b/LibCpp2IL/Properties/AssemblyInfo.cs @@ -8,8 +8,8 @@ [assembly: AssemblyCopyright("Copyright © Samboy063 2019-2022")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("2022.0.1")] -[assembly: AssemblyFileVersion("2022.0.1")] +[assembly: AssemblyVersion("2022.0.2")] +[assembly: AssemblyFileVersion("2022.0.2")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from diff --git a/WasmDisassembler/WasmDisassembler.csproj b/WasmDisassembler/WasmDisassembler.csproj index eeec2de5..3a385a05 100644 --- a/WasmDisassembler/WasmDisassembler.csproj +++ b/WasmDisassembler/WasmDisassembler.csproj @@ -6,7 +6,7 @@ enable 10 Samboy063.WasmDisassembler - 2022.0.0 + 2022.0.2 true MIT git From e21e2cd311b0d93db2ea3d166a1d7d12d0b8b8b7 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Thu, 24 Feb 2022 22:57:17 +0000 Subject: [PATCH 26/76] CI: Specify runtime identifier on net472 --- .github/workflows/dotnet-core.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index 112c9718..75e63fdc 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -27,7 +27,7 @@ jobs: run: dotnet publish /p:Configuration=Release /p:TargetFramework=net6.0 --no-restore -r win-x64 - name: Build - Net Framework 4.8, Windows working-directory: ./Cpp2IL/ - run: dotnet publish /p:Configuration=Release /p:TargetFramework=net472 --no-restore + run: dotnet publish /p:Configuration=Release /p:TargetFramework=net472 --no-restore -r win-x64 - name: Build - Ubuntu x64 working-directory: ./Cpp2IL/ run: dotnet publish /p:Configuration=Release /p:TargetFramework=net6.0 --no-restore -r ubuntu-x64 From 216856de0f8ec5570f72ba2dc7bf34fd3a1ff1ac Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Thu, 24 Feb 2022 23:03:09 +0000 Subject: [PATCH 27/76] CI: Fix upload path for net472 --- .github/workflows/dotnet-core.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index 75e63fdc..ec73f976 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -52,7 +52,7 @@ jobs: uses: actions/upload-artifact@v2 with: name: Cpp2IL-Netframework472-Windows - path: ./Cpp2IL/bin/Release/net472/publish/ + path: ./Cpp2IL/bin/Release/net472/win-x64/publish/ - name: Upload Ubuntu x64 Artifact uses: actions/upload-artifact@v2 with: From dda3bc23b07b3c1a03473947d2e00e1d52be1fdd Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Wed, 2 Mar 2022 22:28:46 +0000 Subject: [PATCH 28/76] Core: AttrRes: Big performance improvements for simple mode --- Cpp2IL.Core/AttributeRestorer.cs | 28 +++++++++++++------ Cpp2IL.Core/AttributeRestorerPost29.cs | 9 ------ Cpp2IL.Core/TokenComparer.cs | 15 ++++++++++ LibCpp2IL/IIl2CppTokenProvider.cs | 7 +++++ .../Il2CppCustomAttributeDataRange.cs | 4 ++- .../Il2CppCustomAttributeTypeRange.cs | 4 ++- 6 files changed, 48 insertions(+), 19 deletions(-) create mode 100644 Cpp2IL.Core/TokenComparer.cs create mode 100644 LibCpp2IL/IIl2CppTokenProvider.cs diff --git a/Cpp2IL.Core/AttributeRestorer.cs b/Cpp2IL.Core/AttributeRestorer.cs index a611047e..f74dd0e5 100644 --- a/Cpp2IL.Core/AttributeRestorer.cs +++ b/Cpp2IL.Core/AttributeRestorer.cs @@ -32,6 +32,8 @@ public static class AttributeRestorer internal static readonly TypeDefinition DummyTypeDefForAttributeList = new TypeDefinition("dummy", "AttributeList", TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit); private static readonly ConcurrentDictionary FieldToParameterMappings = new ConcurrentDictionary(); + private static List _sortedTypeRangeList; + private static Dictionary _attributeAttributeCtorsByModule = new(); static AttributeRestorer() => Initialize(); @@ -68,6 +70,9 @@ private static void Initialize() }; SharedState.FieldsByType[DummyTypeDefForAttributeList] = new List(); + + _sortedTypeRangeList = LibCpp2IlMain.TheMetadata!.attributeTypeRanges.ToList(); + _sortedTypeRangeList.SortByExtractedKey(r => r.token); } /// @@ -86,6 +91,10 @@ internal static void Reset() } _simpleFieldToPropertyCache.Clear(); + + _sortedTypeRangeList.Clear(); + + _attributeAttributeCtorsByModule.Clear(); Initialize(); } @@ -120,7 +129,7 @@ private static void RestoreAttributesInType(Il2CppImageDefinition imageDef, T { var methodDefinition = methodDef.AsManaged(); - GetCustomAttributesByAttributeIndex(imageDef, methodDef.customAttributeIndex, methodDef.token, typeDefinition.Module, keyFunctionAddresses, methodDefinition.FullName) + GetCustomAttributesByAttributeIndex(imageDef, methodDef.customAttributeIndex, methodDef.token, typeDefinition.Module, keyFunctionAddresses, methodDef.DeclaringType!.FullName + "::" + methodDefinition.FullName) .ForEach(attribute => methodDefinition.CustomAttributes.Add(attribute)); } @@ -147,7 +156,7 @@ public static List GetCustomAttributesByAttributeIndex(Il2Cp //Get attributes and look for the serialize field attribute. var attributeTypeRange = LibCpp2IlMain.TheMetadata!.GetCustomAttributeData(imageDef, attributeIndex, token); - if (attributeTypeRange == null) + if (attributeTypeRange == null || attributeTypeRange.count == 0) return attributes; //This exists only as a guideline for if we should run analysis or not, and a fallback in case we cannot. @@ -164,7 +173,7 @@ public static List GetCustomAttributesByAttributeIndex(Il2Cp #else var mustRunAnalysis = attributeConstructors.Any(c => c.HasParameters); - + //Grab generator for this context - be it field, type, method, etc. var attributeGeneratorAddress = GetAddressOfAttributeGeneratorFunction(imageDef, attributeTypeRange); @@ -368,12 +377,15 @@ private static List GenerateAttributesWithoutAnalysis(List t.Namespace == AssemblyPopulator.InjectedNamespaceName && t.Name == "AttributeAttribute"); + if (!_attributeAttributeCtorsByModule.TryGetValue(module, out var attributeCtor)) + { + var attributeType = module.Types.SingleOrDefault(t => t.Namespace == AssemblyPopulator.InjectedNamespaceName && t.Name == "AttributeAttribute"); - if (attributeType == null) - return null; + if (attributeType == null) + return null; - var attributeCtor = attributeType.GetConstructors().First(); + attributeCtor = _attributeAttributeCtorsByModule[module] = attributeType.GetConstructors().First(); + } var ca = new CustomAttribute(attributeCtor); var name = new CustomAttributeNamedArgument("Name", new(module.ImportReference(TypeDefinitions.String), constructor.DeclaringType.Name)); @@ -429,7 +441,7 @@ private static List GetAttributesFromRange(Il2CppCustomAttribute private static ulong GetAddressOfAttributeGeneratorFunction(Il2CppImageDefinition imageDef, Il2CppCustomAttributeTypeRange attributeTypeRange) { - var rangeIndex = Array.IndexOf(LibCpp2IlMain.TheMetadata!.attributeTypeRanges, attributeTypeRange); + var rangeIndex = _sortedTypeRangeList.BinarySearch(attributeTypeRange, new TokenComparer()); if (rangeIndex < 0) { diff --git a/Cpp2IL.Core/AttributeRestorerPost29.cs b/Cpp2IL.Core/AttributeRestorerPost29.cs index 9e787662..2b650788 100644 --- a/Cpp2IL.Core/AttributeRestorerPost29.cs +++ b/Cpp2IL.Core/AttributeRestorerPost29.cs @@ -521,14 +521,5 @@ private static Il2CppTypeEnum ReadBlobType(long pos, out int bytesRead, out Type Il2CppTypeEnum.IL2CPP_TYPE_IL2CPP_TYPE_INDEX => TypeDefinitions.Type.AsUnmanaged(), _ => throw new ArgumentOutOfRangeException(nameof(typeEnum), typeEnum, null) }; - - public class TokenComparer : IComparer - { - public int Compare(Il2CppCustomAttributeDataRange x, Il2CppCustomAttributeDataRange y) - { - if (ReferenceEquals(x, y)) return 0; - return x.token.CompareTo(y.token); - } - } } } \ No newline at end of file diff --git a/Cpp2IL.Core/TokenComparer.cs b/Cpp2IL.Core/TokenComparer.cs new file mode 100644 index 00000000..b89a8d35 --- /dev/null +++ b/Cpp2IL.Core/TokenComparer.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using LibCpp2IL; +using LibCpp2IL.Metadata; + +namespace Cpp2IL.Core +{ + public class TokenComparer : IComparer + { + public int Compare(IIl2CppTokenProvider x, IIl2CppTokenProvider y) + { + if (ReferenceEquals(x, y)) return 0; + return x.Token.CompareTo(y.Token); + } + } +} \ No newline at end of file diff --git a/LibCpp2IL/IIl2CppTokenProvider.cs b/LibCpp2IL/IIl2CppTokenProvider.cs new file mode 100644 index 00000000..b00ba001 --- /dev/null +++ b/LibCpp2IL/IIl2CppTokenProvider.cs @@ -0,0 +1,7 @@ +namespace LibCpp2IL +{ + public interface IIl2CppTokenProvider + { + public uint Token { get; } + } +} \ No newline at end of file diff --git a/LibCpp2IL/Metadata/Il2CppCustomAttributeDataRange.cs b/LibCpp2IL/Metadata/Il2CppCustomAttributeDataRange.cs index 9d2e933c..c945ab31 100644 --- a/LibCpp2IL/Metadata/Il2CppCustomAttributeDataRange.cs +++ b/LibCpp2IL/Metadata/Il2CppCustomAttributeDataRange.cs @@ -1,9 +1,11 @@ namespace LibCpp2IL.Metadata { - public class Il2CppCustomAttributeDataRange + public class Il2CppCustomAttributeDataRange : IIl2CppTokenProvider { //Since v29 public uint token; public uint startOffset; + + public uint Token => token; } } \ No newline at end of file diff --git a/LibCpp2IL/Metadata/Il2CppCustomAttributeTypeRange.cs b/LibCpp2IL/Metadata/Il2CppCustomAttributeTypeRange.cs index 2936a8e0..d1a435aa 100644 --- a/LibCpp2IL/Metadata/Il2CppCustomAttributeTypeRange.cs +++ b/LibCpp2IL/Metadata/Il2CppCustomAttributeTypeRange.cs @@ -1,9 +1,11 @@ namespace LibCpp2IL.Metadata { - public class Il2CppCustomAttributeTypeRange + public class Il2CppCustomAttributeTypeRange : IIl2CppTokenProvider { [Version(Min = 24.1f)] public uint token; public int start; [Version(Max = 27.1f)] public int count; + + public uint Token => token; } } \ No newline at end of file From 52c5bae10be4bda74564164dfe81285d48cf81a3 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Fri, 18 Mar 2022 10:02:31 +0000 Subject: [PATCH 29/76] Core: Minor improvements to local saving and performance --- Cpp2IL.Core/Analysis/AsmAnalyzerBase.cs | 5 ++++- Cpp2IL.Core/Cpp2IlHarmonyPatches.cs | 11 +++++++++++ .../GenericInstanceWriteFailedException.cs | 13 +++++++++++++ Cpp2IL/Cpp2IL.csproj | 1 + 4 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 Cpp2IL.Core/Exceptions/GenericInstanceWriteFailedException.cs diff --git a/Cpp2IL.Core/Analysis/AsmAnalyzerBase.cs b/Cpp2IL.Core/Analysis/AsmAnalyzerBase.cs index 0ee0d65b..bdac2453 100644 --- a/Cpp2IL.Core/Analysis/AsmAnalyzerBase.cs +++ b/Cpp2IL.Core/Analysis/AsmAnalyzerBase.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using Cpp2IL.Core.Analysis.ResultModels; @@ -129,7 +130,9 @@ public StringBuilder BuildILToString() varType = processor.ImportRecursive(git, MethodDefinition); if (varType is ArrayType arr && MiscUtils.GetUltimateElementType(arr).IsGenericParameter) throw new InvalidOperationException(); - + if(varType is ArrayType { ElementType: GenericInstanceType git3}) + varType = processor.ImportRecursive(git3, MethodDefinition).MakeArrayType(); + localDefinition.Variable = new VariableDefinition(processor.ImportReference(varType, MethodDefinition)); body.Variables.Add(localDefinition.Variable); } diff --git a/Cpp2IL.Core/Cpp2IlHarmonyPatches.cs b/Cpp2IL.Core/Cpp2IlHarmonyPatches.cs index 57e5cd18..c1bb3986 100644 --- a/Cpp2IL.Core/Cpp2IlHarmonyPatches.cs +++ b/Cpp2IL.Core/Cpp2IlHarmonyPatches.cs @@ -34,6 +34,9 @@ internal static void Install() Logger.VerboseNewline("\tAdding finalizer to Mono.Cecil.MetadataBuilder:GetCustomAttributeSignature...", "Harmony"); harmony.Patch(AccessTools.Method("Mono.Cecil.MetadataBuilder:GetCustomAttributeSignature"), finalizer: new(typeof(Cpp2IlHarmonyPatches), nameof(FinalizeGetCustomAttributeSignature))); + + Logger.VerboseNewline("\tAdding finalizer to Mono.Cecil.SignatureWriter:WriteGenericInstanceSignature...", "Harmony"); + harmony.Patch(AccessTools.Method("Mono.Cecil.SignatureWriter:WriteGenericInstanceSignature"), finalizer: new(typeof(Cpp2IlHarmonyPatches), nameof(FinalizeWriteGenericInstanceSignature))); Logger.VerboseNewline("\tDone", "Harmony"); } @@ -77,5 +80,13 @@ internal static void Install() return null; } + + public static Exception? FinalizeWriteGenericInstanceSignature(IGenericInstance instance, Exception? __exception) + { + if (__exception != null) + return new GenericInstanceWriteFailedException(instance, __exception); + + return null; + } } } \ No newline at end of file diff --git a/Cpp2IL.Core/Exceptions/GenericInstanceWriteFailedException.cs b/Cpp2IL.Core/Exceptions/GenericInstanceWriteFailedException.cs new file mode 100644 index 00000000..9e70bf9b --- /dev/null +++ b/Cpp2IL.Core/Exceptions/GenericInstanceWriteFailedException.cs @@ -0,0 +1,13 @@ +using System; +using System.Linq; +using Mono.Cecil; + +namespace Cpp2IL.Core.Exceptions +{ + public class GenericInstanceWriteFailedException : Exception + { + public GenericInstanceWriteFailedException(IGenericInstance instance, Exception cause) + : base($"Failed to write generic instance {instance} due to an exception", cause) + { } + } +} \ No newline at end of file diff --git a/Cpp2IL/Cpp2IL.csproj b/Cpp2IL/Cpp2IL.csproj index d942f44e..d007941b 100644 --- a/Cpp2IL/Cpp2IL.csproj +++ b/Cpp2IL/Cpp2IL.csproj @@ -10,6 +10,7 @@ true true net472;net6.0 + true From 66a465821f73b706f6eb78f4a875edcf8f0c8f75 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Wed, 30 Mar 2022 23:09:14 +0100 Subject: [PATCH 30/76] Create dependabot.yml --- .github/dependabot.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..0006a8e2 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,20 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "nuget" # See documentation for possible values + target-branch: "development" + directory: "/" # Location of package manifests + schedule: + interval: "daily" + + - package-ecosystem: "github-actions" + target-branch: "development" + # Workflow files stored in the + # default location of `.github/workflows` + directory: "/" + schedule: + interval: "daily" From a3cca5e3efb596f9c97eecf66e2323d008c57ab7 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Sun, 17 Apr 2022 12:02:50 +0100 Subject: [PATCH 31/76] Core: Add another band-aid fix for dll save failures. --- Cpp2IL.Core/Analysis/AsmAnalyzerBase.cs | 10 ++++++++-- Cpp2IL.Core/Cpp2IlApi.cs | 8 ++++++-- Cpp2IL.Core/Cpp2IlHarmonyPatches.cs | 17 ++++++++++------- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/Cpp2IL.Core/Analysis/AsmAnalyzerBase.cs b/Cpp2IL.Core/Analysis/AsmAnalyzerBase.cs index bdac2453..9d927797 100644 --- a/Cpp2IL.Core/Analysis/AsmAnalyzerBase.cs +++ b/Cpp2IL.Core/Analysis/AsmAnalyzerBase.cs @@ -128,8 +128,14 @@ public StringBuilder BuildILToString() varType = git2.Resolve(); if (varType is GenericInstanceType git) varType = processor.ImportRecursive(git, MethodDefinition); - if (varType is ArrayType arr && MiscUtils.GetUltimateElementType(arr).IsGenericParameter) - throw new InvalidOperationException(); + if (varType is ArrayType arr) + { + if(MiscUtils.GetUltimateElementType(arr).IsGenericParameter) + throw new InvalidOperationException(); + if(arr.ElementType is GenericInstanceType arrGit && arrGit.HasAnyGenericParams()) + varType = arrGit.Resolve().MakeArrayType(); + } + if(varType is ArrayType { ElementType: GenericInstanceType git3}) varType = processor.ImportRecursive(git3, MethodDefinition).MakeArrayType(); diff --git a/Cpp2IL.Core/Cpp2IlApi.cs b/Cpp2IL.Core/Cpp2IlApi.cs index 5f47c1a0..5d175084 100644 --- a/Cpp2IL.Core/Cpp2IlApi.cs +++ b/Cpp2IL.Core/Cpp2IlApi.cs @@ -59,7 +59,7 @@ public static class Cpp2IlApi if (Environment.OSVersion.Platform == PlatformID.Win32NT && !string.IsNullOrEmpty(unityPlayerPath)) { var unityVer = FileVersionInfo.GetVersionInfo(unityPlayerPath); - + Logger.VerboseNewline($"Running on windows and have unity player, so using file version: {unityVer.FileMajorPart}.{unityVer.FileMinorPart}.{unityVer.FileBuildPart}"); return new[] {unityVer.FileMajorPart, unityVer.FileMinorPart, unityVer.FileBuildPart}; @@ -420,14 +420,18 @@ public static void SaveAssemblies(string toWhere, List assem if (reference != null) assembly.MainModule.AssemblyReferences.Remove(reference); +#if !DEBUG try { - assembly.Write(dllPath); +#endif + assembly.Write(dllPath); +#if !DEBUG } catch (Exception e) { throw new DllSaveException(dllPath, e); } +#endif } } diff --git a/Cpp2IL.Core/Cpp2IlHarmonyPatches.cs b/Cpp2IL.Core/Cpp2IlHarmonyPatches.cs index c1bb3986..abab8d94 100644 --- a/Cpp2IL.Core/Cpp2IlHarmonyPatches.cs +++ b/Cpp2IL.Core/Cpp2IlHarmonyPatches.cs @@ -15,6 +15,9 @@ internal static class Cpp2IlHarmonyPatches { internal static void Install() { +#if DEBUG + return; +#endif Logger.InfoNewline("Patching Cecil for better error messages...", "Harmony"); Logger.VerboseNewline("\tInitializing harmony instance 'dev.samboy.cpp2il'...", "Harmony"); @@ -25,16 +28,16 @@ internal static void Install() Logger.VerboseNewline("\tAdding finalizer to Mono.Cecil.Cil.CodeWriter:WriteOperand...", "Harmony"); harmony.Patch(AccessTools.Method("Mono.Cecil.Cil.CodeWriter:WriteOperand"), finalizer: new(typeof(Cpp2IlHarmonyPatches), nameof(FinalizeWriteOperand))); - + Logger.VerboseNewline("\tAdding finalizer to Mono.Cecil.MetadataBuilder:AddType...", "Harmony"); harmony.Patch(AccessTools.Method("Mono.Cecil.MetadataBuilder:AddType"), finalizer: new(typeof(Cpp2IlHarmonyPatches), nameof(FinalizeAddType))); - + Logger.VerboseNewline("\tAdding finalizer to Mono.Cecil.MetadataBuilder:AddProperty...", "Harmony"); harmony.Patch(AccessTools.Method("Mono.Cecil.MetadataBuilder:AddProperty"), finalizer: new(typeof(Cpp2IlHarmonyPatches), nameof(FinalizeAddProperty))); - + Logger.VerboseNewline("\tAdding finalizer to Mono.Cecil.MetadataBuilder:GetCustomAttributeSignature...", "Harmony"); harmony.Patch(AccessTools.Method("Mono.Cecil.MetadataBuilder:GetCustomAttributeSignature"), finalizer: new(typeof(Cpp2IlHarmonyPatches), nameof(FinalizeGetCustomAttributeSignature))); - + Logger.VerboseNewline("\tAdding finalizer to Mono.Cecil.SignatureWriter:WriteGenericInstanceSignature...", "Harmony"); harmony.Patch(AccessTools.Method("Mono.Cecil.SignatureWriter:WriteGenericInstanceSignature"), finalizer: new(typeof(Cpp2IlHarmonyPatches), nameof(FinalizeWriteGenericInstanceSignature))); @@ -64,7 +67,7 @@ internal static void Install() return null; } - + public static Exception? FinalizeAddProperty(PropertyDefinition property, Exception? __exception) { if (__exception != null) @@ -72,7 +75,7 @@ internal static void Install() return null; } - + public static Exception? FinalizeGetCustomAttributeSignature(CustomAttribute attribute, Exception? __exception) { if (__exception != null) @@ -80,7 +83,7 @@ internal static void Install() return null; } - + public static Exception? FinalizeWriteGenericInstanceSignature(IGenericInstance instance, Exception? __exception) { if (__exception != null) From 975a2fc6620cef7d0520ffbed618c1b67877ef2d Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Tue, 19 Apr 2022 22:00:42 +0100 Subject: [PATCH 32/76] Lib: Update sanity checking on codereg count fields --- LibCpp2IL/BinarySearcher.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/LibCpp2IL/BinarySearcher.cs b/LibCpp2IL/BinarySearcher.cs index 5241035c..74dd1a5d 100644 --- a/LibCpp2IL/BinarySearcher.cs +++ b/LibCpp2IL/BinarySearcher.cs @@ -193,6 +193,13 @@ internal ulong FindCodeRegistrationPost2019() { //...and subtract that from our pointer. var address = pCodegenModule - bytesToGoBack; + + if (pCodegenModules.Count == 1) + { + LibLogger.Verbose($"\t\t\tOnly found one codegen module pointer, so assuming it's correct and returning pCodeReg = 0x{address:X}"); + return address; + } + LibLogger.Verbose($"\t\t\tConsidering potential code registration at 0x{address:X}..."); var codeReg = LibCpp2IlMain.Binary!.ReadClassAtVirtualAddress(address); @@ -221,7 +228,8 @@ public static bool ValidateCodeRegistration(Il2CppCodeRegistration codeReg, Dict if (keyValuePair.Key.EndsWith("count", StringComparison.OrdinalIgnoreCase)) { - if (fieldValue > 0x60_000) + //19 Apr 2022: Upped to 0x70000 due to Zenith (which has genericMethodPointersCount = 0x6007B) + if (fieldValue > 0x70_000) { LibLogger.VerboseNewline($"Rejected due to unreasonable count field 0x{fieldValue:X} for field {keyValuePair.Key}"); success = false; From 12fd73ee294eba3cbaf3b2dfff1db6003a9a0bc4 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Fri, 22 Apr 2022 22:21:58 +0100 Subject: [PATCH 33/76] Lib: Actually use position shift lock when reading string literals --- LibCpp2IL/Metadata/Il2CppMetadata.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LibCpp2IL/Metadata/Il2CppMetadata.cs b/LibCpp2IL/Metadata/Il2CppMetadata.cs index 967d3c83..ce5cfbb2 100644 --- a/LibCpp2IL/Metadata/Il2CppMetadata.cs +++ b/LibCpp2IL/Metadata/Il2CppMetadata.cs @@ -387,8 +387,8 @@ public string GetStringFromIndex(int index) public string GetStringLiteralFromIndex(uint index) { var stringLiteral = stringLiterals[index]; - Position = metadataHeader.stringLiteralDataOffset + stringLiteral.dataIndex; - return Encoding.UTF8.GetString(ReadBytes((int) stringLiteral.length)); + + return Encoding.UTF8.GetString(ReadByteArrayAtRawAddress(metadataHeader.stringLiteralDataOffset + stringLiteral.dataIndex, (int) stringLiteral.length)); } } } \ No newline at end of file From 8dab5edea83ae86d85c4661654bb0d9c7355dc2d Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Tue, 26 Apr 2022 23:34:19 +0100 Subject: [PATCH 34/76] Lib: Backport Mach-O support from dev branch --- LibCpp2IL/ClassReadingBinaryReader.cs | 2 + LibCpp2IL/LibCpp2IlMain.cs | 6 ++ LibCpp2IL/MachO/LoadCommandId.cs | 70 +++++++++++++ LibCpp2IL/MachO/MachOCpuSubtype.cs | 11 ++ LibCpp2IL/MachO/MachOCpuType.cs | 31 ++++++ LibCpp2IL/MachO/MachOFile.cs | 135 +++++++++++++++++++++++++ LibCpp2IL/MachO/MachOFileType.cs | 15 +++ LibCpp2IL/MachO/MachOHeader.cs | 32 ++++++ LibCpp2IL/MachO/MachOHeaderFlags.cs | 37 +++++++ LibCpp2IL/MachO/MachOLoadCommand.cs | 36 +++++++ LibCpp2IL/MachO/MachOSection.cs | 45 +++++++++ LibCpp2IL/MachO/MachOSectionFlags.cs | 49 +++++++++ LibCpp2IL/MachO/MachOSegmentCommand.cs | 48 +++++++++ LibCpp2IL/MachO/MachOSegmentFlags.cs | 15 +++ LibCpp2IL/MachO/MachOVmProtection.cs | 19 ++++ 15 files changed, 551 insertions(+) create mode 100644 LibCpp2IL/MachO/LoadCommandId.cs create mode 100644 LibCpp2IL/MachO/MachOCpuSubtype.cs create mode 100644 LibCpp2IL/MachO/MachOCpuType.cs create mode 100644 LibCpp2IL/MachO/MachOFile.cs create mode 100644 LibCpp2IL/MachO/MachOFileType.cs create mode 100644 LibCpp2IL/MachO/MachOHeader.cs create mode 100644 LibCpp2IL/MachO/MachOHeaderFlags.cs create mode 100644 LibCpp2IL/MachO/MachOLoadCommand.cs create mode 100644 LibCpp2IL/MachO/MachOSection.cs create mode 100644 LibCpp2IL/MachO/MachOSectionFlags.cs create mode 100644 LibCpp2IL/MachO/MachOSegmentCommand.cs create mode 100644 LibCpp2IL/MachO/MachOSegmentFlags.cs create mode 100644 LibCpp2IL/MachO/MachOVmProtection.cs diff --git a/LibCpp2IL/ClassReadingBinaryReader.cs b/LibCpp2IL/ClassReadingBinaryReader.cs index f3dadbdc..ac5ca135 100644 --- a/LibCpp2IL/ClassReadingBinaryReader.cs +++ b/LibCpp2IL/ClassReadingBinaryReader.cs @@ -374,5 +374,7 @@ private static FieldInfo[] GetFieldsCached(Type t) CachedFields[t] = ret = t.GetFields(); return ret; } + + public ulong ReadNUint() => is32Bit ? ReadUInt32() : ReadUInt64(); } } \ No newline at end of file diff --git a/LibCpp2IL/LibCpp2IlMain.cs b/LibCpp2IL/LibCpp2IlMain.cs index b7c3b1b2..1bf13850 100644 --- a/LibCpp2IL/LibCpp2IlMain.cs +++ b/LibCpp2IL/LibCpp2IlMain.cs @@ -4,6 +4,7 @@ using System.Linq; using LibCpp2IL.Elf; using LibCpp2IL.Logging; +using LibCpp2IL.MachO; using LibCpp2IL.Metadata; using LibCpp2IL.NintendoSwitch; using LibCpp2IL.Reflection; @@ -180,6 +181,11 @@ public static bool Initialize(byte[] binaryBytes, byte[] metadataBytes, int[] un var wasm = new WasmFile(new MemoryStream(binaryBytes, 0, binaryBytes.Length, false, true), TheMetadata.maxMetadataUsages); Binary = wasm; (codereg, metareg) = wasm.PlusSearch(TheMetadata.methodDefs.Count(x => x.methodIndex >= 0), TheMetadata.typeDefs.Length); + } else if (BitConverter.ToUInt32(binaryBytes, 0) is 0xFEEDFACE or 0xFEEDFACF) + { + var macho = new MachOFile(new MemoryStream(binaryBytes, 0, binaryBytes.Length, false, true), TheMetadata.maxMetadataUsages); + Binary = macho; + (codereg, metareg) = macho.PlusSearch(TheMetadata.methodDefs.Count(x => x.methodIndex >= 0), TheMetadata.typeDefs.Length); } else { diff --git a/LibCpp2IL/MachO/LoadCommandId.cs b/LibCpp2IL/MachO/LoadCommandId.cs new file mode 100644 index 00000000..3321584e --- /dev/null +++ b/LibCpp2IL/MachO/LoadCommandId.cs @@ -0,0 +1,70 @@ +using System.Diagnostics.CodeAnalysis; + +namespace LibCpp2IL.MachO +{ + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum LoadCommandId : uint + { + LC_SEGMENT = 0x1, + LC_SYMTAB = 0x2, + LC_SYMSEG = 0x3, + LC_THREAD = 0x4, + LC_UNIXTHREAD = 0x5, + LC_LOADFVMLIB = 0x6, + LC_IDFVMLIB = 0x7, + LC_IDENT = 0x8, + LC_FVMFILE = 0x9, + LC_PREPAGE = 0xa, + LC_DYSYMTAB = 0xb, + LC_LOAD_DYLIB = 0xc, + LC_ID_DYLIB = 0xd, + LC_LOAD_DYLINKER = 0xe, + LC_ID_DYLINKER = 0xf, + LC_PREBOUND_DYLIB = 0x10, + LC_ROUTINES = 0x11, + LC_SUB_FRAMEWORK = 0x12, + LC_SUB_UMBRELLA = 0x13, + LC_SUB_CLIENT = 0x14, + LC_SUB_LIBRARY = 0x15, + LC_TWOLEVEL_HINTS = 0x16, + LC_PREBIND_CKSUM = 0x17, + LC_LOAD_WEAK_DYLIB = 0x18 | 0x80000000, + LC_SEGMENT_64 = 0x19, + LC_ROUTINES_64 = 0x1a, + LC_UUID = 0x1b, + LC_RPATH = 0x1c | 0x80000000, + LC_CODE_SIGNATURE = 0x1d, + LC_SEGMENT_SPLIT_INFO = 0x1e, + LC_REEXPORT_DYLIB = 0x1f | 0x80000000, + LC_LAZY_LOAD_DYLIB = 0x20, + LC_ENCRYPTION_INFO = 0x21, + LC_DYLD_INFO = 0x22, + LC_DYLD_INFO_ONLY = LC_DYLD_INFO | 0x80000000, + LC_LOAD_UPWARD_DYLIB = 0x23 | 0x80000000, + LC_VERSION_MIN_MACOSX = 0x24, + LC_VERSION_MIN_IPHONEOS = 0x25, + LC_FUNCTION_STARTS = 0x26, + LC_DYLD_ENVIRONMENT = 0x27, + LC_MAIN = 0x28 | 0x80000000, + LC_DATA_IN_CODE = 0x29, + LC_SOURCE_VERSION = 0x2a, + LC_DYLIB_CODE_SIGN_DRS = 0x2b, + LC_ENCRYPTION_INFO_64 = 0x2c, + LC_LINKER_OPTION = 0x2d, + LC_LINKER_OPTIMIZATION_HINT = 0x2e, + LC_VERSION_MIN_TVOS = 0x2f, + LC_VERSION_MIN_WATCHOS = 0x30, + LC_NOTE = 0x31, + LC_BUILD_VERSION = 0x32, + LC_DYLD_EXPORTS_TRIE = 0x33 | 0x80000000, + LC_DYLD_CHAINED_FIXUPS = 0x34 | 0x80000000, + LC_LOAD_WEAK_DYLIB_UUID_REEXPORT_DYLIB = 0x35, + LC_UUID_DYLIB_CODE_SIGN_DRS = 0x36, + LC_DYLD_CHAINED_FIXUPS_COUNT = 0x37, + LC_FUNCTION_STARTS_64 = 0x38, + LC_DYLD_DEBUG = 0x39, + LC_DYLD_CHAINED_FRAMEWORK = 0x3a, + LC_DYLD_CHAINED_DYLIB = 0x3b, + LC_LOAD_DYLINKER_WITH_LIBRARY_PATH = 0x3c, + } +} \ No newline at end of file diff --git a/LibCpp2IL/MachO/MachOCpuSubtype.cs b/LibCpp2IL/MachO/MachOCpuSubtype.cs new file mode 100644 index 00000000..2ff9500a --- /dev/null +++ b/LibCpp2IL/MachO/MachOCpuSubtype.cs @@ -0,0 +1,11 @@ +namespace LibCpp2IL.MachO +{ + public enum MachOCpuSubtype + { + CPU_SUBTYPE_ANY = 100, + + CPU_SUBTYPE_ARM64_ALL = 0, + CPU_SUBTYPE_ARM64_V8 = 1, + CPU_SUBTYPE_ARM64E = 2, + } +} \ No newline at end of file diff --git a/LibCpp2IL/MachO/MachOCpuType.cs b/LibCpp2IL/MachO/MachOCpuType.cs new file mode 100644 index 00000000..37af6db9 --- /dev/null +++ b/LibCpp2IL/MachO/MachOCpuType.cs @@ -0,0 +1,31 @@ +namespace LibCpp2IL.MachO +{ + public enum MachOCpuType + { + CPU_TYPE_ANY = -1, + CPU_TYPE_VAX = 1, + //Skip 2-5 + CPU_TYPE_MC680x0 = 6, + CPU_TYPE_I386 = 7, + CPU_TYPE_X86_64 = CPU_TYPE_I386 | CPU_ARCH_ABI64, + CPU_TYPE_MIPS = 8, + //Skip 9 + CPU_TYPE_MC98000 = 10, + CPU_TYPE_HPPA = 11, + CPU_TYPE_ARM = 12, + CPU_TYPE_ARM64 = CPU_TYPE_ARM | CPU_ARCH_ABI64, + CPU_TYPE_ARM64_32 = CPU_TYPE_ARM | CPU_ARCH_ABI64_32, + CPU_TYPE_MC88000 = 13, + CPU_TYPE_SPARC = 14, + CPU_TYPE_I860 = 15, + CPU_TYPE_ALPHA = 16, + //Skip 17 + CPU_TYPE_POWERPC = 18, + CPU_TYPE_POWERPC64 = CPU_TYPE_POWERPC | CPU_ARCH_ABI64, + + + //Helper values + CPU_ARCH_ABI64 = 0x01000000, + CPU_ARCH_ABI64_32 = 0x02000000, + } +} \ No newline at end of file diff --git a/LibCpp2IL/MachO/MachOFile.cs b/LibCpp2IL/MachO/MachOFile.cs new file mode 100644 index 00000000..699a2cea --- /dev/null +++ b/LibCpp2IL/MachO/MachOFile.cs @@ -0,0 +1,135 @@ +using System.IO; +using System.Linq; +using LibCpp2IL.Logging; + +namespace LibCpp2IL.MachO +{ + public class MachOFile : Il2CppBinary + { + private byte[] _raw; + + private readonly MachOHeader _header; + private readonly MachOLoadCommand[] _loadCommands; + + private readonly MachOSegmentCommand[] Segments64; + private readonly MachOSection[] Sections64; + + public MachOFile(MemoryStream input, long maxMetadataUsages) : base(input, maxMetadataUsages) + { + _raw = input.GetBuffer(); + + LibLogger.Verbose("\tReading Mach-O header..."); + _header = new(); + _header.Read(this); + + switch (_header.Magic) + { + case MachOHeader.MAGIC_32_BIT: + LibLogger.Verbose("Mach-O is 32-bit..."); + is32Bit = true; + break; + case MachOHeader.MAGIC_64_BIT: + LibLogger.Verbose("Mach-O is 64-bit..."); + is32Bit = false; + break; + default: + throw new($"Unknown Mach-O Magic: {_header.Magic}"); + } + + switch (_header.CpuType) + { + case MachOCpuType.CPU_TYPE_I386: + LibLogger.VerboseNewline("Mach-O contains x86_32 instructions."); + InstructionSet = InstructionSet.X86_32; + break; + case MachOCpuType.CPU_TYPE_X86_64: + LibLogger.VerboseNewline("Mach-O contains x86_64 instructions."); + InstructionSet = InstructionSet.X86_64; + break; + case MachOCpuType.CPU_TYPE_ARM: + LibLogger.VerboseNewline("Mach-O contains ARM (32-bit) instructions."); + InstructionSet = InstructionSet.ARM32; + break; + case MachOCpuType.CPU_TYPE_ARM64: + LibLogger.VerboseNewline("Mach-O contains ARM64 instructions."); + InstructionSet = InstructionSet.ARM64; + break; + default: + throw new($"Don't know how to handle a Mach-O CPU Type of {_header.CpuType}"); + } + + if(_header.Magic == MachOHeader.MAGIC_32_BIT) + LibLogger.ErrorNewline("32-bit MACH-O files have not been tested! Please report any issues."); + else + LibLogger.WarnNewline("Mach-O Support is experimental. Please open an issue if anything seems incorrect."); + + LibLogger.Verbose("\tReading Mach-O load commands..."); + _loadCommands = new MachOLoadCommand[_header.NumLoadCommands]; + for (var i = 0; i < _header.NumLoadCommands; i++) + { + _loadCommands[i] = new(); + _loadCommands[i].Read(this); + } + LibLogger.VerboseNewline($"Read {_loadCommands.Length} load commands."); + + Segments64 = _loadCommands.Where(c => c.Command == LoadCommandId.LC_SEGMENT_64).Select(c => c.CommandData).Cast().ToArray(); + Sections64 = Segments64.SelectMany(s => s.Sections).ToArray(); + + LibLogger.VerboseNewline($"\tMach-O contains {Segments64.Length} segments, split into {Sections64.Length} sections."); + } + + public override long RawLength => _raw.Length; + public override byte GetByteAtRawAddress(ulong addr) => _raw[addr]; + + public override long MapVirtualAddressToRaw(ulong uiAddr) + { + var sec = Sections64.FirstOrDefault(s => s.Address <= uiAddr && uiAddr < s.Address + s.Size); + + if (sec == null) + throw new($"Could not find section for virtual address 0x{uiAddr:X}. Lowest section address is 0x{Sections64.Min(s => s.Address):X}, highest section address is 0x{Sections64.Max(s => s.Address + s.Size):X}"); + + return (long) (sec.Offset + (uiAddr - sec.Address)); + } + + public override ulong MapRawAddressToVirtual(uint offset) + { + var sec = Sections64.FirstOrDefault(s => s.Offset <= offset && offset < s.Offset + s.Size); + + if (sec == null) + throw new($"Could not find section for raw address 0x{offset:X}"); + + return sec.Address + (offset - sec.Offset); + } + + public override ulong GetRVA(ulong pointer) + { + return pointer; //TODO? + } + + public override byte[] GetRawBinaryContent() => _raw; + + public override ulong GetVirtualAddressOfExportedFunctionByName(string toFind) + { + return 0; //TODO? + } + + private MachOSection GetTextSection64() + { + var textSection = Sections64.FirstOrDefault(s => s.SectionName == "__text"); + + if (textSection == null) + throw new("Could not find __text section"); + + return textSection; + } + + public override byte[] GetEntirePrimaryExecutableSection() + { + var textSection = GetTextSection64(); + + return _raw.SubArray((int) textSection.Offset, (int) textSection.Size); + } + + public override ulong GetVirtualAddressOfPrimaryExecutableSection() => GetTextSection64().Address; + } +} \ No newline at end of file diff --git a/LibCpp2IL/MachO/MachOFileType.cs b/LibCpp2IL/MachO/MachOFileType.cs new file mode 100644 index 00000000..59343d90 --- /dev/null +++ b/LibCpp2IL/MachO/MachOFileType.cs @@ -0,0 +1,15 @@ +namespace LibCpp2IL.MachO +{ + public enum MachOFileType : uint + { + MH_OBJECT = 0x1, + MH_EXECUTE = 0x2, + MH_DYLIB = 0x6, //Dynamic library + MH_BUNDLE = 0x8, + MH_DSYM = 0xA, + MH_KEXT_BUNDLE = 0xB, //Kernel Extension Bundle + MH_APP_EXTENSION_SAFE = 0x02000000, + MY_SIM_SUPPORT = 0x08000000, + MH_DYLIB_IN_CACHE = 0x80000000, + } +} \ No newline at end of file diff --git a/LibCpp2IL/MachO/MachOHeader.cs b/LibCpp2IL/MachO/MachOHeader.cs new file mode 100644 index 00000000..2f665fd8 --- /dev/null +++ b/LibCpp2IL/MachO/MachOHeader.cs @@ -0,0 +1,32 @@ +namespace LibCpp2IL.MachO +{ + public class MachOHeader + { + public const uint MAGIC_32_BIT = 0xFEEDFACE; + public const uint MAGIC_64_BIT = 0xFEEDFACF; + + public uint Magic; //0xFEEDFACE for 32-bit, 0xFEEDFACF for 64-bit + public MachOCpuType CpuType; //cpu specifier + public MachOCpuSubtype CpuSubtype; //cpu specifier + public MachOFileType FileType; //type of file + public uint NumLoadCommands; //number of load commands + public uint SizeOfLoadCommands; //size of load commands + public MachOHeaderFlags Flags; //flags + + public uint Reserved; //Only on 64-bit + + public void Read(ClassReadingBinaryReader reader) + { + Magic = reader.ReadUInt32(); + CpuType = (MachOCpuType) reader.ReadUInt32(); + CpuSubtype = (MachOCpuSubtype) reader.ReadUInt32(); + FileType = (MachOFileType) reader.ReadUInt32(); + NumLoadCommands = reader.ReadUInt32(); + SizeOfLoadCommands = reader.ReadUInt32(); + Flags = (MachOHeaderFlags) reader.ReadUInt32(); + + if(Magic == MAGIC_64_BIT) + Reserved = reader.ReadUInt32(); + } + } +} \ No newline at end of file diff --git a/LibCpp2IL/MachO/MachOHeaderFlags.cs b/LibCpp2IL/MachO/MachOHeaderFlags.cs new file mode 100644 index 00000000..a0131c30 --- /dev/null +++ b/LibCpp2IL/MachO/MachOHeaderFlags.cs @@ -0,0 +1,37 @@ +using System; + +namespace LibCpp2IL.MachO +{ + [Flags] + public enum MachOHeaderFlags + { + MH_NO_UNDEFINED_SYMBOLS = 0x1, + MH_INCREMENTAL_LINK = 0x2, + MH_DYLDLINK = 0x4, // this file is input for the dynamic linker + MH_BINDATLOAD = 0x8, // this file's undefined symbols are bound at load time + MH_PREBOUND = 0x10, // this file has its undefined symbols prebound + MH_SPLIT_SEGS = 0x20, // this file has its read-only and read-write segments split + MH_LAZY_INIT = 0x40, // the shared library init routine should be run lazily. Obsolete. + MH_TWOLEVEL = 0x80, // this image is using two-level name space bindings + MH_FORCE_FLAT = 0x100, // this executable is forcing all images to use flat name space bindings + MH_NOMULTIDEFS = 0x200, // guarantee of no multiple definitions of symbols in sub-images, so two-level namespace bindings can work + MH_NOFIXPREBINDING = 0x400, // do not have dyld notify the prebinding agent about this executable + MH_PREBINDABLE = 0x800, // the binary is not prebound but can have its prebinding redone. only used when MH_PREBOUND is not set. + MH_ALLMODSBOUND = 0x1000, // this binary binds to all two-level namespace modules of its dependent libraries. only used when MH_PREBINDABLE and MH_TWOLEVEL are both set. + MH_SUBSECTIONS_VIA_SYMBOLS = 0x2000, // safe to divide up the sections into sub-sections via symbols for dead code stripping + MH_CANONICAL = 0x4000, // the binary has been canonicalized via the unprebind operation + MH_WEAK_DEFINES = 0x8000, // the final linked image contains external weak symbols + MH_BINDS_TO_WEAK = 0x10000, // the final linked image uses weak symbols + MH_ALLOW_STACK_EXECUTION = 0x20000, // When this bit is set, all stacks in the task will be given stack execution privilege. Only used in MH_EXECUTE filetypes. + MH_ROOT_SAFE = 0x40000, // When this bit is set, the binary declares it is safe for use in processes with uid zero + MH_SETUID_SAFE = 0x80000, // When this bit is set, the binary declares it is safe for use in processes when issetugid() is true + MH_NO_REEXPORTED_DYLIBS = 0x100000, // When this bit is set on a dylib, the static linker does not need to examine dependent dylibs to see if any are re-exported + MH_PIE = 0x200000, // When this bit is set, the OS will load the main executable at a random address. Only used in MH_EXECUTE filetypes. + MH_DEAD_STRIPPABLE_DYLIB = 0x400000, // Only for use on dylibs. Allows the linker to not link to this dylib if it's not referenced. + MH_HAS_TLV_DESCRIPTORS = 0x800000, // Contains a section of type S_THREAD_LOCAL_VARIABLES.. + MH_NO_HEAP_EXECUTION = 0x1000000, // When this bit is set, the OS will run the main executable with a non-executable heap even on platforms (e.g. i386) that don't require it. Only used in MH_EXECUTE filetypes. + MH_APP_EXTENSION_SAFE = 0x02000000, // The code was linked for use in an application extension. + MH_NLIST_OUTOFSYNC_WITH_DYLDINFO = 0x04000000, // The external symbols listed in the nlist symtab do not include all the symbols that are used by the dynamic linker. + MH_SIM_SUPPORT = 0x08000000, // The binary is suitable for a simulator. + } +} \ No newline at end of file diff --git a/LibCpp2IL/MachO/MachOLoadCommand.cs b/LibCpp2IL/MachO/MachOLoadCommand.cs new file mode 100644 index 00000000..c152c6f0 --- /dev/null +++ b/LibCpp2IL/MachO/MachOLoadCommand.cs @@ -0,0 +1,36 @@ +using System; +using System.Text; + +namespace LibCpp2IL.MachO +{ + public class MachOLoadCommand + { + public LoadCommandId Command; + public uint CommandSize; + + public object? CommandData; + public byte[]? UnknownCommandData = null; + + + public string? UnknownDataAsString => UnknownCommandData == null ? null : Encoding.UTF8.GetString(UnknownCommandData); + + public void Read(ClassReadingBinaryReader reader) + { + Command = (LoadCommandId) reader.ReadUInt32(); + CommandSize = reader.ReadUInt32(); + + switch (Command) + { + case LoadCommandId.LC_SEGMENT: + case LoadCommandId.LC_SEGMENT_64: + var cmd = new MachOSegmentCommand(); + cmd.Read(reader); + CommandData = cmd; + break; + default: + UnknownCommandData = reader.ReadBytes((int) CommandSize - 8); // -8 because we've already read the 8 bytes of the header + break; + } + } + } +} \ No newline at end of file diff --git a/LibCpp2IL/MachO/MachOSection.cs b/LibCpp2IL/MachO/MachOSection.cs new file mode 100644 index 00000000..96eb2b62 --- /dev/null +++ b/LibCpp2IL/MachO/MachOSection.cs @@ -0,0 +1,45 @@ +using System.Text; + +namespace LibCpp2IL.MachO +{ + public class MachOSection + { + public string SectionName; // 16 bytes + public string ContainingSegmentName; // 16 bytes + + public ulong Address; + public ulong Size; + + public uint Offset; + public uint Alignment; + public uint RelocationOffset; + public uint NumberOfRelocations; + public MachOSectionFlags Flags; + + public uint Reserved1; + public uint Reserved2; + + public uint Reserved3; //64-bit only + + public void Read(ClassReadingBinaryReader reader) + { + SectionName = Encoding.UTF8.GetString(reader.ReadBytes(16)).TrimEnd('\0'); + ContainingSegmentName = Encoding.UTF8.GetString(reader.ReadBytes(16)).TrimEnd('\0'); + + Address = reader.ReadNUint(); + Size = reader.ReadNUint(); + + Offset = reader.ReadUInt32(); + Alignment = reader.ReadUInt32(); + RelocationOffset = reader.ReadUInt32(); + NumberOfRelocations = reader.ReadUInt32(); + Flags = (MachOSectionFlags) reader.ReadUInt32(); + + Reserved1 = reader.ReadUInt32(); + Reserved2 = reader.ReadUInt32(); + + if(!reader.is32Bit) + Reserved3 = reader.ReadUInt32(); + } + } +} \ No newline at end of file diff --git a/LibCpp2IL/MachO/MachOSectionFlags.cs b/LibCpp2IL/MachO/MachOSectionFlags.cs new file mode 100644 index 00000000..6bfc7102 --- /dev/null +++ b/LibCpp2IL/MachO/MachOSectionFlags.cs @@ -0,0 +1,49 @@ +using System; + +namespace LibCpp2IL.MachO +{ + [Flags] + public enum MachOSectionFlags : uint + { + TYPE_BITMASK = 0x000000FF, + ATTRIBUTES_BITMASK = 0xFFFFF00, + USER_ATTRIBUTES_BITMASK = 0xFF000000, //User-set attributes + SYSTEM_ATTRIBUTES_BITMASK = 0x00FFFF00, //System-set attributes + + TYPE_REGULAR = 0x0, + TYPE_ZEROFILL = 0x1, //Zero-fill on demand + TYPE_CSTRING_LITERALS = 0x2, //Only literal C strings + TYPE_4BYTE_LITERALS = 0x3, //Only 4-byte literals + TYPE_8BYTE_LITERALS = 0x4, //Only 8-byte literals + TYPE_LITERAL_POINTERS = 0x5, //Only pointers to literals + TYPE_NON_LAZY_SYMBOL_POINTERS = 0x6, //Only non-lazy symbol pointers + TYPE_LAZY_SYMBOL_POINTERS = 0x7, //Only lazy symbol pointers + TYPE_SYMBOL_STUBS = 0x8, //Only symbol stubs + TYPE_MOD_INIT_FUNC_POINTERS = 0x9, //Only function pointers for initialization + TYPE_MOD_TERM_FUNC_POINTERS = 0xA, //Only function pointers for termination + TYPE_COALESCED = 0xB, //Contains symbols that are to be coalesced + TYPE_GB_ZEROFILL = 0xC, //Zero-fill on demand, can be > 4gb + TYPE_INTERPOSING = 0xD, //Only pairs of function pointers for interposing + TYPE_16BYTE_LITERALS = 0xE, //Only 16-byte literals + TYPE_DTRACE_DOF = 0xF, //Contains DTrace Object Format + TYPE_LAZY_DYLIB_SYMBOL_POINTERS = 0x10, //Only lazy symbol pointers to lazy dylibs + TYPE_THREAD_LOCAL_REGULAR = 0x11, // Template of initial values for thread local variables + TYPE_THREAD_LOCAL_ZEROFILL = 0x12, // Zero-fill on demand template for thread local variables + TYPE_THREAD_LOCAL_VARIABLES = 0x13, // Thread local variable descriptors + TYPE_THREAD_LOCAL_VARIABLE_POINTERS = 0x14, // Pointers to thread local variable descriptors + TYPE_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15, // Pointers functions to call to initialize TLV values + TYPE_INIT_FUNC_OFFSETS = 0x16, // 32-bit offsets to initialization functions + + ATTR_PURE_INSTRUCTIONS = 0x80000000, // Section contains only true machine instructions + ATTR_NO_TOC = 0x40000000, // Section contains coalesced symbols that are not to be in a ranlib table of contents + ATTR_STRIP_STATIC_SYMS = 0x20000000, // Ok to strip static symbols in this section in files with the MH_DYLDLINK flag + ATTR_NO_DEAD_STRIP = 0x10000000, // No dead stripping + ATTR_LIVE_SUPPORT = 0x08000000, // Blocks are live if they reference live blocks + ATTR_SELF_MODIFYING_CODE = 0x04000000, // Used with i386 code stubs written on by dyld + ATTR_DEBUG = 0x02000000, // A debug section + + ATTR_SOME_INSTRUCTIONS = 0x00000400, // Section contains some machine instructions + ATTR_EXT_RELOC = 0x00000200, // Section has external relocation entries + ATTR_LOC_RELOC = 0x00000100, // Section has local relocation entries + } +} \ No newline at end of file diff --git a/LibCpp2IL/MachO/MachOSegmentCommand.cs b/LibCpp2IL/MachO/MachOSegmentCommand.cs new file mode 100644 index 00000000..c7646e9c --- /dev/null +++ b/LibCpp2IL/MachO/MachOSegmentCommand.cs @@ -0,0 +1,48 @@ +using System; +using System.Text; + +namespace LibCpp2IL.MachO +{ + public class MachOSegmentCommand + { + public string SegmentName; // 16 bytes + + public ulong VirtualAddress; + public ulong VirtualSize; + public ulong FileOffset; + public ulong FileSize; + + public MachOVmProtection MaxProtection; + public MachOVmProtection InitialProtection; + + public uint NumSections; + + public MachOSegmentFlags Flags; + + public MachOSection[] Sections = Array.Empty(); + + public void Read(ClassReadingBinaryReader reader) + { + SegmentName = Encoding.UTF8.GetString(reader.ReadBytes(16)).TrimEnd('\0'); + + VirtualAddress = reader.ReadNUint(); + VirtualSize = reader.ReadNUint(); + FileOffset = reader.ReadNUint(); + FileSize = reader.ReadNUint(); + + MaxProtection = (MachOVmProtection) reader.ReadInt32(); + InitialProtection = (MachOVmProtection) reader.ReadInt32(); + + NumSections = reader.ReadUInt32(); + + Flags = (MachOSegmentFlags) reader.ReadUInt32(); + + Sections = new MachOSection[NumSections]; + for (var i = 0; i < NumSections; i++) + { + Sections[i] = new(); + Sections[i].Read(reader); + } + } + } +} \ No newline at end of file diff --git a/LibCpp2IL/MachO/MachOSegmentFlags.cs b/LibCpp2IL/MachO/MachOSegmentFlags.cs new file mode 100644 index 00000000..28d336e9 --- /dev/null +++ b/LibCpp2IL/MachO/MachOSegmentFlags.cs @@ -0,0 +1,15 @@ +using System; + +namespace LibCpp2IL.MachO +{ + [Flags] + public enum MachOSegmentFlags + { + SG_NONE = 0x0, + + SG_HIGHVM = 0x1, // The file contents for this segment are for the high part of the virtual space - push the raw data as far to the end of the segment as possible. + SG_FVMLIB = 0x2, // This segment is the VM that is allocated by a fixed VM library, for overlap checking. + SG_NORELOC = 0x4, // This segment has nothing that was relocated in it and nothing relocated to it, so no relocations are needed. + SG_PROTECTED_VERSION_1 = 0x8, // This segment is protected. If the segment starts at file offset 0, the first page of the segment is not protected. All other pages of the segment are protected. + } +} \ No newline at end of file diff --git a/LibCpp2IL/MachO/MachOVmProtection.cs b/LibCpp2IL/MachO/MachOVmProtection.cs new file mode 100644 index 00000000..395c82c0 --- /dev/null +++ b/LibCpp2IL/MachO/MachOVmProtection.cs @@ -0,0 +1,19 @@ +using System; + +namespace LibCpp2IL.MachO +{ + [Flags] + public enum MachOVmProtection + { + PROT_NONE = 0x0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, + PROT_NO_CHANGE = 0x8, + PROT_COPY = 0x10, + PROT_TRUSTED = 0x20, + PROT_IS_MASK = 0x40, + PROT_STRIP_READ = 0x80, + PROT_COPY_FAIL_IF_EXECUTABLE = 0x100, + } +} \ No newline at end of file From e122f15f11e5ffdd969319235485c50075fc3654 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Wed, 27 Apr 2022 13:57:50 +0100 Subject: [PATCH 35/76] Bump version to 2022.0.4 --- Cpp2IL.Core/Cpp2IL.Core.csproj | 6 +++--- Cpp2IL/Properties/AssemblyInfo.cs | 4 ++-- LibCpp2IL/LibCpp2IL.csproj | 2 +- LibCpp2IL/Properties/AssemblyInfo.cs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cpp2IL.Core/Cpp2IL.Core.csproj b/Cpp2IL.Core/Cpp2IL.Core.csproj index fc6aae7c..50ee21f9 100644 --- a/Cpp2IL.Core/Cpp2IL.Core.csproj +++ b/Cpp2IL.Core/Cpp2IL.Core.csproj @@ -6,9 +6,9 @@ enable Samboy063.Cpp2IL.Core Samboy063 - 2022.0.2 - 2022.0.2 - 2022.0.2 + 2022.0.4 + 2022.0.4 + 2022.0.4 Copyright © Samboy063 2019-2022 true MIT diff --git a/Cpp2IL/Properties/AssemblyInfo.cs b/Cpp2IL/Properties/AssemblyInfo.cs index 80422349..750969e0 100644 --- a/Cpp2IL/Properties/AssemblyInfo.cs +++ b/Cpp2IL/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2022.0.2")] -[assembly: AssemblyFileVersion("2022.0.2")] \ No newline at end of file +[assembly: AssemblyVersion("2022.0.4")] +[assembly: AssemblyFileVersion("2022.0.4")] \ No newline at end of file diff --git a/LibCpp2IL/LibCpp2IL.csproj b/LibCpp2IL/LibCpp2IL.csproj index 512ff35f..c60ba90f 100644 --- a/LibCpp2IL/LibCpp2IL.csproj +++ b/LibCpp2IL/LibCpp2IL.csproj @@ -6,7 +6,7 @@ 9 netstandard2.0 Samboy063.LibCpp2IL - 2022.0.2 + 2022.0.4 true MIT git diff --git a/LibCpp2IL/Properties/AssemblyInfo.cs b/LibCpp2IL/Properties/AssemblyInfo.cs index f4ffc1c1..307b36ba 100644 --- a/LibCpp2IL/Properties/AssemblyInfo.cs +++ b/LibCpp2IL/Properties/AssemblyInfo.cs @@ -8,8 +8,8 @@ [assembly: AssemblyCopyright("Copyright © Samboy063 2019-2022")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("2022.0.2")] -[assembly: AssemblyFileVersion("2022.0.2")] +[assembly: AssemblyVersion("2022.0.4")] +[assembly: AssemblyFileVersion("2022.0.4")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from From fdd5e16ef58587a2294fbefd463e631793e03cb5 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Thu, 28 Apr 2022 10:09:09 +0100 Subject: [PATCH 36/76] Lib: Fix reading decompressed NSOs --- LibCpp2IL/LibCpp2IlMain.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LibCpp2IL/LibCpp2IlMain.cs b/LibCpp2IL/LibCpp2IlMain.cs index 1bf13850..bd0d3e71 100644 --- a/LibCpp2IL/LibCpp2IlMain.cs +++ b/LibCpp2IL/LibCpp2IlMain.cs @@ -172,7 +172,7 @@ public static bool Initialize(byte[] binaryBytes, byte[] metadataBytes, int[] un } else if (BitConverter.ToInt32(binaryBytes, 0) == 0x304F534E) //NSO0 { - var nso = new NsoFile(new MemoryStream(binaryBytes, 0, binaryBytes.Length, false, true), TheMetadata.maxMetadataUsages); + var nso = new NsoFile(new MemoryStream(binaryBytes, 0, binaryBytes.Length, true, true), TheMetadata.maxMetadataUsages); nso = nso.Decompress(); Binary = nso; (codereg, metareg) = nso.PlusSearch(TheMetadata.methodDefs.Count(x => x.methodIndex >= 0), TheMetadata.typeDefs.Length); From 1d251edc6148a0a933dbcb7c8dc97d66c85fe4c4 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Mon, 2 May 2022 14:30:15 +0100 Subject: [PATCH 37/76] Core: Support attribute restoration on method parameters --- Cpp2IL.Core/AttributeRestorer.cs | 12 +++++++++++- Cpp2IL.Core/AttributeRestorerPost29.cs | 26 ++++++++++++++++++++++++-- LibCpp2IL/Metadata/Il2CppMetadata.cs | 2 +- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/Cpp2IL.Core/AttributeRestorer.cs b/Cpp2IL.Core/AttributeRestorer.cs index f74dd0e5..7042518f 100644 --- a/Cpp2IL.Core/AttributeRestorer.cs +++ b/Cpp2IL.Core/AttributeRestorer.cs @@ -71,7 +71,7 @@ private static void Initialize() SharedState.FieldsByType[DummyTypeDefForAttributeList] = new List(); - _sortedTypeRangeList = LibCpp2IlMain.TheMetadata!.attributeTypeRanges.ToList(); + _sortedTypeRangeList = LibCpp2IlMain.TheMetadata!.attributeTypeRanges?.ToList() ?? new(); _sortedTypeRangeList.SortByExtractedKey(r => r.token); } @@ -131,6 +131,16 @@ private static void RestoreAttributesInType(Il2CppImageDefinition imageDef, T GetCustomAttributesByAttributeIndex(imageDef, methodDef.customAttributeIndex, methodDef.token, typeDefinition.Module, keyFunctionAddresses, methodDef.DeclaringType!.FullName + "::" + methodDefinition.FullName) .ForEach(attribute => methodDefinition.CustomAttributes.Add(attribute)); + + var ipd = methodDef.InternalParameterData!; + for (var i = 0; i < methodDef.parameterCount; i++) + { + var paramDef = ipd[i]; + var paramDefinition = methodDefinition.Parameters[i]; + + GetCustomAttributesByAttributeIndex(imageDef, paramDef.customAttributeIndex, paramDef.token, typeDefinition.Module, keyFunctionAddresses, $"Parameter {paramDefinition.Name} of {methodDefinition.FullName}") + .ForEach(attribute => paramDefinition.CustomAttributes.Add(attribute)); + } } //Apply custom attributes to properties diff --git a/Cpp2IL.Core/AttributeRestorerPost29.cs b/Cpp2IL.Core/AttributeRestorerPost29.cs index 2b650788..bcc04922 100644 --- a/Cpp2IL.Core/AttributeRestorerPost29.cs +++ b/Cpp2IL.Core/AttributeRestorerPost29.cs @@ -46,6 +46,16 @@ private static void RestoreAttributesInType(Il2CppImageDefinition imageDef, Type GetCustomAttributesByAttributeIndex(imageDef, typeDefinition.Module, methodDef.token) .ForEach(attribute => methodDefinition.CustomAttributes.Add(attribute)); + + var ipd = methodDef.InternalParameterData!; + for (var i = 0; i < methodDef.parameterCount; i++) + { + var paramDef = ipd[i]; + var paramDefinition = methodDefinition.Parameters[i]; + + GetCustomAttributesByAttributeIndex(imageDef, typeDefinition.Module, paramDef.token) + .ForEach(attribute => paramDefinition.CustomAttributes.Add(attribute)); + } } //Apply custom attributes to properties @@ -137,7 +147,7 @@ private static CustomAttribute ReadAndCreateCustomAttribute(ModuleDefinition mod for (var i = 0; i < numCtorArgs; i++) { var ctorParam = managedConstructor.Parameters[i]; - + var val = ReadBlob(pos, out var ctorArgBytesRead, out var typeReference); bytesRead += ctorArgBytesRead; pos += ctorArgBytesRead; @@ -235,8 +245,12 @@ private static CustomAttribute ReadAndCreateCustomAttribute(ModuleDefinition mod } catch (Exception e) { +#if DEBUG + throw; +#else Logger.WarnNewline($"Failed to parse custom attribute {constructor.DeclaringType!.FullName} due to an exception: {e.GetType()}: {e.Message}"); return MakeFallbackAttribute(module, managedConstructor) ?? throw new("Failed to resolve AttributeAttribute type"); +#endif } } @@ -261,7 +275,7 @@ private static CustomAttribute ReadAndCreateCustomAttribute(ModuleDefinition mod if (typeDef == null) throw new Exception($"Couldn't resolve type {objectTypeName}"); - + list.Add(new CustomAttributeArgument(typeDef, o)); } @@ -406,9 +420,17 @@ private static CustomAttribute ReadAndCreateCustomAttribute(ModuleDefinition mod throw new($"Array elements are different but array element type is {arrayElementType}, not IL2CPP_TYPE_OBJECT"); //Create a representation our side of the array + + if(arrTypeDef.Resolve().IsEnum) + arrTypeDef = arrTypeDef.Resolve().GetEnumUnderlyingType(); + var ourRuntimesArrayElementType = typeof(int).Module.GetType(arrTypeDef.FullName!); if (ourRuntimesArrayElementType == typeof(Type)) ourRuntimesArrayElementType = typeof(TypeReference); + + if(ourRuntimesArrayElementType == null) + throw new($"Failed to find type {arrTypeDef.FullName} in corlib module"); + var resultArray = Array.CreateInstance(ourRuntimesArrayElementType, arrLength); //Read the array diff --git a/LibCpp2IL/Metadata/Il2CppMetadata.cs b/LibCpp2IL/Metadata/Il2CppMetadata.cs index ce5cfbb2..b971cecb 100644 --- a/LibCpp2IL/Metadata/Il2CppMetadata.cs +++ b/LibCpp2IL/Metadata/Il2CppMetadata.cs @@ -26,7 +26,7 @@ public class Il2CppMetadata : ClassReadingBinaryReader private Il2CppFieldDefaultValue[] fieldDefaultValues; private Il2CppParameterDefaultValue[] parameterDefaultValues; public Il2CppPropertyDefinition[] propertyDefs; - public Il2CppCustomAttributeTypeRange[] attributeTypeRanges; + public Il2CppCustomAttributeTypeRange[] attributeTypeRanges; //Pre-29 private Il2CppStringLiteral[] stringLiterals; public Il2CppMetadataUsageList[] metadataUsageLists; private Il2CppMetadataUsagePair[] metadataUsagePairs; From b06ff301afabfc70f644f1646da7ee816e413c34 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Mon, 2 May 2022 14:33:37 +0100 Subject: [PATCH 38/76] Bump to 2022.0.5 --- Cpp2IL.Core/Cpp2IL.Core.csproj | 6 +++--- Cpp2IL/Properties/AssemblyInfo.cs | 4 ++-- LibCpp2IL/LibCpp2IL.csproj | 2 +- LibCpp2IL/Properties/AssemblyInfo.cs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cpp2IL.Core/Cpp2IL.Core.csproj b/Cpp2IL.Core/Cpp2IL.Core.csproj index 50ee21f9..9372fc95 100644 --- a/Cpp2IL.Core/Cpp2IL.Core.csproj +++ b/Cpp2IL.Core/Cpp2IL.Core.csproj @@ -6,9 +6,9 @@ enable Samboy063.Cpp2IL.Core Samboy063 - 2022.0.4 - 2022.0.4 - 2022.0.4 + 2022.0.5 + 2022.0.5 + 2022.0.5 Copyright © Samboy063 2019-2022 true MIT diff --git a/Cpp2IL/Properties/AssemblyInfo.cs b/Cpp2IL/Properties/AssemblyInfo.cs index 750969e0..d6842678 100644 --- a/Cpp2IL/Properties/AssemblyInfo.cs +++ b/Cpp2IL/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2022.0.4")] -[assembly: AssemblyFileVersion("2022.0.4")] \ No newline at end of file +[assembly: AssemblyVersion("2022.0.5")] +[assembly: AssemblyFileVersion("2022.0.5")] \ No newline at end of file diff --git a/LibCpp2IL/LibCpp2IL.csproj b/LibCpp2IL/LibCpp2IL.csproj index c60ba90f..55f31abc 100644 --- a/LibCpp2IL/LibCpp2IL.csproj +++ b/LibCpp2IL/LibCpp2IL.csproj @@ -6,7 +6,7 @@ 9 netstandard2.0 Samboy063.LibCpp2IL - 2022.0.4 + 2022.0.5 true MIT git diff --git a/LibCpp2IL/Properties/AssemblyInfo.cs b/LibCpp2IL/Properties/AssemblyInfo.cs index 307b36ba..cf9756d9 100644 --- a/LibCpp2IL/Properties/AssemblyInfo.cs +++ b/LibCpp2IL/Properties/AssemblyInfo.cs @@ -8,8 +8,8 @@ [assembly: AssemblyCopyright("Copyright © Samboy063 2019-2022")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("2022.0.4")] -[assembly: AssemblyFileVersion("2022.0.4")] +[assembly: AssemblyVersion("2022.0.5")] +[assembly: AssemblyFileVersion("2022.0.5")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from From 0d89ca3e79ddb32ea200a43ab0e7aabf7cf5ac4a Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Sun, 8 May 2022 17:34:08 +0100 Subject: [PATCH 39/76] Cpp2IL: Fix simple-attribute-restoration not being applied if analysis is enabled --- Cpp2IL/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cpp2IL/Program.cs b/Cpp2IL/Program.cs index ab5e11fb..d22c47c9 100644 --- a/Cpp2IL/Program.cs +++ b/Cpp2IL/Program.cs @@ -406,7 +406,7 @@ public static int MainWithArgs(Cpp2IlRuntimeArgs runtimeArgs) Logger.InfoNewline($"Applying type, method, and field attributes for {Cpp2IlApi.GeneratedAssemblies.Count} assemblies...This may take a couple of seconds"); var start = DateTime.Now; - Cpp2IlApi.RunAttributeRestorationForAllAssemblies(keyFunctionAddresses, parallel: LibCpp2IlMain.MetadataVersion >= 29 || LibCpp2IlMain.Binary!.InstructionSet is InstructionSet.X86_32 or InstructionSet.X86_64); + Cpp2IlApi.RunAttributeRestorationForAllAssemblies(runtimeArgs.SimpleAttributeRestoration ? null : keyFunctionAddresses, parallel: LibCpp2IlMain.MetadataVersion >= 29 || LibCpp2IlMain.Binary!.InstructionSet is InstructionSet.X86_32 or InstructionSet.X86_64); Logger.InfoNewline($"Finished Applying Attributes in {(DateTime.Now - start).TotalMilliseconds:F0}ms"); From 10e3b32768647248eab87a9e40a6a35f089da72c Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Sun, 8 May 2022 17:34:44 +0100 Subject: [PATCH 40/76] Core+Lib: Some tweaks to make key function address scanning better --- Cpp2IL.Core/Arm64KeyFunctionAddresses.cs | 26 +++++++---- Cpp2IL.Core/BaseKeyFunctionAddresses.cs | 55 +++--------------------- Cpp2IL.Core/WasmKeyFunctionAddresses.cs | 2 +- Cpp2IL.Core/X86KeyFunctionAddresses.cs | 8 ++-- LibCpp2IL/Elf/ElfFile.cs | 6 +++ LibCpp2IL/Il2CppBinary.cs | 2 + LibCpp2IL/MachO/MachOFile.cs | 5 ++- LibCpp2IL/NintendoSwitch/NsoFile.cs | 2 + LibCpp2IL/PE/PE.cs | 18 ++++++++ LibCpp2IL/Wasm/WasmFile.cs | 2 + 10 files changed, 63 insertions(+), 63 deletions(-) diff --git a/Cpp2IL.Core/Arm64KeyFunctionAddresses.cs b/Cpp2IL.Core/Arm64KeyFunctionAddresses.cs index 0eed1106..1b24e7a9 100644 --- a/Cpp2IL.Core/Arm64KeyFunctionAddresses.cs +++ b/Cpp2IL.Core/Arm64KeyFunctionAddresses.cs @@ -34,7 +34,17 @@ public Arm64KeyFunctionAddresses() { if (LibCpp2IlMain.Binary is not NsoFile) { - Logger.VerboseNewline($"\tLast attribute generator function is at address 0x{attributeGeneratorList[^1]:X}. Skipping everything before that."); + //The below code is flawed on at least some binaries - e.g. I have an APK for which this trims out the exports. + + var startFrom = attributeGeneratorList[^1]; + + //GetExportedFunctionPointers is only defined for ELF and PE files. Mach-O may die here. + var minExport = LibCpp2IlMain.Binary.GetAllExportedIl2CppFunctionPointers().Min(); + + if(minExport != 0) + startFrom = Math.Min(startFrom, minExport); + + Logger.VerboseNewline($"\tBinary likely contains nothing useful before 0x{startFrom:X} (first attribute generator at 0x{attributeGeneratorList[0]: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 @@ -42,10 +52,10 @@ public Arm64KeyFunctionAddresses() //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); + var toRemove = (int) (startFrom - primaryExecutableSectionVa); primaryExecutableSection = primaryExecutableSection.Skip(toRemove).ToArray(); - primaryExecutableSectionVa = attributeGeneratorList[^1]; + primaryExecutableSectionVa = startFrom; Logger.VerboseNewline($"\tBy trimming out attribute generator functions, reduced decompilation work by {toRemove} of {oldLength} bytes (a {toRemove * 100 / (float) oldLength:f1}% saving)"); @@ -71,7 +81,7 @@ public Arm64KeyFunctionAddresses() $"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]); + 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) @@ -91,7 +101,7 @@ public Arm64KeyFunctionAddresses() { 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]; + 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) @@ -129,9 +139,9 @@ public Arm64KeyFunctionAddresses() _allInstructions = disassembler.Disassemble(primaryExecutableSection, (long) primaryExecutableSectionVa).ToList(); } - protected override IEnumerable FindAllThunkFunctions(ulong addr, uint maxBytesBack = 0, params ulong[] addressesToIgnore) + protected override IEnumerable FindAllThunkFunctions(ulong addr, bool mustBeJumpNotCall, uint maxBytesBack = 0, params ulong[] addressesToIgnore) { - var allBranchesToAddr = _allInstructions.Where(i => i.Mnemonic is "b" or "bl") + var allBranchesToAddr = _allInstructions.Where(i => mustBeJumpNotCall ? i.Mnemonic is "b" : i.Mnemonic is "b" or "bl") .Where(i => i.Details.Operands[0].IsImmediate() && i.Details.Operands[0].Immediate == (long) addr) .ToList(); @@ -287,7 +297,7 @@ protected override void AttemptInstructionAnalysisToFillGaps() //Looks like we've been inlined and this is just vm::Object::New. addrVmObjectNew = probableResult; - var allThunks = FindAllThunkFunctions((ulong) addrVmObjectNew, 16, (ulong) probableResult).ToList(); + var allThunks = FindAllThunkFunctions((ulong) addrVmObjectNew, false, 16, (ulong) probableResult).ToList(); allThunks.SortByExtractedKey(GetCallerCount); //Sort in ascending order by caller count allThunks.Reverse(); //Reverse to be in descending order diff --git a/Cpp2IL.Core/BaseKeyFunctionAddresses.cs b/Cpp2IL.Core/BaseKeyFunctionAddresses.cs index 72f0d3d8..6b3824dd 100644 --- a/Cpp2IL.Core/BaseKeyFunctionAddresses.cs +++ b/Cpp2IL.Core/BaseKeyFunctionAddresses.cs @@ -156,53 +156,7 @@ private void FindThunks() { Logger.Verbose("\t\tLooking for il2cpp_codegen_object_new as a thunk of vm::Object::New..."); - var potentialThunks = FindAllThunkFunctions(il2cpp_vm_object_new, 16); - - //Sort by caller count in ascending order - var list = potentialThunks.Select(ptr => (ptr, count: GetCallerCount(ptr))).ToList(); - list.SortByExtractedKey(pair => pair.count); - - //Sort in descending order - most called first - list.Reverse(); - - //Take first as the target - il2cpp_codegen_object_new = list.FirstOrDefault().ptr; - - Logger.VerboseNewline($"Found at 0x{il2cpp_codegen_object_new:X}"); - } - - if (il2cpp_type_get_object != 0) - { - Logger.Verbose("\t\tMapping il2cpp_resolve_icall 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}"); - } - - if (il2cpp_resolve_icall != 0) - { - Logger.Verbose("\t\tMapping il2cpp_resolve_icall to InternalCalls::Resolve..."); - InternalCalls_Resolve = FindFunctionThisIsAThunkOf(il2cpp_resolve_icall); - Logger.VerboseNewline($"Found at 0x{InternalCalls_Resolve:X}"); - } - - if (il2cpp_string_new != 0) - { - Logger.Verbose("\t\tMapping il2cpp_string_new to String::New..."); - il2cpp_vm_string_new = FindFunctionThisIsAThunkOf(il2cpp_string_new); - Logger.VerboseNewline($"Found at 0x{il2cpp_vm_string_new:X}"); - } - - if (il2cpp_string_new_wrapper != 0) - { - Logger.Verbose("\t\tMapping il2cpp_string_new_wrapper to String::NewWrapper..."); - il2cpp_vm_string_newWrapper = FindFunctionThisIsAThunkOf(il2cpp_string_new_wrapper); - Logger.VerboseNewline($"Found at 0x{il2cpp_vm_string_newWrapper:X}"); - } - - if (il2cpp_vm_string_newWrapper != 0) - { - Logger.Verbose("\t\tMapping String::NewWrapper to il2cpp_codegen_string_new_wrapper..."); - il2cpp_codegen_string_new_wrapper = FindAllThunkFunctions(il2cpp_vm_string_newWrapper, 0, il2cpp_string_new_wrapper).FirstOrDefault(); + var potentialThunks = FindAllThunkFunctions(il2cpp_vm_object_new, false, 0, il2cpp_string_new_wrapper).FirstOrDefault(); Logger.VerboseNewline($"Found at 0x{il2cpp_codegen_string_new_wrapper:X}"); } @@ -230,7 +184,7 @@ private void FindThunks() if (il2cpp_vm_exception_raise != 0) { Logger.Verbose("\t\tMapping il2cpp::vm::Exception::Raise to il2cpp_codegen_raise_exception..."); - il2cpp_codegen_raise_exception = FindAllThunkFunctions(il2cpp_vm_exception_raise, 4, il2cpp_raise_exception).FirstOrDefault(); + il2cpp_codegen_raise_exception = FindAllThunkFunctions(il2cpp_vm_exception_raise, false, 4, il2cpp_raise_exception).FirstOrDefault(); Logger.VerboseNewline($"Found at 0x{il2cpp_codegen_raise_exception:X}"); } @@ -251,7 +205,7 @@ private void FindThunks() if (il2cpp_vm_array_new_specific != 0) { Logger.Verbose("\t\tLooking for SzArrayNew as a thunk function proxying Array::NewSpecific..."); - SzArrayNew = FindAllThunkFunctions(il2cpp_vm_array_new_specific, 4, il2cpp_array_new_specific).FirstOrDefault(); + SzArrayNew = FindAllThunkFunctions(il2cpp_vm_array_new_specific, true, 8, il2cpp_array_new_specific).FirstOrDefault(); Logger.VerboseNewline($"Found at 0x{SzArrayNew:X}"); } } @@ -262,10 +216,11 @@ private void FindThunks() /// Given a function at addr, find a function which serves no purpose other than to call addr. /// /// The address of the function to call. + /// If true, only return thunks using a no-return jump, not a call (e.g b not bl in arm64, jmp not call in x86) /// The maximum number of bytes to go back from any branching instructions to find the actual start of the thunk function. /// A list of function addresses which this function must not return /// The address of the first function in the file which thunks addr, starts within maxBytesBack bytes of the branch, and is not contained within addressesToIgnore, else 0 if none can be found. - protected abstract IEnumerable FindAllThunkFunctions(ulong addr, uint maxBytesBack = 0, params ulong[] addressesToIgnore); + protected abstract IEnumerable FindAllThunkFunctions(ulong addr, bool mustBeJumpNotCall, uint maxBytesBack = 0, params ulong[] addressesToIgnore); /// /// Given a function at thunkPtr, return the address of the function that said function exists only to call. diff --git a/Cpp2IL.Core/WasmKeyFunctionAddresses.cs b/Cpp2IL.Core/WasmKeyFunctionAddresses.cs index 5ce70811..a9971fb9 100644 --- a/Cpp2IL.Core/WasmKeyFunctionAddresses.cs +++ b/Cpp2IL.Core/WasmKeyFunctionAddresses.cs @@ -9,7 +9,7 @@ protected override ulong GetObjectIsInstFromSystemType() return 0; } - protected override IEnumerable FindAllThunkFunctions(ulong addr, uint maxBytesBack = 0, params ulong[] addressesToIgnore) + protected override IEnumerable FindAllThunkFunctions(ulong addr, bool mustBeJumpNotCall, uint maxBytesBack = 0, params ulong[] addressesToIgnore) { yield break; } diff --git a/Cpp2IL.Core/X86KeyFunctionAddresses.cs b/Cpp2IL.Core/X86KeyFunctionAddresses.cs index c60f2867..be3b7a63 100644 --- a/Cpp2IL.Core/X86KeyFunctionAddresses.cs +++ b/Cpp2IL.Core/X86KeyFunctionAddresses.cs @@ -23,7 +23,7 @@ private InstructionList DisassembleTextSection() return _cachedDisassembledBytes; } - protected override IEnumerable FindAllThunkFunctions(ulong addr, uint maxBytesBack = 0, params ulong[] addressesToIgnore) + protected override IEnumerable FindAllThunkFunctions(ulong addr, bool mustBeJumpNotCall, uint maxBytesBack = 0, params ulong[] addressesToIgnore) { //Disassemble .text var allInstructions = DisassembleTextSection(); @@ -47,7 +47,8 @@ protected override IEnumerable FindAllThunkFunctions(ulong addr, uint max //Double-cc = thunk if (previousByte == 0xCC && nextByte == 0xCC) { - yield return matchingJmp.IP; + if(!mustBeJumpNotCall || matchingJmp.Mnemonic is Mnemonic.Jmp) + yield return matchingJmp.IP; continue; } @@ -61,7 +62,8 @@ protected override IEnumerable FindAllThunkFunctions(ulong addr, uint max if (LibCpp2IlMain.Binary!.GetByteAtRawAddress(offsetInPe - backtrack) == 0xCC) { - yield return matchingJmp.IP - (backtrack - 1); + if(!mustBeJumpNotCall || matchingJmp.Mnemonic is Mnemonic.Jmp) + yield return matchingJmp.IP - (backtrack - 1); break; } } diff --git a/LibCpp2IL/Elf/ElfFile.cs b/LibCpp2IL/Elf/ElfFile.cs index c1dcc99c..b2e25835 100644 --- a/LibCpp2IL/Elf/ElfFile.cs +++ b/LibCpp2IL/Elf/ElfFile.cs @@ -633,6 +633,12 @@ public override ulong MapRawAddressToVirtual(uint offset) public override byte[] GetRawBinaryContent() => _raw; + public override ulong[] GetAllExportedIl2CppFunctionPointers() => _exportTable + .Where(p => p.Key.StartsWith("il2cpp_")) + .Select(p => p.Value.VirtualAddress) + .Where(va => va > 0) + .ToArray(); + public override ulong GetVirtualAddressOfExportedFunctionByName(string toFind) { if (!_exportTable.TryGetValue(toFind, out var exportedSymbol)) diff --git a/LibCpp2IL/Il2CppBinary.cs b/LibCpp2IL/Il2CppBinary.cs index 07a0a17f..f4de9207 100644 --- a/LibCpp2IL/Il2CppBinary.cs +++ b/LibCpp2IL/Il2CppBinary.cs @@ -387,6 +387,8 @@ public ulong GetMethodPointer(int methodIndex, int methodDefinitionIndex, int im public abstract byte[] GetRawBinaryContent(); public abstract ulong GetVirtualAddressOfExportedFunctionByName(string toFind); + + public abstract ulong[] GetAllExportedIl2CppFunctionPointers(); public abstract byte[] GetEntirePrimaryExecutableSection(); diff --git a/LibCpp2IL/MachO/MachOFile.cs b/LibCpp2IL/MachO/MachOFile.cs index 699a2cea..ffd15802 100644 --- a/LibCpp2IL/MachO/MachOFile.cs +++ b/LibCpp2IL/MachO/MachOFile.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using System.Linq; using LibCpp2IL.Logging; @@ -108,6 +109,8 @@ public override ulong GetRVA(ulong pointer) public override byte[] GetRawBinaryContent() => _raw; + public override ulong[] GetAllExportedIl2CppFunctionPointers() => Array.Empty(); + public override ulong GetVirtualAddressOfExportedFunctionByName(string toFind) { return 0; //TODO? diff --git a/LibCpp2IL/NintendoSwitch/NsoFile.cs b/LibCpp2IL/NintendoSwitch/NsoFile.cs index fa50b5f0..6a83fccf 100644 --- a/LibCpp2IL/NintendoSwitch/NsoFile.cs +++ b/LibCpp2IL/NintendoSwitch/NsoFile.cs @@ -349,6 +349,8 @@ public override ulong GetRVA(ulong pointer) } public override byte[] GetRawBinaryContent() => _raw; + + public override ulong[] GetAllExportedIl2CppFunctionPointers() => Array.Empty(); public override ulong GetVirtualAddressOfExportedFunctionByName(string toFind) { diff --git a/LibCpp2IL/PE/PE.cs b/LibCpp2IL/PE/PE.cs index f5f07589..24ed0c88 100644 --- a/LibCpp2IL/PE/PE.cs +++ b/LibCpp2IL/PE/PE.cs @@ -154,6 +154,24 @@ private void LoadPeExportTable() } } + public override ulong[] GetAllExportedIl2CppFunctionPointers() + { + if(peExportedFunctionPointers == null) + LoadPeExportTable(); + + return peExportedFunctionPointers!.Where((e, i) => GetExportedFunctionName(i).StartsWith("il2cpp_")).Select(e => (ulong)e).ToArray(); + } + + private string GetExportedFunctionName(int index) + { + if (peExportedFunctionPointers == null) + LoadPeExportTable(); + + var stringAddress = peExportedFunctionNamePtrs[index]; + var rawStringAddress = MapVirtualAddressToRaw(stringAddress + peImageBase); + return ReadStringToNull(rawStringAddress); + } + public override ulong GetVirtualAddressOfExportedFunctionByName(string toFind) { if (peExportedFunctionPointers == null) diff --git a/LibCpp2IL/Wasm/WasmFile.cs b/LibCpp2IL/Wasm/WasmFile.cs index 807dc13f..dd09e967 100644 --- a/LibCpp2IL/Wasm/WasmFile.cs +++ b/LibCpp2IL/Wasm/WasmFile.cs @@ -227,6 +227,8 @@ public override ulong GetRVA(ulong pointer) { return pointer; } + + public override ulong[] GetAllExportedIl2CppFunctionPointers() => Array.Empty(); public override ulong GetVirtualAddressOfExportedFunctionByName(string toFind) { From 9c965c89d4eacd573352dbed41f2130a77026dd7 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Sun, 8 May 2022 17:35:11 +0100 Subject: [PATCH 41/76] Core: Arm64: Implement requirements for array allocation detection --- .../Actions/ARM64/Arm64AllocateArrayAction.cs | 60 +++++++++++++++++++ .../ARM64/Arm64ManagedFunctionCallAction.cs | 11 ++++ .../Base/AbstractArrayAllocationAction.cs | 1 - .../AsmAnalyzerArmV8A.InstructionChecks.cs | 4 ++ 4 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 Cpp2IL.Core/Analysis/Actions/ARM64/Arm64AllocateArrayAction.cs diff --git a/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64AllocateArrayAction.cs b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64AllocateArrayAction.cs new file mode 100644 index 00000000..b98f9949 --- /dev/null +++ b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64AllocateArrayAction.cs @@ -0,0 +1,60 @@ +using System; +using Cpp2IL.Core.Analysis.Actions.Base; +using Cpp2IL.Core.Analysis.ResultModels; +using Gee.External.Capstone.Arm64; +using Mono.Cecil; + +namespace Cpp2IL.Core.Analysis.Actions.ARM64 +{ + public class Arm64AllocateArrayAction : AbstractArrayAllocationAction + { + public Arm64AllocateArrayAction(MethodAnalysis context, Arm64Instruction associatedInstruction) : base(context, associatedInstruction) + { + //X0 => type of array + //X1 => size + //Return array in x0 + var typeConstant = context.GetConstantInReg("x0"); + + if (typeConstant == null) + return; + + if (typeConstant.Value is TypeReference reference) + { + TypeOfArray = reference; + } + + var sizeOperand = context.GetOperandInRegister("x1"); + + if (sizeOperand == null) + return; + + if (sizeOperand is LocalDefinition {KnownInitialValue: ulong or uint} local) + { + RegisterUsedLocal(local, context); + SizeAllocated = Convert.ToInt32(local.KnownInitialValue); + } + else if (sizeOperand is ConstantDefinition {Value: ulong sizeC}) + { + SizeAllocated = (int) sizeC; + } else if (sizeOperand is ConstantDefinition {Value: uint sizeCSmall}) + { + SizeAllocated = (int) sizeCSmall; + } else if (sizeOperand is ConstantDefinition {Value: int sizeCSmallUnsigned}) + { + SizeAllocated = sizeCSmallUnsigned; + } + else if (sizeOperand is LocalDefinition localDefinition) + { + LocalArraySize = true; + LocalUsedForArraySize = localDefinition; + RegisterUsedLocal(localDefinition, context); + } + + if (TypeOfArray is not ArrayType arrayType) + return; + + LocalWritten = context.MakeLocal(arrayType, reg: "x0", knownInitialValue: new AllocatedArray(SizeAllocated, arrayType)); + RegisterUsedLocal(LocalWritten, context); //Used implicitly until I can find out what's causing these issues + } + } +} \ No newline at end of file diff --git a/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64ManagedFunctionCallAction.cs b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64ManagedFunctionCallAction.cs index 77331057..4a17cec2 100644 --- a/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64ManagedFunctionCallAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/ARM64/Arm64ManagedFunctionCallAction.cs @@ -72,5 +72,16 @@ private void HandleReturnType(MethodAnalysis context) } } } + + public override string? ToPsuedoCode() + { + if (wasArrayInstantiation) + { + var arrayType = ((ArrayType) ((LocalDefinition) Arguments![0]!).Type!).ElementType; + return $"{Arguments![0]!.GetPseudocodeRepresentation()} = new {arrayType}[] {{{string.Join(", ", instantiatedArrayValues!)}}}"; + } + + return base.ToPsuedoCode(); + } } } \ No newline at end of file diff --git a/Cpp2IL.Core/Analysis/Actions/Base/AbstractArrayAllocationAction.cs b/Cpp2IL.Core/Analysis/Actions/Base/AbstractArrayAllocationAction.cs index 9496602f..423b6eac 100644 --- a/Cpp2IL.Core/Analysis/Actions/Base/AbstractArrayAllocationAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/Base/AbstractArrayAllocationAction.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using Cpp2IL.Core.Analysis.ResultModels; using Mono.Cecil; using Mono.Cecil.Cil; diff --git a/Cpp2IL.Core/Analysis/AsmAnalyzerArmV8A.InstructionChecks.cs b/Cpp2IL.Core/Analysis/AsmAnalyzerArmV8A.InstructionChecks.cs index 33065b79..480af6d2 100644 --- a/Cpp2IL.Core/Analysis/AsmAnalyzerArmV8A.InstructionChecks.cs +++ b/Cpp2IL.Core/Analysis/AsmAnalyzerArmV8A.InstructionChecks.cs @@ -68,6 +68,10 @@ private void CheckForSingleOpInstruction(Arm64Instruction instruction) { Analysis.Actions.Add(new Arm64ManagedFunctionCallAction(Analysis, instruction)); } + else if (jumpTarget == _keyFunctionAddresses.il2cpp_array_new_specific || jumpTarget == _keyFunctionAddresses.il2cpp_vm_array_new_specific || jumpTarget == _keyFunctionAddresses.SzArrayNew) + { + Analysis.Actions.Add(new Arm64AllocateArrayAction(Analysis, instruction)); + } else if (jumpTarget == _keyFunctionAddresses.il2cpp_object_new || jumpTarget == _keyFunctionAddresses.il2cpp_vm_object_new || jumpTarget == _keyFunctionAddresses.il2cpp_codegen_object_new) { Analysis.Actions.Add(new Arm64NewObjectAction(Analysis, instruction)); From 171302816c04d1d8acafb0da55ddf8520991eb5c Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Thu, 12 May 2022 13:12:21 +0100 Subject: [PATCH 42/76] Lib: Disable check for metadata usages != 0 on v27 because it's not always true --- LibCpp2IL/BinarySearcher.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LibCpp2IL/BinarySearcher.cs b/LibCpp2IL/BinarySearcher.cs index 74dd1a5d..1858a706 100644 --- a/LibCpp2IL/BinarySearcher.cs +++ b/LibCpp2IL/BinarySearcher.cs @@ -334,8 +334,8 @@ public ulong FindMetadataRegistrationPost24_5() if (LibCpp2IlMain.MetadataVersion >= 27f && (metaReg.metadataUsagesCount != 0 || metaReg.metadataUsages != 0)) { //Too many metadata usages - should be 0 on v27 - LibLogger.VerboseNewline($"\t\t\tRejecting metadata registration 0x{va:X} because it has {metaReg.metadataUsagesCount} metadata usages at a pointer of 0x{metaReg.metadataUsages:X}. We're on v27, these should be 0."); - continue; + LibLogger.VerboseNewline($"\t\t\tWarning: metadata registration 0x{va:X} has {metaReg.metadataUsagesCount} metadata usages at a pointer of 0x{metaReg.metadataUsages:X}. We're on v27, these should be 0."); + // continue; } if (metaReg.typeDefinitionsSizesCount != LibCpp2IlMain.TheMetadata!.typeDefs.Length) From b0acbd5e53a789cf4a1aedd1cfc93a7d5f30d918 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Wed, 15 Jun 2022 00:05:08 +0100 Subject: [PATCH 43/76] Lib: Elf: backport reloc changes from dev branch --- LibCpp2IL/Elf/ElfFile.cs | 1 + LibCpp2IL/Elf/ElfRelocationType.cs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/LibCpp2IL/Elf/ElfFile.cs b/LibCpp2IL/Elf/ElfFile.cs index b2e25835..1370ee4c 100644 --- a/LibCpp2IL/Elf/ElfFile.cs +++ b/LibCpp2IL/Elf/ElfFile.cs @@ -290,6 +290,7 @@ private void ProcessRelocations() (ElfRelocationType.R_386_JMP_SLOT, InstructionSet.X86_32) => (symValue, true), // S (ElfRelocationType.R_AMD64_64, InstructionSet.X86_64) => (symValue + addend, true), // S + A + (ElfRelocationType.R_AMD64_RELATIVE, InstructionSet.X86_64) => (addend, true), //Base address + A _ => (0, false) }; diff --git a/LibCpp2IL/Elf/ElfRelocationType.cs b/LibCpp2IL/Elf/ElfRelocationType.cs index 3d11ead9..9ac22449 100644 --- a/LibCpp2IL/Elf/ElfRelocationType.cs +++ b/LibCpp2IL/Elf/ElfRelocationType.cs @@ -18,6 +18,7 @@ public enum ElfRelocationType : uint R_386_GLOB_DAT = 6, R_386_JMP_SLOT = 7, - R_AMD64_64 = 1 + R_AMD64_64 = 1, + R_AMD64_RELATIVE = 8, } } \ No newline at end of file From 6adee3ce237932781c8bd79ff84480132539a879 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Wed, 15 Jun 2022 00:05:29 +0100 Subject: [PATCH 44/76] Core: AttrRes: Fix potential OOB read caused by incorrect index --- Cpp2IL.Core/AttributeRestorer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cpp2IL.Core/AttributeRestorer.cs b/Cpp2IL.Core/AttributeRestorer.cs index 7042518f..1b8fc675 100644 --- a/Cpp2IL.Core/AttributeRestorer.cs +++ b/Cpp2IL.Core/AttributeRestorer.cs @@ -451,7 +451,7 @@ private static List GetAttributesFromRange(Il2CppCustomAttribute private static ulong GetAddressOfAttributeGeneratorFunction(Il2CppImageDefinition imageDef, Il2CppCustomAttributeTypeRange attributeTypeRange) { - var rangeIndex = _sortedTypeRangeList.BinarySearch(attributeTypeRange, new TokenComparer()); + var rangeIndex = _sortedTypeRangeList.BinarySearch(imageDef.customAttributeStart, (int) imageDef.customAttributeCount, attributeTypeRange, new TokenComparer()); if (rangeIndex < 0) { From 7d98e833470099c99de2e1be26a47f9365fd52f6 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Wed, 15 Jun 2022 21:18:54 +0100 Subject: [PATCH 45/76] [skip ci] Probably about time I changed this --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 30eff7fe..a60cdf8b 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Due to, well, very much forseen circumstances, we are in the process of rewriting most of cpp2il from the ground up over in the development branch. -As such, bug fixes, and new updates, are likely to be sparse until the work is done, which I expect to be sometime in January 2022. +As such, bug fixes, and new updates, are likely to be sparse until the work is done, which ~~I expect to be sometime in January 2022~~ will be when it will be, basically. ### Need Help? Join [the discord](https://discord.gg/XdggT7XZXm)! From 2fa04512ba2459a49430855bd7f6aff1ba61b1c1 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Thu, 30 Jun 2022 17:33:47 +0100 Subject: [PATCH 46/76] AttrRes: Don't sort type ranges, that's dumb and won't work. --- Cpp2IL.Core/AttributeRestorer.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Cpp2IL.Core/AttributeRestorer.cs b/Cpp2IL.Core/AttributeRestorer.cs index 1b8fc675..e0eee3ab 100644 --- a/Cpp2IL.Core/AttributeRestorer.cs +++ b/Cpp2IL.Core/AttributeRestorer.cs @@ -32,7 +32,7 @@ public static class AttributeRestorer internal static readonly TypeDefinition DummyTypeDefForAttributeList = new TypeDefinition("dummy", "AttributeList", TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit); private static readonly ConcurrentDictionary FieldToParameterMappings = new ConcurrentDictionary(); - private static List _sortedTypeRangeList; + private static List _attributeTypeRanges; private static Dictionary _attributeAttributeCtorsByModule = new(); static AttributeRestorer() => Initialize(); @@ -71,8 +71,7 @@ private static void Initialize() SharedState.FieldsByType[DummyTypeDefForAttributeList] = new List(); - _sortedTypeRangeList = LibCpp2IlMain.TheMetadata!.attributeTypeRanges?.ToList() ?? new(); - _sortedTypeRangeList.SortByExtractedKey(r => r.token); + _attributeTypeRanges = LibCpp2IlMain.TheMetadata!.attributeTypeRanges?.ToList() ?? new(); } /// @@ -92,7 +91,7 @@ internal static void Reset() _simpleFieldToPropertyCache.Clear(); - _sortedTypeRangeList.Clear(); + _attributeTypeRanges.Clear(); _attributeAttributeCtorsByModule.Clear(); @@ -451,7 +450,7 @@ private static List GetAttributesFromRange(Il2CppCustomAttribute private static ulong GetAddressOfAttributeGeneratorFunction(Il2CppImageDefinition imageDef, Il2CppCustomAttributeTypeRange attributeTypeRange) { - var rangeIndex = _sortedTypeRangeList.BinarySearch(imageDef.customAttributeStart, (int) imageDef.customAttributeCount, attributeTypeRange, new TokenComparer()); + var rangeIndex = _attributeTypeRanges.BinarySearch(imageDef.customAttributeStart, (int) imageDef.customAttributeCount, attributeTypeRange, new TokenComparer()); if (rangeIndex < 0) { From 6aca31e8a501947439d9dd9e646f72dcffa1752e Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Sun, 3 Jul 2022 01:01:45 +0100 Subject: [PATCH 47/76] Fix possible exception if encountering a type for which the name is just "<" --- Cpp2IL.Core/Utils/MiscUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cpp2IL.Core/Utils/MiscUtils.cs b/Cpp2IL.Core/Utils/MiscUtils.cs index cae3572b..5e064686 100644 --- a/Cpp2IL.Core/Utils/MiscUtils.cs +++ b/Cpp2IL.Core/Utils/MiscUtils.cs @@ -334,7 +334,7 @@ public static TypeReference ImportTypeInto(MemberReference importInto, Il2CppTyp //Generics are dumb. var genericParams = Array.Empty(); - if (definedType == null && name.Contains("<")) + if (definedType == null && name.Contains("<") && name != "<") { //Replace < > with the number of generic params after a ` genericParams = GetGenericParams(name[(name.IndexOf("<", StringComparison.Ordinal) + 1)..^1]); From 4c5679bde2adb448d032c5afe0c478ee9596497b Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Sun, 3 Jul 2022 01:30:05 +0100 Subject: [PATCH 48/76] Lib: Elf: Change default search behavior to use PlusSearch --- LibCpp2IL/Elf/ElfFile.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/LibCpp2IL/Elf/ElfFile.cs b/LibCpp2IL/Elf/ElfFile.cs index 1370ee4c..b98c0734 100644 --- a/LibCpp2IL/Elf/ElfFile.cs +++ b/LibCpp2IL/Elf/ElfFile.cs @@ -592,15 +592,17 @@ private void ProcessInitializers() { LibLogger.VerboseNewline("Searching for il2cpp structures in an ELF binary using non-arch-specific method..."); - var searcher = new BinarySearcher(this, LibCpp2IlMain.TheMetadata!.methodDefs.Count(x => x.methodIndex >= 0), LibCpp2IlMain.TheMetadata!.typeDefs.Length); - - LibLogger.VerboseNewline("\tLooking for code reg (this might take a while)..."); - var codeReg = LibCpp2IlMain.MetadataVersion >= 24.2f ? searcher.FindCodeRegistrationPost2019() : searcher.FindCodeRegistrationPre2019(); - LibLogger.VerboseNewline($"\tGot code reg 0x{codeReg:X}"); + // var searcher = new BinarySearcher(this, LibCpp2IlMain.TheMetadata!.methodDefs.Count(x => x.methodIndex >= 0), LibCpp2IlMain.TheMetadata!.typeDefs.Length); - LibLogger.VerboseNewline($"\tLooking for meta reg ({(LibCpp2IlMain.MetadataVersion >= 27f ? "post-27" : "pre-27")})..."); - var metaReg = LibCpp2IlMain.MetadataVersion >= 27f ? searcher.FindMetadataRegistrationPost24_5() : searcher.FindMetadataRegistrationPre24_5(); - LibLogger.VerboseNewline($"\tGot meta reg 0x{metaReg:x}"); + var (codeReg, metaReg) = PlusSearch(LibCpp2IlMain.TheMetadata!.methodDefs.Count(x => x.methodIndex >= 0), LibCpp2IlMain.TheMetadata!.typeDefs.Length); + + // LibLogger.VerboseNewline("\tLooking for code reg (this might take a while)..."); + // var codeReg = LibCpp2IlMain.MetadataVersion >= 24.2f ? searcher.FindCodeRegistrationPost2019() : searcher.FindCodeRegistrationPre2019(); + // LibLogger.VerboseNewline($"\tGot code reg 0x{codeReg:X}"); + // + // LibLogger.VerboseNewline($"\tLooking for meta reg ({(LibCpp2IlMain.MetadataVersion >= 27f ? "post-27" : "pre-27")})..."); + // var metaReg = LibCpp2IlMain.MetadataVersion >= 27f ? searcher.FindMetadataRegistrationPost24_5() : searcher.FindMetadataRegistrationPre24_5(); + // LibLogger.VerboseNewline($"\tGot meta reg 0x{metaReg:x}"); return (codeReg, metaReg); From 4d6f48c35e8c98a94dc6b091681ca6e87a4fe778 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Sat, 6 Aug 2022 00:28:48 +0100 Subject: [PATCH 49/76] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a60cdf8b..416162c9 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,7 @@ If you do not wish for the output to be coloured, set the Environment Variable ` ## Credits -This application is built using .NET 5.0. +This application is built primarily using .NET 6.0, but a .NET Framework 4.7.2 build is also published for legacy purposes. It uses the following libraries, for which I am very thankful: From 53fd3c3f56fdddac48d63e14ed2c99b82c53340a Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Mon, 26 Sep 2022 00:57:49 +0100 Subject: [PATCH 50/76] [skip ci] Re-add mistakenly(?) removed code in KFA --- Cpp2IL.Core/BaseKeyFunctionAddresses.cs | 50 ++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/Cpp2IL.Core/BaseKeyFunctionAddresses.cs b/Cpp2IL.Core/BaseKeyFunctionAddresses.cs index 6b3824dd..619d3f42 100644 --- a/Cpp2IL.Core/BaseKeyFunctionAddresses.cs +++ b/Cpp2IL.Core/BaseKeyFunctionAddresses.cs @@ -156,7 +156,55 @@ private void FindThunks() { Logger.Verbose("\t\tLooking for il2cpp_codegen_object_new as a thunk of vm::Object::New..."); - var potentialThunks = FindAllThunkFunctions(il2cpp_vm_object_new, false, 0, il2cpp_string_new_wrapper).FirstOrDefault(); + var potentialThunks = FindAllThunkFunctions(il2cpp_vm_object_new, false, 16); + + //Sort by caller count in ascending order + var list = potentialThunks.Select(ptr => (ptr, count: GetCallerCount(ptr))).ToList(); + list.SortByExtractedKey(pair => pair.count); + + //Sort in descending order - most called first + list.Reverse(); + + //Take first as the target + il2cpp_codegen_object_new = list.FirstOrDefault().ptr; + + Logger.VerboseNewline($"Found at 0x{il2cpp_codegen_object_new:X}"); + } + + if (il2cpp_type_get_object != 0) + { + Logger.Verbose("\t\tMapping il2cpp_resolve_icall 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}"); + } + + if (il2cpp_resolve_icall != 0) + { + Logger.Verbose("\t\tMapping il2cpp_resolve_icall to InternalCalls::Resolve..."); + InternalCalls_Resolve = FindFunctionThisIsAThunkOf(il2cpp_resolve_icall); + Logger.VerboseNewline($"Found at 0x{InternalCalls_Resolve:X}"); + } + + if (il2cpp_string_new != 0) + { + Logger.Verbose("\t\tMapping il2cpp_string_new to String::New..."); + il2cpp_vm_string_new = FindFunctionThisIsAThunkOf(il2cpp_string_new); + Logger.VerboseNewline($"Found at 0x{il2cpp_vm_string_new:X}"); + } + + if (il2cpp_string_new_wrapper != 0) + { + Logger.Verbose("\t\tMapping il2cpp_string_new_wrapper to String::NewWrapper..."); + il2cpp_vm_string_newWrapper = FindFunctionThisIsAThunkOf(il2cpp_string_new_wrapper); + Logger.VerboseNewline($"Found at 0x{il2cpp_vm_string_newWrapper:X}"); + } + + if (il2cpp_vm_string_newWrapper != 0) + { + Logger.Verbose("\t\tMapping String::NewWrapper to il2cpp_codegen_string_new_wrapper..."); + il2cpp_codegen_string_new_wrapper = FindAllThunkFunctions(il2cpp_vm_string_newWrapper, false, 0, il2cpp_string_new_wrapper).FirstOrDefault(); + + // var potentialThunks = FindAllThunkFunctions(il2cpp_vm_object_new, false, 0, il2cpp_string_new_wrapper).FirstOrDefault(); Logger.VerboseNewline($"Found at 0x{il2cpp_codegen_string_new_wrapper:X}"); } From fc8f02a72e69e53e36c34db665986e97c8fb1e52 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Mon, 26 Sep 2022 00:59:32 +0100 Subject: [PATCH 51/76] Bump core, lib, and wrapper to 2022.0.6 in prep for release --- .github/workflows/dotnet-core.yml | 15 +++++++++++++++ Cpp2IL.Core/Cpp2IL.Core.csproj | 6 +++--- Cpp2IL/Properties/AssemblyInfo.cs | 4 ++-- LibCpp2IL/LibCpp2IL.csproj | 2 +- LibCpp2IL/Properties/AssemblyInfo.cs | 4 ++-- 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index ec73f976..0311fa3b 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -63,3 +63,18 @@ jobs: with: name: Cpp2IL-Mac path: ./Cpp2IL/bin/Release/net6.0/osx-x64/publish/Cpp2IL + - name: Upload LibCpp2IL nuget package + uses: actions/upload-artifact@v2 + with: + name: LibCpp2IL.nupkg + path: ./LibCpp2IL/bin/Release/LibCpp2IL*.nupkg + - name: Upload Cpp2IL.Core nuget package + uses: actions/upload-artifact@v2 + with: + name: LibCpp2IL.nupkg + path: ./Cpp2IL.Core/bin/Release/Cpp2IL.Core*.nupkg + - name: Upload WasmDisassembler nuget package + uses: actions/upload-artifact@v2 + with: + name: WasmDisassembler.nupkg + path: ./WasmDisassembler/bin/Release/WasmDisassembler*.nupkg \ No newline at end of file diff --git a/Cpp2IL.Core/Cpp2IL.Core.csproj b/Cpp2IL.Core/Cpp2IL.Core.csproj index 9372fc95..14527789 100644 --- a/Cpp2IL.Core/Cpp2IL.Core.csproj +++ b/Cpp2IL.Core/Cpp2IL.Core.csproj @@ -6,9 +6,9 @@ enable Samboy063.Cpp2IL.Core Samboy063 - 2022.0.5 - 2022.0.5 - 2022.0.5 + 2022.0.6 + 2022.0.6 + 2022.0.6 Copyright © Samboy063 2019-2022 true MIT diff --git a/Cpp2IL/Properties/AssemblyInfo.cs b/Cpp2IL/Properties/AssemblyInfo.cs index d6842678..b7334d0a 100644 --- a/Cpp2IL/Properties/AssemblyInfo.cs +++ b/Cpp2IL/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2022.0.5")] -[assembly: AssemblyFileVersion("2022.0.5")] \ No newline at end of file +[assembly: AssemblyVersion("2022.0.6")] +[assembly: AssemblyFileVersion("2022.0.6")] \ No newline at end of file diff --git a/LibCpp2IL/LibCpp2IL.csproj b/LibCpp2IL/LibCpp2IL.csproj index 55f31abc..3d45d05c 100644 --- a/LibCpp2IL/LibCpp2IL.csproj +++ b/LibCpp2IL/LibCpp2IL.csproj @@ -6,7 +6,7 @@ 9 netstandard2.0 Samboy063.LibCpp2IL - 2022.0.5 + 2022.0.6 true MIT git diff --git a/LibCpp2IL/Properties/AssemblyInfo.cs b/LibCpp2IL/Properties/AssemblyInfo.cs index cf9756d9..26f206e9 100644 --- a/LibCpp2IL/Properties/AssemblyInfo.cs +++ b/LibCpp2IL/Properties/AssemblyInfo.cs @@ -8,8 +8,8 @@ [assembly: AssemblyCopyright("Copyright © Samboy063 2019-2022")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("2022.0.5")] -[assembly: AssemblyFileVersion("2022.0.5")] +[assembly: AssemblyVersion("2022.0.6")] +[assembly: AssemblyFileVersion("2022.0.6")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from From c4a854c5c41fbaf831b518509a82956a4f734b0f Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Mon, 26 Sep 2022 01:05:14 +0100 Subject: [PATCH 52/76] Add missing prefixes to ci nuget pkg upload --- .github/workflows/dotnet-core.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index 0311fa3b..05511e01 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -67,14 +67,14 @@ jobs: uses: actions/upload-artifact@v2 with: name: LibCpp2IL.nupkg - path: ./LibCpp2IL/bin/Release/LibCpp2IL*.nupkg + path: ./LibCpp2IL/bin/Release/Samboy063.LibCpp2IL*.nupkg - name: Upload Cpp2IL.Core nuget package uses: actions/upload-artifact@v2 with: name: LibCpp2IL.nupkg - path: ./Cpp2IL.Core/bin/Release/Cpp2IL.Core*.nupkg + path: ./Cpp2IL.Core/bin/Release/Samboy063.Cpp2IL.Core*.nupkg - name: Upload WasmDisassembler nuget package uses: actions/upload-artifact@v2 with: name: WasmDisassembler.nupkg - path: ./WasmDisassembler/bin/Release/WasmDisassembler*.nupkg \ No newline at end of file + path: ./WasmDisassembler/bin/Release/Samboy063.WasmDisassembler*.nupkg From 15b56bc103d09c018ac4bed103bdcf8294fbe7ec Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Mon, 26 Sep 2022 21:13:41 +0100 Subject: [PATCH 53/76] Lib: Add support for v29.1, Bump core + lib to 2022.0.7 --- Cpp2IL.Core/Cpp2IL.Core.csproj | 6 +++--- Cpp2IL/Properties/AssemblyInfo.cs | 4 ++-- LibCpp2IL/BinaryStructures/Il2CppCodeRegistration.cs | 9 +++++++-- LibCpp2IL/LibCpp2IL.csproj | 2 +- LibCpp2IL/Metadata/Il2CppMetadata.cs | 9 ++++++++- LibCpp2IL/Properties/AssemblyInfo.cs | 4 ++-- 6 files changed, 23 insertions(+), 11 deletions(-) diff --git a/Cpp2IL.Core/Cpp2IL.Core.csproj b/Cpp2IL.Core/Cpp2IL.Core.csproj index 14527789..d774ec96 100644 --- a/Cpp2IL.Core/Cpp2IL.Core.csproj +++ b/Cpp2IL.Core/Cpp2IL.Core.csproj @@ -6,9 +6,9 @@ enable Samboy063.Cpp2IL.Core Samboy063 - 2022.0.6 - 2022.0.6 - 2022.0.6 + 2022.0.7 + 2022.0.7 + 2022.0.7 Copyright © Samboy063 2019-2022 true MIT diff --git a/Cpp2IL/Properties/AssemblyInfo.cs b/Cpp2IL/Properties/AssemblyInfo.cs index b7334d0a..6ce423a7 100644 --- a/Cpp2IL/Properties/AssemblyInfo.cs +++ b/Cpp2IL/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2022.0.6")] -[assembly: AssemblyFileVersion("2022.0.6")] \ No newline at end of file +[assembly: AssemblyVersion("2022.0.7")] +[assembly: AssemblyFileVersion("2022.0.7")] \ No newline at end of file diff --git a/LibCpp2IL/BinaryStructures/Il2CppCodeRegistration.cs b/LibCpp2IL/BinaryStructures/Il2CppCodeRegistration.cs index f20dec27..9ab568ff 100644 --- a/LibCpp2IL/BinaryStructures/Il2CppCodeRegistration.cs +++ b/LibCpp2IL/BinaryStructures/Il2CppCodeRegistration.cs @@ -25,8 +25,13 @@ public class Il2CppCodeRegistration [Version(Max = 24.5f)] //Removed in v27 public ulong customAttributeGeneratorListAddress; - public ulong unresolvedVirtualCallCount; - public ulong unresolvedVirtualCallPointers; + public ulong unresolvedVirtualCallCount; //Renamed to unresolvedIndirectCallCount in v29.1 + public ulong unresolvedVirtualCallPointers; //Renamed to unresolvedIndirectCallPointers in v29.1 + + [Version(Min = 29.1f)] + public ulong unresolvedInstanceCallPointers; + [Version(Min = 29.1f)] + public ulong unresolvedStaticCallPointers; public ulong interopDataCount; public ulong interopData; diff --git a/LibCpp2IL/LibCpp2IL.csproj b/LibCpp2IL/LibCpp2IL.csproj index 3d45d05c..01a3e633 100644 --- a/LibCpp2IL/LibCpp2IL.csproj +++ b/LibCpp2IL/LibCpp2IL.csproj @@ -6,7 +6,7 @@ 9 netstandard2.0 Samboy063.LibCpp2IL - 2022.0.6 + 2022.0.7 true MIT git diff --git a/LibCpp2IL/Metadata/Il2CppMetadata.cs b/LibCpp2IL/Metadata/Il2CppMetadata.cs index b971cecb..d1ce4260 100644 --- a/LibCpp2IL/Metadata/Il2CppMetadata.cs +++ b/LibCpp2IL/Metadata/Il2CppMetadata.cs @@ -98,7 +98,14 @@ public class Il2CppMetadata : ClassReadingBinaryReader else actualVersion = version; //2017.1.0 was the first v24 version } - else actualVersion = version; //Covers v29 + else if (version == 29) + { + if (unityVersion.IsGreaterEqual(2022, 1, 0, UnityVersionType.Beta, 7)) + actualVersion = 29.1f; //2022.1.0b7 introduces v29.1 which adds two new pointers to codereg + else + actualVersion = 29; //2021.3.0 introduces v29 + } + else actualVersion = version; LibLogger.InfoNewline($"\tUsing actual IL2CPP Metadata version {actualVersion}"); diff --git a/LibCpp2IL/Properties/AssemblyInfo.cs b/LibCpp2IL/Properties/AssemblyInfo.cs index 26f206e9..3944f978 100644 --- a/LibCpp2IL/Properties/AssemblyInfo.cs +++ b/LibCpp2IL/Properties/AssemblyInfo.cs @@ -8,8 +8,8 @@ [assembly: AssemblyCopyright("Copyright © Samboy063 2019-2022")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("2022.0.6")] -[assembly: AssemblyFileVersion("2022.0.6")] +[assembly: AssemblyVersion("2022.0.7")] +[assembly: AssemblyFileVersion("2022.0.7")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from From 844e286b3858b31966af0e01bd0a5e22c864140f Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Mon, 26 Sep 2022 21:17:59 +0100 Subject: [PATCH 54/76] [skip ci] Fix Lib and core nupkgs getting put in the same zip --- .github/workflows/dotnet-core.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index 05511e01..1e46795a 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -71,7 +71,7 @@ jobs: - name: Upload Cpp2IL.Core nuget package uses: actions/upload-artifact@v2 with: - name: LibCpp2IL.nupkg + name: Cpp2IL.Core.nupkg path: ./Cpp2IL.Core/bin/Release/Samboy063.Cpp2IL.Core*.nupkg - name: Upload WasmDisassembler nuget package uses: actions/upload-artifact@v2 From 2a50d2a51da152bde18b2110fd057db333038b4f Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Sat, 1 Oct 2022 20:38:41 +0100 Subject: [PATCH 55/76] CI: Do an initial build, not publish --- .github/workflows/dotnet-core.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index 1e46795a..fea5dde2 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -20,7 +20,7 @@ jobs: - name: Install dependencies run: dotnet restore - name: Build - Common Lib - run: dotnet publish /p:Configuration=Release --no-restore + run: dotnet build /p:Configuration=Release --no-restore working-directory: ./LibCpp2IL/ - name: Build - Windows x64 working-directory: ./Cpp2IL/ From 73dd6505edc767d38840696ce720223717f20639 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Sat, 1 Oct 2022 20:48:25 +0100 Subject: [PATCH 56/76] Lib: Remove RuntimeIdentifiers --- LibCpp2IL/LibCpp2IL.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/LibCpp2IL/LibCpp2IL.csproj b/LibCpp2IL/LibCpp2IL.csproj index 01a3e633..ecf32712 100644 --- a/LibCpp2IL/LibCpp2IL.csproj +++ b/LibCpp2IL/LibCpp2IL.csproj @@ -2,7 +2,6 @@ enable - ubuntu-x64;win-x64;osx-x64 9 netstandard2.0 Samboy063.LibCpp2IL From c2a3a28fe8d4f34a5f4d56fa078269f5f3145457 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Thu, 24 Nov 2022 20:01:13 +0000 Subject: [PATCH 57/76] x86: CMFA: At least print all methods if we don't know which --- .../Actions/x86/Important/CallManagedFunctionAction.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Cpp2IL.Core/Analysis/Actions/x86/Important/CallManagedFunctionAction.cs b/Cpp2IL.Core/Analysis/Actions/x86/Important/CallManagedFunctionAction.cs index 705c329e..06163b57 100644 --- a/Cpp2IL.Core/Analysis/Actions/x86/Important/CallManagedFunctionAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/x86/Important/CallManagedFunctionAction.cs @@ -218,7 +218,10 @@ public CallManagedFunctionAction(MethodAnalysis context, Instructio if (possibleTarget != null) ManagedMethodBeingCalled = SharedState.UnmanagedToManagedMethods[possibleTarget]; else - AddComment($"Failed to resolve any matching method (there are {listOfCallableMethods.Count} at this address)"); + { + var methodSigs = listOfCallableMethods.Select(m => m.DeclaringType?.FullName + "::" + m.Name).Take(10).ToList(); + AddComment($"Failed to resolve any matching method (there are {listOfCallableMethods.Count} at this address - {string.Join(", ", methodSigs)})."); + } if (ManagedMethodBeingCalled != null && MethodUtils.GetMethodInfoArg(ManagedMethodBeingCalled, context) is ConstantDefinition {Value: GenericMethodReference gmr} arg) { From 258335c8f1f716156b9254c4951847ac63edbcfd Mon Sep 17 00:00:00 2001 From: M47Z <38143854+IamM47Z@users.noreply.github.com> Date: Fri, 25 Nov 2022 19:26:12 +0100 Subject: [PATCH 58/76] Add .app support Adds a MachO handler to get the files from a .app Unity IL2Cpp app --- Cpp2IL/Program.cs | 56 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/Cpp2IL/Program.cs b/Cpp2IL/Program.cs index d22c47c9..4491ebfd 100644 --- a/Cpp2IL/Program.cs +++ b/Cpp2IL/Program.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; @@ -7,6 +7,7 @@ using System.Reflection; using System.Runtime; using System.Runtime.InteropServices; +using System.Xml.Linq; using CommandLine; using Cpp2IL.Core; using Cpp2IL.Core.Utils; @@ -37,10 +38,13 @@ internal class Program private static void ResolvePathsFromCommandLine(string gamePath, string? inputExeName, ref Cpp2IlRuntimeArgs args) { if (Directory.Exists(gamePath)) - HandleWindowsGamePath(gamePath, inputExeName, ref args); + if (Path.GetExtension(gamePath).ToLowerInvariant() == ".app") + HandleMachOGamePath(gamePath, ref args); + else + HandleWindowsGamePath(gamePath, inputExeName, ref args); else if (File.Exists(gamePath) && Path.GetExtension(gamePath).ToLowerInvariant() == ".apk") HandleSingleApk(gamePath, ref args); - else if(File.Exists(gamePath) && Path.GetExtension(gamePath).ToLowerInvariant() == ".xapk") + else if (File.Exists(gamePath) && Path.GetExtension(gamePath).ToLowerInvariant() == ".xapk") HandleXapk(gamePath, ref args); else throw new SoftException($"Could not find a valid unity game at {gamePath}"); @@ -243,6 +247,52 @@ private static void HandleXapk(string gamePath, ref Cpp2IlRuntimeArgs args) args.Valid = true; } + private static void HandleMachOGamePath(string gamePath, ref Cpp2IlRuntimeArgs args) + { + //APP + //Metadata: Contents/Resources/Data/il2cpp_data/Metadata/global-metadata.dat + //Binary: Contents/Frameworks/GameAssembly.dylib + + Logger.InfoNewline($"Attempting to extract required files from APP {gamePath}", "APP"); + + var binary = Path.Combine(gamePath, "Contents", "Frameworks", "GameAssembly.dylib"); + var globalMetadata = Path.Combine(gamePath, "Contents", "Resources", "Data", "il2cpp_data", "Metadata", "global-metadata.dat"); + var globalgamemanagers = Path.Combine(gamePath, "Contents", "Resources", "Data", "globalgamemanagers"); + + if (binary == null) + throw new SoftException("Could not find GameAssembly.dylib inside the app"); + if (globalMetadata == null) + throw new SoftException("Could not find global-metadata.dat inside the app"); + if (globalgamemanagers == null) + throw new SoftException("Could not find globalgamemanagers inside the app"); + + args.PathToAssembly = binary; + args.PathToMetadata = globalMetadata; + + Logger.VerboseNewline("Attempting to get unity version..."); + + Logger.InfoNewline("Reading globalgamemanagers to determine unity version...", "APP"); + var uv = (File.Exists(globalgamemanagers) ? + Cpp2IlApi.GetVersionFromGlobalGameManagers(File.ReadAllBytes(globalgamemanagers)) : null); + Logger.VerboseNewline($"First-attempt unity version detection gave: {(uv == null ? "null" : string.Join(".", uv))}"); + + if (uv == null) + { + Logger.Warn("Could not determine unity version, probably due to not running on windows and not having any assets files to determine it from. Enter unity version, if known, in the format of (xxxx.x.x), else nothing to fail: "); + var userInputUv = Console.ReadLine(); + uv = userInputUv?.Split('.').Select(int.Parse).ToArray(); + + if (uv == null) + throw new SoftException("Failed to determine unity version. If you're not running on windows, I need a globalgamemanagers file, or you need to use the force options."); + } + + args.UnityVersion = uv; + + Logger.InfoNewline($"Determined game's unity version to be {string.Join(".", args.UnityVersion)}", "APP"); + + args.Valid = true; + } + private static Cpp2IlRuntimeArgs GetRuntimeOptionsFromCommandLine(string[] commandLine) { var parserResult = Parser.Default.ParseArguments(commandLine); From 13f6f676558abb35012d1ff720e32140fa21314a Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Fri, 25 Nov 2022 20:26:31 +0000 Subject: [PATCH 59/76] NSO: Symbol table is actually optional --- LibCpp2IL/NintendoSwitch/NsoFile.cs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/LibCpp2IL/NintendoSwitch/NsoFile.cs b/LibCpp2IL/NintendoSwitch/NsoFile.cs index 6a83fccf..32a20929 100644 --- a/LibCpp2IL/NintendoSwitch/NsoFile.cs +++ b/LibCpp2IL/NintendoSwitch/NsoFile.cs @@ -181,11 +181,25 @@ private void ReadSymbolTable() LibLogger.Verbose($"\tReading NSO symbol table..."); var hash = GetDynamicEntry(ElfDynamicType.DT_HASH); + + if (hash == null) + { + LibLogger.WarnNewline("\tNo DT_HASH found in NSO, symbols will not be resolved"); + return; + } + Position = MapVirtualAddressToRaw(hash.Value); ReadUInt32(); //Ignored var symbolCount = ReadUInt32(); var symTab = GetDynamicEntry(ElfDynamicType.DT_SYMTAB); + + if (symTab == null) + { + LibLogger.WarnNewline("\tNo DT_SYMTAB found in NSO, symbols will not be resolved"); + return; + } + SymbolTable = ReadClassArrayAtVirtualAddress((ulong) MapVirtualAddressToRaw(symTab.Value), symbolCount); LibLogger.VerboseNewline($"\tGot {SymbolTable.Length} symbols"); @@ -197,8 +211,8 @@ private void ApplyRelocations() try { - var dtRela = GetDynamicEntry(ElfDynamicType.DT_RELA); - var dtRelaSize = GetDynamicEntry(ElfDynamicType.DT_RELASZ); + var dtRela = GetDynamicEntry(ElfDynamicType.DT_RELA) ?? throw new(); //Using exceptions as control flow :) + var dtRelaSize = GetDynamicEntry(ElfDynamicType.DT_RELASZ) ?? throw new(); relaEntries = ReadClassArrayAtVirtualAddress(dtRela.Value, (long) (dtRelaSize.Value / 24)); //24 being sizeof(ElfRelaEntry) on 64-bit } catch @@ -224,7 +238,7 @@ private void ApplyRelocations() } } - public ElfDynamicEntry GetDynamicEntry(ElfDynamicType tag) => dynamicEntries.Find(x => x.Tag == tag); + public ElfDynamicEntry? GetDynamicEntry(ElfDynamicType tag) => dynamicEntries.Find(x => x.Tag == tag); public NsoFile Decompress() { From a73d668253cd055b2a7db1a26d2d54cc739fa9ab Mon Sep 17 00:00:00 2001 From: AndnixSH <40742924+AndnixSH@users.noreply.github.com> Date: Sun, 18 Dec 2022 16:48:59 +0100 Subject: [PATCH 60/76] Update README.md (#107) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 416162c9..7e1da331 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ If you do not wish for the output to be coloured, set the Environment Variable ` | NSO (Switch) | N/A | N/A | N/A | ✔️ | Switch is ArmV8, that is the only supported instruction set. Compression supported. | | APK (Android) | ✔ | ❌ | ✔️ | ✔️ | Unpacks the APK, then delegates to ELF loader. | | WASM (WebAssembly) | N/A | N/A | N/A | N/A | WASM is its own instruction set, which **is** supported for dumps but not analyzed yet | -| Mach-O (Mac OS)| ❌ | ❌ | N/A? | ❌ | Not supported yet, but planned | +| Mach-O (Mac OS)| ❌ | ❌ | N/A | ❌ | Not supported yet, but planned | ## Supported Analysis Features Table From 0d195fbf1844323719a00d3c055ae184ae4b8e8b Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Fri, 23 Dec 2022 18:05:27 +0000 Subject: [PATCH 61/76] [skip ci] fix a small log format error --- LibCpp2IL/BinarySearcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LibCpp2IL/BinarySearcher.cs b/LibCpp2IL/BinarySearcher.cs index 1858a706..6ecf44ae 100644 --- a/LibCpp2IL/BinarySearcher.cs +++ b/LibCpp2IL/BinarySearcher.cs @@ -196,7 +196,7 @@ internal ulong FindCodeRegistrationPost2019() if (pCodegenModules.Count == 1) { - LibLogger.Verbose($"\t\t\tOnly found one codegen module pointer, so assuming it's correct and returning pCodeReg = 0x{address:X}"); + LibLogger.VerboseNewline($"\t\t\tOnly found one codegen module pointer, so assuming it's correct and returning pCodeReg = 0x{address:X}"); return address; } From 7a3cdd303540488bce56369344acdf1e4484faf3 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Fri, 23 Dec 2022 18:13:39 +0000 Subject: [PATCH 62/76] Backport additional meta reg sanity check from dev branch --- LibCpp2IL/BinarySearcher.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/LibCpp2IL/BinarySearcher.cs b/LibCpp2IL/BinarySearcher.cs index 6ecf44ae..cd947510 100644 --- a/LibCpp2IL/BinarySearcher.cs +++ b/LibCpp2IL/BinarySearcher.cs @@ -349,6 +349,13 @@ public ulong FindMetadataRegistrationPost24_5() LibLogger.VerboseNewline($"\t\t\tRejecting metadata registration 0x{va:X} because it has {metaReg.numTypes} types, while we have {LibCpp2IlMain.TheMetadata!.typeDefs.Length} type defs"); continue; } + + if (metaReg.fieldOffsetsCount != LibCpp2IlMain.TheMetadata!.typeDefs.Length) + { + //If we see any cases of failing to find meta reg and this line is in verbose log, maybe the assumption (num field offsets == num type defs) is wrong. + LibLogger.VerboseNewline($"\t\t\tRejecting metadata registration 0x{va:X} because it has {metaReg.fieldOffsetsCount} field offsets, while metadata file defines {LibCpp2IlMain.TheMetadata!.typeDefs.Length} type defs"); + continue; + } LibLogger.VerboseNewline($"\t\t\tAccepting metadata reg as VA 0x{va:X}"); return va; From 43dd2529d564e2fb0d1d682225984c0b9db010e5 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Mon, 30 Jan 2023 01:19:03 +0000 Subject: [PATCH 63/76] Cpp2IL: Read version from UnityPlayer dll instead of player exe to fix 2021.2 bug --- Cpp2IL/Program.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Cpp2IL/Program.cs b/Cpp2IL/Program.cs index 4491ebfd..078d1149 100644 --- a/Cpp2IL/Program.cs +++ b/Cpp2IL/Program.cs @@ -62,15 +62,20 @@ private static void HandleWindowsGamePath(string gamePath, string? inputExeName, if (exeName == null) throw new SoftException("Failed to locate any executable in the provided game directory. Make sure the path is correct, and if you *really* know what you're doing (and know it's not supported), use the force options, documented if you provide --help."); - var unityPlayerPath = Path.Combine(gamePath, $"{exeName}.exe"); + var playerExePath = Path.Combine(gamePath, $"{exeName}.exe"); args.PathToMetadata = Path.Combine(gamePath, $"{exeName}_Data", "il2cpp_data", "Metadata", "global-metadata.dat"); - if (!File.Exists(args.PathToAssembly) || !File.Exists(unityPlayerPath) || !File.Exists(args.PathToMetadata)) + if (!File.Exists(args.PathToAssembly) || !File.Exists(playerExePath) || !File.Exists(args.PathToMetadata)) throw new SoftException("Invalid game-path or exe-name specified. Failed to find one of the following:\n" + $"\t{args.PathToAssembly}\n" + - $"\t{unityPlayerPath}\n" + + $"\t{playerExePath}\n" + $"\t{args.PathToMetadata}\n"); + var unityPlayerPath = Path.Combine(gamePath, "UnityPlayer.dll"); + + if (!File.Exists(unityPlayerPath)) + unityPlayerPath = playerExePath; + Logger.VerboseNewline($"Found probable windows game at path: {gamePath}. Attempting to get unity version..."); var gameDataPath = Path.Combine(gamePath, $"{exeName}_Data"); var uv = Cpp2IlApi.DetermineUnityVersion(unityPlayerPath, gameDataPath); From 4f3d9b18378a6fce82ba5cd902141a36656cc2a4 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Thu, 16 Feb 2023 02:41:34 +0000 Subject: [PATCH 64/76] Lib: Mach-O export support. --- Cpp2IL.Core/Arm64KeyFunctionAddresses.cs | 1 - Cpp2IL.Core/Cpp2IlApi.cs | 3 + LibCpp2IL/ClassReadingBinaryReader.cs | 19 +++-- LibCpp2IL/MachO/MachODynamicLinkerCommand.cs | 43 ++++++++++ LibCpp2IL/MachO/MachOExportEntry.cs | 20 +++++ LibCpp2IL/MachO/MachOExportTrie.cs | 85 ++++++++++++++++++++ LibCpp2IL/MachO/MachOFile.cs | 17 +++- LibCpp2IL/MachO/MachOLoadCommand.cs | 17 ++++ LibCpp2IL/MachO/MachOSymtabCommand.cs | 36 +++++++++ LibCpp2IL/MachO/MachOSymtabEntry.cs | 38 +++++++++ 10 files changed, 269 insertions(+), 10 deletions(-) create mode 100644 LibCpp2IL/MachO/MachODynamicLinkerCommand.cs create mode 100644 LibCpp2IL/MachO/MachOExportEntry.cs create mode 100644 LibCpp2IL/MachO/MachOExportTrie.cs create mode 100644 LibCpp2IL/MachO/MachOSymtabCommand.cs create mode 100644 LibCpp2IL/MachO/MachOSymtabEntry.cs diff --git a/Cpp2IL.Core/Arm64KeyFunctionAddresses.cs b/Cpp2IL.Core/Arm64KeyFunctionAddresses.cs index 1b24e7a9..73683e54 100644 --- a/Cpp2IL.Core/Arm64KeyFunctionAddresses.cs +++ b/Cpp2IL.Core/Arm64KeyFunctionAddresses.cs @@ -38,7 +38,6 @@ public Arm64KeyFunctionAddresses() var startFrom = attributeGeneratorList[^1]; - //GetExportedFunctionPointers is only defined for ELF and PE files. Mach-O may die here. var minExport = LibCpp2IlMain.Binary.GetAllExportedIl2CppFunctionPointers().Min(); if(minExport != 0) diff --git a/Cpp2IL.Core/Cpp2IlApi.cs b/Cpp2IL.Core/Cpp2IlApi.cs index 5d175084..82e0eb58 100644 --- a/Cpp2IL.Core/Cpp2IlApi.cs +++ b/Cpp2IL.Core/Cpp2IlApi.cs @@ -373,8 +373,11 @@ public static void GenerateMetadataForAllAssemblies(string rootFolder) { CheckLibInitialized(); + Logger.Info("Generating type dumps for all assemblies..."); foreach (var assemblyDefinition in SharedState.AssemblyList) GenerateMetadataForAssembly(rootFolder, assemblyDefinition); + + Logger.InfoNewline("OK."); } public static void GenerateMetadataForAssembly(string rootFolder, AssemblyDefinition assemblyDefinition) diff --git a/LibCpp2IL/ClassReadingBinaryReader.cs b/LibCpp2IL/ClassReadingBinaryReader.cs index ac5ca135..d6ff13d1 100644 --- a/LibCpp2IL/ClassReadingBinaryReader.cs +++ b/LibCpp2IL/ClassReadingBinaryReader.cs @@ -264,7 +264,6 @@ private object ReadAndConvertPrimitive(bool overrideArchCheck, Type type) public virtual string ReadStringToNull(long offset) { - var builder = new List(); var obtained = false; PositionShiftLock.Enter(ref obtained); @@ -275,12 +274,8 @@ public virtual string ReadStringToNull(long offset) try { Position = offset; - byte b; - while ((b = (byte) _memoryStream.ReadByte()) != 0) - builder.Add(b); - - return Encoding.UTF8.GetString(builder.ToArray()); + return ReadStringToNullAtCurrentPos(); } finally { @@ -288,6 +283,18 @@ public virtual string ReadStringToNull(long offset) } } + public string ReadStringToNullAtCurrentPos() + { + var builder = new List(); + byte b; + var sanity = 0; + while ((b = (byte)_memoryStream.ReadByte()) != 0 && ++sanity < 32768) + builder.Add(b); + + + return Encoding.UTF8.GetString(builder.ToArray()); + } + public byte[] ReadByteArrayAtRawAddress(long offset, int count) { var obtained = false; diff --git a/LibCpp2IL/MachO/MachODynamicLinkerCommand.cs b/LibCpp2IL/MachO/MachODynamicLinkerCommand.cs new file mode 100644 index 00000000..39821712 --- /dev/null +++ b/LibCpp2IL/MachO/MachODynamicLinkerCommand.cs @@ -0,0 +1,43 @@ +using System; + +namespace LibCpp2IL.MachO +{ + public class MachODynamicLinkerCommand + { + public int RebaseOffset; + public int RebaseSize; + public int BindOffset; + public int BindSize; + public int WeakBindOffset; + public int WeakBindSize; + public int LazyBindOffset; + public int LazyBindSize; + public int ExportOffset; + public int ExportSize; + + public MachOExportEntry[] Exports = Array.Empty(); + + public void Read(ClassReadingBinaryReader reader) + { + RebaseOffset = reader.ReadInt32(); + RebaseSize = reader.ReadInt32(); + BindOffset = reader.ReadInt32(); + BindSize = reader.ReadInt32(); + WeakBindOffset = reader.ReadInt32(); + WeakBindSize = reader.ReadInt32(); + LazyBindOffset = reader.ReadInt32(); + LazyBindSize = reader.ReadInt32(); + ExportOffset = reader.ReadInt32(); + ExportSize = reader.ReadInt32(); + + var returnTo = reader.BaseStream.Position; + + reader.BaseStream.Position = ExportOffset; + + var exports = new MachOExportTrie(reader); + Exports = exports.Entries.ToArray(); + + reader.BaseStream.Position = returnTo; + } + } +} \ No newline at end of file diff --git a/LibCpp2IL/MachO/MachOExportEntry.cs b/LibCpp2IL/MachO/MachOExportEntry.cs new file mode 100644 index 00000000..872bce8e --- /dev/null +++ b/LibCpp2IL/MachO/MachOExportEntry.cs @@ -0,0 +1,20 @@ +namespace LibCpp2IL.MachO +{ + public class MachOExportEntry + { + public string Name; + public long Address; + public long Flags; + public long Other; + public string? ImportName; + + public MachOExportEntry(string name, long address, long flags, long other, string? importName) + { + Name = name; + Address = address; + Flags = flags; + Other = other; + ImportName = importName; + } + } +} \ No newline at end of file diff --git a/LibCpp2IL/MachO/MachOExportTrie.cs b/LibCpp2IL/MachO/MachOExportTrie.cs new file mode 100644 index 00000000..97ac3e6a --- /dev/null +++ b/LibCpp2IL/MachO/MachOExportTrie.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; + +namespace LibCpp2IL.MachO +{ + public class MachOExportTrie + { + public List Entries = new(); + + private ClassReadingBinaryReader _reader; + private long _basePtr; + + public MachOExportTrie(ClassReadingBinaryReader reader) + { + _reader = reader; + _basePtr = reader.BaseStream.Position; + + var children = ParseNode("", 0); + while (children.Count > 0) + { + var current = children[0]; + children.RemoveAt(0); + children.AddRange(ParseNode(current.Name, current.Offset)); + } + } + + private List ParseNode(string name, int offset) + { + var children = new List(); + _reader.BaseStream.Position = _basePtr + offset; + + var terminalSize = _reader.BaseStream.ReadLEB128Unsigned(); + var childrenIndex = _reader.BaseStream.Position + (long)terminalSize; + if (terminalSize != 0) + { + var flags = (ExportFlags) _reader.BaseStream.ReadLEB128Unsigned(); + var address = 0L; + var other = 0L; + string? importName = null; + + if ((flags & ExportFlags.ReExport) != 0) + { + other = _reader.BaseStream.ReadLEB128Signed(); + importName = _reader.ReadStringToNullAtCurrentPos(); + } + else + { + address = _reader.BaseStream.ReadLEB128Signed(); + if ((flags & ExportFlags.StubAndResolver) != 0) + other = _reader.BaseStream.ReadLEB128Signed(); + } + + Entries.Add(new(name, address, (long) flags, other, importName)); + } + + _reader.BaseStream.Position = childrenIndex; + var numChildren = _reader.BaseStream.ReadLEB128Unsigned(); + for (var i = 0ul; i < numChildren; i++) + { + var childName = _reader.ReadStringToNullAtCurrentPos(); + var childOffset = _reader.BaseStream.ReadLEB128Unsigned(); + children.Add(new Node {Name = name + childName, Offset = (int) childOffset}); + } + + return children; + } + + [Flags] + private enum ExportFlags + { + KindRegular = 0, + KindThreadLocal = 1, + KindAbsolute = 2, + WeakDefinition = 4, + ReExport = 8, + StubAndResolver = 0x10 + } + + private struct Node + { + public string Name; + public int Offset; + } + } +} \ No newline at end of file diff --git a/LibCpp2IL/MachO/MachOFile.cs b/LibCpp2IL/MachO/MachOFile.cs index ffd15802..e2f5d61d 100644 --- a/LibCpp2IL/MachO/MachOFile.cs +++ b/LibCpp2IL/MachO/MachOFile.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using LibCpp2IL.Logging; @@ -14,7 +15,8 @@ public class MachOFile : Il2CppBinary private readonly MachOSegmentCommand[] Segments64; private readonly MachOSection[] Sections64; - + private Dictionary _exportsDict; + public MachOFile(MemoryStream input, long maxMetadataUsages) : base(input, maxMetadataUsages) { _raw = input.GetBuffer(); @@ -76,6 +78,12 @@ public MachOFile(MemoryStream input, long maxMetadataUsages) : base(input, maxMe Segments64 = _loadCommands.Where(c => c.Command == LoadCommandId.LC_SEGMENT_64).Select(c => c.CommandData).Cast().ToArray(); Sections64 = Segments64.SelectMany(s => s.Sections).ToArray(); + var dyldData = _loadCommands.FirstOrDefault(c => c.Command is LoadCommandId.LC_DYLD_INFO or LoadCommandId.LC_DYLD_INFO_ONLY)?.CommandData as MachODynamicLinkerCommand; + var exports = dyldData?.Exports ?? Array.Empty(); + _exportsDict = exports.ToDictionary(e => e.Name[1..], e => e.Address); //Skip the first character, which is a leading underscore inserted by the compiler + + LibLogger.VerboseNewline($"Found {_exportsDict.Count} exports in the DYLD info load command."); + LibLogger.VerboseNewline($"\tMach-O contains {Segments64.Length} segments, split into {Sections64.Length} sections."); } @@ -109,11 +117,14 @@ public override ulong GetRVA(ulong pointer) public override byte[] GetRawBinaryContent() => _raw; - public override ulong[] GetAllExportedIl2CppFunctionPointers() => Array.Empty(); + public override ulong[] GetAllExportedIl2CppFunctionPointers() => _exportsDict.Values.Select(v => (ulong) v).ToArray(); public override ulong GetVirtualAddressOfExportedFunctionByName(string toFind) { - return 0; //TODO? + if (!_exportsDict.TryGetValue(toFind, out var addr)) + return 0; + + return (ulong) addr; } private MachOSection GetTextSection64() diff --git a/LibCpp2IL/MachO/MachOLoadCommand.cs b/LibCpp2IL/MachO/MachOLoadCommand.cs index c152c6f0..60a40943 100644 --- a/LibCpp2IL/MachO/MachOLoadCommand.cs +++ b/LibCpp2IL/MachO/MachOLoadCommand.cs @@ -23,10 +23,27 @@ public void Read(ClassReadingBinaryReader reader) { case LoadCommandId.LC_SEGMENT: case LoadCommandId.LC_SEGMENT_64: + { var cmd = new MachOSegmentCommand(); cmd.Read(reader); CommandData = cmd; break; + } + case LoadCommandId.LC_SYMTAB: + { + var cmd = new MachOSymtabCommand(); + cmd.Read(reader); + CommandData = cmd; + break; + } + case LoadCommandId.LC_DYLD_INFO: + case LoadCommandId.LC_DYLD_INFO_ONLY: + { + var cmd = new MachODynamicLinkerCommand(); + cmd.Read(reader); + CommandData = cmd; + break; + } default: UnknownCommandData = reader.ReadBytes((int) CommandSize - 8); // -8 because we've already read the 8 bytes of the header break; diff --git a/LibCpp2IL/MachO/MachOSymtabCommand.cs b/LibCpp2IL/MachO/MachOSymtabCommand.cs new file mode 100644 index 00000000..efd3818c --- /dev/null +++ b/LibCpp2IL/MachO/MachOSymtabCommand.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; + +namespace LibCpp2IL.MachO +{ + public class MachOSymtabCommand + { + public uint SymbolTableOffset; + public uint NumSymbols; + public uint StringTableOffset; + public uint StringTableSize; + + public MachOSymtabEntry[] Symbols = Array.Empty(); + + public void Read(ClassReadingBinaryReader reader) + { + SymbolTableOffset = reader.ReadUInt32(); + NumSymbols = reader.ReadUInt32(); + StringTableOffset = reader.ReadUInt32(); + StringTableSize = reader.ReadUInt32(); + + var returnTo = reader.BaseStream.Position; + + reader.BaseStream.Position = SymbolTableOffset; + + Symbols = new MachOSymtabEntry[NumSymbols]; + for (var i = 0; i < NumSymbols; i++) + { + Symbols[i] = new(); + Symbols[i].Read(reader, this); + } + + reader.BaseStream.Position = returnTo; + } + } +} \ No newline at end of file diff --git a/LibCpp2IL/MachO/MachOSymtabEntry.cs b/LibCpp2IL/MachO/MachOSymtabEntry.cs new file mode 100644 index 00000000..7175709c --- /dev/null +++ b/LibCpp2IL/MachO/MachOSymtabEntry.cs @@ -0,0 +1,38 @@ +namespace LibCpp2IL.MachO +{ + public class MachOSymtabEntry + { + public uint NameOffset; + public byte Type; + public byte Section; + public ushort Description; + public ulong Value; // Architecture sized + + public string Name; + + public bool IsExternal => (Type & 0b1) == 0b1; + public bool IsSymbolicDebugging => (Type & 0b1110_0000) != 0; + public bool IsPrivateExternal => (Type & 0b0001_0000) == 0b0001_0000; + + private byte TypeBits => (byte) (Type & 0b1110); + + public bool IsTypeUndefined => Section == 0 && TypeBits == 0b0000; + public bool IsTypeAbsolute => Section == 0 && TypeBits == 0b0010; + public bool IsTypePreboundUndefined => Section == 0 && TypeBits == 0b1100; + public bool IsTypeIndirect => Section == 0 && TypeBits == 0b1010; + public bool IsTypeSection => TypeBits == 0b1110; + + public void Read(ClassReadingBinaryReader reader, MachOSymtabCommand machOSymtabCommand) + { + NameOffset = reader.ReadUInt32(); + Type = reader.ReadByte(); + Section = reader.ReadByte(); + Description = reader.ReadUInt16(); + Value = reader.ReadNUint(); + + var returnTo = reader.BaseStream.Position; + Name = reader.ReadStringToNull(machOSymtabCommand.StringTableOffset + NameOffset); + reader.BaseStream.Position = returnTo; + } + } +} \ No newline at end of file From dc9dcd9bce20c7ba935a38bf092ea4e52b2451a6 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Thu, 16 Feb 2023 20:12:30 +0000 Subject: [PATCH 65/76] Lib: increase v27 module count sanity limit --- LibCpp2IL/BinarySearcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LibCpp2IL/BinarySearcher.cs b/LibCpp2IL/BinarySearcher.cs index cd947510..fa055ea8 100644 --- a/LibCpp2IL/BinarySearcher.cs +++ b/LibCpp2IL/BinarySearcher.cs @@ -147,7 +147,7 @@ internal ulong FindCodeRegistrationPost2019() else { //but in v27 it's close to the LAST codegen module (winrt.dll is an exception), so we need to work back until we find an xref. - var sanityCheckNumberOfModules = 200UL; + var sanityCheckNumberOfModules = 400UL; var pSomewhereInCodegenModules = pMscorlibCodegenEntryInCodegenModulesList.AsEnumerable(); var numModuleDefs = LibCpp2IlMain.TheMetadata!.imageDefinitions.Length; var initialBacktrack = (ulong) numModuleDefs - 5L; From c974329fac97f8b88fb3f762ec1c80ff9ecbd23b Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Sun, 23 Apr 2023 19:11:04 -0700 Subject: [PATCH 66/76] Core: Add handling for fake this param on static methods in v24 Closes #218 --- Cpp2IL.Core/Utils/MethodUtils.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Cpp2IL.Core/Utils/MethodUtils.cs b/Cpp2IL.Core/Utils/MethodUtils.cs index c98fc4d8..a30647bd 100644 --- a/Cpp2IL.Core/Utils/MethodUtils.cs +++ b/Cpp2IL.Core/Utils/MethodUtils.cs @@ -149,7 +149,9 @@ private static bool CheckParameters64(MethodReference method, MethodAnalysis< var actualArgs = new List(); if (!isInstance) { - actualArgs.Add(GetValueFromAppropriateX64Reg(method.Parameters.GetValueSafely(0)?.ParameterType?.ShouldBeInFloatingPointRegister(), "xmm0", "rcx", context)); + if(LibCpp2IlMain.MetadataVersion > 24f) + //We can only add rcx/xmm0 if we're > v24, because il2cpp used to always emit an unused parameter for a non-existent this pointer. + actualArgs.Add(GetValueFromAppropriateX64Reg(method.Parameters.GetValueSafely(0)?.ParameterType?.ShouldBeInFloatingPointRegister(), "xmm0", "rcx", context)); actualArgs.Add(GetValueFromAppropriateX64Reg(method.Parameters.GetValueSafely(1)?.ParameterType?.ShouldBeInFloatingPointRegister(), "xmm1", "rdx", context)); actualArgs.Add(GetValueFromAppropriateX64Reg(method.Parameters.GetValueSafely(2)?.ParameterType?.ShouldBeInFloatingPointRegister(), "xmm2", "r8", context)); actualArgs.Add(GetValueFromAppropriateX64Reg(method.Parameters.GetValueSafely(3)?.ParameterType?.ShouldBeInFloatingPointRegister(), "xmm3", "r9", context)); @@ -356,7 +358,8 @@ private static bool CheckParametersArmV8(MethodReference method, MethodAnalys //See MethodAnalysis#HandleArm64Parameters for a detailed explanation of how this works. arguments = null; - var xCount = isInstance ? 1 : 0; + //instance methods have a `this` param, and prior to v24.1, so do static methods, even though they don't use it. + var xCount = isInstance || LibCpp2IlMain.MetadataVersion <= 24f ? 1 : 0; var vCount = 0; var ret = new List(); From 7a785045d40a675e117a89aad52be49115b9d009 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Sun, 23 Apr 2023 19:20:38 -0700 Subject: [PATCH 67/76] Lib: Log metadata magic if it mismatches --- LibCpp2IL/Metadata/Il2CppMetadata.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LibCpp2IL/Metadata/Il2CppMetadata.cs b/LibCpp2IL/Metadata/Il2CppMetadata.cs index d1ce4260..c8e15b74 100644 --- a/LibCpp2IL/Metadata/Il2CppMetadata.cs +++ b/LibCpp2IL/Metadata/Il2CppMetadata.cs @@ -55,10 +55,10 @@ public class Il2CppMetadata : ClassReadingBinaryReader public static Il2CppMetadata? ReadFrom(byte[] bytes, int[] unityVer) { - if (BitConverter.ToUInt32(bytes, 0) != 0xFAB11BAF) + if (BitConverter.ToUInt32(bytes, 0) is var magic && magic != 0xFAB11BAF) { //Magic number is wrong - throw new FormatException("Invalid or corrupt metadata (magic number check failed)"); + throw new FormatException($"Invalid or corrupt metadata. Magic number was 0x{magic:X}, expected 0xFAB11BAF."); } var version = BitConverter.ToInt32(bytes, 4); From 7a141152d9acbcc6a6c9366a82cee32e9c505c1a Mon Sep 17 00:00:00 2001 From: lilmayofuksu <11977128+lilmayofuksu@users.noreply.github.com> Date: Mon, 1 May 2023 00:56:11 +0300 Subject: [PATCH 68/76] Fix static method call arguments in Il2Cpp <= 24 --- Cpp2IL.Core/Analysis/ResultModels/MethodAnalysis.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Cpp2IL.Core/Analysis/ResultModels/MethodAnalysis.cs b/Cpp2IL.Core/Analysis/ResultModels/MethodAnalysis.cs index 7f334c39..4eb281dc 100644 --- a/Cpp2IL.Core/Analysis/ResultModels/MethodAnalysis.cs +++ b/Cpp2IL.Core/Analysis/ResultModels/MethodAnalysis.cs @@ -123,6 +123,10 @@ internal MethodAnalysis(MethodDefinition method, ulong methodStart, ulong initia method.Body.ThisParameter.Name = "this"; FunctionArgumentLocals.Add(MakeLocal(method.DeclaringType, "this", _parameterDestRegList.RemoveAndReturn(0)).WithParameter(method.Body.ThisParameter)); } + else if (LibCpp2IlMain.MetadataVersion <= 24f) + { + FunctionArgumentLocals.Add(MakeLocal(TypeDefinitions.Object, "dummythis", _parameterDestRegList.RemoveAndReturn(0))); + } while (args.Count > 0) { @@ -727,4 +731,4 @@ public void RegisterInstructionTargetToSwapOut(Instruction jumpInstruction, ulon JumpTargetsToFixByAction[target].Add(jumpInstruction); } } -} \ No newline at end of file +} From 4c5326d65e278f979fb1f09d60b20b92af9c00b1 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Wed, 14 Jun 2023 03:46:21 +0100 Subject: [PATCH 69/76] Core: Fix stupidity with get_Item methods in pseudocode --- Cpp2IL.Core/Analysis/Actions/Base/AbstractCallAction.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cpp2IL.Core/Analysis/Actions/Base/AbstractCallAction.cs b/Cpp2IL.Core/Analysis/Actions/Base/AbstractCallAction.cs index bb92cffe..ab4b4fcf 100644 --- a/Cpp2IL.Core/Analysis/Actions/Base/AbstractCallAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/Base/AbstractCallAction.cs @@ -44,7 +44,7 @@ protected AbstractCallAction(MethodAnalysis context, T instruction) : base(co if (ManagedMethodBeingCalled is MethodDefinition mDef) { - if (mDef.Name.StartsWith("get_")) + if (mDef.Name.StartsWith("get_") && mDef.Parameters.Count <= 0) { var unmanaged = mDef.AsUnmanaged(); var prop = unmanaged.DeclaringType!.Properties!.FirstOrDefault(p => p.Getter == unmanaged); From 563a91cdc253d66d85492c79d73b2c3b8553461a Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Wed, 20 Sep 2023 23:09:50 +0100 Subject: [PATCH 70/76] Update dotnet-core.yml --- .github/workflows/dotnet-core.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index fea5dde2..c99c6424 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -5,6 +5,8 @@ on: branches: [ new-analysis ] pull_request: branches: [ new-analysis ] + workflow_dispatch: + jobs: release: From 20ccab24a327694c85b5508edd9674e5a30f1e25 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Mon, 30 Oct 2023 21:08:41 +0000 Subject: [PATCH 71/76] Core: Fix v29 CA blob object array parsing bug --- Cpp2IL.Core/AttributeRestorerPost29.cs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/Cpp2IL.Core/AttributeRestorerPost29.cs b/Cpp2IL.Core/AttributeRestorerPost29.cs index bcc04922..4f346acf 100644 --- a/Cpp2IL.Core/AttributeRestorerPost29.cs +++ b/Cpp2IL.Core/AttributeRestorerPost29.cs @@ -127,7 +127,10 @@ private static CustomAttribute ReadAndCreateCustomAttribute(ModuleDefinition mod { bytesRead = 0; var managedConstructor = constructor.AsManaged(); + +#if !DEBUG try +#endif { var ret = new CustomAttribute(module.ImportReference(managedConstructor)); @@ -243,15 +246,13 @@ private static CustomAttribute ReadAndCreateCustomAttribute(ModuleDefinition mod return ret; } +#if !DEBUG catch (Exception e) { -#if DEBUG - throw; -#else Logger.WarnNewline($"Failed to parse custom attribute {constructor.DeclaringType!.FullName} due to an exception: {e.GetType()}: {e.Message}"); return MakeFallbackAttribute(module, managedConstructor) ?? throw new("Failed to resolve AttributeAttribute type"); -#endif } +#endif } private static object? WrapArrayValuesInCustomAttributeArguments(TypeReference typeReference, Array arr) @@ -271,12 +272,20 @@ private static CustomAttribute ReadAndCreateCustomAttribute(ModuleDefinition mod var objectTypeName = o?.GetType()?.FullName ?? throw new NullReferenceException($"Object {o} in array {arr} of variable type and length {arr.Length} is null, so cannot determine its type"); - var typeDef = MiscUtils.TryLookupTypeDefKnownNotGeneric(objectTypeName); + TypeDefinition typeDef; + if(o is TypeDefinition) + //Special case TypeDefinition => Type + typeDef = TypeDefinitions.Type; + else + typeDef = MiscUtils.TryLookupTypeDefKnownNotGeneric(objectTypeName); if (typeDef == null) throw new Exception($"Couldn't resolve type {objectTypeName}"); - list.Add(new CustomAttributeArgument(typeDef, o)); + var typedParam = new CustomAttributeArgument(typeDef, o); + + //Needs to be wrapped in an additional argument with type System.Object, because cecil + list.Add(new CustomAttributeArgument(TypeDefinitions.Object, typedParam)); } return list.ToArray(); @@ -541,6 +550,7 @@ private static Il2CppTypeEnum ReadBlobType(long pos, out int bytesRead, out Type Il2CppTypeEnum.IL2CPP_TYPE_STRING => TypeDefinitions.String.AsUnmanaged(), Il2CppTypeEnum.IL2CPP_TYPE_BYREF => TypeDefinitions.TypedReference.AsUnmanaged(), Il2CppTypeEnum.IL2CPP_TYPE_IL2CPP_TYPE_INDEX => TypeDefinitions.Type.AsUnmanaged(), + Il2CppTypeEnum.IL2CPP_TYPE_OBJECT => TypeDefinitions.Object.AsUnmanaged(), _ => throw new ArgumentOutOfRangeException(nameof(typeEnum), typeEnum, null) }; } From 4daa40a92be8d7c9e7f7f55fc543ad4978450978 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Mon, 24 Jun 2024 16:33:06 +0100 Subject: [PATCH 72/76] Backport "Lib: Support v31" --- LibCpp2IL/Metadata/Il2CppMetadata.cs | 9 +++++++-- LibCpp2IL/Metadata/Il2CppMethodDefinition.cs | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/LibCpp2IL/Metadata/Il2CppMetadata.cs b/LibCpp2IL/Metadata/Il2CppMetadata.cs index c8e15b74..98f976f9 100644 --- a/LibCpp2IL/Metadata/Il2CppMetadata.cs +++ b/LibCpp2IL/Metadata/Il2CppMetadata.cs @@ -62,9 +62,9 @@ public class Il2CppMetadata : ClassReadingBinaryReader } var version = BitConverter.ToInt32(bytes, 4); - if (version is < 24 or > 29) + if (version is < 24 or > 31) { - throw new FormatException("Unsupported metadata version found! We support 24-29, got " + version); + throw new FormatException("Unsupported metadata version found! We support 24-31, got " + version); } LibLogger.VerboseNewline($"\tIL2CPP Metadata Declares its version as {version}"); @@ -104,6 +104,11 @@ public class Il2CppMetadata : ClassReadingBinaryReader actualVersion = 29.1f; //2022.1.0b7 introduces v29.1 which adds two new pointers to codereg else actualVersion = 29; //2021.3.0 introduces v29 + } else if (version == 31) + { + //2022.3.33 introduces v31. Unity why would you bump this on a minor version. + //Adds one new field (return type token) to method def + actualVersion = 31; } else actualVersion = version; diff --git a/LibCpp2IL/Metadata/Il2CppMethodDefinition.cs b/LibCpp2IL/Metadata/Il2CppMethodDefinition.cs index 1f42deab..036a427b 100644 --- a/LibCpp2IL/Metadata/Il2CppMethodDefinition.cs +++ b/LibCpp2IL/Metadata/Il2CppMethodDefinition.cs @@ -13,6 +13,7 @@ public class Il2CppMethodDefinition public int nameIndex; public int declaringTypeIdx; public int returnTypeIdx; + [Version(Min = 31)] public uint returnParameterToken; public int parameterStart; [Version(Max = 24)] public int customAttributeIndex; public int genericContainerIndex; From 122bef378d75c6c5715790315b201dbfff44ab46 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Sat, 12 Oct 2024 17:39:51 +0100 Subject: [PATCH 73/76] Fix build not working due to deprecated upload action --- .github/workflows/dotnet-core.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index c99c6424..5d3d8a8a 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -41,42 +41,42 @@ jobs: dotnet build dotnet test -v=n --no-build - name: Upload Common Lib Files - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: LibCpp2IL path: ./LibCpp2IL/bin/Release/netstandard2.0/publish/LibCpp2IL* - name: Upload Win x64 Artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: Cpp2IL-Windows path: ./Cpp2IL/bin/Release/net6.0/win-x64/publish/Cpp2IL.exe - name: Upload Netframework Artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: Cpp2IL-Netframework472-Windows path: ./Cpp2IL/bin/Release/net472/win-x64/publish/ - name: Upload Ubuntu x64 Artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: Cpp2IL-Linux path: ./Cpp2IL/bin/Release/net6.0/ubuntu-x64/publish/Cpp2IL - name: Upload OSX x64 Artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: Cpp2IL-Mac path: ./Cpp2IL/bin/Release/net6.0/osx-x64/publish/Cpp2IL - name: Upload LibCpp2IL nuget package - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: LibCpp2IL.nupkg path: ./LibCpp2IL/bin/Release/Samboy063.LibCpp2IL*.nupkg - name: Upload Cpp2IL.Core nuget package - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: Cpp2IL.Core.nupkg path: ./Cpp2IL.Core/bin/Release/Samboy063.Cpp2IL.Core*.nupkg - name: Upload WasmDisassembler nuget package - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: WasmDisassembler.nupkg path: ./WasmDisassembler/bin/Release/Samboy063.WasmDisassembler*.nupkg From bdb8941c4ec3f584cd52547c71321db687d5dab6 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Wed, 6 Nov 2024 17:37:08 +0000 Subject: [PATCH 74/76] Chore: Fix opening the solution with .NET 9 preview SDK installed --- .gitignore | 9 +++++++++ global.json | 7 +++++++ 2 files changed, 16 insertions(+) create mode 100644 global.json diff --git a/.gitignore b/.gitignore index f25463cd..a411d957 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,15 @@ Il2CppBinaryAnalyzer/bin/ WasmDisassembler/obj/ WasmDisassembler/bin/ +Cpp2IL.*/obj/ +Cpp2IL.*/bin/ + +# Because these are a pain when switching branches +StableNameDotNet/obj/ +StableNameDotNet/bin/ +artifacts/ +NuGet.Config + .vs *.user diff --git a/global.json b/global.json new file mode 100644 index 00000000..2ddda36c --- /dev/null +++ b/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "8.0.0", + "rollForward": "latestMinor", + "allowPrerelease": false + } +} \ No newline at end of file From 84bc1476a59f80c32f08e89d99393dcdfe237db3 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Wed, 6 Nov 2024 17:54:05 +0000 Subject: [PATCH 75/76] Fix bad sdk version --- global.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/global.json b/global.json index 2ddda36c..13300bb9 100644 --- a/global.json +++ b/global.json @@ -1,7 +1,7 @@ { "sdk": { - "version": "8.0.0", + "version": "6.0.0", "rollForward": "latestMinor", "allowPrerelease": false } -} \ No newline at end of file +} From 84e44b9536c243a5e7c8e5d19876e826140f32f1 Mon Sep 17 00:00:00 2001 From: Sam Byass Date: Thu, 6 Mar 2025 21:11:00 +0000 Subject: [PATCH 76/76] Lib: Backport 2021.3 v31 support --- LibCpp2IL/BinaryStructures/Il2CppCodeRegistration.cs | 10 +++++++--- LibCpp2IL/Metadata/Il2CppMetadata.cs | 8 +++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/LibCpp2IL/BinaryStructures/Il2CppCodeRegistration.cs b/LibCpp2IL/BinaryStructures/Il2CppCodeRegistration.cs index 9ab568ff..9674ecf1 100644 --- a/LibCpp2IL/BinaryStructures/Il2CppCodeRegistration.cs +++ b/LibCpp2IL/BinaryStructures/Il2CppCodeRegistration.cs @@ -27,10 +27,14 @@ public class Il2CppCodeRegistration public ulong unresolvedVirtualCallCount; //Renamed to unresolvedIndirectCallCount in v29.1 public ulong unresolvedVirtualCallPointers; //Renamed to unresolvedIndirectCallPointers in v29.1 - - [Version(Min = 29.1f)] + + //Not in 2021.3 version of v31, but present in 2022.3 (v31.1) + [Version(Min = 29.1f, Max = 29.1f)] + [Version(Min = 31.1f)] public ulong unresolvedInstanceCallPointers; - [Version(Min = 29.1f)] + + [Version(Min = 29.1f, Max = 29.1f)] + [Version(Min = 31.1f)] public ulong unresolvedStaticCallPointers; public ulong interopDataCount; diff --git a/LibCpp2IL/Metadata/Il2CppMetadata.cs b/LibCpp2IL/Metadata/Il2CppMetadata.cs index 98f976f9..d3f56905 100644 --- a/LibCpp2IL/Metadata/Il2CppMetadata.cs +++ b/LibCpp2IL/Metadata/Il2CppMetadata.cs @@ -108,7 +108,13 @@ public class Il2CppMetadata : ClassReadingBinaryReader { //2022.3.33 introduces v31. Unity why would you bump this on a minor version. //Adds one new field (return type token) to method def - actualVersion = 31; + //2021.3.40 backported the new field but NOT the changes from v29.1, so there's a 31.1 now. + if (unityVersion.IsGreaterEqual(2022, 3, 33, UnityVersionType.Final, 1)) + //V31 with changes in codereg + actualVersion = 31.1f; + else + //v31 WITHOUT changes in codereg + actualVersion = 31; } else actualVersion = version;