C#拦截/更改/重定向方法

时间:2014-01-12 23:43:08

标签: c# cil mono.cecil

假设我在第三方dll中有一个私有的,“实例”,非静态的bool方法。所有这个方法都返回一个值。没有其他的。我将如何拦截对此方法的调用,更改它的IL OpCodes /方法体,或将其重定向到额外的,重写或派生的方法。

我不想反编译第三方dll,手动更改源代码并重新编译它。 我也不想将程序集保存到磁盘,因为这也涉及使用“重新编译”的程序集而不是原始程序集。

我基本上希望能够使用原始dll - 没有替换或文件更改。我只是想做我上面提到的。

有什么方法可以解决这个问题吗?如果是这样,你可以详细说明或发布参考/教程/等。

另外,我知道虚拟,覆盖和新修饰符,但请记住我:没有所说的第三方dll的源代码,无法访问源代码,不想用dotPeek等反编译并重新编译。

谢谢!

编辑: 我忘了提到剩下的基础设施: MainProgram加载ThirdPartyDLL。 MainProgram还加载MyPluginDLL。 我正在尝试从MyPluginDLL更改ThirdPartyDLL中的方法,以便当MainProgram调用所述方法时,它将调用更改的方法。我希望能够在不保存新装配并使用新装配重新启动MainProgram的情况下执行此操作。基本上,我想在启动时或MainProgram运行时执行此操作。

1 个答案:

答案 0 :(得分:4)

这是使用Mono Cecil更改内存中的程序集并在其上执行方法的代码示例。请注意,修改和保存程序集非常慢。这应该在您的应用程序启动时完成。

class Program
{
    static void Main(string[] args)
    {
        Assembly assembly;
        using (MemoryStream assemblyStream = new MemoryStream(File.ReadAllBytes("TargetDLL.dll")))
        {
            // 1. Get the reference to third-party method.
            AssemblyDefinition assemblyDef = AssemblyDefinition.ReadAssembly(assemblyStream);
            TypeDefinition targetDLLType = assemblyDef.Modules[0].GetType("TargetDLL.Foo");
            MethodDefinition barMethod = targetDLLType.Methods[0];

            // 2. Let's see what Foo.Bar returns...
            assembly = Assembly.Load(assemblyStream.ToArray());
            Console.WriteLine(CallMethod<int>(assembly.GetType("TargetDLL.Foo"), "Bar"));

            // 3. Boot up the IL processor.
            var processor = barMethod.Body.GetILProcessor();

            // 4 View the unmodified IL.
            Console.WriteLine("Original code");
            PrintIL(processor);

            // 5. Modify the code.
            // 5.a Clear the method of all IL.
            processor.Body.Instructions.Clear();

            // 5.b Inject our custom return value.
            processor.Emit(OpCodes.Ldc_I4, 1337);
            processor.Emit(OpCodes.Ret);

            // 6. And how does it look now?
            Console.WriteLine();
            Console.WriteLine("New code");
            PrintIL(processor);

            // 7. Save it.
            assemblyDef.Write(assemblyStream);
            assembly = Assembly.Load(assemblyStream.ToArray());

            // 8. Result value.
            Console.WriteLine(CallMethod<int>(assembly.GetType("TargetDLL.Foo"), "Bar"));
        }

        Console.WriteLine("END");
        Console.ReadKey(true);
    }

    static void PrintIL(ILProcessor processor)
    {
        foreach (var instruction in processor.Body.Instructions)
        {
            Console.WriteLine(instruction);
        }
    }

    static T CallMethod<T>(Type type, string method)
    {
        return (T)type.InvokeMember("Bar", BindingFlags.Static | BindingFlags.InvokeMethod | BindingFlags.Public, null, null,
            null);
    }
}

在同一目录中添加名为TargetDLL.dll的以下DLL。包含此代码:

namespace TargetDLL
{
    public static class Foo
    {
        public static int Bar()
        {
            return 0;
        }
    }
}

修改

我认为你的错误与Mono.Cecil的版本有关。我正在使用master branch on GitHub中的 9.5.0 。下载ZIP文件并根据需要构建项目。