Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d9ef60d
Client manager components
Equinox- Aug 22, 2017
2c7b522
Proper delegate naming
Equinox- Aug 22, 2017
4b2fee7
Offline mode fallbacks for the chat manager.
Equinox- Aug 23, 2017
140000d
Test-time reflected event checker
Equinox- Aug 25, 2017
a36e8a4
Merge branch 'staging' into session-mgr-cmp
Equinox- Sep 10, 2017
aa784c1
Null protection in multiplayer manager detach
Equinox- Sep 10, 2017
9d8988a
MyLog logs to the Torch logging system
Equinox- Sep 11, 2017
b42d43c
Redirect Keen console to Info, all else to trace.
Equinox- Sep 11, 2017
0073e10
Fixed issues with operand replacing, and branch instructions.
Equinox- Sep 11, 2017
e57f885
Log errors in ReflectedManager
Equinox- Sep 11, 2017
57acb27
Don't crash when modifying constructor
Equinox- Sep 12, 2017
0810e76
Once the game is created we can patch it with impunity.
Equinox- Sep 12, 2017
b1145c8
Utility method to invert common load and store instructions
Equinox- Sep 12, 2017
373c476
Better guessing on the generic operand type
Equinox- Sep 12, 2017
a61b646
ReflectedMethodInfo allows non-public type names.
Equinox- Sep 12, 2017
5eceb21
Comments
Equinox- Sep 12, 2017
96f813a
IMultiplayerManager: added list of banned steam ID's
jimmble Sep 20, 2017
d8e2072
documentation added
jimmble Sep 20, 2017
f1fc49d
Merge branch 'staging' into session-mgr-cmp
jimmble Sep 20, 2017
eb7f7f4
MultiplayerManagerLobby doesn't implement IMultiplayerManagerServer
jimmble Sep 20, 2017
9c505c4
banned player list made readonly, lobby fakes support
jimmble Sep 20, 2017
205dd1a
Merge pull request #120 from blaho/session-mgr-cmp
Equinox- Sep 20, 2017
0574d59
Merge branch 'staging' into session-mgr-cmp
Equinox- Sep 22, 2017
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fixed issues with operand replacing, and branch instructions.
  • Loading branch information
Equinox- committed Sep 11, 2017
commit 0073e101dde5ccb5d945a490d004f508eba436d3
44 changes: 29 additions & 15 deletions Torch/Managers/PatchManager/MSIL/MsilInstruction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Reflection.Emit;
using System.Text;
using Torch.Managers.PatchManager.Transpile;
using Torch.Utils;
using Label = System.Windows.Controls.Label;

namespace Torch.Managers.PatchManager.MSIL
Expand All @@ -13,8 +14,6 @@ namespace Torch.Managers.PatchManager.MSIL
/// </summary>
public class MsilInstruction
{
private MsilOperand _operandBacking;

/// <summary>
/// Creates a new instruction with the given opcode.
/// </summary>
Expand Down Expand Up @@ -97,16 +96,7 @@ public MsilInstruction(OpCode opcode)
/// <summary>
/// The operand for this instruction, or null.
/// </summary>
public MsilOperand Operand
{
get => _operandBacking;
set
{
if (_operandBacking != null && value.GetType() != _operandBacking.GetType())
throw new ArgumentException($"Operand for {OpCode.Name} must be {_operandBacking.GetType().Name}");
_operandBacking = value;
}
}
public MsilOperand Operand { get; }

/// <summary>
/// Labels pointing to this instruction.
Expand All @@ -128,11 +118,12 @@ public MsilInstruction InlineValue<T>(T o)
/// <summary>
/// Makes a copy of the instruction with a new opcode.
/// </summary>
/// <param name="code">The new opcode</param>
/// <param name="newOpcode">The new opcode</param>
/// <returns>The copy</returns>
public MsilInstruction CopyWith(OpCode code)
public MsilInstruction CopyWith(OpCode newOpcode)
{
var result = new MsilInstruction(code) { Operand = this.Operand };
var result = new MsilInstruction(newOpcode);
Operand?.CopyTo(result.Operand);
foreach (MsilLabel x in Labels)
result.Labels.Add(x);
return result;
Expand Down Expand Up @@ -172,5 +163,28 @@ public override string ToString()
sb.Append(OpCode.Name).Append("\t").Append(Operand);
return sb.ToString();
}



#pragma warning disable 169
[ReflectedMethod(Name = "StackChange")]
private static Func<OpCode, int> _stackChange;
#pragma warning restore 169

public int StackChange()
{
int num = _stackChange.Invoke(OpCode);
if ((OpCode == OpCodes.Call || OpCode == OpCodes.Callvirt || OpCode == OpCodes.Newobj) &&
Operand is MsilOperandInline<MethodBase> inline)
{
MethodBase op = inline.Value;
if (op is MethodInfo mi && mi.ReturnType != typeof(void))
num++;
num -= op.GetParameters().Length;
if (!op.IsStatic && OpCode != OpCodes.Newobj)
num--;
}
return num;
}
}
}
2 changes: 2 additions & 0 deletions Torch/Managers/PatchManager/MSIL/MsilOperand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ protected MsilOperand(MsilInstruction instruction)
/// </summary>
public MsilInstruction Instruction { get; }

internal abstract void CopyTo(MsilOperand operand);

internal abstract void Read(MethodContext context, BinaryReader reader);

internal abstract void Emit(LoggingIlGenerator generator);
Expand Down
15 changes: 12 additions & 3 deletions Torch/Managers/PatchManager/MSIL/MsilOperandBrTarget.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using System.Reflection.Emit;
using Torch.Managers.PatchManager.Transpile;

Expand All @@ -22,15 +23,23 @@ internal override void Read(MethodContext context, BinaryReader reader)
{
int val = Instruction.OpCode.OperandType == OperandType.InlineBrTarget
? reader.ReadInt32()
: reader.ReadByte();
Target = context.LabelAt((int) reader.BaseStream.Position + val);
: reader.ReadSByte();
Target = context.LabelAt((int)reader.BaseStream.Position + val);
}

internal override void Emit(LoggingIlGenerator generator)
{
generator.Emit(Instruction.OpCode, Target.LabelFor(generator));
}

internal override void CopyTo(MsilOperand operand)
{
var lt = operand as MsilOperandBrTarget;
if (lt == null)
throw new ArgumentException($"Target {operand?.GetType().Name} must be of same type {GetType().Name}", nameof(operand));
lt.Target = Target;
}

/// <inheritdoc />
public override string ToString()
{
Expand Down
27 changes: 20 additions & 7 deletions Torch/Managers/PatchManager/MSIL/MsilOperandInline.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using Torch.Managers.PatchManager.Transpile;
Expand All @@ -22,6 +23,15 @@ internal MsilOperandInline(MsilInstruction instruction) : base(instruction)
/// </summary>
public T Value { get; set; }

internal override void CopyTo(MsilOperand operand)
{
var lt = operand as MsilOperandInline<T>;
if (lt == null)
throw new ArgumentException($"Target {operand?.GetType().Name} must be of same type {GetType().Name}", nameof(operand));
lt.Value = Value;
;
}

/// <inheritdoc />
public override string ToString()
{
Expand Down Expand Up @@ -66,7 +76,7 @@ internal MsilOperandInt8(MsilInstruction instruction) : base(instruction)
internal override void Read(MethodContext context, BinaryReader reader)
{
Value =
(sbyte) reader.ReadByte();
(sbyte)reader.ReadByte();
}

internal override void Emit(LoggingIlGenerator generator)
Expand Down Expand Up @@ -209,16 +219,19 @@ internal MsilOperandParameter(MsilInstruction instruction) : base(instruction)

internal override void Read(MethodContext context, BinaryReader reader)
{
Value =
context.Method.GetParameters()[
Instruction.OpCode.OperandType == OperandType.ShortInlineVar
? reader.ReadByte()
: reader.ReadUInt16()];
int paramID =
Instruction.OpCode.OperandType == OperandType.ShortInlineVar
? reader.ReadByte()
: reader.ReadUInt16();
if (paramID == 0 && !context.Method.IsStatic)
throw new ArgumentException("Haven't figured out how to ldarg with the \"this\" argument");
Value = context.Method.GetParameters()[paramID - (context.Method.IsStatic ? 0 : 1)];
}

internal override void Emit(LoggingIlGenerator generator)
{
generator.Emit(Instruction.OpCode, Value.Position);
var methodInfo = Value.Member as MethodBase;
generator.Emit(Instruction.OpCode, Value.Position + (methodInfo != null && methodInfo.IsStatic ? 0 : 1));
}
}

Expand Down
17 changes: 16 additions & 1 deletion Torch/Managers/PatchManager/MSIL/MsilOperandSwitch.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using System.Linq;
using System.Reflection.Emit;
using Torch.Managers.PatchManager.Transpile;
Expand All @@ -19,6 +20,20 @@ internal MsilOperandSwitch(MsilInstruction instruction) : base(instruction)
/// </summary>
public MsilLabel[] Labels { get; set; }


internal override void CopyTo(MsilOperand operand)
{
var lt = operand as MsilOperandSwitch;
if (lt == null)
throw new ArgumentException($"Target {operand?.GetType().Name} must be of same type {GetType().Name}", nameof(operand));
if (Labels == null)
lt.Labels = null;
else
{
lt.Labels = new MsilLabel[Labels.Length];
Array.Copy(Labels, lt.Labels, Labels.Length);
}
}
internal override void Read(MethodContext context, BinaryReader reader)
{
int length = reader.ReadInt32();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public void Emit(OpCode op)
/// <inheritdoc cref="ILGenerator.Emit(OpCode, LocalBuilder)"/>
public void Emit(OpCode op, LocalBuilder arg)
{
_log?.Trace($"Emit\t{op,_opcodePadding} L:{arg.LocalIndex} {arg.LocalType}");
_log?.Trace($"Emit\t{op,_opcodePadding} Local:{arg.LocalIndex}/{arg.LocalType}");
Backing.Emit(op, arg);
}

Expand Down
76 changes: 64 additions & 12 deletions Torch/Managers/PatchManager/Transpile/MethodContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Reflection.Emit;
using NLog;
using Torch.Managers.PatchManager.MSIL;
using Torch.Utils;

namespace Torch.Managers.PatchManager.Transpile
{
Expand Down Expand Up @@ -52,20 +53,19 @@ private void ReadInstructions()
using (var reader = new BinaryReader(memory))
while (memory.Length > memory.Position)
{
var count = 1;
var opcodeOffset = (int) memory.Position;
var instructionValue = (short)memory.ReadByte();
if (Prefixes.Contains(instructionValue))
{
instructionValue = (short)((instructionValue << 8) | memory.ReadByte());
count++;
}
if (!OpCodeLookup.TryGetValue(instructionValue, out OpCode opcode))
throw new Exception($"Unknown opcode {instructionValue:X}");
if (opcode.Size != count)
throw new Exception($"Opcode said it was {opcode.Size} but we read {count}");
if (opcode.Size != memory.Position - opcodeOffset)
throw new Exception($"Opcode said it was {opcode.Size} but we read {memory.Position - opcodeOffset}");
var instruction = new MsilInstruction(opcode)
{
Offset = (int)memory.Position
Offset = opcodeOffset
};
_instructions.Add(instruction);
instruction.Operand?.Read(this, reader);
Expand All @@ -76,22 +76,74 @@ private void ResolveLabels()
{
foreach (var label in Labels)
{
int min = 0, max = _instructions.Count - 1;
while (min <= max)
int min = 0, max = _instructions.Count;
while (min != max)
{
var mid = min + ((max - min) / 2);
if (label.Key < _instructions[mid].Offset)
max = mid - 1;
else
int mid = (min + max) / 2;
if (_instructions[mid].Offset < label.Key)
min = mid + 1;
else
max = mid;
}
#if DEBUG
if (min >= _instructions.Count || min < 0)
{
_log.Trace(
$"Want offset {label.Key} for {label.Value}, instruction offsets at\n {string.Join("\n", _instructions.Select(x => $"IL_{x.Offset:X4} {x}"))}");
}
MsilInstruction prevInsn = min > 0 ? _instructions[min - 1] : null;
if ((prevInsn == null || prevInsn.Offset >= label.Key) ||
_instructions[min].Offset < label.Key)
_log.Error($"Label {label.Value} wanted {label.Key} but instruction is at {_instructions[min].Offset}. Previous instruction is at {prevInsn?.Offset ?? -1}");
#endif
_instructions[min]?.Labels?.Add(label.Value);
}
}


[Conditional("DEBUG")]
public void CheckIntegrity()
{
var entryStackCount = new Dictionary<MsilLabel, Dictionary<MsilInstruction, int>>();
var currentStackSize = 0;
foreach (MsilInstruction insn in _instructions)
{
// I don't want to deal with this, so I won't
if (insn.OpCode == OpCodes.Br || insn.OpCode == OpCodes.Br_S || insn.OpCode == OpCodes.Jmp ||
insn.OpCode == OpCodes.Leave || insn.OpCode == OpCodes.Leave_S)
break;
foreach (MsilLabel label in insn.Labels)
if (entryStackCount.TryGetValue(label, out Dictionary<MsilInstruction, int> dict))
dict.Add(insn, currentStackSize);
else
(entryStackCount[label] = new Dictionary<MsilInstruction, int>()).Add(insn, currentStackSize);

currentStackSize += insn.StackChange();

if (insn.Operand is MsilOperandBrTarget br)
if (entryStackCount.TryGetValue(br.Target, out Dictionary<MsilInstruction, int> dict))
dict.Add(insn, currentStackSize);
else
(entryStackCount[br.Target] = new Dictionary<MsilInstruction, int>()).Add(insn, currentStackSize);
}
foreach (KeyValuePair<MsilLabel, Dictionary<MsilInstruction, int>> label in entryStackCount)
{
if (label.Value.Values.Aggregate(new HashSet<int>(), (a, b) =>
{
a.Add(b);
return a;
}).Count > 1)
{
_log.Warn($"Label {label.Key} has multiple entry stack counts");
foreach (KeyValuePair<MsilInstruction, int> kv in label.Value)
_log.Warn($"{kv.Key.Offset:X4} {kv.Key} => {kv.Value}");
}
}
}

public string ToHumanMsil()
{
return string.Join("\n", _instructions.Select(x => $"IL_{x.Offset:X4}: {x}"));
return string.Join("\n", _instructions.Select(x => $"IL_{x.Offset:X4}: {x.StackChange()} {x}"));
}

private static readonly Dictionary<short, OpCode> OpCodeLookup;
Expand Down
19 changes: 11 additions & 8 deletions Torch/Managers/PatchManager/Transpile/MethodTranspiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@ namespace Torch.Managers.PatchManager.Transpile
{
internal class MethodTranspiler
{
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
public static readonly Logger _log = LogManager.GetCurrentClassLogger();

internal static void Transpile(MethodBase baseMethod, IEnumerable<MethodInfo> transpilers, LoggingIlGenerator output, Label? retLabel)
{
var context = new MethodContext(baseMethod);
context.Read();
context.CheckIntegrity();
_log.Trace("Input Method:");
_log.Trace(context.ToHumanMsil);

var methodContent = (IEnumerable<MsilInstruction>) context.Instructions;
var methodContent = (IEnumerable<MsilInstruction>)context.Instructions;
foreach (var transpiler in transpilers)
methodContent = (IEnumerable<MsilInstruction>)transpiler.Invoke(null, new object[] { methodContent });
methodContent = FixBranchAndReturn(methodContent, retLabel);
Expand All @@ -35,15 +36,17 @@ private static IEnumerable<MsilInstruction> FixBranchAndReturn(IEnumerable<MsilI
MsilInstruction j = new MsilInstruction(OpCodes.Br).InlineTarget(new MsilLabel(retTarget.Value));
foreach (MsilLabel l in i.Labels)
j.Labels.Add(l);
_log.Trace($"Replacing {i} with {j}");
yield return j;
continue;
}
if (_opcodeReplaceRule.TryGetValue(i.OpCode, out OpCode replaceOpcode))
else if (_opcodeReplaceRule.TryGetValue(i.OpCode, out OpCode replaceOpcode))
{
yield return i.CopyWith(replaceOpcode);
continue;
var result = i.CopyWith(replaceOpcode);
_log.Trace($"Replacing {i} with {result}");
yield return result;
}
yield return i;
else
yield return i;
}
}

Expand All @@ -57,7 +60,7 @@ static MethodTranspiler()
if (opcode.OperandType == OperandType.ShortInlineBrTarget &&
opcode.Name.EndsWith(".s", StringComparison.OrdinalIgnoreCase))
{
var other = (OpCode?) typeof(OpCodes).GetField(field.Name.Substring(0, field.Name.Length - 2),
var other = (OpCode?)typeof(OpCodes).GetField(field.Name.Substring(0, field.Name.Length - 2),
BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
if (other.HasValue && other.Value.OperandType == OperandType.InlineBrTarget)
_opcodeReplaceRule.Add(opcode, other.Value);
Expand Down