C# 运行时替换方法(需要unsafe编译)

第一种方法的基本原理是将函数指针替换成想要的方法(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 }

第二种方法请查阅连接

时间: 2024-11-06 18:09:00

C# 运行时替换方法(需要unsafe编译)的相关文章

运行时在方法内部获取该方法的名称及参数相关信息

package com.dongjak.scripts.java.反射; import java.lang.reflect.Method; import net.sf.json.JSONObject; import com.dongjak.annotations.LogTarget; /** * * @author dongjak * * */ public class 运行时在方法内部获取该方法的名称及参数相关信息 { public static void main(String[] args

OC运行时和方法机制笔记

在OC当中,属性是对字段的一种特殊封装手段. 在编译期,编译器会将对字段的访问替换为内存偏移量,实质是一种硬编码. 如果增加一个字段,那么对象的内存排布就会改变,需要重新编译才行. OC的做法是,把实例变量当做一种存储偏移量所用的特殊变量交给类对象来管理,偏移量会在运行期动态查找,这样无论何时访问实例变量,总能找到正确的地址. 可以在对象的内部(.m当中)直接使用下划线变量而不用getter或setter,会绕过方法派发过程,绕过内存管理和线程安全管理语义,会提升性能,同样不会触发KVO,会避免

运行时之方法交换

在没有一个类的实现源码的情况下,想改变其中一个方法的实现,除了继承它重写.和借助类别重名方法暴力抢先之外,还有就是方法交换 方法交换的原理:在OC中调用一个方法其实是向一个对象发送消息,查找消息的唯一依据是selector的名字.利用OC的动态特性,可以实现在运行时偷换selector方法的实现,达到和方法挂钩的目的. 每一个类都有一个方法列表,存放在selector的名字和方法实现的映射关系,imp有点像函数指针,指向具体的方法实现. 可以利用method_exchanggeimplement

MEF在运行时替换插件

利用AppDomain的ShadowCopy特性. var setup = new AppDomainSetup { CachePath = cachePath, ShadowCopyFiles = "true", ShadowCopyDirectories = pluginPath }; 创建程序集 domain = AppDomain.CreateDomain("Host_AppDomain", AppDomain.CurrentDomain.Evidence,

运行时(方法交换)

Method method1 = class_getInstanceMethod([XMGPerson class], @selector(run)); Method method2 = class_getInstanceMethod([XMGPerson class], @selector(study)); method_exchangeImplementations(method1, method2);

java 利用java运行时的方法得到当前屏幕截图的方法(转)

将截屏图片保存到本地路径: package com.test; import java.awt.AWTException; import java.awt.Dimension; import java.awt.HeadlessException; import java.awt.Rectangle; import java.awt.Robot; import java.awt.Toolkit; import java.awt.image.BufferedImage; import java.io

linux下实现在程序运行时的函数替换(热补丁)【转】

转自:http://www.cnblogs.com/leo0000/p/5632642.html 声明:以下的代码成果,是参考了网上的injso技术,在本文的最后会给出地址,同时非常感谢injso技术原作者的分享. 但是injso文章中的代码存在一些问题,所以后面出现的代码是经过作者修改和检测的.也正因为这些错误,加深了我的学习深度. 最近因为在学习一些调试的技术,但是很少有提到如何在函数运行时实现函数替换的. 为什么会想到这一点?因为在学习调试时,难免会看到一些内核方面的调试技术,内核中的调试

在程序运行时实现函数替换

声明:以下的代码成果,是参考了网上的injso技术,文章最后会给出地址. 另外一个,injso文章中的代码实际上不能够运行起来的,后面出现的代码都是经过我个人修改和检测的. 最近因为在学习一些调试的技术,但是很少有提到如何在函数运行时实现函数替换的. 为什么会想到这一点?因为在学习调试时,难免会看到一些内核方面的调试技术,内核中的调试有一个kprobe,很强大,可以实现运行时的函数替换.其原理就是hook,钩子,但是学习了这个kprobe之后会发现,kprobe内部有检测所要钩的函数是不是属于内

编译时、运行时、构建时(一)

在开发和设计的时候,我们需要考虑编译时,运行时以及构建时这三个概念.理解这几个概念可以更好地帮助你去了解一些基本的原理.下面是初学者晋级中级水平需要知道的一些问题. Q.下面的代码片段中,行A和行B所标识的代码有什么区别呢? public class ConstantFolding { static final int number1 = 5; static final int number2 = 6; static int number3 = 5; static int number4= 6;