Skip to content

Commit

Permalink
Arm64 improvements - throw helpers, literals, etc.
Browse files Browse the repository at this point in the history
  • Loading branch information
Sam Byass committed Sep 22, 2021
1 parent 87a8642 commit bb98da2
Show file tree
Hide file tree
Showing 13 changed files with 496 additions and 197 deletions.
87 changes: 87 additions & 0 deletions Cpp2IL.Core/Analysis/Actions/ARM64/Arm64CallThrowHelperAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Cpp2IL.Core.Analysis.Actions.Base;
using Cpp2IL.Core.Analysis.ResultModels;
using Gee.External.Capstone.Arm64;
using LibCpp2IL;
using Mono.Cecil;

namespace Cpp2IL.Core.Analysis.Actions.ARM64
{
public class Arm64CallThrowHelperAction : AbstractExceptionThrowerAction<Arm64Instruction>
{
private static readonly ConcurrentDictionary<long, TypeDefinition> _exceptionsThrownByAddress = new();
private static readonly List<long> _checkedAddresses = new();

public static bool IsThrowHelper(long pointer, int depth = 0)
{
if (depth >= 5)
return false;

if (_exceptionsThrownByAddress.ContainsKey(pointer))
return true;

if (_checkedAddresses.Contains(pointer))
return false;

_checkedAddresses.Add(pointer);

//This will only return up to the first branch, because it's an unmanaged function, but that's fine for these purposes
var funcBody = Utils.GetArm64MethodBodyAtVirtualAddress((ulong)pointer, false);

var registerPages = new Dictionary<string, long>();
foreach (var arm64Instruction in funcBody.Where(i => i.Mnemonic is "adrp"))
{
registerPages[arm64Instruction.Details.Operands[0].Register.Name.ToLowerInvariant()] = arm64Instruction.Details.Operands[1].Immediate;
}

var registerAddresses = new Dictionary<string, long>();
foreach (var arm64Instruction in funcBody.Where(i => i.Mnemonic is "add" && i.Details.Operands.Length == 3))
{
var regName = arm64Instruction.Details.Operands[1].RegisterSafe()?.Name;
if (regName != null && registerPages.TryGetValue(regName, out var page) && arm64Instruction.Details.Operands[2].IsImmediate())
{
var destName = arm64Instruction.Details.Operands[0].RegisterSafe()?.Name;
registerAddresses[destName ?? "invalid"] = page + arm64Instruction.Details.Operands[2].Immediate;
}
}

foreach (var potentialLiteralAddress in registerAddresses.Values)
{
if (Utils.TryGetLiteralAt(LibCpp2IlMain.Binary!, (ulong)LibCpp2IlMain.Binary!.MapVirtualAddressToRaw((ulong)potentialLiteralAddress)) is not { } literal)
continue;
if (Utils.TryLookupTypeDefKnownNotGeneric($"System.{literal}") is not { } exceptionType)
continue;

Logger.VerboseNewline($"Identified direct exception thrower: 0x{pointer:X} throws {exceptionType.FullName}. Instructions were {string.Join(", ", funcBody.Select(i => $"0x{i.Address:X} {i.Mnemonic}"))}", "Analyze");
_exceptionsThrownByAddress.TryAdd(pointer, exceptionType);
return true;
}

//Check for inherited exception throwers.
foreach (var nextPtr in
from i in funcBody
where i.Mnemonic is "b" or "bl" && i.Details.Operands[0].IsImmediate()
select i.Details.Operands[0].Immediate
into nextPtr
where IsThrowHelper(nextPtr, depth + 1)
select nextPtr)
{
_exceptionsThrownByAddress.TryAdd(pointer, _exceptionsThrownByAddress[nextPtr]);
return true;
}

return false;
}

public static TypeDefinition? GetExceptionThrown(long ptr) => _exceptionsThrownByAddress.TryGetValue(ptr, out var ex) ? ex : null;

public Arm64CallThrowHelperAction(MethodAnalysis<Arm64Instruction> context, Arm64Instruction instruction) : base(context, instruction)
{
var functionAddress = instruction.Details.Operands[0].Immediate;
_exceptionType = _exceptionsThrownByAddress[functionAddress];
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Cpp2IL.Core.Analysis.Actions.Base;
using Cpp2IL.Core.Analysis.ResultModels;
using Gee.External.Capstone.Arm64;
using Mono.Cecil.Cil;

namespace Cpp2IL.Core.Analysis.Actions.ARM64
{
public class Arm64UnknownGlobalToConstantAction : BaseAction<Arm64Instruction>
{
private UnknownGlobalAddr? _globalAddr;
private ConstantDefinition? _constantMade;

public Arm64UnknownGlobalToConstantAction(MethodAnalysis<Arm64Instruction> context, Arm64Instruction instruction, ulong globalAddress) : base(context, instruction)
{
var destReg = Utils.GetRegisterNameNew(instruction.Details.Operands[0].RegisterSafe()?.Id ?? Arm64RegisterId.Invalid);

if(string.IsNullOrEmpty(destReg))
return;

_globalAddr = new(globalAddress);
_constantMade = context.MakeConstant(typeof(Il2CppString), _globalAddr, reg: destReg);
}

public override Instruction[] ToILInstructions(MethodAnalysis<Arm64Instruction> context, ILProcessor processor)
{
throw new System.NotImplementedException();
}

public override string? ToPsuedoCode()
{
throw new System.NotImplementedException();
}

public override string ToTextSummary()
{
return $"Loads {_globalAddr} into new constant {_constantMade}";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Cpp2IL.Core.Analysis.Actions.Base;
using Cpp2IL.Core.Analysis.ResultModels;
using Gee.External.Capstone.Arm64;
using Mono.Cecil.Cil;

namespace Cpp2IL.Core.Analysis.Actions.ARM64
{
public class Arm64UnmanagedLiteralToConstantAction : BaseAction<Arm64Instruction>
{
private readonly ConstantDefinition? _constantMade;
public readonly Il2CppString? Il2CppString;

public Arm64UnmanagedLiteralToConstantAction(MethodAnalysis<Arm64Instruction> context, Arm64Instruction instruction, string literal, ulong address) : base(context, instruction)
{
var destReg = Utils.GetRegisterNameNew(instruction.Details.Operands[0].RegisterSafe()?.Id ?? Arm64RegisterId.Invalid);

if(string.IsNullOrEmpty(destReg))
return;

Il2CppString = new(literal, address);
_constantMade = context.MakeConstant(typeof(Il2CppString), Il2CppString, reg: destReg);
}

public override Instruction[] ToILInstructions(MethodAnalysis<Arm64Instruction> context, ILProcessor processor)
{
throw new System.NotImplementedException();
}

public override string? ToPsuedoCode()
{
throw new System.NotImplementedException();
}

public override string ToTextSummary()
{
return $"Loads il2cpp string {Il2CppString} into new constant {_constantMade}";
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Cpp2IL.Core.Analysis.Actions.Base;
using System;
using Cpp2IL.Core.Analysis.Actions.Base;
using Cpp2IL.Core.Analysis.ResultModels;
using Gee.External.Capstone.Arm64;

Expand All @@ -13,7 +14,15 @@ protected BaseArm64ConditionalJumpAction(MethodAnalysis<Arm64Instruction> contex

protected sealed override bool IsImplicitNRE()
{
//TODO
var body = Utils.GetArm64MethodBodyAtVirtualAddress(JumpTarget);

for (var i = 0; i < Math.Min(3, body.Count); i++)
{
if (body[i].Mnemonic is "b" or "bl" && body[i].Details.Operands[0].IsImmediate() && Arm64CallThrowHelperAction.IsThrowHelper(body[i].Details.Operands[0].Immediate))
if (Arm64CallThrowHelperAction.GetExceptionThrown(body[i].Details.Operands[0].Immediate)?.Name == "NullReferenceException")
return true;
}

return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.Linq;
using Cpp2IL.Core.Analysis.ResultModels;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using Instruction = Iced.Intel.Instruction;

namespace Cpp2IL.Core.Analysis.Actions.Base
{
public abstract class AbstractExceptionThrowerAction<T> : BaseAction<T>
{
protected TypeDefinition? _exceptionType;

protected AbstractExceptionThrowerAction(MethodAnalysis<T> context, T instruction) : base(context, instruction)
{
}

public sealed override Mono.Cecil.Cil.Instruction[] ToILInstructions(MethodAnalysis<T> context, ILProcessor processor)
{
if (_exceptionType == null)
throw new TaintedInstructionException();

var ctor = _exceptionType.GetConstructors().FirstOrDefault(c => !c.HasParameters);

if (ctor == null)
{
var exceptionCtor = Utils.ExceptionReference.GetConstructors().First(c => c.HasParameters && c.Parameters.Count == 1 && c.Parameters[0].ParameterType.Name == "String");
return new[]
{
processor.Create(OpCodes.Ldstr, $"Exception of type {_exceptionType.FullName}, but couldn't find a no-arg ctor"),
processor.Create(OpCodes.Newobj, processor.ImportReference(exceptionCtor)),
processor.Create(OpCodes.Throw)
};
}

return new[]
{
processor.Create(OpCodes.Newobj, processor.ImportReference(ctor)),
processor.Create(OpCodes.Throw)
};
}

public sealed override string? ToPsuedoCode()
{
return $"throw new {_exceptionType}()";
}

public sealed override string ToTextSummary()
{
return $"[!] Constructs and throws an exception of kind {_exceptionType}\n";
}

public sealed override bool IsImportant()
{
return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@

namespace Cpp2IL.Core.Analysis.Actions.x86.Important
{
public class CallExceptionThrowerFunction : BaseAction<Instruction>
public class CallExceptionThrowerFunction : AbstractExceptionThrowerAction<Instruction>
{
private static readonly ConcurrentDictionary<ulong, TypeDefinition?> ExceptionThrowers = new ConcurrentDictionary<ulong, TypeDefinition?>();
private TypeDefinition? _exceptionType;
private static readonly ConcurrentDictionary<ulong, TypeDefinition?> ExceptionThrowers = new();

internal static void Reset() => ExceptionThrowers.Clear();

Expand Down Expand Up @@ -116,45 +115,5 @@ public CallExceptionThrowerFunction(MethodAnalysis<Instruction> context, Instruc
if(_exceptionType != null)
context.MakeLocal(_exceptionType, reg: "rax");
}

public override Mono.Cecil.Cil.Instruction[] ToILInstructions(MethodAnalysis<Instruction> context, ILProcessor processor)
{
if (_exceptionType == null)
throw new TaintedInstructionException();

var ctor = _exceptionType.GetConstructors().FirstOrDefault(c => !c.HasParameters);

if (ctor == null)
{
var exceptionCtor = Utils.ExceptionReference.GetConstructors().First(c => c.HasParameters && c.Parameters.Count == 1 && c.Parameters[0].ParameterType.Name == "String");
return new[]
{
processor.Create(OpCodes.Ldstr, $"Exception of type {_exceptionType.FullName}, but couldn't find a no-arg ctor"),
processor.Create(OpCodes.Newobj, processor.ImportReference(exceptionCtor)),
processor.Create(OpCodes.Throw)
};
}

return new[]
{
processor.Create(OpCodes.Newobj, processor.ImportReference(ctor)),
processor.Create(OpCodes.Throw)
};
}

public override string? ToPsuedoCode()
{
return $"throw new {_exceptionType}()";
}

public override string ToTextSummary()
{
return $"[!] Constructs and throws an exception of kind {_exceptionType}\n";
}

public override bool IsImportant()
{
return true;
}
}
}
Loading

0 comments on commit bb98da2

Please sign in to comment.