Skip to content

Instantly share code, notes, and snippets.

@pardeike
Last active November 18, 2020 17:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pardeike/d5a37e13c6c3a7b15e0b89c7afa5e563 to your computer and use it in GitHub Desktop.
Save pardeike/d5a37e13c6c3a7b15e0b89c7afa5e563 to your computer and use it in GitHub Desktop.
Complex reverse patching
/* the following will print
A: val1=123, val2=456
B: val1=123
With C, we also have 456
C: val1=123
B: done
A: done
Press any key to exit
*/
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
namespace ComplexReversePatching
{
class Program
{
static void Main()
{
var harmony = new Harmony("test");
harmony.PatchAll();
var a2 = new Aclass();
a2.A(123, 456);
Console.Write("Press any key to exit");
Console.ReadKey();
}
}
static class Tools
{
internal static IEnumerable<CodeInstruction> ReplaceWithAppendedArgument(this IEnumerable<CodeInstruction> instructions, MethodInfo from, MethodInfo to, int argIndex)
{
var list = instructions.ToList();
var i = list.FindIndex(code => code.Calls(from));
if (i < 0) Console.WriteLine($"Cannot find {from}");
list.Insert(i++, new CodeInstruction(OpCodes.Ldarg, argIndex));
list[i].opcode = OpCodes.Call;
list[i].operand = to;
return list.AsEnumerable();
}
}
//
[HarmonyPatch(typeof(Aclass), "A")]
static class APatch
{
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
var from = SymbolExtensions.GetMethodInfo(() => new Bclass().B(default));
var to = SymbolExtensions.GetMethodInfo(() => BPatch.Bnew(default, default, default));
return instructions.ReplaceWithAppendedArgument(from, to, 2);
}
}
[HarmonyPatch]
static class BPatch
{
[HarmonyReversePatch]
[HarmonyPatch(typeof(Bclass), "B")]
[MethodImpl(MethodImplOptions.NoInlining)]
internal static void Bnew(Bclass instance, int val1, int val2)
{
IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
var from = SymbolExtensions.GetMethodInfo(() => new Cclass().C(default));
var to = SymbolExtensions.GetMethodInfo(() => CReplacement.Cnew(default, default, default));
return instructions.ReplaceWithAppendedArgument(from, to, 2);
}
}
}
static class CReplacement
{
internal static void Cnew(Cclass instance, int val1, int val2)
{
Console.WriteLine($"With C, we also have {val2}");
instance.C(val1);
}
}
//
public class Aclass
{
public void A(int val1, int val2)
{
Console.WriteLine($"A: val1={val1}, val2={val2}");
var b = new Bclass();
b.B(val1);
Console.WriteLine($"A: done");
}
}
public class Bclass
{
public void B(int val1)
{
Console.WriteLine($"B: val1={val1}");
var c = new Cclass();
c.C(val1);
Console.WriteLine($"B: done");
}
}
public class Cclass
{
public void C(int val1)
{
Console.WriteLine($"C: val1={val1}");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment