将托管dll注入到非托管进程中
为什么要写这篇文章
1,如果你想注入带窗体的dll,C#写界面比C++容易的多;
2,或许你想利用.net的某些功能,比如利用.Net Remoting从外部控制被注入的dll;
3,或许你是一个C#程序员,使用C#的时候总感觉更舒适些,比如笔者。同时,你希望必要时也能在宿主中调用C++函数,提供更大的灵活性,本文的方法也能做到。
注入托管dll的不同之处
首先,为什么托管dll 不能像非托管dll那样用LoadLibrary注入? 我们知道,.net语言,如C#,VB.net等,都是运行在CLR(公共语言运行时)上的,也就是我们通常所说的虚拟机,而我们所说的非托管进程是没有加载虚拟机的。那为什么托管dll一定要在CLR上运行?托管dll虽然符合windows的PE格式规范,但是代码是以IL的形式保存在.Text 区的,而不是机器码,CLR会在运行时JIT编译成机器码再交给操作系统执行,这也就为什么托管代码称之为”托管”的意义。
所以,要想注入托管dll,首先需要在目标进程中启动CLR,然后让CLR来加载managed dll。
注入的方式
首先,我们注入一个非托管的dll,再通过它加载CLR并加载托管dll。所以工程需要3个模块:注入器,一个注入的非托管dll和注入的托管dll。
我们首先看如何注入非托管dll,这里是通过远程线程来实现的,如果你已经熟悉这个技术,可以跳过:
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 }
这段代码通过远程线程注入Native.dll,网上这方面文章很多,也可以根据不同的需要采用服务输入法或者Hook等方式注入。需要注意的是,我们通过GetProcAddress获取LoadLibrary的函数地址,事实上获取的是注入器中该函数的虚拟地址,而不是宿主的。由于LoadLibrary函数位于系统dll中,在每个进程中都被加载到相同的虚拟地址上,所以我们才能这么做。LoadLibrary函数的参数,dll的路径字符串需要通过VirtualAllocEx和WriteProcessMemory在宿主进程上创建,而不能把注入器上的字符串地址传给LoadLibrary。总之,必须记住的一点是,远程线程是在另一个虚拟地址空间上执行的,远程执行的函数体本身或者他引用的虚拟地址都不能是注入器进程中的虚拟地址,而必须是宿主进程的虚拟地址。
再来看被注入的非托管的NativeDll.dll的代码:
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->GetInte易做图ce(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->ExecuteInDefaultAppDomain(
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
补充:Web开发 , ASP.Net ,