1. 程式人生 > >C# 運行時替換方法(需要unsafe編譯)

C# 運行時替換方法(需要unsafe編譯)

version log aes reflect erl dha ram ins text

第一種方法的基本原理是將函數指針替換成想要的方法(https://stackoverflow.com/questions/7299097/dynamically-replace-the-contents-of-a-c-sharp-method);還有一種方式是用 methodInfo.GetMethodBody().GetILAsByteArray() 獲得IL字節碼,然後(利用C++?)編寫註入代碼對其字節碼重新寫入(https://www.codeproject.com/Articles/463508/NET-CLR-Injection-Modify-IL-Code-during-Run-time)。

這兩種方法都需要先調用 RuntimeHelpers.PrepareMethod

(https://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.runtimehelpers.preparemethod(v=vs.110).aspx)來準備方法。

第一種方法實現簡便,下面是演示代碼:

  1 /* https://stackoverflow.com/questions/7299097/dynamically-replace-the-contents-of-a-c-sharp-method
  2    For .NET 4 and above
  3 
  4 "C:\Program Files (x86)\MSBuild\14.0\Bin\csc.exe" /unsafe+ /out:replacemethod.exe replacemethod.cs && start "replacemethod.exe" replacemethod.exe
5 Foo1 is called. 6 Foo2 is called. 7 Foo2 returns. 8 Foo3 is called. I‘m foo3‘s argument. 9 Foo4 is called. 10 -------------------- 11 Version x64 Relaese 12 Version x64 Relaese 13 Version x64 Relaese 14 Version x64 Relaese 15 -------------------- 16 Bar1 is called. 17 Bar2 is called.
18 Bar2 returns. 19 Bar3 is called. I‘m foo3‘s argument. 20 Bar4 is called. 21 22 Press any key to EXIT... 23 */ 24 25 using System; 26 using System.Reflection; 27 using System.Runtime.CompilerServices; 28 29 class Program { 30 public static void Main(params string[] args){ 31 var sp = new string(-, 20); 32 33 Target targetInstance = new Target(); 34 35 targetInstance.Test(); 36 Console.WriteLine(sp); 37 38 Injection.Install(1); 39 Injection.Install(2); 40 Injection.Install(3); 41 Injection.Install(4); 42 43 Console.WriteLine(sp); 44 targetInstance.Test(); 45 46 Console.Write("\nPress any key to EXIT..."); 47 Console.ReadKey(true); 48 } 49 } 50 51 52 public class Injection { 53 public static void Install(int funcNum) { 54 MethodInfo methodToReplace = typeof(Target).GetMethod("Foo"+funcNum, BindingFlags.Instance|BindingFlags.NonPublic|BindingFlags.Public); 55 MethodInfo methodToInject = typeof(Injection).GetMethod("Bar"+funcNum, BindingFlags.Instance|BindingFlags.NonPublic|BindingFlags.Public); 56 RuntimeHelpers.PrepareMethod(methodToReplace.MethodHandle); 57 RuntimeHelpers.PrepareMethod(methodToInject.MethodHandle); 58 59 unsafe { 60 if (IntPtr.Size == 4) 61 { 62 int* inj = (int*)methodToInject.MethodHandle.Value.ToPointer() + 2; 63 int* tar = (int*)methodToReplace.MethodHandle.Value.ToPointer() + 2; 64 #if DEBUG 65 Console.WriteLine("Version x86 Debug"); 66 byte* injInst = (byte*)*inj; 67 byte* tarInst = (byte*)*tar; 68 int* injSrc = (int*)(injInst + 1); 69 int* tarSrc = (int*)(tarInst + 1); 70 *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5); 71 #else 72 Console.WriteLine("Version x86 Relaese"); 73 *tar = *inj; 74 #endif 75 } 76 else 77 { 78 long* inj = (long*)methodToInject.MethodHandle.Value.ToPointer() + 1; 79 long* tar = (long*)methodToReplace.MethodHandle.Value.ToPointer() + 1; 80 #if DEBUG 81 Console.WriteLine("Version x64 Debug"); 82 byte* injInst = (byte*)*inj; 83 byte* tarInst = (byte*)*tar; 84 int* injSrc = (int*)(injInst + 1); 85 int* tarSrc = (int*)(tarInst + 1); 86 *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5); 87 #else 88 Console.WriteLine("Version x64 Relaese"); 89 *tar = *inj; 90 #endif 91 } 92 } 93 } 94 95 private void Bar1() { 96 Console.WriteLine("Bar1 is called."); 97 } 98 private string Bar2() { 99 Console.WriteLine("Bar2 is called."); 100 return "Bar2 returns."; 101 } 102 private void Bar3(string arg) { 103 Console.WriteLine("Bar3 is called. " + arg); 104 } 105 private void Bar4() { 106 Console.WriteLine("Bar4 is called."); 107 } 108 } 109 110 111 public class Target { 112 public void Test() { 113 Foo1(); 114 Console.WriteLine(Foo2()); 115 Foo3("I‘m foo3‘s argument."); 116 Foo4(); 117 } 118 119 private void Foo1() { 120 Console.WriteLine("Foo1 is called."); 121 } 122 public string Foo2() { 123 Console.WriteLine("Foo2 is called."); 124 return "Foo2 returns."; 125 } 126 private void Foo3(string arg) { 127 Console.WriteLine("Foo3 is called. "+arg); 128 } 129 private void Foo4() { 130 Console.WriteLine("Foo4 is called."); 131 } 132 }

第二種方法請查閱連接。

C# 運行時替換方法(需要unsafe編譯)