Contents

Introduction

.Net is a powerful language for developing software quickly and reliably. However, there are certain tasks for which .net is unfit. This paper highlights one particular case, DLL injection. A .net DLL (aka managed DLL) cannot be injected inside a remote process in which the .net runtime has not been loaded. Furthermore, even if the .net runtime is loaded in a process one would like to inject, how can methods within the .net DLL be invoked? What about architecture? Does a 64 bit process require different attention than a 32 bit process? The goal of this paper is to show how to perform all of these tasks using documented APIs. Together we will:

  • Start the .net clr (common language runtime) in an arbitrary process regardless of bitness.
  • Load a custom .net assembly in an arbitrary process.
  • Execute managed code in the context of an arbitrary process.

Back To Fundamentals

Several things need to happen in order for us to achieve our goal. To make the problem more manageable, it will be broken down into several pieces and then reassembled at the end. The steps to solving this puzzle are:

  1. Load The CLR (Fundamentals) - Covers how to start the .net framework inside of an unmanaged process.
  2. Load The CLR (Advanced) - Covers how to load a custom .net assembly and invoke managed methods from unmanaged code.
  3. DLL Injection (Fundamentals) - Covers how to execute unmanaged code in a remote process.
  4. DLL Injection (Advanced) - Covers how to execute arbitrary exported functions in a remote process.
  5. Putting It All Together - A solution presents itself; putting it all together.

Note: The author uses the standard convention of function to refer to c++ functions, and method to refer to c# functions. The term "remote" process refers to any process other than the current process.

Load The CLR

Writing an unmanaged application that can load both the .net runtime and an arbitrary assembly into itself is the first step towards achieving our goal.

Fundamentals

The following sample program illustrates how a c++ application can load the .net runtime into itself:

 Collapse | Copy Code
#include <metahost.h>

#pragma comment(lib, "mscoree.lib")

#import "mscorlib.tlb" raw_interfaces_only \
high_property_prefixes("_get","_put","_putref") \
rename("ReportEvent", "InteropServices_ReportEvent") int wmain(int argc, wchar_t* argv[])
{
char c;
wprintf(L"Press enter to load the .net runtime...");
while (getchar() != '\n'); HRESULT hr;
ICLRMetaHost *pMetaHost = NULL;
ICLRRuntimeInfo *pRuntimeInfo = NULL;
ICLRRuntimeHost *pClrRuntimeHost = NULL; // build runtime
hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost));
hr = pMetaHost->GetRuntime(L"v4.0.30319", IID_PPV_ARGS(&pRuntimeInfo));
hr = pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost,
IID_PPV_ARGS(&pClrRuntimeHost)); // start runtime
hr = pClrRuntimeHost->Start(); wprintf(L".Net runtime is loaded. Press any key to exit...");
while (getchar() != '\n'); return 0;
}

In the above code, the calls of interest are:

CLRCreateInstance - given CLSID_CLRMetaHost, gets a pointer to an instance of ICLRMetaHost
ICLRMetaHost::GetRuntime - get a pointer of type ICLRRuntimeInfo pointing to a specific .net runtime
ICLRRuntimeInfo::GetInterface - load the CLR into the current process and get a ICLRRuntimeHost pointer
ICLRRuntimeHost::Start - explicitly start the CLR, implicitly called when managed code is first loaded

At the time of this writing, the valid version values for ICLRMetaHost::GetRuntime are NULL"v1.0.3705""v1.1.4322","v2.0.50727", and "v4.0.30319" where NULL loads the latest version of the runtime. The desired runtime should be installed on the system, and the version values above should be present in either%WinDir%\Microsoft.NET\Framework or %WinDir%\Microsoft.NET\Framework64.

Compiling and running the code above produces the following output as seen from the console and Process Hacker:

Once pressing enter it can be observed, via Process Hacker, that the .net runtime has been loaded. Notice the additional tabs referencing .net on the properties pane:

The above sample code is not included in the source download. However, it is recommended as an exercise for the reader to build and run the sample.

Advanced

With the first piece of the puzzle in place, the next step is to load an arbitrary .net assembly into a process and invoke methods inside that .net assembly.

To continue building off the above example, the CLR has just been loaded into the process. This was achieved by obtaining a pointer to the CLR interface; this pointer is stored in the variable pClrRuntimeHost. UsingpClrRuntimeHost a call was made to ICLRRuntimeHost::Start to initialize the CLR into the process.

Now that the CLR has been initialized, pClrRuntimeHost can make a call toICLRRuntimeHost::ExecuteInDefaultAppDomain to load and invoke methods in an arbitrary .net assembly. The function has the following signature:

 Collapse | Copy Code
HRESULT ExecuteInDefaultAppDomain (
[in] LPCWSTR pwzAssemblyPath,
[in] LPCWSTR pwzTypeName,
[in] LPCWSTR pwzMethodName,
[in] LPCWSTR pwzArgument,
[out] DWORD *pReturnValue
);

A brief description of each parameter:

pwzAssemblyPath - the full path to the .net assembly; this can be either an exe or a dll file
pwzTypeName - the fully qualified typename of the method to invoke
pwzMethodName - the name of the method to invoke
pwzArgument - an optional argument to pass into the method
pReturnValue - the return value of the method

Not every method inside a .net assembly can be invoked via ICLRRuntimeHost::ExecuteInDefaultAppDomain. Valid .net methods must have the following signature:

 Collapse | Copy Code
static int pwzMethodName (String pwzArgument);

As a side note, access modifiers such as public, protected, private, and internal do not affect the visibility of the method; therefore, they have been excluded from the signature.

The following .net application will be used in all the examples that follow as the managed .net assembly to be injected inside processes:

 Collapse | Copy Code
using System;
using System.Windows.Forms; namespace InjectExample
{
public class Program
{
static int EntryPoint(String pwzArgument)
{
System.Media.SystemSounds.Beep.Play(); MessageBox.Show(
"I am a managed app.\n\n" +
"I am running inside: [" +
System.Diagnostics.Process.GetCurrentProcess().ProcessName +
"]\n\n" + (String.IsNullOrEmpty(pwzArgument) ?
"I was not given an argument" :
"I was given this argument: [" + pwzArgument + "]")); return 0;
} static void Main(string[] args)
{
EntryPoint("hello world");
}
}
}

The example application above was written in such a way that it can be called viaICLRRuntimeHost::ExecuteInDefaultAppDomain or run standalone; with either approach producing similar behavior. The ultimate goal is that when injected into an unmanaged remote process, the application above will execute in the context of that process, and display a message box showing the remote process name.

Building on top of the sample code from the Fundamentals section, the following c++ program will load the above .net assembly and execute the EntryPoint method:

 Collapse | Copy Code
#include <metahost.h>

#pragma comment(lib, "mscoree.lib")

#import "mscorlib.tlb" raw_interfaces_only \
high_property_prefixes("_get","_put","_putref") \
rename("ReportEvent", "InteropServices_ReportEvent") int wmain(int argc, wchar_t* argv[])
{
HRESULT hr;
ICLRMetaHost *pMetaHost = NULL;
ICLRRuntimeInfo *pRuntimeInfo = NULL;
ICLRRuntimeHost *pClrRuntimeHost = NULL; // build runtime
hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost));
hr = pMetaHost->GetRuntime(L"v4.0.30319", IID_PPV_ARGS(&pRuntimeInfo));
hr = pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost,
IID_PPV_ARGS(&pClrRuntimeHost)); // start runtime
hr = pClrRuntimeHost->Start(); // execute managed assembly
DWORD pReturnValue;
hr = pClrRuntimeHost->ExecuteInDefaultAppDomain(
L"T:\\FrameworkInjection\\_build\\debug\\anycpu\\InjectExample.exe",
L"InjectExample.Program",
L"EntryPoint",
L"hello .net runtime",
&pReturnValue); // free resources
pMetaHost->Release();
pRuntimeInfo->Release();
pClrRuntimeHost->Release(); return 0;
}

The following screenshot shows the output of the application:

So far two pieces of the puzzle are in place. It is now understood how to load the CLR and execute arbitrary methods from within unmanaged code. But how can this be done in arbitrary processes?

DLL Injection

DLL injection is a strategy used to execute code inside a remote process by loading a DLL in the remote process. Many DLL injection tactics focus on code executing inside of DllMain. Unfortunately, attempting to start the CLR from withinDllMain will cause the Windows loader to deadlock. This can be independently verified by writing a sample DLL that attempts to start the CLR from within DllMain. Verification is left as an excercise for the reader. For more information regarding .net initialization, the Windows loader, and loader lock; please refer to the following MSDN articles:

Inevitably the result is that the CLR cannot be started while the Windows loader is initializing another module. Each lock is process specific and managed by Windows. Remember, any module that attempts to acquire more than one lock on the loader when a lock has already been acquired will deadlock.

Fundamentals

The issue regarding the Windows loader seems sizable; and whenever a problem seems large it helps to break it down into more manageable pieces. Developing a strategy to inject a barebones DLL inside of a remote process is a good start. Take the following sample code:

 Collapse | Copy Code
#define WIN32_LEAN_AND_MEAN
#include <windows.h> BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

The above code implements a simple DLL. To inject this DLL in a remote process, the following Windows APIs are needed:

OpenProcess - acquire a handle to a process
GetModuleHandle - acquire a handle to the given module
LoadLibrary - load a library in the address space of the calling process
GetProcAddress - get the VA (virtual address) of an exported function from a library
VirtualAllocEx - allocate space in a given process
WriteProcessMemory - write bytes into a given process at a given address
CreateRemoteThread - spawn a thread in a remote process

Moving on, the purpose of the function below is to execute an exported function of a DLL that has been loaded inside a remote process:

 Collapse | Copy Code
DWORD_PTR Inject(const HANDLE hProcess, const LPVOID function,
const wstring& argument)
{
// allocate some memory in remote process
LPVOID baseAddress = VirtualAllocEx(hProcess, NULL, GetStringAllocSize(argument),
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); // write argument into remote process
BOOL isSucceeded = WriteProcessMemory(hProcess, baseAddress, argument.c_str(),
GetStringAllocSize(argument), NULL); // make the remote process invoke the function
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)function, baseAddress, NULL, 0); // wait for thread to exit
WaitForSingleObject(hThread, INFINITE); // free memory in remote process
VirtualFreeEx(hProcess, baseAddress, 0, MEM_RELEASE); // get the thread exit code
DWORD exitCode = 0;
GetExitCodeThread(hThread, &exitCode); // close thread handle
CloseHandle(hThread); // return the exit code
return exitCode;
}

Remember that the goal in this section is to load a library in a remote process. The next question is how can the above function be used to inject a DLL in a remote process? The answer lies in the assumption that kernel32.dll is mapped inside the address space of every process. LoadLibrary is an exported function of kernel32.dll and it has a function signature that matches LPTHREAD_START_ROUTINE, so it can be passed as the start routine to CreateRemoteThread. Recall that the purpose of LoadLibrary is to load a library in the address space of the calling process, and the purpose of CreateRemoteThread is to spawn a thread in a remote process. The following snippet illustrates how to load our test DLL inside of a process with ID 3344:

 Collapse | Copy Code
// get handle to remote process with PID 3344
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 3344); // get address of LoadLibrary
FARPROC fnLoadLibrary = GetProcAddress(GetModuleHandle(L"Kernel32"), "LoadLibraryW"); // inject test.dll into the remote process
Inject(hProcess, fnLoadLibrary, L"T:\\test\\test.dll");

To continue building off the above example, once CreateRemoteThread is invoked, a call is made toWaitForSingleObject to wait for the thread to exit. Next a call is made to GetExitCodeThread to obtain the result. Coincidentally, when LoadLibrary is passed into CreateRemoteThread, a successful invocation will result in thelpExitCode of GetExitCodeThread returning the base address of the loaded library in the context of the remote process. This works great for 32-bit applications, but not for 64-bit applications. The reason is that lpExitCode ofGetExitCodeThread is a DWORD value even on 64-bit machines, hence the address is truncated.

At this point three pieces of the puzzle are in place. To recap, the following problems have been solved:

  1. Loading the CLR using unmanaged code
  2. Executing arbitrary .net assemblies from unmanaged code
  3. Injecting DLLs

Advanced

Now that loading a DLL in a remote process is understood; discussion on how to start the CLR in a remote process can continue.

When LoadLibrary returns, the lock on the loader will be free. With the DLL in the address space of the remote process, exported functions can be invoked via subsequent calls to CreateRemoteThread; granted that the function signature matches LPTHREAD_START_ROUTINE. However, this invariably leads to more questions. How can exported functions be invoked inside a remote process, and how can pointers to these functions be obtained? Since GetProcAddress does not have a matching LPTHREAD_START_ROUTINE signature, how can the addresses of functions inside the DLL be obtained? Furthermore, even if GetProcAddress could be invoked, it still expects a handle to the remote DLL. How can this handle be obtained for 64-bit machines?

It is time to divide and conquer again. The following function reliably returns the handle (coincidentally the base address) of a given module in a given process on both x86 and x64 systems:

 Collapse | Copy Code
DWORD_PTR GetRemoteModuleHandle(const int processId, const wchar_t* moduleName)
{
MODULEENTRY32 me32;
HANDLE hSnapshot = INVALID_HANDLE_VALUE; // get snapshot of all modules in the remote process
me32.dwSize = sizeof(MODULEENTRY32);
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processId); // can we start looking?
if (!Module32First(hSnapshot, &me32))
{
CloseHandle(hSnapshot);
return 0;
} // enumerate all modules till we find the one we are looking for
// or until every one of them is checked
while (wcscmp(me32.szModule, moduleName) != 0 && Module32Next(hSnapshot, &me32)); // close the handle
CloseHandle(hSnapshot); // check if module handle was found and return it
if (wcscmp(me32.szModule, moduleName) == 0)
return (DWORD_PTR)me32.modBaseAddr; return 0;
}

Knowing the base address of the DLL in the remote process is a step in the right direction. It is time to devise a strategy for acquiring the address of an arbitrary exported function. To recap, it is known how to call LoadLibrary and obtain a handle to the loaded module in a remote process. Knowing this, it is trivial to invoke LoadLibrary locally (within the calling process) and obtain a handle to the loaded module. This handle (which is also the base address of the module) may or may not be the same as the one in the remote process, even though it is the same library. Nevertheless, with some basic math we can acquire the address for any exported function we wish. The idea is that although the base address of a module may differ from process to process, the offset of any given function relative to the base address of the module is constant. As an example, take the following exported function found in theBootstrap DLL project in the source download:

 Collapse | Copy Code
__declspec(dllexport) HRESULT ImplantDotNetAssembly(_In_ LPCTSTR lpCommand)

Before this function can be invoked remotely, the Bootstrap.dll module must first be loaded in the remote process. Using Process Hacker, here is a screenshot of where the Windows loader placed the Bootstrap.dll module in memory when injected into Firefox:

Continuing with our strategy, here is a sample program that loads the Bootstrap.dll module locally (within the calling process):

 Collapse | Copy Code
#include <windows.h>

int wmain(int argc, wchar_t* argv[])
{
HMODULE hLoaded = LoadLibrary(
L"T:\\FrameworkInjection\\_build\\release\\x86\\Bootstrap.dll");
system("pause");
return 0;
}

When the above program is run, here is a screenshot of where Windows decided to load the Bootstrap.dll module:

The next step is, within wmain, to make a call to GetProcAddress to get the address of the ImplantDotNetAssemblyfunction:

 Collapse | Copy Code
#include <windows.h>

int wmain(int argc, wchar_t* argv[])
{
HMODULE hLoaded = LoadLibrary(
L"T:\\FrameworkInjection\\_build\\debug\\x86\\Bootstrap.dll"); // get the address of ImplantDotNetAssembly
void* lpInject = GetProcAddress(hLoaded, "ImplantDotNetAssembly"); system("pause");
return 0;
}

The address of a function within a module will always be higher than the module's base address. Here is where basic math comes in to play. Below is a table to help illustrate:

Firefox.exe | Bootstrap.dll @ 0x50d0000 | ImplantDotNetAssembly @ ?
test.exe | Bootstrap.dll @ 0xf270000 | ImplantDotNetAssembly @ 0xf271490 (lpInject)

Test.exe shows that Bootstrap.dll is loaded at address 0xf270000, and that ImplantDotNetAssembly can be found in memory at address 0xf271490. Subtracting the address of ImplantDotNetAssembly from the address of Bootstrap.dllwill give the offset of the function relative to the base address of the module. The math shows thatImplantDotNetAssembly is (0xf271490 - 0xf270000) = 0x1490 bytes into the module. This offset can then be added onto the base address of the Bootstrap.dll module in the remote process to reliably give the address ofImplantDotNetAssembly relative to the remote process. The math to compute the address ofImplantDotNetAssembly in Firefox.exe shows that the function is located at address (0x50d0000 + 0x1490) = 0x50d1490. The following function computes the offset of a given function in a given module:

 Collapse | Copy Code
DWORD_PTR GetFunctionOffset(const wstring& library, const char* functionName)
{
// load library into this process
HMODULE hLoaded = LoadLibrary(library.c_str()); // get address of function to invoke
void* lpInject = GetProcAddress(hLoaded, functionName); // compute the distance between the base address and the function to invoke
DWORD_PTR offset = (DWORD_PTR)lpInject - (DWORD_PTR)hLoaded; // unload library from this process
FreeLibrary(hLoaded); // return the offset to the function
return offset;
}

It is worth noting that ImplantDotNetAssembly intentionally matches the signature of LPTHREAD_START_ROUTINE; as should all methods passed to CreateRemoteThread. Armed with the ability to execute arbitrary functions in a remote DLL, the logic to initialize the CLR has been put inside function ImplantDotNetAssembly in Bootstrap.dll. OnceBootstrap.dll is loaded in a remote process, the address of ImplantDotNetAssembly can be computed for the remote instance and then invoked via CreateRemoteThread. This is it, the final piece of the puzzle; now it is time to put it all together.

Putting It All Together

The main reason behind using an unmanaged DLL (Bootstrap.dll) to load the CLR is that if the CLR is not running in the remote process, the only way to start it is from unmanaged code (barring use of a scripting language such as Python, which has its own set of dependencies).

In addition, it would be nice for the Inject application to be flexible enough to accept input on the command line; because who would want to recompile every time their app changes? The Inject application accepts the following options:

-  m     The name of the managed method to execute. ex~ EntryPoint
-  i     The fully qualified path of the managed assembly to inject inside of the remote process. ex~ C:\InjectExample.exe
-  l     The fully qualified type name of the managed assembly. ex~ InjectExample.Program
-  a     An optional argument to pass into the managed function.
-  n     The process ID or the name of the process to inject. ex~ 1500 or notepad.exe

The wmain method for Inject looks like:

 Collapse | Copy Code
int wmain(int argc, wchar_t* argv[])
{
// parse args (-m -i -l -a -n)
if (!ParseArgs(argc, argv))
{
PrintUsage();
return -1;
} // enable debug privileges
EnablePrivilege(SE_DEBUG_NAME, TRUE); // get handle to remote process
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, g_processId); // inject bootstrap.dll into the remote process
FARPROC fnLoadLibrary = GetProcAddress(GetModuleHandle(L"Kernel32"),
"LoadLibraryW");
Inject(hProcess, fnLoadLibrary, GetBootstrapPath()); // add the function offset to the base of the module in the remote process
DWORD_PTR hBootstrap = GetRemoteModuleHandle(g_processId, BOOTSTRAP_DLL);
DWORD_PTR offset = GetFunctionOffset(GetBootstrapPath(), "ImplantDotNetAssembly");
DWORD_PTR fnImplant = hBootstrap + offset; // build argument; use DELIM as tokenizer
wstring argument = g_moduleName + DELIM + g_typeName + DELIM +
g_methodName + DELIM + g_Argument; // inject the managed assembly into the remote process
Inject(hProcess, (LPVOID)fnImplant, argument); // unload bootstrap.dll out of the remote process
FARPROC fnFreeLibrary = GetProcAddress(GetModuleHandle(L"Kernel32"),
"FreeLibrary");
CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)fnFreeLibrary,
(LPVOID)hBootstrap, NULL, 0); // close process handle
CloseHandle(hProcess); return 0;
}

Below is a screenshot of the Inject.exe app being called to inject the .net assembly InjectExample.exe into notepad.exe along with the command line that was used:

"T:\FrameworkInjection\_build\release\x64\Inject.exe" -m EntryPoint -i "T:\FrameworkInjection\_build\release\anycpu\InjectExample.exe" -l InjectExample.Program -a "hello inject" -n "notepad.exe"

It is worthwhile to mention and distinguish the differences between injecting a DLL that was built targetting x86x64, or AnyCPU. Under normal circumstances, the x86 flavor of Inject.exe and Bootstrap.dll should be used to inject x86processes, and the x64 flavor should be used to inject x64 processes. It is the responsibility of the caller to ensure the proper binaries are used. AnyCPU is a platform available in .net. Targetting AnyCPU tells the CLR to JIT the assembly for the appropriate architecture. This is why the same InjectExample.exe assembly can be injected into either an x86 orx64 process.

Running The Code

Moving on to the fun stuff! There are a few prerequisites for getting the code to run using the default settings.

To build the code:

  1. Visual Studio 2012 Express +
  2. Visual Studio 2012 Express Update 1 +

To run the code:

  1. .Net Framework 4.0 +
  2. Visual C++ Redistributable for Visual Studio 2012 Update 1 +
  3. Windows XP SP3 +

To simplify the the task of building the code, there is a file called build.bat in the source download that will take care of the heavy lifting provided the prerequisites have been installed. It will compile both the debug and release builds along with the corresponding x86, x64, and AnyCPU builds. Each project can also be built independently, and will compile from Visual Studio. The script build.bat will place the binaries in a folder named _build.

The code is also well commented. In addition, c++ 11.0 and .net 4.0 were chosen because both runtimes will execute on all Windows OSes from XP SP3 x86 to Windows 8 x64. As a side note, Microsoft added XP SP3 support for the C++ 11.0 runtime in VS 2012 U1.

Closing Notes

Traditionally, at this point papers tend to wind down and reflect on the techniques learned and other areas of improvement. While this is an excellent place to perform such an exercise, the author decided to do something different and present the reader with a basket full of:

As mentioned in the Introduction, .net is a powerful language. For example, the Reflection API in .net can be leveraged to obtain type information about assemblies. The significance of this is that .net can be used to scan an assembly and return the valid methods that can be used for injection! The source download contains a .net project called InjectGUI. This project contains a managed wrapper around our unmanaged Inject application. InjectGUI displays a list of running processes, decides whether to invoke the 32 or 64 bit version of Inject, as well as scans .net assemblies for valid injectable methods. Within InjectGUI is a file named InjectWrapper.cs which contains the wrapper logic.

There is also a helper class called MethodItem which has the following definition:

 Collapse | Copy Code
public class MethodItem
{
public string TypeName { get; set; }
public string Name { get; set; }
public string ParameterName { get; set; }
}

The following snippet from the ExtractInjectableMethods method will obtain a Collection of type List<MethodItem>, which matches the required method signature:

 Collapse | Copy Code
// find all methods that match:
// static int pwzMethodName (String pwzArgument) private void ExtractInjectableMethods()
{
// ... // open assembly
Assembly asm = Assembly.LoadFile(ManagedFilename); // get valid methods
InjectableMethods =
(from c in asm.GetTypes()
from m in c.GetMethods(BindingFlags.Static |
BindingFlags.Public | BindingFlags.NonPublic)
where m.ReturnType == typeof(int) &&
m.GetParameters().Length == 1 &&
m.GetParameters().First().ParameterType == typeof(string)
select new MethodItem
{
Name = m.Name,
ParameterName = m.GetParameters().First().Name,
TypeName = m.ReflectedType.FullName
}).ToList(); // ...
}

Now that the valid (injectable) methods have been extracted, the UI should also know if the process to be injected is 32 or 64 bit. To do this, a little help is needed from the Windows API:

 Collapse | Copy Code
[DllImport("kernel32.dll", SetLastError = true, CallingConvention =
CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsWow64Process([In] IntPtr process,
[Out] out bool wow64Process);

IsWow64Process is only defined on 64 bit OSes, and it returns true if the app is 32 bit. In .net 4.0, the following property has been introduced: Environment.Is64BitOperatingSystem. This can be used to help determine if theIsWow64Process function is defined as seen in this wrapper function:

 Collapse | Copy Code
private static bool IsWow64Process(int id)
{
if (!Environment.Is64BitOperatingSystem)
return true; IntPtr processHandle;
bool retVal; try
{
processHandle = Process.GetProcessById(id).Handle;
}
catch
{
return false; // access is denied to the process
} return IsWow64Process(processHandle, out retVal) && retVal;
}

The logic inside the InjectGUI project is fairly straightforward. Some understanding of WPF and Dependency Propertiesis necessary to understand the UI, however, all the pertinent logic related to injection is in the InjectWrapper class. The UI is built using Modern UI for WPF, and the icons are borrowed from Modern UI Icons. Both projects are open source, and the author is affiliated with neither. Below is a screenshot of InjectGUI in action:

Closing Notes Part Deux

This concludes the discussion on injecting .net assemblies into unmanaged processes. Don't cry because it's over. Smile☺ because it happened. ~ William Shakespeare

C# dll玩注入!.net运行时库内置!?的更多相关文章

  1. 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(6)-Unity 2.x依赖注入by运行时注入[附源码]

    原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(6)-Unity 2.x依赖注入by运行时注入[附源码] Unity 2.x依赖注入(控制反转)IOC,对 ...

  2. 玩Web虎-运行时受保护文件不可复制

    1. 直接复制粘贴,提示“操作无法完成,因为文件已在system中打开” 2.拔下加密锁后,复制粘贴,依然上错 3.用NoVirusThanks的 kernel-mode driver loader ...

  3. [转帖]运行时库(runtime library)

    运行时库(runtime library) https://blog.csdn.net/xitie8523/article/details/82712105 没学过这些东西 或者当时上课没听 又或者 ...

  4. /MD, /MDD, /ML, /MT,/MTD(使用运行时库)

    1. VC编译选项 多线程(/MT)多线程调试(/MTd)多线程 DLL (/MD)多线程调试 DLL (/MDd) 2. C 运行时库                                 ...

  5. /MD、/MT、/LD( 使用 多线程版本 运行时库的C runtime library)

    /MD./MT./LD(使用运行时库)(微软官网解释) Visual C++ 编译器选项 /MD./ML./MT./LD 区别 指定与你项目连接的运行期库 /MT多线程应用程序 /Mtd多线程应用程序 ...

  6. Visual C++中对运行时库的支持

    原文地址:http://blog.csdn.net/wqvbjhc/article/details/6612099 一.什么是C运行时库 1)C运行时库就是 C run-time library,是 ...

  7. vs2015部署---下一代VC运行时库系统:the Universal CRT

    前言 其实the Universal CRT(通用C运行时库)已经不能算是“下一代”,因为它已经在前两年伴随着Visual Studio 2015和Windows10发布.但是由于之前使用VS2015 ...

  8. VC运行时库(/MD、/MT等)

    VC项目属性→配置属性→C/C++→代码生成→运行时库 可以采用的方式有:多线程(/MT).多线程调试(/MTd).多线程DLL(/MD).多线程调试DLL(/MDd).单线程(/ML).单线程调试( ...

  9. C运行时库(C Run-time Library)详解(提供的另一个最重要的功能是为应用程序添加启动函数。Visual C++对控制台程序默认使用单线程的静态链接库,而MFC中的CFile类已暗藏了多线程)

    一.什么是C运行时库 1)C运行时库就是 C run-time library,是 C 而非 C++ 语言世界的概念:取这个名字就是因为你的 C 程序运行时需要这些库中的函数. 2)C 语言是所谓的“ ...

随机推荐

  1. 实训第一天--增删改查加hibernate+搭建环境常见问题

    1.     搭建环境 安装 1)谷歌浏览器 2)jdk-7u67-windows-i586.exe 3)Tomcat7 4)NavicatforMySQL 两种方式: ftp://172.21.95 ...

  2. IIS7.5 asp+access数据库连接失败处理 64位系统

    IIS7.5 asp+access数据库连接失败处理(SRV 2008R2 x64/win7 x64) IIS7.5不支持oledb4.0驱动?把IIS运行模式设置成32位就可以了,微软没有支持出64 ...

  3. 监听指定端口数据交互(HttpListenerContext )

    很怀念以前做机票的日子,,,,可惜回不去 以前的项目中的,拿来贴贴 场景:同步第三方数据,监听指定地址(指定时间间隔,否则不满足,因为需要处理粘包问题,改篇未实现) 主要内容四个文件:下面分别说下每个 ...

  4. RAC配置、安装

    RAC  配置及安装 2012年12月30日 星期日 21:49 ******************************************************************* ...

  5. Deep Learning 学习随记(八)CNN(Convolutional neural network)理解

    前面Andrew Ng的讲义基本看完了.Andrew讲的真是通俗易懂,只是不过瘾啊,讲的太少了.趁着看完那章convolution and pooling, 自己又去翻了翻CNN的相关东西. 当时看讲 ...

  6. Xcode添加静态库以及编译选项配置常见问题

    一,Xcode编译出现Link错误,出现"duplicate symbols for architecture i386 clang"提示.问题:链接时,项目有重名文件.解决:根据 ...

  7. 用response输出一个验证码

    package servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.Servle ...

  8. SGU 155.Cartesian Tree

    时间限制:0.25s 空间限制:6M 题意: 给出n(n< 50000)个含双关键字(key,val)的节点,构造一颗树使该树,按key值是一颗二分查找树,按val值是一个小根堆. Soluti ...

  9. 2 - Annotations标注

    下面是TestNG标注和参数的一个快速预览 @BeforeSuite 被标注的方法会在这个套件的所有测试执行之前执行  @AfterSuite 被标注的方法会在这个套件的所有测试执行之后执行 @Bef ...

  10. 绑定下拉框时避免触发SelectedIndexChanged事件

    在从数据库读取数据集绑定到下拉框时会立即触发其SelectedIndexChanged事件造成异常,可对其SelectedIndexChanged事件采取先解除后附加的方法解决. cmbXl_gt.V ...