diff --git a/Cpp2IL.Core/Analysis/Actions/Base/AbstractComparisonAction.cs b/Cpp2IL.Core/Analysis/Actions/Base/AbstractComparisonAction.cs index 343a6cc6..046a6761 100644 --- a/Cpp2IL.Core/Analysis/Actions/Base/AbstractComparisonAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/Base/AbstractComparisonAction.cs @@ -73,7 +73,7 @@ protected AbstractComparisonAction(MethodAnalysis context, T associatedInstru if (!string.IsNullOrEmpty(argumentOneType?.FullName) && !argumentOneType!.IsArray) { var argumentOneTypeDefinition = argumentOneType.Resolve(); - if (argumentOneTypeDefinition.IsEnum) + if (argumentOneTypeDefinition?.IsEnum == true) { var underLyingType = typeof(int).Module.GetType(argumentOneTypeDefinition.GetEnumUnderlyingType().FullName); constantDefinition.Type = underLyingType; diff --git a/Cpp2IL.Core/Analysis/Actions/x86/GlobalStringRefToConstantAction.cs b/Cpp2IL.Core/Analysis/Actions/x86/GlobalStringRefToConstantAction.cs index 9043fc6c..622aa38f 100644 --- a/Cpp2IL.Core/Analysis/Actions/x86/GlobalStringRefToConstantAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/x86/GlobalStringRefToConstantAction.cs @@ -12,7 +12,7 @@ public class GlobalStringRefToConstantAction : BaseAction { public readonly string? ResolvedString; public ConstantDefinition? ConstantWritten; - public LocalDefinition LastKnownLocalInReg; + public LocalDefinition? LastKnownLocalInReg; private string? _destReg; public GlobalStringRefToConstantAction(MethodAnalysis context, Instruction instruction) : base(context, instruction) diff --git a/Cpp2IL.Core/Analysis/Actions/x86/Important/AddConstantToRegAction.cs b/Cpp2IL.Core/Analysis/Actions/x86/Important/AddConstantToRegAction.cs index c6ffc985..43af188c 100644 --- a/Cpp2IL.Core/Analysis/Actions/x86/Important/AddConstantToRegAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/x86/Important/AddConstantToRegAction.cs @@ -42,6 +42,12 @@ public override Mono.Cecil.Cil.Instruction[] ToILInstructions(MethodAnalysis context, Instruction instruction, BaseAction baseAction) : base(context, instruction, baseAction) { - var expectedIndex = context.Actions.IndexOf(_associatedCompare) + 1; - if (expectedIndex < context.Actions.Count && context.Actions[expectedIndex] is GlobalStringRefToConstantAction globalStringRefToConstantAction) - { - AssociatedStringLoad = globalStringRefToConstantAction; - } - else if (context.Actions[context.Actions.IndexOf(_associatedCompare) - 1] is GlobalStringRefToConstantAction globalStringRefToConstantAction2) - { - AssociatedStringLoad = globalStringRefToConstantAction2; - } - else - { - // TODO: Account for other scenarios where its overwriting an old string thats not found? - } - - context.Actions.Remove(AssociatedStringLoad); - - _destReg = instruction.Op0Kind == OpKind.Register ? Utils.GetRegisterNameNew(instruction.Op0Register) : null; - - var whatWeWant = context.DeclaringType.Module.ImportReference(Utils.StringReference); - - var localAtDest = AssociatedStringLoad.LastKnownLocalInReg; - - if ("System.String".Equals(localAtDest?.Type?.FullName)) - LocalCreated = localAtDest; - else - LocalCreated = context.MakeLocal(whatWeWant, null, _destReg, AssociatedStringLoad.ResolvedString); - - RegisterUsedLocal(LocalCreated, context); + if(_associatedCompare == null) + return; + + var expectedIndex = context.Actions.IndexOf(_associatedCompare) + 1; + if (expectedIndex < context.Actions.Count && context.Actions[expectedIndex] is GlobalStringRefToConstantAction globalStringRefToConstantAction) + { + AssociatedStringLoad = globalStringRefToConstantAction; + } + else if (context.Actions[context.Actions.IndexOf(_associatedCompare) - 1] is GlobalStringRefToConstantAction globalStringRefToConstantAction2) + { + AssociatedStringLoad = globalStringRefToConstantAction2; + } + else + { + // TODO: Account for other scenarios where its overwriting an old string thats not found? + AddComment("Could not find associated string load. Bailing out."); + return; + } + + context.Actions.Remove(AssociatedStringLoad); + + _destReg = instruction.Op0Kind == OpKind.Register ? Utils.GetRegisterNameNew(instruction.Op0Register) : null; + + var whatWeWant = context.DeclaringType.Module.ImportReference(Utils.StringReference); + + var localAtDest = AssociatedStringLoad.LastKnownLocalInReg; + + if ("System.String".Equals(localAtDest?.Type?.FullName)) + LocalCreated = localAtDest; + else + LocalCreated = context.MakeLocal(whatWeWant, null, _destReg, AssociatedStringLoad.ResolvedString); + + RegisterUsedLocal(LocalCreated, context); } public override Mono.Cecil.Cil.Instruction[] ToILInstructions(MethodAnalysis context, ILProcessor processor) @@ -54,18 +58,21 @@ public override Mono.Cecil.Cil.Instruction[] ToILInstructions(MethodAnalysis(); + if (AssociatedStringLoad == null) + throw new TaintedInstructionException("Associated string load was null"); + + var ret = new List(); var target = processor.Create(OpCodes.Nop); - - + + ret.Add(processor.Create(OpCodes.Ldstr, AssociatedStringLoad.ResolvedString)); ret.Add(processor.Create(OpCodes.Stloc, LocalCreated.Variable)); - + ret.AddRange(_associatedCompare.ArgumentOne.GetILToLoad(context, processor)); if (!OnlyNeedToLoadOneOperand()) ret.AddRange(_associatedCompare.ArgumentTwo!.GetILToLoad(context, processor)); - + ret.Add(processor.Create(GetJumpOpcode(), target)); ret.Add(processor.Create(OpCodes.Ldstr, (_moveAction as GlobalStringRefToConstantAction)!.ResolvedString)); @@ -76,14 +83,14 @@ public override Mono.Cecil.Cil.Instruction[] ToILInstructions(MethodAnalysis context, Instr if (LibCpp2IlMain.Binary!.is32Bit && stringConstant != null) context.Stack.Pop(); - _stringValue = (stringConstant?.Value as Il2CppString)?.ContainedString; + var il2CppString = stringConstant?.Value as Il2CppString; + + _stringValue = il2CppString?.ContainedString; if(_stringValue == null) return; + il2CppString!.HasBeenUsedAsAString = true; + _localMade = context.MakeLocal(Utils.StringReference, reg: "rax", knownInitialValue: _stringValue); } diff --git a/Cpp2IL.Core/Analysis/Actions/x86/Important/ImmediateToArrayAction.cs b/Cpp2IL.Core/Analysis/Actions/x86/Important/ImmediateToArrayAction.cs index 909c8f4e..ebb1d5f6 100644 --- a/Cpp2IL.Core/Analysis/Actions/x86/Important/ImmediateToArrayAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/x86/Important/ImmediateToArrayAction.cs @@ -33,7 +33,8 @@ public ImmediateToArrayAction(MethodAnalysis context, Instruction i protected override Mono.Cecil.Cil.Instruction[] GetInstructionsToLoadValue(MethodAnalysis context, ILProcessor processor) => new[] { - processor.Create(OpCodes.Ldc_I8, _immediateValue), + processor.Create(OpCodes.Ldc_I8, (long) _immediateValue), + processor.Create(OpCodes.Conv_U8), }; } } \ No newline at end of file diff --git a/Cpp2IL.Core/Analysis/Actions/x86/LookupICallAction.cs b/Cpp2IL.Core/Analysis/Actions/x86/LookupICallAction.cs index 32c41f47..326b4ac7 100644 --- a/Cpp2IL.Core/Analysis/Actions/x86/LookupICallAction.cs +++ b/Cpp2IL.Core/Analysis/Actions/x86/LookupICallAction.cs @@ -25,8 +25,9 @@ public LookupICallAction(MethodAnalysis context, Instruction instru if (!(constant.Value is Il2CppString str)) return; - + fullMethodSignature = str.ContainedString; + str.HasBeenUsedAsAString = true; //Prevent this from being reinterpreted var split = fullMethodSignature.Split(new[] {"::"}, StringSplitOptions.None); diff --git a/Cpp2IL.Core/Analysis/MethodUtils.cs b/Cpp2IL.Core/Analysis/MethodUtils.cs index 3722510c..6eef1744 100644 --- a/Cpp2IL.Core/Analysis/MethodUtils.cs +++ b/Cpp2IL.Core/Analysis/MethodUtils.cs @@ -70,7 +70,7 @@ private static bool CheckSingleParamNew(IAnalysedOperand arg, TypeReference para break; //Forgive primitive coercion. if (parameterType.FullName is "System.String" or "System.Object" && cons.Value is string) break; //Forgive unmanaged string literal as managed string or object param - if (parameterType.IsPrimitive && cons.Value is Il2CppString cppString) + if (parameterType.IsPrimitive && cons.Value is Il2CppString {HasBeenUsedAsAString: false} cppString) { //Il2CppString contains any unknown global address that looks vaguely like a string //We try and re-interpret it here, most commonly as a floating point value, as integer constants are usually immediate values. diff --git a/Cpp2IL.Core/Analysis/ResultModels/ConstantDefinition.cs b/Cpp2IL.Core/Analysis/ResultModels/ConstantDefinition.cs index 64c2a609..06704bf2 100644 --- a/Cpp2IL.Core/Analysis/ResultModels/ConstantDefinition.cs +++ b/Cpp2IL.Core/Analysis/ResultModels/ConstantDefinition.cs @@ -39,10 +39,8 @@ public override string ToString() return Convert.ToString(intValue)!; } - if (Type == typeof(float)) - return Convert.ToString((float) Value, CultureInfo.InvariantCulture); - if (Type == typeof(double)) - return Convert.ToString((double) Value, CultureInfo.InvariantCulture); + if (Type == typeof(float) || Type == typeof(double)) + return Convert.ToString(Convert.ToDouble(Value), CultureInfo.InvariantCulture); if (Type == typeof(UnknownGlobalAddr)) return Value.ToString()!; @@ -78,12 +76,12 @@ public Instruction[] GetILToLoad(MethodAnalysis context, I return new[] {ilProcessor.Create(Convert.ToInt64(Value) != 0 ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0)}; if (Type == typeof(int)) - return new[] {ilProcessor.Create(OpCodes.Ldc_I4, Convert.ToInt32(Value))}; + return new[] {ilProcessor.Create(OpCodes.Ldc_I4, (int) Utils.ReinterpretBytes((IConvertible) Value, Type))}; if (Type == typeof(uint)) return new[] { - ilProcessor.Create(OpCodes.Ldc_I4, (int) Convert.ToUInt32(Value)), + ilProcessor.Create(OpCodes.Ldc_I4, (int) Utils.ReinterpretBytes((IConvertible) Value, Type)), ilProcessor.Create(OpCodes.Conv_U4) //Convert to uint }; diff --git a/Cpp2IL.Core/Analysis/ResultModels/Il2CppString.cs b/Cpp2IL.Core/Analysis/ResultModels/Il2CppString.cs index 66c54507..1ad22f1c 100644 --- a/Cpp2IL.Core/Analysis/ResultModels/Il2CppString.cs +++ b/Cpp2IL.Core/Analysis/ResultModels/Il2CppString.cs @@ -7,6 +7,7 @@ public class Il2CppString { public string ContainedString; public ulong Address; + public bool HasBeenUsedAsAString; public Il2CppString(string containedString, ulong addr) { diff --git a/Cpp2IL.Core/Extensions.cs b/Cpp2IL.Core/Extensions.cs index 70f46f5e..368a009b 100644 --- a/Cpp2IL.Core/Extensions.cs +++ b/Cpp2IL.Core/Extensions.cs @@ -308,6 +308,28 @@ public static MethodReference ImportRecursive(this ILProcessor processor, Generi public static MethodReference ImportParameterTypes(this ILProcessor processor, MethodReference input) { + ParameterDefinition ImportParam(ParameterDefinition parameter, ILProcessor processor) + { + if (parameter.ParameterType is GenericInstanceType git) + return new(processor.ImportReference(git.Resolve())); + + if (parameter.ParameterType is not GenericParameter and not ByReferenceType {ElementType: GenericParameter}) + return new(processor.ImportReference(parameter.ParameterType)); + + return parameter; + } + + if (input is GenericInstanceMethod gim) + { + //Preserve generic method arguments + //We don't have to worry about overwriting parameters because this is a GIM, so it was specially-constructed for this one call. + var importedParams = gim.Parameters.Select(p => ImportParam(p, processor)).ToList(); + gim.Parameters.Clear(); + importedParams.ForEach(gim.Parameters.Add); + + return gim; + } + //Copy over basic properties var output = new MethodReference(input.Name, input.ReturnType, input.DeclaringType) { @@ -323,12 +345,7 @@ public static MethodReference ImportParameterTypes(this ILProcessor processor, M //Copy params but import each one that needs importing. foreach (var parameter in input.Parameters) { - if(parameter.ParameterType is GenericInstanceType git) - output.Parameters.Add(new(processor.ImportReference(git.Resolve()))); - else if(parameter.ParameterType is not GenericParameter) - output.Parameters.Add(new(processor.ImportReference(parameter.ParameterType))); - else - output.Parameters.Add(parameter); + output.Parameters.Add(ImportParam(parameter, processor)); } return output; diff --git a/Cpp2IL.Core/Utils.cs b/Cpp2IL.Core/Utils.cs index 8c7e4179..13c0e4e3 100644 --- a/Cpp2IL.Core/Utils.cs +++ b/Cpp2IL.Core/Utils.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; using Cpp2IL.Core.Analysis; @@ -877,7 +878,7 @@ public static bool TryCoerceToUlong(object value, out ulong ret) } } - public static IConvertible ReinterpretBytes(IConvertible original, TypeReference desired) => ReinterpretBytes(original, typeof(int).Module.GetType(desired.FullName)); + public static IConvertible ReinterpretBytes(IConvertible original, TypeReference desired) => ReinterpretBytes(original, typeof(int).Module.GetType((desired is TypeSpecification typeSpec ? typeSpec.ElementType : desired).FullName)); public static IConvertible ReinterpretBytes(IConvertible original, Type desired) { @@ -900,6 +901,14 @@ public static IConvertible ReinterpretBytes(IConvertible original, Type desired) double d => BitConverter.GetBytes(d), _ => throw new($"ReinterpretBytes: Cannot get byte array from {original} (type {original.GetType()}") }; + + //Pad out with leading zeros if we have to + var requiredLength = LibCpp2ILUtils.VersionAwareSizeOf(desired); + + if (requiredLength > rawBytes.Length) + { + rawBytes = ((byte) 0).Repeat(requiredLength - rawBytes.Length).Concat(rawBytes).ToArray(); + } if (desired == typeof(bool)) return BitConverter.ToBoolean(rawBytes, 0);