.NET Inside Out Part 5 — Capturing thread creation to catch exceptions

This is the fifth part of the .NET Inside Out series. For your convenience you can find other parts in the table of contents in Part 1 – Virtual and non-virtual calls in C#

Unhandled exception crashes application domain which in turn crashes whole process. What if we want to create some background job and handle its exceptions? We can create tasks and add continuation for failure, we can use asynchronous code and catch exceptions in place of awaiting. However, sometimes we are forced to use ordinary threads. It is manageable if we control the thread start function — we can simply catch exceptions there. However, what should we do if one of libraries used by our process creates thread and doesn’t handle exceptions? Well, we can try to modify CLR code to handle this case. Let’s go.

Idea

Thread class has a constructor accepting thread start function. We can inject our code into it and wrap function in catch handler. However, since this function is ngened (its code is compiled to machine code when .NET is being installed) we can’t simply override it by rewriting CLR metadata. We also cannot modify it directly without unblocking memory pages since they are protected by the OS. We already know how to inject jump into method but this case is a little different — we want to execute the original code but we want to modify method parameter.

Let’s start with something easy: wrapping thread start function with exception handler.

This is rather obvious.

Next, we need to unblock memory page in order to modify it. Here is the code:

Now, before we modify original code, we need to examine it and see what exactly we modify. We want to jump to a method which will modify parameter on a stack and get back to original code without changing any other things (registers, stack etc). Since we will override few starting bytes, we need to execute them directly.

In theory we could copy existing constructor code somewhere else and execute it as a whole. But this is pretty difficult — all jumps and calls are usually based on relative addresses so we would need to recalculate them manually. It is easier to simply jump back to the same code and execute few lines manually.

So our helper function looks like this:

As stated in comments, original method is called using fastcall convention. This means that parameters are passed via ECX and EDX registers and return value is in EAX.

We first remember ECX (since it might be modified by helper function). All parameters are already in registers so we simply call helper method modifying thread start function. Next, we restore ECX and other registers. Finally, we execute function preamble (which we overwrite with jump instruction in original code) and jump back to thread code.

All we need to do is calculate addresses:

And we are done. Test program looks as follows:

You can see the code here. It was tested on .NET 4.5 with Any CPU on Windows 10 Professional x64.

EDIT:
I am extending this note some time later.It looks like there are two changes required. First, you need to unlock the helper method code as well:

Also, I don’t know why I calculated the jump address in this way:

As now it looks like there is an off-by-one error. It should be:

I am sure it was working when I was testing it last time so apparently something in my environment has changed. Yet another reason not to play with things like that!