4

Let's say I have a private, "instance," non-static, bool method in a third-party dll. All this method does is return a value. Nothing else. How would I go about intercepting calls to this method, changing it's IL OpCodes/method body, or redirecting it to an extra, overridden or derived method.

I do not want to decompile the third-party dll, manually change the source, and recompile it. I also would rather not to save the assembly to disk as this would also involve using a "recompiled" assembly instead of the original.

I basically want to be able to use the original dll - no replacements or file changes. I just want to do what I mentioned above.

Is there any way to go about this? If so, can you elaborate or post references/tutorials/etc.

Also, I know of virtual, override, and new modifiers, but remember that I: do not have the source of said third-party dll, cannot get access to the source, do not want to decompile with something such as dotPeek and recompile.

Thanks!

Edit: I forgot to mention the rest of the infrastructure: MainProgram loads ThirdPartyDLL. MainProgram also loads MyPluginDLL. I'm trying to change a method in ThirdPartyDLL from MyPluginDLL so that when MainProgram calls said method, it will call the changed method. I want to be able to do this WITHOUT saving a new assembly and restarting the MainProgram with the new assembly. Essentially, I want to do this either at startup or when MainProgram is running.

JasonMArcher
  • 14,195
  • 22
  • 56
  • 52
user3150838
  • 61
  • 1
  • 5
  • I bet you're looking for [Mono.Cecil](http://www.mono-project.com/Cecil). [Tutorials are here](http://stackoverflow.com/questions/1513319/mono-cecil-documentation-and-tutorials) – Measurity Jan 12 '14 at 23:44
  • @Measuring, I know I am. But where am I looking? I only know of ReadAssembly and SaveAssembly. Remember that I do not want to save and use a new assembly. I want to use the same assembly file/dll without having to save an altered one. – user3150838 Jan 12 '14 at 23:48
  • Are you trying to intercept ALL calls to this method or just YOUR calls to this method? – Pete Garafano Jan 12 '14 at 23:48
  • You can hold an assembly in memory and change it, execute it. It's on the internet. I found it before. Do it like this: Load the assembly in memory, inject a method into the method you want to keep track of and make it call your code. – Measurity Jan 12 '14 at 23:50
  • @TheGreatCO, All calls. But, keep in mind my end goal is simply to change this method's return value. – user3150838 Jan 12 '14 at 23:51
  • @Measuring, I'm unable to find more on this, can you expand please? – user3150838 Jan 13 '14 at 00:09

1 Answers1

4

Here is a code example to change an assembly in memory and execute methods on it, using Mono Cecil. Note that modifying and saving Assemblies is very slow. This should be done at startup of your application.

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);
    }
}

Add the following DLL with the name TargetDLL.dll in the same directory. Containing this code:

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

Edit

I think your error has to do with the version of Mono.Cecil. I'm using 9.5.0 from the master branch on GitHub. Download the ZIP file and build the project as required.

Measurity
  • 1,316
  • 1
  • 12
  • 24
  • Also, please check the edit I made in the original post. Hope it helps you understand – user3150838 Jan 13 '14 at 01:11
  • Also, will your method work if the class and method are non-static? – user3150838 Jan 13 '14 at 01:18
  • @user3150838 Yes, you can find any type with Reflection if you specify the BindingFlags right. Say: private int Hello() = BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic – Measurity Jan 13 '14 at 01:26
  • Thanks - but I still can't get this method to work. The error still persists. Even if the error wasn't there, I don't think it would work with my infrastructure. More detail about the infrastructure in the original post – user3150838 Jan 13 '14 at 01:36