Using Funceval to Inject code into another app

It's possible for a managed debugger to attach to an app and inject arbitrary stub code into the app, without any prior cooperation from the app.  The more technical description is that while the debuggee is stopped, the debugger can load an arbitrary module into the debuggee's address space and then hijack the debuggee thread from the breakpoint to go execute an arbitrary method from that newly module. And it can do all of this without any prior cooperation from the debuggee.

Yes, this is evil.

Why would you want to do this?
Several folks have asked me and have had different reasons.
Rick + I were talking with Jamie Cansdale and he asked about it. We also met with some PSS folks who wanted this sort of technique so that they could inject monitoring code into an existing application.
I make no comments about whether it's a good idea. But I promised I'd write a blog about it, so here it is...

How do you do it?
Say you have some arbitrary module (say stub.dll) file containg the following source code:

using System;
class MyStub
public static void Worker() {
Console.WriteLine("Inside the worker"); // This could do anything!

That worker function could run any arbitrary code. It could even spin up its own thread which would then live on after the Worker() had returned.

Clearly, you could load this into your own process with reflection and execute MyStub.Worker like so:

        Assembly a = Assembly.LoadFrom(@"c:\temp\stub.dll");
Type t = a.GetType("MyStub");
MethodInfo m = t.GetMethod("Worker", BindingFlags.Static | BindingFlags.Public);
null, null);  // Now execute the worker method

But how do you get the debugger to load this into the debuggee's process?

I said before that Funceval is evil, and this is one more reason why.  You could have the debugger use func-eval to execute those calls from out of process.  Most debuggers don't have the UI to let you enter the snippets above as is (particularly because of the assignments). But VS can call functions as part of an expression.

So compress the snippet above into a single expression like so:

Assembly.LoadFrom(@"c:\temp\stub.dll").GetType("MyStub").GetMethod("Worker", BindingFlags.Static | BindingFlags.Public).Invoke(null, null);

And now you can run it from VS's immediate window. 

See for yourself
So here's the demo to see with your own eyes:
1) Compile the source snippet above for MyStub.Worker into some dll (eg, "stub.dll").
2) In VS, run some random debuggee
3) In VS, open the source file for MyStub.Worker and place breakpoint in Worker().
4) In VS, stop your debuggee at some place where you can do func-eval. Most any source-level breakpoint or 1st-chance exception will work. If you attached to an app running optimized code, this will be more difficult. (I still need to blog about func-eval restrictions)
5) Go to the immediate window and enter the big expression:
    Assembly.LoadFrom(@"c:\temp\stub.dll").GetType("MyStub").GetMethod("Worker", BindingFlags.Static | BindingFlags.Public).Invoke(null, null);

    Make sure that the strings for the filename match (eg, "c:\temp\stub.dll") in the call to LoadFrom.

7) You'll observe you hit the breakpoint in MyStub.Worker, which is proof that it was indeed loaded and executed. This takes advantage of a VS feature called "Nested Break States", where if you funceval something from the immediate window, and that hits a breakpoint, you can debug the func-eval. When you return from the func-eval, you'll be back at the original place where you first started. The callstack will look something like this:

> stub.dll!MyStub.Worker() Line 5 C#
   [Native to Managed Transition]
   [Managed to Native Transition]
   Evaluation of: Assembly.LoadFrom(@"c:\temp\stub.dll").GetType("MyStub").GetMethod("Worker", BindingFlags.Static |    BindingFlags.Public).Invoke(null, null);
   MyApp.exe!Program.Main(string[] args = {Dimensions:[1]}) Line 49 + 0x5 bytes C#
   [Native to Managed Transition]

VS conveniently marks where in the callstack the func-eval occurred with that frame that says "Evaluation of ...".

In summary:
This is all public functionality, but it does require some pretty rich func-eval support, which MDbg doesn't have. In particular, you need to handle some function overload resolution, chaining return values, generating strings, and resolving the BindingFlags enum argument. I thought about writing an MDbg extension to demo it, but it's just so much easier to show you in VS.


Comments (5)

  1. Thanks Mike!

    I look forward to doing some truely evil stuff! 😉


  2. Sam says:

    Could you use this technique to intercept functions and substitute them on-the-fly with new implementations?

  3. Sam – maybe. There are some restrictions about when you can do a func-eval. And you’d still need to skip over the original method (perhaps by doing a SetIp after the func-eval to the end of the method).

    Seems like it could work in theory. But it may be tough to automate.

    What are you thinking of?

  4. Sam Saffron says:

    A couple of things,

    1. Instrumentation without using ICorProfile, hooking a list of functions and timing them (potentially extracting params)

    2. Hot patching – Patching a single function in a large app without needing to recompile the whole dll

  5. I created a blog category for Func-eval (aka Property Evaluation), and I updated some of my old…

Skip to main content