Page 1 of 2 12 LastLast
Results 1 to 15 of 24
  1. #1
    Jason's Avatar
    Join Date
    Apr 2010
    Gender
    male
    Location
    /dev/null
    Posts
    5,704
    Reputation
    918
    Thanks
    7,676
    My Mood
    Mellow

    CLR Injector Source

    Hey guys, today I'm going to release my CLRInjector class to the public. I've had this a while, and been tweaking it quite a bit over time.

    A CLR injector is quite a bit more effort to write than a standard injector. For everyone who knows the basics of the CLR and has the basic concept of CLR injection worked out, you can skip to the [TL;DR] section marked below.

    What is a CLR Injector?
    In a sentence: An injector that is capable of loading .NET modules (.dlls) into remote processes.

    Before we continue, there are a few need things we need to cover.

    What is the difference between native dlls, and managed (.NET) dlls?
    Contrary to popular belief, not much. To paraphrase someone from codingthewheel:
    Quote Originally Posted by codingthewheel
    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.
    ..
    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.
    This accurately describes how many people (including myself) view the .NET framework. We either categorize applications into "managed" or "native" code, either we have this "useful monster" or we don't.
    However, this is not entirely true. All applications are native, some just (ab)use the .NET Runtime, otherwise known as the "CLR" (common language runtime).
    Quote Originally Posted by codingthewheel
    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".

    But applications are not "managed" or "native". They're always native. Sometimes they erect 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!
    Just like the way C++/native code is just a cover story for assembler..which is just mneumonics for machine code, "managed" code is just another step in the cycle.

    How is this useful information?
    Well, now we know the crux of the issue: The only difference between .NET dlls and 'native' dlls is the snaggle-toothed monster called the Common Language Runtime. That means, logically, to execute our managed modules inside a process, we just need to make sure the process has the CLR initialized.

    However, now we have another problem; How do we initialize the CLR with managed code, if you need to CLR to execute managed code? The short answer is we can't. We'll still need to resort to some native code to initialize the CLR, then we can start executing our managed code. See the end of this post for the native .dll that provides an external function to load managed dlls.

    [TL;DR]
    Alright, so far we've established that the only thing stopping us from injecting .NET dlls into processes is the lack of the CLR in normal processes. Unfortunately that's not the only problem we have.
    What's another difference between dlls you write in C++ for hacking, and dlls you write in VB.NET/C#? If you guessed "entry point", you were right, 1000 points to you.

    You'll probably be used to seeing something like this in C++ dlls:
    Code:
    BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved)
    {
        //....
    }
    But in C#/VB.NET there's not really an equivalent, is there? For this purpose I've devised my own explicit entry point for .NET dlls being injected by my CLRInjector class.

    According to microsoft (on the topic of executing managed methods)
    Quote Originally Posted by Microsoft
    The invoked method must have the following signature:
    Code:
    static int pwzMethodName (String pwzArgument)
    where pwzMethodName represents the name of the invoked method, and pwzArgument represents the string value passed as a parameter to that method.
    Basically means the entry point in dlls injected by this injector code must match either
    Code:
    C#
    static int pwzMethodName (String pwzArgument)
    
    VB.NET
    Shared Function pwzMethodName(ByVal pwzArgument As String) As Integer
    I've gone one step further and stated that the entry point must also be called "DllMain", as you would write it in a normal C++ dll. This means anyone writing a .NET dll to be injected MUST declare a DllMain method somewhere within one of its classes that matches the following method signature:
    Code:
    C#
    static int DllMain(String arg)
    
    VB.NET
    Shared Function DllMain(ByVal arg As String) As Integer
    The parameter "arg" is never set by the injector, so don't bother accessing it.

    The C# injector source
    The moment you've all been waiting for: The actual source code to this motherfucker. Sorry about the wall of text before, but this is a fairly complicated topic if you're only reading about it for the first time, so I tried to at least explain the basics to people here. The code is heavily commented, so you should be able to get a fair idea of what everything does. After this, I'll post a quick step-by-step overview of the process the injector goes through, and then some examples of actually using the injector in a process.

    First, here's a helper class named "WinAPI" which basically just exposes managed versions of some core Windows API functions. You'll need this in combination with my injector code:
    WinAPI.cs
    Code:
    using System.Runtime.InteropServices;
    using System;
    
    namespace WindowsApi
    {
        public static class WinAPI
        {
            [DllImport("kernel32.dll")]
            public static extern int OpenProcess(uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandke, int dwProcessId);
    
            [DllImport("kernel32.dll")]
            public static extern bool CloseHandle(int h);
    
            [DllImport("kernel32.dll")]
            public static extern uint VirtualAllocEx(int hProcess, uint lpAddress, int dwSize, int flAllocationType, int flProtect);
    
            [DllImport("kernel32.dll")]
            public static extern bool VirtualFreeEx(int hProcess, uint lpAddress, int dwSize, int dwFreeType);
    
            [DllImport("kernel32.dll")]
            public static extern bool WriteProcessMemory(int hProcess, uint lpAddress, byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead);
    
            [DllImport("kernel32.dll")]
            public static extern bool ReadProcessMemory(int hProcess, uint lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead);
    
            [DllImport("kernel32.dll")]
            public static extern int CreateRemoteThread(int hProcess, int lpThreadAttributes, int dwStackSize, uint lpStartAddress, uint lpParameter, int dwCreationFlags, int lpThreadId);
    
            [DllImport("kernel32.dll")]
            public static extern uint GetModuleHandleA(string lpModuleName);
    
            [DllImport("kernel32.dll")]
            public static extern uint GetProcAddress(uint hModule, string lpProcName);
    
            [DllImport("kernel32.dll")]
            public static extern uint WaitForSingleObject(int hObject, int dwTimeout);
    
            [DllImport("kernel32.dll")]
            public static extern bool GetExitCodeThread(int hThread, out uint lpExitCode);
    
            public static byte[] ReadRemoteMemory(int hProc, uint address, uint len)
            {
                byte[] buffer = new byte[len];
                int bytes = 0;
                if (!ReadProcessMemory(hProc, address, buffer, buffer.Length, out bytes) || bytes != len)
                    buffer = null;
                return buffer;
            }
    
            //very straightforward way to run a thread and capture the return.
            //will return -1 (uint.MaxValue == -1 as a signed integer) if it fails.
            public static uint RunThread(int hProcess, uint lpStartAddress, uint lpParam)
            {
                uint dwThreadRet = uint.MaxValue; //-1 as a signed integer.
                int hThread = CreateRemoteThread(hProcess, 0, 0, lpStartAddress, lpParam, 0, 0);
                if (hThread > 0)
                {
                    if (WaitForSingleObject(hThread, 1000) == 0x0L) //wait 1 second for a response
                        GetExitCodeThread(hThread, out dwThreadRet);
                }
                return dwThreadRet;
            }
    
            //Create a pointer to memory in the remote process
            public static uint CreateRemotePointer(int hProcess, byte[] pData, int flProtect)
            {
                uint pAlloc = 0;
                if (pData != null && hProcess > 0)
                {
                    pAlloc = VirtualAllocEx(hProcess, 0, pData.Length, 0x1000 | 0x2000, flProtect);
                    int nBytes = 0;
                    if (pAlloc == 0 || !WriteProcessMemory(hProcess, pAlloc, pData, pData.Length, out nBytes) || nBytes != pData.Length)
                    {
                        VirtualFreeEx(hProcess, pAlloc, 0, 0x8000);
                        pAlloc = 0;
                    }
                }
                return pAlloc;
            }
    
            /*
             * This is my GetProcAddressEx function. Basically, it does exactly what GetProcAddress does,
             * but for a module in a remote process. It looks up the module, and from there parses the lexicographically
             * ordered export function table (commonly known as the EAT). It calculates based on ordinal offset.
             * However, if you find this method doesn't work for you for whatever reason (i rushed the binary search algorithm)
             * i've provided another GetProcAddressEx method below with the same parameters which basically just calls the 'official'
             * GetProcAddress function from within the remote process and captures the return. Up to you -Jason
             */
            public static uint GetProcAddressEx(int hProc, uint hModule, string lpProcName)
            {
                uint procAddress = 0;
                byte[] pDosHd = ReadRemoteMemory(hProc, hModule, 0x40); //attempt to read the DOS header from memory
                if (pDosHd != null && BitConverter.ToUInt16(pDosHd, 0) == 0x5A4D) //compare the expected DOS "MZ" signature to whatever we just read
                {
                    uint e_lfanew = BitConverter.ToUInt32(pDosHd, 0x3C); //read the e_lfanew number
                    if (e_lfanew > 0) 
                    {
                        byte[] pNtHd = ReadRemoteMemory(hProc, hModule + e_lfanew, 0x108); //read the NT_HEADERS.
                        if (pNtHd != null && BitConverter.ToUInt32(pNtHd, 0) == 0x4550) //check the NT_HEADERS signature (PE\0\0)
                        {
                            uint expDirPtr = BitConverter.ToUInt32(pNtHd, 0x78); //get the pointer to the export directory (first data directory)
                            if (expDirPtr != 0) //does this module even export functions?
                            {
                                byte[] pExpDir = ReadRemoteMemory(hProc, hModule + expDirPtr, 0x28); //Read the export directory from the process
                                uint pEat = BitConverter.ToUInt32(pExpDir, 0x1C); //pointer to the export address table
                                uint pOrd = BitConverter.ToUInt32(pExpDir, 0x24); //pointer to the ordinal table.
                                uint ordbase = BitConverter.ToUInt32(pExpDir, 0x10); //the base ordinal number (default is 1)
    
                                int index = SearchExports(hProc, hModule, pExpDir, lpProcName); //search the exported names table for the specified function
                                if (pEat > 0 && pOrd > 0 && index > -1) //check the function was found
                                {
                                    byte[] bOrd = ReadRemoteMemory(hProc, (uint)(hModule + pOrd + (index << 1)), 0x2); //read the ordinal number for the function from the process
                                    int ord = (int)(bOrd == null ? -1 : BitConverter.ToUInt16(bOrd, 0)); //get the ordinal number for this function
    
                                    if (ord != -1) //just a final check to make sure we have a valid ordinal
                                    {
                                        //reference the Export Address Table to find the function address for our ordinal (don't forget to factor in the ordinal base)
                                        //Unlike zero-based indexing, the 'ordinal number' indexing starts at 1, so subtract 1 from the ordbase to get zero-based index
                                        byte[] addr = ReadRemoteMemory(hProc, (uint)(hModule + pEat + ((ord - (ordbase - 1)) << 2)), 0x4);
                                        if (addr != null)
                                            procAddress = hModule + BitConverter.ToUInt32(addr, 0); //wooooooo found it.
                                    }
                                }
                            }
                        }
                    }
                }
                return procAddress;
            }
    
            private static int SearchExports(int hProcess, uint hModule, byte[] exports, string name)
            {
                uint cntExports = BitConverter.ToUInt32(exports, 0x18); //number of named exported functions
                uint ptrNameTable = BitConverter.ToUInt32(exports, 0x20); //pointer to the export name table
                int rva = -1; 
    
                if (cntExports > 0 && ptrNameTable > 0)
                {
                    byte[] rawPtrs = ReadRemoteMemory(hProcess, hModule + ptrNameTable, cntExports << 2); //be lazy and read all the name pointers at once.
                    if (rawPtrs != null)
                    {
                        //quickly convert that series of bytes into pointer values that make sense. 
                        uint[] namePtrs = new uint[cntExports];
                        for (int i = 0; i < namePtrs.Length; i++)
                            namePtrs[i] = BitConverter.ToUInt32(rawPtrs, i << 2);
    
                        //binary search, huzzah! Part of the PE specification is that all exported functions are ordered lexicographically in a PE file.
                        int start = 0, end = namePtrs.Length - 1, middle = 0;
                        string curvalue = string.Empty;
                        //basically just search through all the exports looking for the specified function
                        while (start <= end && rva == -1)
                        {
                            middle = start + ((end - start) / 2);
                            curvalue = AnsiStringFromPTR(hProcess, hModule + namePtrs[middle]);
                            if (curvalue.Equals(name))
                                rva = middle;
                            else if (curvalue.CompareTo(name) < 0)
                                start = middle - 1;
                            else
                                end = middle + 1;
                        }
                    }
                }
                return rva;
            }
    
            /* pretty shitty way to read an unknown length, ANSI string from a remote process; basically just reads an arbitrary length 
             *(256 character) segment of memory and hopes to jeebus that the function name isn't > 256 characters.
             * this is pretty safe for most applications, but there's no real "function name length limit" defined in the PE specification
             * so I can't say that it will NEVER fuck up.
             */
            private static string AnsiStringFromPTR(int hProcess, uint rva)
            {
                byte[] asBytes = ReadRemoteMemory(hProcess, rva, 0x100);
                string value = string.Empty;
                if (asBytes != null)
                {
                    value = System.Text.Encoding.ASCII.GetString(asBytes);
                    if (value.IndexOf('\0') > 0)
                        value = value.Substring(0, value.IndexOf('\0'));
                }
                return value;
            }
    
    
            /* if you don't like my sexy GetProcAddressEx above, feel free to use this instead. Basically just follows 
             * the same logic as calling LoadLibrary in the remote process, but has to generate an asm stub for the additional parameters.
             */  
            /*
            public static uint GetProcAddressEx(int hProc, uint hModule, string lpProcName)
            {
                uint procAddress = 0;
                byte[] asmStub =    {
    						            0x68, 0x00, 0x00, 0x00, 0x00, //push
    						            0x68, 0x00, 0x00, 0x00, 0x00, //push
    						            0xE8, 0x00, 0x00, 0x00, 0x00, //call
    						            0xC3 //ret
    					            };
                int[] criticalPoints = { 1, 6, 11 };
    
                uint dwGetProcAddress = GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetProcAddress");
                uint lpParam = CreateRemotePointer(hProc, System.Text.Encoding.ASCII.GetBytes(lpProcName), 0x04);
                uint lpStub = VirtualAllocEx(hProc, 0, asmStub.Length, 0x1000 | 0x2000, 0x40);
                if (lpParam > 0 && lpStub > 0)
                {
                    byte[][] criticalData = {
                                              BitConverter.GetBytes(lpParam),
                                              BitConverter.GetBytes(hModule),
                                              BitConverter.GetBytes(dwGetProcAddress - (lpStub + 0x0F))
                                            };
                    for (int i = 0; i < criticalPoints.Length; i++)
                        for (int j = 0; j < criticalData[i].Length; j++)
                            asmStub[criticalPoints[i] + j] = criticalData[i][j];
                    
                    if (WriteProcessMemory(hProc, lpStub, asmStub, asmStub.Length, 0))
                        procAddress = RunThread(hProc, lpStub, 0);
                    VirtualFreeEx(hProc, lpParam, 0, 0x8000);
                    VirtualFreeEx(hProc, lpStub, 0, 0x8000);
                }
                return procAddress;
            }
            */
        }
    }
    Next up, the actual source code for the loader:

    CLRInjector.cs
    Code:
    using System.Collections.Generic;
    using System.Reflection;
    using System.Linq;
    using System.Text;
    using WindowsApi;
    using System;
    
    namespace CLRLoaderLibrary
    {
        /**
         * CLRInjector class written by Jason.
         * This static class is designed to inject .NET libraries (C#/VB.NET)
         * into remote processes which may not have the CLR (Common Language Runtime)
         * loaded. Not all .NET dlls are viable for injection, only those designed to 
         * be injected specifically by the coder can be injected with this loader.
         * 
         * The target .NET dll must have an entry point (function) defined somewhere 
         * that matches the following signature:
         * 
         * C#
         * static int DllMain(String arg)
         * 
         * VB.NET
         * Shared Function DllMain(ByVal arg As String) As Integer
        **/
        public static class CLRInjector
        {
            private static string                   __netversion     =  CLRInjector.GetHighestNetVersion(); //initialize the injector with the latest .NET version.
            private static string                   __lasterror      =  string.Empty; //instance variable to hold the last error thrown.
            private static Dictionary<byte,string>  __errorList      =  new Dictionary<byte, string>(); //holds the error codes for the injector
            private static readonly int[]           __patchAddresses =  { 0x01, 0x0B, 0x10, 0x15, 0x1A };
            private static byte[]                   __asmStub        =  {
                                                                            0x68, 0x00, 0x00, 0x00, 0x00, //push lpVersion
                                                                            0x68, 0x00, 0x00, 0x00, 0x00, //push lpParams
                                                                            0x68, 0x00, 0x00, 0x00, 0x00, //push lpEntryPoint
                                                                            0x68, 0x00, 0x00, 0x00, 0x00, //push lpClass
                                                                            0x68, 0x00, 0x00, 0x00, 0x00, //push lpDll
                                                                            0xE8, 0x00, 0x00, 0x00, 0x00, //call BootstrapDll
                                                                            0xC3 //ret
                                                                        };
    
            //initialize all the error messages. This method doesn't have to be called to inject, but is recommended if
            //you need detailed error reporting for failed injection.
            public static void Initialize()
            {
                CLRInjector.__errorList = new Dictionary<byte, string>();
                //these are the error codes generated by the injector.
                CLRInjector.__errorList.Add(0x01, "Error 0x01: Failed to create CLR instance");
                CLRInjector.__errorList.Add(0x02, "Error 0x02: Failed to get CLR runtime info");
                CLRInjector.__errorList.Add(0x04, "Error 0x04: CLR is not loadable in the current process");
                CLRInjector.__errorList.Add(0x08, "Error 0x08: Failed to create runtime host interface");
                CLRInjector.__errorList.Add(0x10, "Error 0x10: Failed to start the CLR");
                CLRInjector.__errorList.Add(0x20, "Error 0x20: Unable to execute managed dll");
                CLRInjector.__errorList.Add(0x21, "Error 0x21: Failed to allocate memory in the remote process");
                CLRInjector.__errorList.Add(0x22, "Error 0x22: Incorrect bootstrap parameters");
                CLRInjector.__errorList.Add(0x23, "Error 0x23: Failed to write bootstrap stub to remote process");
                CLRInjector.__errorList.Add(0x24, "Error 0x24: Failed to open a handle to the remote process");
                CLRInjector.__errorList.Add(0x25, "Error 0x25: Failed to create thread in the remote process");
                CLRInjector.__errorList.Add(0x26, "Error 0x26: Failed to inject CLR bootstrap");
                CLRInjector.__errorList.Add(0x27, "Error 0x27: Unable to find .NET Framework");
                CLRInjector.__errorList.Add(0x28, "Error 0x28: Unable to locate DllMain entry point");
            }
    
            public static void ClearErrors()
            {
                CLRInjector.__lasterror = string.Empty;
            }
    
            public static string GetLastError()
            {
                return CLRInjector.__lasterror;
            }
    
            private static void SetLastError(byte errCode)
            {
                if (CLRInjector.__errorLis*****ntainsKey(errCode))
                    CLRInjector.__lasterror = CLRInjector.__errorList[errCode];
            }
    
            //Obtains the highest .NET Framework version from the Framework folder. We need this value for the bootstrap loader.
            //by loading the highest framework available, the chances of successful injection of the CLR dll increases.
            private static string GetHighestNetVersion()
            {
                //enumerate all the v****** paths from the Framework directory and order them highest to lowest.
                string netVersion = string.Empty;
                string dotNetPath = System****.Path.Combine(Environment.GetEnvironmentVariable("WINDIR"), @"Microsoft.NET\Framework");
                if (System****.Directory.Exists(dotNetPath))
                {
                    var lstVersions = System****.Directory.GetDirectories(dotNetPath, "v*").Select(d => System****.Path.GetFileName(d)).OrderByDescending<string, string>(d => d);
                    netVersion = lstVersions.Count<string>() > 0 ? lstVersions.First<string>() : "";
                }
                return netVersion;
            }
    
            /// <summary>Inject a managed dll into a remote process</summary>
            /// <param name="hProcess">Existing, valid handle into the remote process</param>
            /// <param name="pBootstrap">Pointer to the native bootstrap function in the remote process</param>
            /// <param name="dll">Path to the CLR dll.</param>
            /// <returns>True if the injection was successful, false otherwise.</returns>
            public static bool Inject(int hProcess, uint pBootstrap, string dll)
            {
                bool successful = false;
    			string classPath = GetClassPath(dll);
    			if (!string.IsNullOrEmpty(CLRInjector.__netversion))
                {
    				if (!string.IsNullOrEmpty(classPath))
    				{
    					if (hProcess > 0)
    					{
    						uint pAlloc = MapBootstrap(hProcess, dll, classPath, pBootstrap); //map the bootstrap stub to the remote process.
    						if (pAlloc > 0) //if MapBootstrap fails, it returns 0 and also sets the last error.
    						{
    							int hResult = (int)WinAPI.RunThread(hProcess, pAlloc, 0);
    							if (hResult != -1)
    							{
    								if (!(successful = (hResult & 0xFF) == 0)) //if no errors were thrown, BootstrapDll will return 0.
    									CLRInjector.SetLastError((byte)(hResult & 0xFF)); //attempt to set the error corresponding to BootstrapDll's return.
    							}
    							else { CLRInjector.SetLastError(0x25); }
    
    							//clean up the allocated memory (note that the last patch value wasn't a reference value, so don't treat it as a pointer)
    							CleanBootstrap(hProcess, pAlloc);
    						}
    					}
    					else { CLRInjector.SetLastError(0x24); }
    				}
    				else { CLRInjector.SetLastError(0x28); }
    			}
    			else { CLRInjector.SetLastError(0x27); }
                return successful;
            }
    
            /// <summary>Inject a managed dll into a remote process</summary>
            /// <param name="pid">ID of the remote process</param>
            /// <param name="pBootstrapDll">Path to the native bootstrap dll.</param>
            /// <param name="dll">Path to the CLR dll.</param>
            /// <returns>True if the injection was successful, false otherwise.</returns>
            public static bool Inject(int pid, string pBootstrapDll, string dll)
            {
    			bool success = false;
                if (!string.IsNullOrEmpty(CLRInjector.__netversion))
                {
                    int tempHwnd = WinAPI.OpenProcess(0x43A, false, pid);
                    uint pBootstrap = 0;
                    uint hModule = 0;
    
                    //use standard loadlibrary injection to load the bootstrap dll into the remote process, then find the entry point for BootstrapDll
                    if (tempHwnd > 0)
                    {
                        uint pBootStr = WinAPI.CreateRemotePointer(tempHwnd, Encoding.Unicode.GetBytes(pBootstrapDll + "\0"), 0x04);
                        uint LLW = WinAPI.GetProcAddress(WinAPI.GetModuleHandleA("kernel32.dll"), "LoadLibraryW");
                        if (pBootStr > 0 && LLW > 0)
                        {
                            hModule = WinAPI.RunThread(tempHwnd, LLW, pBootStr);
                            success = hModule > 0 && (pBootstrap = WinAPI.GetProcAddressEx(tempHwnd, hModule, "_BootstrapDll@20")) > 0;
                            WinAPI.VirtualFreeEx(tempHwnd, pBootStr, 0, 0x8000);
                        }
                        else { CLRInjector.SetLastError(0x21); }
    
                        if (success)
                        {
                            success = CLRInjector.Inject(tempHwnd, pBootstrap, dll);
                            //don't forget to unload the Bootstrap module from the remote process, no point leaving more detectable ends lying around.
                            uint FL = WinAPI.GetProcAddress(WinAPI.GetModuleHandleA("kernel32.dll"), "FreeLibrary");
                            WinAPI.RunThread(tempHwnd, FL, hModule);
                        }
                        else { CLRInjector.SetLastError(0x26); }
                        WinAPI.CloseHandle(tempHwnd);
                    }
                    else { CLRInjector.SetLastError(0x24); }
                }
                else { CLRInjector.SetLastError(0x27); }
    			
                return success;
            }
    
            public static string GetClassPath(string lpClrModulePath)
            {
                string classPath = string.Empty;
                try
                {
                    //okay this is where things get nasty. Basically, we can't load any assemblies into the main
                    //AppDomain, or we can't unload them again: not very polite when people are trying to use the injector and realize their file is locked
                    //until they close the injector. This means we have to remote everything to a seperate AppDomain, which means using the CLRInfoProxy class I wrote 
                    //below. We create an instance of this class in the temporary AppDomain, then call its internal methods across the domains so that it loads all
                    //assemblies into its own domain, not ours. We can then unload the temporary AppDomain and everyone goes home a happy camper.
                    AppDomain tempDomain = AppDomain.CreateDomain("Temporary Domain"); //create a temporary domain, give it a really original name.
                    CLRInfoProxy proxy = (CLRInfoProxy)tempDomain.CreateInstanceAndUnwrap(typeof(CLRInfoProxy).Assembly.FullName, typeof(CLRInfoProxy).FullName); //create an instance of our proxy in the temp domain.
                    if (proxy != null) //Null references are poopnoodles.
                        classPath = proxy.GetClassPath(lpClrModulePath); //Get the class path (if any)
                    AppDomain.Unload(tempDomain); //unload the AppDomain (WOOOOO)
                }
                catch { }
                return classPath;
            }
    
            //clean up the bootstrap stub from a process
            private static void CleanBootstrap(int hProcess, uint pAlloc)
            {
                //free all the allocated memory and cleanup
                for (int i = 0; i < CLRInjector.__patchAddresses.Length - 1; i++)
                {
                    int a = CLRInjector.__patchAddresses[i];
                    uint pStr = BitConverter.ToUInt32(WinAPI.ReadRemoteMemory(hProcess, (uint)(pAlloc + a), sizeof(uint)), 0);
                    WinAPI.VirtualFreeEx(hProcess, pStr, 0, 0x8000); //free the string from the remote process to avoid memory leaks.
                }
                WinAPI.VirtualFreeEx(hProcess, pAlloc, 0, 0x8000);
            }
       
            //Map the bootstrap assembly stub into the remote process ready to be called.
            private static uint MapBootstrap(int hProcess, string dll, string classtype, uint pBootstrap)
            {
                uint ulMappedAddr = 0;
                //create a series of wide-strings in the remote process as the parameters for BootstrapDll.
                uint pDll = WinAPI.CreateRemotePointer(hProcess, Encoding.Unicode.GetBytes(dll), 0x04); //the path to the CLR module.
                uint pClass = WinAPI.CreateRemotePointer(hProcess, Encoding.Unicode.GetBytes(classtype), 0x04); //map the "<Namespace>.<classname>" of the CLR module containing the DllMain method.
                uint pVersion = WinAPI.CreateRemotePointer(hProcess, Encoding.Unicode.GetBytes(CLRInjector.__netversion), 0x04); //highest .NET version found on the system.
                uint pEntryPoint = WinAPI.CreateRemotePointer(hProcess, Encoding.Unicode.GetBytes("DllMain"), 0x04); //entry point in the dll must be called "DllMain"
    
                ulMappedAddr = WinAPI.VirtualAllocEx(hProcess, 0, CLRInjector.__asmStub.Length, 0x1000 | 0x2000, 0x40); //attempt to allocate space for the assembly stub to fit in the remote process.
                if (ulMappedAddr > 0)
                {
                    //the data to patch into the assembly stub.
                    byte[][] patchData = {
                                            BitConverter.GetBytes(pVersion),
                                            BitConverter.GetBytes(pEntryPoint),
                                            BitConverter.GetBytes(pClass),
                                            BitConverter.GetBytes(pDll),
                                            BitConverter.GetBytes(pBootstrap - (ulMappedAddr + 30)) //E8 call is a relative address so calculate the relative distance
                                         }; //patch values
    
                    if (!patchData.Any<byte[]>(b => BitConverter.ToUInt32(b, 0) == 0)) //make sure all the patch values hold valid values (i.e are not null)
                    {
                        for (int i = 0; i < __patchAddresses.Length; i++)
                            for (int j = 0; j < patchData[i].Length; j++)
                                CLRInjector.__asmStub[__patchAddresses[i] + j] = patchData[i][j]; //patch the assembly stub with the patch values in the appropriate places.
    
                        int nBytes = 0;
                        if (!WinAPI.WriteProcessMemory(hProcess, ulMappedAddr, CLRInjector.__asmStub, CLRInjector.__asmStub.Length, out nBytes) || nBytes != CLRInjector.__asmStub.Length) //try to write the assembly stub into the remote process.
                        {
                            //free all the allocated memory and cleanup
                            CleanBootstrap(hProcess, ulMappedAddr);
                            ulMappedAddr = 0;
                        }
                        else { CLRInjector.SetLastError(0x23); }
                    }
                    else { CLRInjector.SetLastError(0x22); }
                }
                else { CLRInjector.SetLastError(0x21);  }
    
                return ulMappedAddr;
            }
        }
    
        /* This is a proxy class designed to run in a seperate AppDomain 
         * due to some shitness of the CLR, you cannot unload individual assemblies
         * from AppDomains, only the whole AppDomain at once. This means that if
         * we were to write the GetClassPath method in the main AppDomain, the CLR
         * module would be locked for writing until the injector shutdown, regardless of
         * whether the target process is still running
        **/
        internal class CLRInfoProxy : MarshalByRefObject
        {
            //Try to locate the classpath of a class that defines "DllMain"
            public string GetClassPath(string lpClrDll)
            {
                string entry = string.Empty;
                try
                {
                    //load the CLR assembly into the AppDomain
                    Assembly asm = Assembly.LoadFrom(lpClrDll);
                    if (asm != null)
                    {
                        Type[] asmTypes = asm.GetTypes(); //Reflect the types (classes..etc)
                        for (int i = 0; i < asmTypes.Length && string.IsNullOrEmpty(entry); i++)
                        {
                            //get all the "static int DllMain" methods...
                            var methods = asmTypes[i].GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static).Where<MethodInfo>(m => m.Name == "DllMain" && m.ReturnType == typeof(int));
                            //last stop: validation of the params
                            foreach (var mi in methods)
                            {
                                ParameterInfo[] paramInfo = mi.GetParameters();
                                if (paramInfo.Length == 1 && paramInfo[0].ParameterType.Equals(typeof(string)))
                                    entry = asmTypes[i].FullName;
                            }
                        }
                    }
                }
                catch { }
                return entry;
            }
        }
    }
    IMPORTANT:
    Any project that uses this code MUST be compiled as x86 (Build>>Configuration Manager>>Active solution platform>>New>>x86) or this will fail to work.

    Alright, now for a quick overview of what the injector's steps are.
    1. Check for the max .NET framework version on the users computer
    2. Attempt to find the location of DllMain in the target .dll
    3. Load the native CLRBootstrap.dll module into the process using standard injection techniques.
    4. Map out the parameters for loading into the remote process
    5. Call BootstrapDll function in CLRBootstrap with the specified parameters to load the CLR and start executing the desired .NET dll from its "DllMain" entry point
    6. Unload the CLRBootstrap native dll from the process.

    That's all there is to it really. There's a few variations in injections depending on the parameters, but for the most part it's the same.

    How do I use this atrocious beast?
    It's quite simple really, I've done all the work for you. (this is for making your own injector)

    Step 1. Create a new Windows Forms project using Visual Studio 2008/2010.
    Step 2. Change the build options to compile to x86 (google it if you can't figure out how, or see the MPGH SDK post in my signature, I explain it there)
    Step 3.a: Download the procompiled "CLRLoaderLibrary.dll" I've attached to this post and then reference it to your project (Project >> Add Reference >> "Browse" tab >> find dll)
    or
    Step 3.b [only use this step if you're writing your project in C#, it will not work if you use VB.NET] Go to the Project menu in Visual Studio and choose "Add Class", name it "WinAPI", delete everything that Visual Studio generates for you and paste in the WinAPI.cs code I posted above. Add another class in the same way but call it "CLRInjector" and copy and paste in the CLRInjector.cs code from above.
    Step 4. Go to your main class (Form1) and add the following import:
    Code:
    C#
    using CLRLoaderLibrary;
    
    VB.NET
    Imports CLRLoaderLibrary
    That's my code set up done, how you write your injector is up to you. The next section is just some basic usage of the Injector.

    Go to the designer for your form and double click the form. This should take you back into the code view inside the Form1_Load event. Put the following code in:
    Code:
    C#
    CLRInjector.Initialize();
    
    VB.NET
    CLRInjector.Initialize()
    You'll now be able to get detailed error messages from the injector if it ever fails to inject.

    Let's add a new button to the form, double click it and you'll end up back in the code-view.
    Here's a VERY VERY VERY simple usage of the injector, you can obviously add textboxes and shit so you don't have to hardcode values, but this is just for your benefit:
    Code:
    C#
    string dll = @"B:\My Documents\Visual Studio 2008\Projects\CombatArmsMenu\CombatArmsMenu\bin\x86\Release\CombatArmsMenu.dll";
    string bootstrapDll = "CLRBootstrap.dll"; //I put the dll in the same folder as the injector.
    var procs = System.Diagnositics.Process.GetProcessesByName("Engine");
    if (procs.Length > 0)
        if (!CLRInjector.Inject(procs[0].Id, bootstrapDll, dll))
    	    MessageBox.Show(CLRInjector.GetLastError());
    		
    VB.NET
    Dim dll As String = @"B:\My Documents\Visual Studio 2008\Projects\CombatArmsMenu\CombatArmsMenu\bin\x86\Release\CombatArmsMenu.dll"
    Dim bootstrapDll As String = "CLRBootstrap.dll"
    Dim procs() As Process = Process.GetProcessesByName("Engine")
    If procs.Length > 0 Then
        If Not CLRInjector.Inject(procs(0).Id, bootstrapDll, dll) Then
    	    MessageBox.Show(CLRInjector.GetLastError());
    	End If
    End If
    Huzzah, that's done. If anyone has any questions, comments or improvements, please leave a message.

    Virus Scans
    CLRLoaderLibrary (Managed Dll):
    [x][x]
    [x][x]

    CLRBootstrap (Native dll)
    [x][x]
    [x][x]

    Credits
    Myself - Wrote pretty much everything
    Microsoft - For creating such a sexy Framework, and for the tips on loading the CLR with the new methods.
    Codingthewheel - For abolishing my fear of the snaggletoothed monster.

    Catcha cunts,
    Jason.
    <b>Downloadable Files</b> Downloadable Files
    Last edited by Jason; 05-01-2012 at 01:10 PM.

    Quote Originally Posted by Jeremy S. Anderson
    There are only two things to come out of Berkley, Unix and LSD,
    and I don’t think this is a coincidence
    You can win the rat race,
    But you're still nothing but a fucking RAT.


    ++Latest Projects++
    [Open Source] Injection Library
    Simple PE Cipher
    FilthyHooker - Simple Hooking Class
    CLR Injector - Inject .NET dlls with ease
    Simple Injection - An in-depth look
    MPGH's .NET SDK
    eJect - Simple Injector
    Basic PE Explorer (BETA)

  2. The Following 90 Users Say Thank You to Jason For This Useful Post:

    ♪~ ᕕ(ᐛ)ᕗ (08-04-2012),159753cado25 (11-23-2016),6ixth (02-27-2014),abwen (12-27-2012),Adler_lug (12-04-2017),Afro_ (06-28-2015),akbargain (09-29-2018),amwoods (11-05-2012),andsonac (11-01-2013),ayxis (02-27-2017),bastouf (05-17-2013),binhcen (02-11-2024),BlackSource (09-16-2012),Bosss228 (10-15-2012),bprg (07-12-2012),Ch40zz-C0d3r (05-06-2012),chunglinh1995 (05-15-2016),cmc5414 (08-24-2012),codegnome (07-24-2012),daantjepaal (05-04-2012),[MPGH]Dave84311 (05-01-2012),DecepTiveX (02-16-2013),dedea (05-04-2012),demtrios (05-26-2012),Devolten (12-28-2012),Drake (05-01-2012),DreadNought_ (11-13-2012),DyNamoTR (03-02-2013),ed234 (10-31-2012),FASZ1 (03-10-2013),feilang864 (11-30-2012),flameswor10 (05-01-2012),[MPGH]Flengo (05-28-2012),fraak (08-11-2012),fredemm (02-28-2013),fwsefwsgrgwhergr (11-05-2016),Geometrical (12-27-2013),gibam761 (05-01-2012),guitarman1209 (12-23-2012),Hassan (05-01-2012),[MPGH]Hero (10-10-2012),houxin (05-20-2017),j3xh0 (05-03-2012),jamesbond14042 (05-28-2017),jangminrui (02-20-2019),jknow (12-31-2012),jmentbk (05-03-2012),kaykayxx9 (05-07-2019),kensi123 (10-10-2016),kokkinogenis (05-31-2017),kronl2 (12-04-2012),Lifack (10-19-2012),liorgindin (05-20-2013),low1989 (05-23-2015),luzj (02-20-2013),MasDai (10-26-2012),maxparos (01-30-2013),mindaugas242424 (01-27-2013),NavanBethrax (10-10-2016),nbss (05-02-2012),nellyblack (03-03-2013),Neox. (07-07-2014),nikazizi97 (12-03-2016),nt47 (05-14-2019),Otaviomorais (10-08-2012),pDevice (07-19-2012),pepechu (11-15-2016),Pille22 (07-08-2013),r41lblast (05-31-2013),Rav3nphx (03-09-2018),Reflex- (05-03-2012),rlane187 (05-17-2017),ruzar (06-27-2013),senarisk (07-01-2015),Shadow` (08-18-2012),stefsot (05-06-2012),TCL-100K (05-14-2017),tdr2010 (10-09-2012),The Decoder (12-01-2012),topblast (05-01-2012),toxicsoul (05-06-2012),Traxful (10-08-2012),tylahb (10-21-2012),Unknowned (11-01-2013),Ureus (08-27-2012),V I (02-14-2013),Vahan96 (06-21-2012),versx (08-30-2015),Womanizer` (05-03-2012),Zkma (08-15-2012)

  3. #2
    Dave84311's Avatar
    Join Date
    Dec 2005
    Gender
    male
    Location
    The Wild Wild West
    Posts
    35,836
    Reputation
    5782
    Thanks
    41,290
    My Mood
    Devilish
    Quote Originally Posted by Jason View Post
    An injector that is capable of loading .NET modules (.dlls) into remote processes.
    Looks good sunshine.





    THE EYE OF AN ADMINISTRATOR IS UPON YOU. ANY WRONG YOU DO IM GONNA SEE, WHEN YOU'RE ON MPGH, LOOK BEHIND YOU, 'CAUSE THATS WHERE IM GONNA BE


    "First they ignore you. Then they laugh at you. Then they fight you. Then you lose.” - Dave84311

    HAVING VIRTUAL DETOX

  4. #3
    Hassan's Avatar
    Join Date
    May 2010
    Gender
    male
    Location
    System.Threading.Tasks
    Posts
    4,764
    Reputation
    495
    Thanks
    2,132
    My Mood
    Dead
    Pretty cool Jason. Good work !

  5. #4
    Drake's Avatar
    Join Date
    Mar 2010
    Gender
    male
    Location
    Belgium,Oost-Vlaanderen
    Posts
    12,680
    Reputation
    1801
    Thanks
    4,929
    / Approved

  6. #5
    Ze Polvinho 1's Avatar
    Join Date
    Mar 2012
    Gender
    male
    Posts
    21
    Reputation
    10
    Thanks
    0
    Looks clean or just answering

  7. #6
    Reflex-'s Avatar
    Join Date
    Mar 2011
    Gender
    male
    Location
    192.168.1.01
    Posts
    6,625
    Reputation
    584
    Thanks
    2,267
    My Mood
    Dead
    Quote Originally Posted by Ze Polvinho 1 View Post
    Looks clean or just answering
    That's for you to decide.

    OnTopic: Nice job.

  8. #7
    pDevice's Avatar
    Join Date
    Feb 2012
    Gender
    male
    Location
    d3d9.h
    Posts
    1,306
    Reputation
    15
    Thanks
    420
    My Mood
    Stressed
    interesting! I take a look ...



  9. #8
    Jason's Avatar
    Join Date
    Apr 2010
    Gender
    male
    Location
    /dev/null
    Posts
    5,704
    Reputation
    918
    Thanks
    7,676
    My Mood
    Mellow
    Anyone given this a go yet?

    Quote Originally Posted by Jeremy S. Anderson
    There are only two things to come out of Berkley, Unix and LSD,
    and I don’t think this is a coincidence
    You can win the rat race,
    But you're still nothing but a fucking RAT.


    ++Latest Projects++
    [Open Source] Injection Library
    Simple PE Cipher
    FilthyHooker - Simple Hooking Class
    CLR Injector - Inject .NET dlls with ease
    Simple Injection - An in-depth look
    MPGH's .NET SDK
    eJect - Simple Injector
    Basic PE Explorer (BETA)

  10. #9
    Broderick's Avatar
    Join Date
    Apr 2011
    Gender
    male
    Location
    Basement.
    Posts
    100
    Reputation
    42
    Thanks
    30
    Hmm, this presents some interesting possibilities. So basically people can now write hacks in C#/VB.NET and inject them pretty much the same way you would do with a regular injector? Neat.
    The fish trap exists because of the fish.
    Once you've gotten the fish you can forget the trap.
    The rabbit snare exists because of the rabbit.
    Once you've gotten the rabbit, you can forget the snare.
    Words exist because of meaning.
    Once you've gotten the meaning, you can forget the words.
    Where can I find a man who has forgotten words so I can talk with him?

  11. #10
    Womanizer`'s Avatar
    Join Date
    Apr 2008
    Gender
    male
    Posts
    1,438
    Reputation
    103
    Thanks
    1,593
    My Mood
    Angelic
    I hope this wont get leeched. You have used a lot of time to do this. Lots of respect goes to you!

    Thanks!

  12. #11
    Jason's Avatar
    Join Date
    Apr 2010
    Gender
    male
    Location
    /dev/null
    Posts
    5,704
    Reputation
    918
    Thanks
    7,676
    My Mood
    Mellow
    Quote Originally Posted by Broderick View Post
    Hmm, this presents some interesting possibilities. So basically people can now write hacks in C#/VB.NET and inject them pretty much the same way you would do with a regular injector? Neat.
    Indeed it does. I'll be releasing an injector sometime soon which will be able to inject people's .NET dlls. People can now hack with .NET. I've already written some D3D hooks and basic memory patching hacks in C# just to test it out and it works great in CA.

    Quote Originally Posted by Jeremy S. Anderson
    There are only two things to come out of Berkley, Unix and LSD,
    and I don’t think this is a coincidence
    You can win the rat race,
    But you're still nothing but a fucking RAT.


    ++Latest Projects++
    [Open Source] Injection Library
    Simple PE Cipher
    FilthyHooker - Simple Hooking Class
    CLR Injector - Inject .NET dlls with ease
    Simple Injection - An in-depth look
    MPGH's .NET SDK
    eJect - Simple Injector
    Basic PE Explorer (BETA)

  13. #12
    Ch40zz-C0d3r's Avatar
    Join Date
    Apr 2011
    Gender
    male
    Posts
    831
    Reputation
    44
    Thanks
    401
    My Mood
    Twisted
    Thank you looks awesome!
    But I get an error, I compiled with vb.net 2010 and set the output platform to x86.
    Error:
    Code:
    Error 0x26: Failed to inject CLR bootstrap
    We can just inject to 32-bit process on 64bit when we compiled it x86, thats the reason!
    Last edited by Ch40zz-C0d3r; 05-06-2012 at 10:57 AM.

    Progress with my game - "Disbanded"
    • Fixed FPS lag on spawning entities due to the ent_preload buffer!
    • Edit the AI code to get some better pathfinding
    • Fixed the view bug within the sniper scope view. The mirror entity is invisible now!
    • Added a new silencer for ALL weapons. Also fixed the rotation bugs
    • Added a ton of new weapons and the choice to choose a silencer for every weapon
    • Created a simple AntiCheat, noobs will cry like hell xD
    • The name will be Disbanded, the alpha starts on the 18th august 2014



    Some new physics fun (Serversided, works on every client)



    My new AI
    https://www.youtube.com/watch?v=EMSB1GbBVl8

    And for sure my 8 months old gameplay with 2 friends
    https://www.youtube.com/watch?v=Na2kUdu4d_k

  14. #13
    Jason's Avatar
    Join Date
    Apr 2010
    Gender
    male
    Location
    /dev/null
    Posts
    5,704
    Reputation
    918
    Thanks
    7,676
    My Mood
    Mellow
    Quote Originally Posted by Ch40zz-C0d3r View Post
    Thank you looks awesome!
    But I get an error, I compiled with vb.net 2010 and set the output platform to x86.
    Error:
    Code:
    Error 0x26: Failed to inject CLR bootstrap
    We can just inject to 32-bit process on 64bit when we compiled it x86, thats the reason!
    Yes, at the moment this injector is built only to support the x86 architecture. Future versions may add x64 support, but seeing as most programs that you'll likely be injecting are built for x86, this is probably the best option for now.

    Quote Originally Posted by Jeremy S. Anderson
    There are only two things to come out of Berkley, Unix and LSD,
    and I don’t think this is a coincidence
    You can win the rat race,
    But you're still nothing but a fucking RAT.


    ++Latest Projects++
    [Open Source] Injection Library
    Simple PE Cipher
    FilthyHooker - Simple Hooking Class
    CLR Injector - Inject .NET dlls with ease
    Simple Injection - An in-depth look
    MPGH's .NET SDK
    eJect - Simple Injector
    Basic PE Explorer (BETA)

  15. #14
    Ch40zz-C0d3r's Avatar
    Join Date
    Apr 2011
    Gender
    male
    Posts
    831
    Reputation
    44
    Thanks
    401
    My Mood
    Twisted
    Ok, I played much with this arround now.
    How can I convert this to x64? I really need this, please help. Tried to release it on AnyCPU and x64. but then it crashes when I want to compile my application using this

    Progress with my game - "Disbanded"
    • Fixed FPS lag on spawning entities due to the ent_preload buffer!
    • Edit the AI code to get some better pathfinding
    • Fixed the view bug within the sniper scope view. The mirror entity is invisible now!
    • Added a new silencer for ALL weapons. Also fixed the rotation bugs
    • Added a ton of new weapons and the choice to choose a silencer for every weapon
    • Created a simple AntiCheat, noobs will cry like hell xD
    • The name will be Disbanded, the alpha starts on the 18th august 2014



    Some new physics fun (Serversided, works on every client)



    My new AI
    https://www.youtube.com/watch?v=EMSB1GbBVl8

    And for sure my 8 months old gameplay with 2 friends
    https://www.youtube.com/watch?v=Na2kUdu4d_k

  16. #15
    Jason's Avatar
    Join Date
    Apr 2010
    Gender
    male
    Location
    /dev/null
    Posts
    5,704
    Reputation
    918
    Thanks
    7,676
    My Mood
    Mellow
    Quote Originally Posted by Ch40zz-C0d3r View Post
    Ok, I played much with this arround now.
    How can I convert this to x64? I really need this, please help. Tried to release it on AnyCPU and x64. but then it crashes when I want to compile my application using this
    What are you injecting that's x64?

    Quote Originally Posted by Jeremy S. Anderson
    There are only two things to come out of Berkley, Unix and LSD,
    and I don’t think this is a coincidence
    You can win the rat race,
    But you're still nothing but a fucking RAT.


    ++Latest Projects++
    [Open Source] Injection Library
    Simple PE Cipher
    FilthyHooker - Simple Hooking Class
    CLR Injector - Inject .NET dlls with ease
    Simple Injection - An in-depth look
    MPGH's .NET SDK
    eJect - Simple Injector
    Basic PE Explorer (BETA)

Page 1 of 2 12 LastLast

Similar Threads

  1. INJECToR SOURCE
    By martijno0o0 in forum Visual Basic Programming
    Replies: 20
    Last Post: 01-12-2010, 10:06 AM
  2. ~ DLL Injector Source Code ~
    By Silk[H4x] in forum Visual Basic Programming
    Replies: 32
    Last Post: 12-16-2009, 11:18 PM
  3. Need new injector source (warrock)
    By weide43 in forum Visual Basic Programming
    Replies: 2
    Last Post: 11-27-2009, 04:20 PM
  4. Combat Arms Injector Source Code
    By Melikepie in forum Combat Arms Discussions
    Replies: 6
    Last Post: 10-21-2009, 03:24 PM
  5. Injector source help
    By qsc in forum C++/C Programming
    Replies: 7
    Last Post: 06-17-2009, 04:33 PM