.NET中如何向类构造函数插入IL代码调用?
在.NET中运行时注入静态方法调用到第三方类构造函数的方案
方案一:使用Mono.Cecil修改程序集(预加载修改)
这种方式适合在应用启动时先修改目标程序集的字节码,再加载使用。
步骤:
- 引用Mono.Cecil NuGet包(
Install-Package Mono.Cecil) - 加载目标程序集的字节流
- 定位到目标类及其公开构造函数
- 在构造函数的IL指令流中插入静态方法调用的IL代码
- 保存修改后的程序集(可保存到内存或磁盘)
- 加载修改后的程序集并使用
代码示例:
using Mono.Cecil; using Mono.Cecil.Cil; using System; using System.IO; using System.Linq; public static class AssemblyPatcher { public static AssemblyDefinition PatchAssembly(string assemblyPath, Type staticMethodType, string staticMethodName) { // 加载目标程序集 var assemblyDef = AssemblyDefinition.ReadAssembly(assemblyPath); // 替换为实际目标类名 var targetType = assemblyDef.MainModule.Types.First(t => t.Name == "TargetClass"); // 获取无参公开构造函数(带参构造需调整参数匹配逻辑) var ctor = targetType.Methods.First(m => m.IsConstructor && m.IsPublic && !m.HasParameters); // 导入要调用的静态方法引用 var module = assemblyDef.MainModule; var methodRef = module.ImportReference(staticMethodType.GetMethod(staticMethodName, Type.EmptyTypes)); // 在构造函数第一条指令前插入静态方法调用 var ilProcessor = ctor.Body.GetILProcessor(); var firstInstruction = ctor.Body.Instructions.First(); ilProcessor.InsertBefore(firstInstruction, ilProcessor.Create(OpCodes.Call, methodRef)); return assemblyDef; } // 使用示例 public static void Main() { var targetAssemblyPath = "TargetAssembly.dll"; var patchedAssembly = PatchAssembly(targetAssemblyPath, typeof(MyStaticClass), "MyStaticMethod"); // 保存到内存并加载 using var ms = new MemoryStream(); patchedAssembly.Write(ms); ms.Position = 0; var loadedAssembly = System.Reflection.Assembly.Load(ms.ToArray()); // 创建实例时会自动触发注入的静态方法 var targetInstance = Activator.CreateInstance(loadedAssembly.GetType("TargetClass")); } } // 要注入的静态方法类 public static class MyStaticClass { public static void MyStaticMethod() { Console.WriteLine("注入的静态方法被调用了"); } }
方案二:使用Harmony进行运行时内存补丁(无需修改程序集文件)
Harmony是专门用于.NET运行时方法补丁的库,适合在程序运行过程中动态修改方法逻辑,无需修改原程序集文件。
步骤:
- 引用Harmony NuGet包(
Install-Package Lib.Harmony) - 创建Harmony实例
- 定义补丁方法,指定在目标构造函数执行前/后调用静态方法
- 对目标构造函数应用补丁
代码示例:
using HarmonyLib; using System; public static class ConstructorPatcher { public static void ApplyPatch() { var harmony = new Harmony("com.yourcompany.target.patch"); // 获取目标类的无参公开构造函数(带参构造需匹配参数) var targetCtor = typeof(TargetClass).GetConstructor(Type.EmptyTypes); // 应用前缀补丁:构造函数执行前调用静态方法 harmony.Patch(targetCtor, prefix: new HarmonyMethod(typeof(MyStaticClass).GetMethod(nameof(MyStaticClass.MyStaticMethod)))); } } // 要注入的静态方法类 public static class MyStaticClass { public static void MyStaticMethod() { Console.WriteLine("注入的静态方法被调用了"); } } // 使用示例 public static void Main() { // 确保目标程序集已加载(可通过Assembly.Load加载) ConstructorPatcher.ApplyPatch(); // 创建实例时自动触发注入逻辑 var targetInstance = new TargetClass(); }
注意事项:
- 若目标构造函数带参数,补丁方法的参数需与构造函数参数匹配
- Harmony支持前缀(执行前)、后缀(执行后)、最终(无论异常都执行)三种补丁类型,可按需选择
关键说明
- 预加载修改方案需确保目标程序集尚未被CLR加载;Harmony方案支持已加载程序集的内存补丁
- 若目标程序集是强签名的,修改后需用
Mono.Cecil.StrongNameKeyPair重新签名才能加载 - 运行时修改程序集需要足够权限,受限环境(如沙箱)中可能无法执行
内容的提问来源于stack exchange,提问作者DuckQueen
相关产品推荐
相关产品推荐

