How To Inject a Managed .NET Assembly (DLL) Into Another Process

Introduction

Today we’re going to kill two birds—

  • How to inject a .NET assembly (DLL) into a native process?
  • How to inject a .NET assembly (DLL) into a managed process?

—with one stone: by using the CLR Hosting API.

But first, let’s talk about monsters.

To many .NET developers, the .NET runtime is like the slobbering, snaggle-toothed monster sitting in your barcalounger, that everybody in the household politely ignores because hey. It’s a frikkin’ monster; don’t make eye contact with it.

Monsters, Inc.

[more]

The monster occasionally grunts at you. It smells like a wet dog. And it clutters up half your living room with its bulk. But you tolerate it, because this is a useful monster. It vamooses those sticky kitchen leftovers; does the laundry; feeds the dog; vacuums the carpet; and makes sure the doors are locked at night. It takes care of you, even if your home doesn’t quite feel like your home anymore.

[more]

Now, we’re accustomed to thinking of Windows applications as being either:

  • Native OR
  • Managed

Either our application has a big, smelly, snaggle-toothed monster sitting quietly on the barcalounger, or it doesn’t.

Right?

Well, what if I said that the whole managed-vs.-native dichotomy is an illusion that’s been pulled around your eyes to blind you to the truth?

Morpheus: The Matrix is everywhere. It is all around us. Even now, in this very room. You can see it when you look out your window or when you turn on your television. You can feel it when you go to work… when you go to church… when you pay your taxes. It is the world that has been pulled over your eyes to blind you from the truth.

Neo: What truth?

Morpheus: That you are a slave, Neo. Like everyone else you were born into bondage. Into a prison that you cannot taste or see or touch. A prison for your mind.

What truth? you ask.

That you are locked in a programmatic worldview designed to turn a human being into one of these:

Okay, scratch that.

Cut.

After having a friend review this article prior to posting, I’m told I’m way out on a limb here.

James bud—you’re crazy. The .NET framework is nothing like the Matrix. Stop using the Matrix in your posts. For starters, .NET uses Unicode internally, whereas the Matrix uses [censored].

Ah. Right. The old what-programming-language-do-they-use-in-the-Matrix? debate.

I guess the point I was trying to make is this: there’s no fundamental difference between a managed process and a native one on Windows. A managed process is simply a native process in which some special code which we call the “.NET runtime” happens to be running, and in which we have access to a special library known as the “.NET framework”.

Now, this “.NET runtime” code is capable of doing some special things, to be sure. For example, it knows how to generate binary executable code on the fly in a process known as just-in-time compilation. It knows how to rig things properly such that “managed-ness” happens correctly, that our managed code runs by the rules we expect it to run by, and so forth.

But applications are not “managed” or “native”. They’re always native. Sometimes they eruct an infrastructure known as the managed runtime, and we can then start calling them “managed” but they never lose that core nativity. In fact, it’s impossible to execute managed code without executing native code! The entire phrase is a misnomer!

There is no such thing as managed executable code. Only managed code which is later converted to executable code.

Like everything else in programming, the managed runtime is an illusion, albeit a useful one.

Loading the .NET Runtime Yourself

So if a managed process is simply a native process in which some special code is running, there must be a way to load the .NET infrastructure into a native, non-.NET process, right?

Right.

Surely this is a complex and messy process, requiring special knowledge of Windows and .NET framework internals?

Maybe, but all the complexity has been placed behind one of these:

I say this because starting the .NET runtime is (pretty much) a one-liner, by design:

HRESULT hr = pointerToTheDotNetRuntimeInterface->Start();

The only trick is getting the target process (the process into which you’d like to inject your managed assembly) to execute this code.

Let’s explore.

Step 1: Create the Managed Assembly

So you have some managed code you’d like to run inside the target process. Package this code inside (for example) a typical .NET class library. Here’s a simple C# class containing one method:

namespace MyNamespace
{
    public class MyClass
    {
        // This method will be called by native code inside the target process…
        public static int MyMethod(String pwzArgument)
        {
            MessageBox.Show(“Hello World”);
            return 0;
        }

    }
}

This method should take a String and return an int (we’ll see why below). This is your managed code entry point—the function that the native code is going to call.

Step 2: Create the Bootstrap DLL

Here’s the thing. You don’t really “inject” a managed assembly into another process. Instead, you inject a native DLL, and that DLL executes some code which invokes the .NET runtime, and the .NET runtime causes your managed assembly to be loaded. 

This makes sense, as the .NET runtime understands what needs to happen in order to load and start executing code in a managed assembly.

So you’ll need to create a (simple) C++ DLL containing code similar to the following:

#include “MSCorEE.h”

void StartTheDotNetRuntime()
{
    // Bind to the CLR runtime..
    ICLRRuntimeHost *pClrHost = NULL;
    HRESULT hr = CorBindToRuntimeEx(
        NULL, L“wks”, 0, CLSID_CLRRuntimeHost,
        IID_ICLRRuntimeHost, (PVOID*)&pClrHost);

    // Push the big START button shown above
    hr = pClrHost->Start();

    // Okay, the CLR is up and running in this (previously native) process.
    // Now call a method on our managed C# class library.
    DWORD dwRet = 0;
    hr = pClrHost->ExecuteInDefaultAppDomain(
        L“c:\PathToYourManagedAssembly\MyManagedAssembly.dll”,
        L“MyNamespace.MyClass”, L“MyMethod”, L“MyParameter”, &dwRet);

    // Optionally stop the CLR runtime (we could also leave it running)
    hr = pClrHost->Stop();

    // Don’t forget to clean up.
    pClrHost->Release();
}

This code makes a few simple calls to the CLR Hosting API in order to bind to and start the .NET runtime inside the target process.

  1. Call CorBindToRuntimeEx in order to retrieve a pointer to the ICLRRuntimeHost interface.
  2. Call ICLRRuntimeHost::Start in order to launch the .NET runtime, or attach to the .NET runtime if it’s already running.
  3. Call ICLRRuntimeHost::ExecuteInDefaultAppDomain to load your managed assembly and invoke the specified method—in this case, “MyClass.MyMethod”, which we implemented above.

The ExecuteInDefaultAppDomain loads the specified assembly and executes the specified method on the specified class inside that assembly. This method must take a single parameter, of type string, and it must return an int. We defined such a method above, in our C# class.

ExecuteInDefaultAppDomain will work for the majority of applications. But if the target process is itself a .NET application, and if it features multiple application domains, you can use other methods on the ICLRRunTimeHost interface to execute a particular method on a particular domain, to enumerate application domains, and so forth.

The single toughest thing about getting the above code running is dealing with the fact that the CLR Hosting API is exposed, like many core Windows services, via COM, and working with raw COM interfaces isn’t everybody’s idea of fun.

Step 3: Inject the Bootstrap DLL into the Target Process

The last step is to inject the bootstrap DLL into the target process. Any DLL injection method will suffice, and as this topic is covered thoroughly elsewhere on the Internet and here on Coding the Wheel, I won’t rehash it. Just get your bootstrap DLL into the target process by any means necessary.

Conclusion

That’s it.

Of course, we’ve only scratched the surface of the CLR Hosting API and its powers. For a more in-depth description, consult Bart De Smet’s CLR Hosting Series. If that’s more detail than you need, Damian Mehers has an interesting post describing how he used .NET code injection to tweak Windows Media Center. Last but not least, if you’re planning on hosting the CLR in production code, you might want to go all-out and pick up a copy of Customizing the Microsoft .NET Framework Common Language Runtime from Microsoft Press, though this shouldn’t be necessary for everyday use.

And for those of you who are following the poker botting series, this technique allows you to have the best of both worlds: you can create AI or controller logic using a managed language like C#, while retaining most of the benefits of having code run inside the poker client process.

Good luck.

Posted by on August 22, 2008 in Uncategorized, , , ,

Comments

  • Anonymous says:

    It’s interesting. This is all straightforward stuff and yet I would say not 1 out of 5 .NET developers understand the foundation on which their code runs. That’s why when you do searches for this kind of information, there’s really not much there. I’d like to see a follow-up to this post describing, from the perspective of a typical programmer, how to support large-scale CLR integration in native projects?

    Or maybe there is some resource out there already which describes this? Maybe? Possibly?

  • Anonymous says:

    Also, how did you do the blue diagram under ‘create bootstrap dll’ with the arrows?

  • Ed K. says:

    The post I’ve been waiting for. Thanks. Now I will be converting all your code to managed. Thanks. :)

  • Anonymous says:

    I just knew I was going to learn something new today. Now on to my normal weekend allusion, ie cutting the grass….

  • Angry says:

    I just, okay. Is there NO WAY TO DO THIS WITHOUT RESORTING TO BITS AND PIECES NATIVE CODE?

    Let’s say you live in a world in which C# is your only tool and you can’t write the C++ shim DLL.

    What then?

    • Ryios says:

      You are SOL….

      You have to write a native dll somehow, whether that be c, c++, delphi, anything that can compile to a native dll.

      Optionally you could make an ASM snippet and use CreateRemoteThread to inject that….

  • Lol the bit about the matrix made me laugh. Another great article. Keep up the good work you’re doing great at distracting me from revising today.

  • Anonymous says:

    I came across your page from your profile on the StackOverflow beta. Good article, but you should update it to mention that if your class library is a different version of .NET (i.e., injecting a .NET 3.5 library into a .NET 2.0 host) you can cause problems for the target app.

  • Singularity says:

    lolz. kudos.

  • Anonymous says:

    I ran the code, works great. Kewl. Well I haven’t tried it with any poker clients, but it works.

  • Terry Smith says:

    What a fast update! You’re spoiling us!

  • ehsanul says:

    Don’t stop using the Matrix James! There are few things that don’t fit with the Matrix. And the designers of the Matrix (the super-artificially-intelligent robots) obviously directly write all their code in binary.

  • Anonymous says:

    These are the kinds of posts I mean. I vote for more of these posts, and less of the botting posts. Enough poker botting, already. Let’s just talk programming.

  • Anonymous says:

    Anonymous… I believe you are missing the entire point of the website…

  • Anonymous says:

    Im using easyhook to inject my dll in a poker client. It works great but the problem is that the client doesn’s use DrawText, DrawTextEx or ExtTextOut to write text to the chat box (everyones action is printed in the chat box). If I use the browser version of the same poker client it works with the ExtTextOut api though. Any ideas on which api to hook for the .exe version of the poker client?

    Could it be that the exe version uses images to print the text in the chat box?

    Great articles btw

  • Anonymous says:

    Before you go converting your all your code to .NET, go download a copy of Reflector and look at how easy it is to decompile any .NET assembly (dll)

    http://www.red-gate.com/products/reflector/

  • LastChance says:

    So why would we use this way rather than using the method of InstallingHook that would done in building a poker bot part 1?

  • Me encanta esta pagina!
    Saludos desde ARgentina!

  • Terry Smith says:

    Ah well. I guess it’s about time for Daniel Radcliffe to start posting again. He still hasn’t told us if he boned the chick who plays Hermione yet.

  • JeremyX says:

    lol @ terry’s comment but somehow i don’t think emma watson is the type of chick you "bone"…

  • Terry Smith says:

    What, are you saying she’s unbonable, like the chick in Code Monkeys?

    Sorry about these off topic posts folks, but I’m bored and, like Tucker max, when I get bored I make my own entertainment.

    If only there was, say, a POKER BOT posting to unbore me….

  • Anonymous says:

    Does this work on Mono? Anybody know?

  • Terry Smith says:

    I don’t know if you knew this, but the RSS feed hasn’t updated yet…

  • cd says:

    Great Article! I’m not very familiar with what goes on "behind the scenes" in the .NET runtime process. I was wondering, though, is the a way of loading .NET assembly from your bootstrap dll straight from a memory structure instead of a managed dll? In other words, instead of this line:

    pClrHost->ExecuteInDefaultAppDomain(L"c:\PathToYourManagedAssembly\MyManagedAssembly.dll", ….

    have something like:

    pClrHost->ExecuteInDefaultAppDomain(*pointer_to_mem_location_full_of_.NET_assembly, …

    If it is feasible, let me know. I have a few other things I’d like to run by you in regards to this. Please hit me up in email if you get the chance.

  • Flava says:

    I am feeling a little stupid, so ok, you have ‘injected’ your c# code into your target process, how do you then overload the print text methods? I thought this had to be done with c++ or am I missing the point (which wouldn’t suprise me!).

  • Anmar says:

    This is a great article, just the one i’ve been waiting for ! Thanks !

    But i faced a problem here,

    I’ve created the native dll and the .net assembly dll with a simple class.

    In the native dll:
    I’ve called CorBindToRuntimeEx, i successfully got a pointer to ICLRRuntimeHost.

    When i try to ->Start(), the process actually loads the run time, the workstation dll, and mscorlib.dll and then the CPU usage gets to 45% and the application keeps running normally, BUT upon any user input to the application (like clicking on a button) it completely hangs up, and the CPU usage goes to almost 50% and stays like that until i End Task the application.

    So i couldn’t get it to ->Start(). :(

    Anyone can help me here? and Thanks.

  • Anmar says:

    Just to give more information about my problem that could help.

    My code runs by a simple call from DllMain, at DLL_PROCESS_ATTACH

    Any help appreciated

  • Anmar says:

    Ok, i solved it. The problem was that i was doing this from the DllMain.
    I created a thread and let everything happen from the thread, and it worked successfully.

    Thanks for the nice article..

  • [b]Exactly[/b] what I was looking for. Very nice, thanks. Please don’t stop using the matrix in your posts. Wait. You didn’t. You only wanted us to think you did. Maybe I should get some sleep…

  • Anonymous says:

    Is there a way to handle garbled input in the lpString parameter of the ExtTextOutW API function? I’ve tried hooking it in C# and the hook works – but the contents lpString doesn’t belong to the unicode charset (passing it on to the original ExtTextOutW works fine though).

    Thanks

  • Anonymous says:

    Bytecode, P-Code, Assembly,… All the same? The point you make is true, on the lowest level they are,… but isn’t everything on the lowest level the same? aka nothing…

    :D

  • Opariti says:

    I had the chance to use this example in an IPC case, and I found it useful, straightforward.
    I have two questions:
    1 – how can I get back from C# a string of characters (BSTR, TCHAR) by calling ExecuteInDefaultAppDomain() instead of a DWORD, the dwRet in your example.
    2 – I understand that in the calling (native) process space is in fact loaded just the boostrapper, and that the C# objects are "elsewhere". This is important to know – idf true – since it solves major IPC issues in mobile environments (if the construction works for Compact Frameworks …)
    Thanks!

  • Manu says:

    Hello, where can I find MSCorEE.h file? i need it to compile c++ dll

    thanks

  • Tri says:

    I tried the code and was able to inject the dll, but my manage dll didn’t run?
    Any help?

    Thanks

  • Tri says:

    If I run the code from .exe (without injection) then it work. When I tried to run it by using injection, the managed code didn’t do nothing.

    Do you know why? Thanks

  • Mike says:

    Great article!

    And thank you to Anmar. Your solution worked for me too.

  • melol says:

    Anmar big thanks for your idea, i got it working with a second thread too :D i nearly gave up <3

  • PulsarBlow says:

    Your intro made me laught so much !
    Keep that sense of humor, its priceless =)

    /Cheers

  • A Nobody says:

    Great article and I got it working with .Net 3.5 but my question is what would change if I’d go ahead and use .Net 4.0 and what advantages would that give me ?

    Keep up the good work.

  • Coding the Wheel says:

    @A Nobody:

    This technique should still work in .NET 4.0. As for the advantages you get, .NET 4 has a bunch of new toys to play with:

    [MSDN: What's new in .NET 4.0][1]

    [1]: http://msdn.microsoft.com/en-us/library/ms171868.aspx

    Have fun…

  • jon says:

    thanks

    have you went up against .Net v4??

  • jon says:

    the idea works in .NET v4.0 but the access to AppDomains needs to be cracked,
    does not work yet in v4.0

  • Maxi says:

    you rock~!

  • coach bags says:

    Hello,I love reading through your blog, I wanted to leave a little comment to support you and wish you a good continuation. Wishing you the best of luck for all your blogging efforts.

  • Anonymous says:

    Here is a modified version to work with dot net 4

    HRESULT hr;
    //ICLRRuntimeHost *pClrHost = NULL;
    ICLRMetaHost *pMetaHost = NULL;
    hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&pMetaHost);//Dot net 4 New method for loading the CLR
    MessageBox(NULL, L”CLRCreateInstance Done.”, NULL, NULL);

    ICLRRuntimeInfo * lpRuntimeInfo = NULL;
    //the dot net version -> v4.0.30319 MUST match the folder name under C:WindowsMicrosoft.NETFramework.. otherwise IT WILL NOT WORK!!
    hr = pMetaHost->GetRuntime(L”v4.0.30319″, IID_ICLRRuntimeInfo, (LPVOID*)&lpRuntimeInfo);
    MessageBox(NULL, L”pMetaHost->GetRuntime Done.”, NULL, NULL);

    ICLRRuntimeHost * lpRuntimeHost = NULL;
    hr = lpRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID *)&lpRuntimeHost);
    MessageBox(NULL, L”lpRuntimeInfo->GetInterface Done.”, NULL, NULL);

    /*ICorRuntimeHost* lpCorRuntimeHost = NULL;
    hr = lpRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, IID_ICorRuntimeHost, (LPVOID *)&lpCorRuntimeHost);*/

    // Start the CLR for loading the C# DLL
    hr = lpRuntimeHost->Start();
    MessageBox(NULL, L”lpRuntimeHost->Start() Done.”, NULL, NULL);

    // Okay, the CLR is up and running in this (previously native) process.
    // Now call a method on our managed C# class library.
    DWORD dwRet = 0;//the return code, used for checking if everything goes ok or not.
    /*hr = lpRuntimeHost->ExecuteInDefaultAppDomain(
    L”TestCSharpDLL_01.dll”,
    L”TestCSharpDLL_01.TestCSharpDLL_01″, L”ShowMessage”, L”C# Loaded”, &dwRet);*/
    hr = lpRuntimeHost->ExecuteInDefaultAppDomain(
    L”test.dll”,
    L”namespace.classname”, L”functionname”, L”parameters”, &dwRet);
    // Optionally stop the CLR runtime (we could also leave it running)
    //hr = lpRuntimeHost->Stop();

    // clean up.
    lpRuntimeHost->Release();

    now I have a question..
    after injecting my dll in to the target process this code doesn’t work because it makes a dead lock in the dllmain function..and when I start a thread and pass this function to it..I can’t use delegate from C#..
    any idea ??

  • Anonymous says:

    no it does not work in dot net 4.0 :(

    Thanks for the great artical anyway.

    Could you stick your patch maybe on paste bin mate, that wall of text above is hard to read, thanks kindly.

  • Onur Gumus says:

    It does work for .net 4.0 , using like this :
    http://social.msdn.microsoft.com/Forums/en/clr/thread/b6ee146b-f5e3-4f55-aa6f-db5a21bc1d6e

    for guys who try to run it from dllMain don’t do it. instead try to realize the truth :P
    What truth ?
    You should put the code a function then call that function from your injector executable

  • jerseys discount says:

    Thanks for the great artical anyway.

    Could you stick your patch maybe on paste bin mate, that wall of text above is hard to read, thanks kindly.

    http://www.jerseysdiscount.net

  • Willum says:

    I don’t think so about the monsters that It takes care of you, [hot water system prices][1] even “if your home doesn’t quite feel like your home anymore” these can never be friendly with us don’t you think.

    [1]: http://www.hoth2o.com.au

  • Anonymous says:

    I am gay.

  • Arjun Tyagi says:

    Is it possible to inject a DLL file into a process such as explorer or svchost using C#?

    I know this is possible in C++ but is it in C#?

    If so would it matter how the DLL was written, e.g. would it differ betweeen a C++ DLL or a Visual Studio C# .NET DLL?

    If this is at all possible could someone post the code that I could use to do this. Thank you very much.

  • Thanks for posting Some Great ideas and I’ll try to return back With A Completely different browser to check out Things! Also,
    I put a link to your blog at my site, hope you do not mind?
    [Casino Articles][1] [Gambling Articles][2]

    [1]: http://www.casinoarticlesdirectory.com/
    [2]: http://gamblingarticlesdirectory.com/

  • ANUJ says:

    I am a new user of this site so here i saw multiple articles and posts posted by this site.
    I curious more interest in some of them hope you will give more information on this topics in your next articles.

    [Book of ra][1] || http://biggestcasinodirectory.com/

    [1]: http://casinosgamblingdirectory.com/

  • I am glad to found such useful post. I really increased my knowledge after read your post which will be beneficial for me.

  • Rank Page says:

    spielothek spiele ||
    bingo spielen kostenlos
    I simply stumbled upon your website and wanted to say that I have really enjoyed reading your blog post

  • Arnaud D says:

    In this sample provided by Microsoft, you get the 2 actual methods working with .NET 4.0 with complete comments included

    http://code.msdn.microsoft.com/CppHostCLR-e6581ee0

  • Arnaud D says:

    In this sample provided by Microsoft, you get the 2 actual methods working with .NET 4.0 with complete comments included

    http://code.msdn.microsoft.com/CppHostCLR-e6581ee0

  • Leave a Reply