Skip to content

Commit

Permalink
General code cleanup to reduce exceptions in analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
Sam Byass committed Oct 30, 2021
1 parent 418aa86 commit 827d2d1
Show file tree
Hide file tree
Showing 12 changed files with 104 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ protected AbstractComparisonAction(MethodAnalysis<T> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class GlobalStringRefToConstantAction : BaseAction<Instruction>
{
public readonly string? ResolvedString;
public ConstantDefinition? ConstantWritten;
public LocalDefinition LastKnownLocalInReg;
public LocalDefinition? LastKnownLocalInReg;
private string? _destReg;

public GlobalStringRefToConstantAction(MethodAnalysis<Instruction> context, Instruction instruction) : base(context, instruction)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ public override Mono.Cecil.Cil.Instruction[] ToILInstructions(MethodAnalysis<Ins

var typeAddingTo = typeof(int).Module.GetType(_valueInReg.Type.FullName);

if (typeAddingTo == null)
throw new TaintedInstructionException($"Value in reg is {_valueInReg} with type {_valueInReg.Type}, which we can't find in the system, so can't cast our constant {_constantBeingAdded} to");

if (_valueInReg?.Variable == null)
throw new TaintedInstructionException("Value is reg has no variable. Stripped? Or a function param?");

if (typeAddingTo == typeof(int))
instructions.AddRange(context.MakeConstant(typeAddingTo, _constantBeingAdded).GetILToLoad(context, processor));
else if (typeAddingTo == typeof(uint))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,49 +1,53 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using Cpp2IL.Core.Analysis.Actions.Base;
using Cpp2IL.Core.Analysis.ResultModels;
using Iced.Intel;
using Mono.Cecil.Cil;
using MonoMod.Utils;
using Instruction = Iced.Intel.Instruction;

namespace Cpp2IL.Core.Analysis.Actions.x86.Important
{
public class ConditionalGlobalStringRefToConstantAction : ConditionalMoveAction
{
private GlobalStringRefToConstantAction AssociatedStringLoad;
private LocalDefinition LocalCreated;
private GlobalStringRefToConstantAction? AssociatedStringLoad;
private LocalDefinition? LocalCreated;
private string? _destReg;

public ConditionalGlobalStringRefToConstantAction(MethodAnalysis<Instruction> context, Instruction instruction, BaseAction<Instruction> 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<Instruction> context, ILProcessor processor)
Expand All @@ -54,18 +58,21 @@ public override Mono.Cecil.Cil.Instruction[] ToILInstructions(MethodAnalysis<Ins
if (LocalCreated == null)
throw new TaintedInstructionException("Local created was null");

var ret = new List<Mono.Cecil.Cil.Instruction>();
if (AssociatedStringLoad == null)
throw new TaintedInstructionException("Associated string load was null");

var ret = new List<Mono.Cecil.Cil.Instruction>();
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));
Expand All @@ -76,14 +83,14 @@ public override Mono.Cecil.Cil.Instruction[] ToILInstructions(MethodAnalysis<Ins
return ret.ToArray();
}

public override string? ToPsuedoCode()
public override string ToPsuedoCode()
{
return $"string {LocalCreated?.Name} = {GetArgumentOnePseudocodeValue()} {GetJumpOpCodePseudoCodeValue()} {GetArgumentTwoPseudocodeValue()} ? \"{(_moveAction as GlobalStringRefToConstantAction).ResolvedString}\" : \"{AssociatedStringLoad.ResolvedString}\"";
return $"string {LocalCreated?.Name} = {GetArgumentOnePseudocodeValue()} {GetJumpOpCodePseudoCodeValue()} {GetArgumentTwoPseudocodeValue()} ? \"{(_moveAction as GlobalStringRefToConstantAction)?.ResolvedString}\" : \"{AssociatedStringLoad?.ResolvedString}\"";
}

public override string ToTextSummary()
{
return $"[!] Sets local {LocalCreated?.Name} in {_destReg} to \"{(_moveAction as GlobalStringRefToConstantAction)!.ResolvedString}\" if {GetArgumentOnePseudocodeValue()} {GetJumpOpCodePseudoCodeValue()} {GetArgumentTwoPseudocodeValue()} else it sets the local to \"{AssociatedStringLoad.ResolvedString}\"";
return $"[!] Sets local {LocalCreated?.Name} in {_destReg} to \"{(_moveAction as GlobalStringRefToConstantAction)!.ResolvedString}\" if {GetArgumentOnePseudocodeValue()} {GetJumpOpCodePseudoCodeValue()} {GetArgumentTwoPseudocodeValue()} else it sets the local to \"{AssociatedStringLoad?.ResolvedString ?? "[Unknown (ANALYSIS ERROR)]"}\"";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@ public GlobalStringToMonoStringAction(MethodAnalysis<Instruction> 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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ public ImmediateToArrayAction(MethodAnalysis<Instruction> context, Instruction i

protected override Mono.Cecil.Cil.Instruction[] GetInstructionsToLoadValue(MethodAnalysis<Instruction> context, ILProcessor processor) => new[]
{
processor.Create(OpCodes.Ldc_I8, _immediateValue),
processor.Create(OpCodes.Ldc_I8, (long) _immediateValue),
processor.Create(OpCodes.Conv_U8),
};
}
}
3 changes: 2 additions & 1 deletion Cpp2IL.Core/Analysis/Actions/x86/LookupICallAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ public LookupICallAction(MethodAnalysis<Instruction> 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);

Expand Down
2 changes: 1 addition & 1 deletion Cpp2IL.Core/Analysis/MethodUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
10 changes: 4 additions & 6 deletions Cpp2IL.Core/Analysis/ResultModels/ConstantDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()!;
Expand Down Expand Up @@ -78,12 +76,12 @@ public Instruction[] GetILToLoad<TAnalysis>(MethodAnalysis<TAnalysis> 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
};

Expand Down
1 change: 1 addition & 0 deletions Cpp2IL.Core/Analysis/ResultModels/Il2CppString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public class Il2CppString
{
public string ContainedString;
public ulong Address;
public bool HasBeenUsedAsAString;

public Il2CppString(string containedString, ulong addr)
{
Expand Down
29 changes: 23 additions & 6 deletions Cpp2IL.Core/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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;
Expand Down
11 changes: 10 additions & 1 deletion Cpp2IL.Core/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand All @@ -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);
Expand Down

0 comments on commit 827d2d1

Please sign in to comment.