Last active
November 18, 2020 17:01
-
-
Save pardeike/d5a37e13c6c3a7b15e0b89c7afa5e563 to your computer and use it in GitHub Desktop.
Complex reverse patching
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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