Why write this article?
1. If you want to inject a dll with a form, the C # Write interface is much easier than the C ++ interface;
2. You may want to use some. net functions, such as using. Net Remoting to control the injected dll from the outside;
3. Maybe you are a C # programmer. When using C #, you always feel more comfortable, such as the author. At the same time, you want to call the C ++ function in the host when necessary to provide greater flexibility, and the methods described in this article can also be done.
Differences between injection-hosted dll
First, why cannot a hosted dll be injected with LoadLibrary like an unmanaged dll? We know that ,. net language, such as C # And VB.net, all run on CLR (Common Language Runtime), which is also known as virtual machines, what we call an unmanaged process is that it does not load virtual machines. So why must a hosted dll be run on the CLR? Although the hosted dll complies with the PE format specification of windows, the code is saved in the form of IL. in the Text area, instead of the machine code, the CLR will compile the JIT into the machine code at runtime and then hand it over to the operating system for execution. This is why hosting code is called "hosting.
Therefore, to inject a hosted dll, you must first start the CLR in the target process and then let the CLR load the managed dll.
Injection Method
First, we inject an unmanaged dll, and then load the CLR and the hosted dll through it. Therefore, the project requires three modules: the injector, an injected Unmanaged dll and a injected hosted dll.
First, let's take a look at how to inject an unmanaged dll, which is implemented through a remote thread. If you are familiar with this technology, you can skip this step:
InjectDemo. cpp:
View sourceprint? 01 int _ tmain (int argc, _ TCHAR * argv [])
02 {
03 int pid;
04 void * pNativeDllRemote;
05 FARPROC pLoadLibrary;
06 TCHAR szNativeDllPath [_ MAX_PATH] = _ T ("D: \ Code \ InjectDemo \ Debug \ NativeDll. dll ");
07
08 cout <"input the process id to inject" <endl;
09 cin> pid;
10
11 HANDLE hProcess = OpenProcess (PROCESS_ALL_ACCESS, 0, pid );
12 if (hProcess = 0)
13 return 1;
14
15 HMODULE hKernel32 =: GetModuleHandle (_ T ("Kernel32 "));
16 if (sizeof (TCHAR) = 2)
17 pLoadLibrary =: GetProcAddress (hKernel32, "LoadLibraryW"); // if path is unicode, use "LoadLibraryW"
18 else
19 pLoadLibrary =: GetProcAddress (hKernel32, "LoadLibraryA ");
20 pNativeDllRemote = VirtualAllocEx (hProcess, NULL, sizeof (szNativeDllPath), MEM_COMMIT, PAGE_READWRITE );
21: WriteProcessMemory (hProcess, pNativeDllRemote, (void *) szNativeDllPath, sizeof (szNativeDllPath), NULL );
22 HANDLE hThread = CreateRemoteThread (hProcess, NULL, 0, (LPTHREAD_START_ROUTINE) pLoadLibrary, pNativeDllRemote, 0, NULL );
23: WaitForSingleObject (hThread, INFINITE );
24 // DWORD exitcode;
25 // GetExitCodeThread (hThread, & exitcode );
26: CloseHandle (hThread );
27 return 0;
28}
This code is injected with Native. dll through a remote thread. There are many articles on the Internet, and the service input method or Hook method can be used for injection according to different needs. It should be noted that we get the LoadLibrary function address through GetProcAddress. In fact, we get the virtual address of the function in the injector, rather than the host. Because the LoadLibrary function is located in the system dll, it is loaded to the same virtual address in every process, so we can do this. The LoadLibrary function parameter. The dll path string needs to be created on the host process through VirtualAllocEx and WriteProcessMemory, rather than passing the string address on the injector to LoadLibrary. In short, it must be remembered that the remote thread is executed in another virtual address space, the remotely executed function body itself or its referenced virtual address cannot be the virtual address in the injector process, but must be the virtual address of the host process.
Let's take a look at the injected unmanaged NativeDll. dll code:
NativeDll. cpp:
View sourceprint? 01 # include <windows. h>
02 # include "stdafx. h"
03 # include "NativeDll. h"
04 # include "MSCorEE. h"
05 # include "metahost. h"
06
07 dword callback StartTheDotNetRuntime (LPVOID lp)
08 {
09
10 HRESULT hr = S_ OK;
11 ICLRMetaHost * m_pMetaHost = NULL;
12 ICLRRuntimeInfo * m_pRuntimeInfo = NULL;
13 ICLRRuntimeHost * pClrHost = NULL;
14
15 hr = CLRCreateInstance (CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID *) & m_pMetaHost );
16 if (hr! = S_ OK)
17 return hr;
18 hr = m_pMetaHost-> GetRuntime (L "v4.0.30319", IID_ICLRRuntimeInfo, (LPVOID *) & m_pRuntimeInfo );
19 if (hr! = S_ OK)
20 return hr;
21 hr = m_pRuntimeInfo-> GetInterface (CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID *) & pClrHost );
22 if (FAILED (hr) return hr;
23 HRESULT hrStart = pClrHost-> Start ();
24
25 DWORD dwRet = 0;
26 hr = pClrHost-> executeindefaappappdomain (
27 L "d: \ Code \ InjectDemo \ Debug \ ManagedDll. dll ",
28 L "ManagedDll. Class1", L "Start", L "nothing to post", & dwRet );
29
30 hr = pClrHost-> Stop ();
31
32 pClrHost-> Release ();
33
34 return S_ OK;
35}
36
37
38 bool winapi DllMain (
39 HINSTANCE hinstDLL,
40 DWORD fdwReason,
41 LPVOID lpvReserved
42)
43 {
44 switch (fdwReason)
45 {
46 case DLL_PROCESS_ATTACH:
47 CreateThread (0, 0, StartTheDotNetRuntime, 0, 0 );
48 break;
49 case DLL_THREAD_ATTACH:
50 break;
51 case DLL_THREAD_DETACH:
52 break;
53 case DLL_PROCESS_DETACH:
54 break;
55 default:
56 break;
57}
58 return true;
59}
This code is the focus of this article. in the dll DllMain function, create a thread to load the CLR. CLR is actually a group of COM servers. To call them, we need to reference the header file "MSCorEE. h "and" metahost. h ", and add the pair MSCorEE to the linker. lib. The GetRuntime function is used to specify the loaded CLR version. You need to enter the complete version number. According to my tests, 2.0 and 4.0 can be loaded successfully, but 3.5 does not seem to work. After successfully starting CLR, execute executeindefaappappdomain to load the specified hosted dll and execute its static method. After the execution is completed, stop and release CLR.
Then, in managed ManagedDll. dll, we can open a window:
Class1.cs:
View sourceprint? 01 using System. Windows. Forms;
02
03 namespace ManagedDll
04 {
05 public class Class1
06 {
07 public static int Start (string argument)
08 {
09 Application. Run (new MainForm ());
10 return 0;
11}
12}
13}
Or enable a. net Remoting service so that the injector can control the process externally:
View sourceprint? 01 using System. Threading;
02 using System. Runtime. Remoting;
03
04 namespace ManagedDll
05 {
06 public class Class1
07 {
08 public static int Start (string argument)
09 {
10 RemotingConfiguration. Configure ("ManagedDll. dll. config ");
11 while (true)
12 {
13 Thread. Sleep (1000 );
14}
15}
16}
17}
Note that when using. net Remoting, you must put the hosted dll in the same directory as the host; otherwise, the reflection mechanism will fail. This injection method provides great flexibility. You can write the logic code in ManagedDll. dll, which can also be written in NativeDll. dll and export it in ManagedDll. dll reference, and then through the window or. net Remoting call.