每个进程都有独立的地址空间,违规访问可能会导致内存访问违规,但是有时候我们需要打破进程的界限,访问另一个进程的地址空间,这里就可以用到我们的DLL注入技术了,将我们的DLL插入到另一个进程的地址空间,一旦你的DLL进入另一个进程的地址空间,你就可以对该进程为所欲为了。。。
下面介绍几种DLL注入技术:
- 利用注册表注入DLL
我们只要将该值设置为我们的DLL的路径,如图所示
这里有一点要注意的是,AppInit_DLLs关键字的值可以包含多个DLL文件名,每个文件名用空格隔开,也就是说我们的DLL文件名不应该出现空格,且列出的第一个DLL文件名可以包含一个路径,其它包含路径则被忽略,所以我们最好将我们的DLL放到windows系统目录中,这样就不需要设置路径了。
当重新启动计算机及Windows进行初始化时,系统将保存这个关键字的值。然后,当User32.dll库被映射到进程中时,它将接收到一个DLL_PROCESS_ATTACH通知。当这个通知被处理时,User32.dll便检索保存的这个关键字中的值,并且为字符串中指定的每个DLL调用LoadLibrary函数。
所有的GUI程序都会用到User32.dll这个库,所以每个运行的GUI程序都会加载我们的DLL,这样也就加大了进程奔溃的可能性了。
2. 使用Windows挂钩注入DLL
通过SetWindowsHookEx函数将DLL注入到进程的地址空间中,该函数的原型如下:
HHOOK SetWindowsHookEx( int idHook,
HOOKPROC lpfn,
HINSTANCE hMod,
DWORD dwThreadId
);
最后一个参数dwThreadId指向的是被注入进程内的某个线程ID。这里以WH_GETMESSAGE挂钩来描述其执行步骤。
(1) 进程A调用SetWindowsHookEx(WH_GETMESSAGE,lpfn,hMod,dwThreadId)对线程dwThread执行WH_GETMESSAGE挂钩
(2) 线程dwThreadId准备提取消息进行处理,这时它会被系统监控到
(3) 系统检查SetWindowsHookEx中hMod1指向的DLL是否已被载入到线程dwThreadId所在的进程(假设进程B)地址空间中,若否,则载入,并且将进程B中
的DLL映像的自动跟踪计数递增1。这时,假设DLL被载入到进程B的hMod2位置。
(4) 系统计算lpfn在进程B中的位置,计算方式:lpfn + hMod2 - hMod1。
(5) 系统在进程B中递增该DLL(hMod2)的锁计数
(6) 系统在进程B的地址空间中调用lpfn函数(其位置在:lpfn + hMod2 - hMod1)
(7) lpfn函数返回时候,系统递减该DLL(hMod2)在进程B中的锁计数
这里补充的一点是,为什么每次在调用lpfn函数前要递增DLL的锁计数呢?这不得不先了解一下UnhookWindowsHookEx函数,当一个线程调用UnhookWindowsHookEx函数时,系统将遍历它将DLL插入到的各个进程的内部列表,并且对DLL的自动跟踪计数进行递减。当自动跟踪计数递减为0时,DLL就自动从进程的地址空间中被删除了。那么,如果该自动跟踪计数没有递增,那么当进程B的线程试图执行GetMsgProc函数中的代码时,系统中运行的另一个线程就可以调用UnhookWindowsHookEx函数将DLL卸载。示例代码:
(1)创建用于注入的DLL代码:
//HookInjDLL.cpp
#include <windows.h>
#include <assert.h>
#define HOOKLIBAPI __declspec(dllexport)
#include "HookInjDLL.h"
HINSTANCE g_hDll = NULL;
#pragma data_seg("Shared")
HHOOK g_hook = NULL;
DWORD g_curThreadId = 0;
#pragma data_seg()
#pragma comment(linker,"/SECTION:Shared,RWS")
LRESULT CALLBACK GetMsgProc( int code,
WPARAM wParam,
LPARAM lParam
)
{
static BOOL bload = FALSE;
if(!bload)
{
//通知客户端已经注入成功。。。
PostThreadMessage(g_curThreadId,WM_NULL,0,0);
bload = TRUE;
}
return CallNextHookEx(g_hook,code,wParam,lParam);
}
BOOL InjectHookDll(DWORD dwThreadId)
{
BOOL fOk = FALSE;
if(dwThreadId!=0)
{
assert(g_hook == NULL);
g_hook = SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,g_hDll,dwThreadId);
g_curThreadId = GetCurrentThreadId();
fOk = (g_hook != NULL);
if(fOk)
//通知目标进程加载DLL,并调用GetMsgProc
PostThreadMessage(dwThreadId,WM_NULL,0,0);
}
else //dwThreadId = 0,则卸载钩子
{
assert(g_hook!=NULL);
fOk = UnhookWindowsHookEx(g_hook);
g_hook = NULL;
}
return fOk;
}
BOOL
WINAPI
DllMain(
HINSTANCE hDllHandle,
DWORD nReason,
LPVOID Reserved
)
{
BOOLEAN bSuccess = TRUE;
// Perform global initialization.
switch ( nReason )
{
case DLL_PROCESS_ATTACH:
// For optimization.
g_hDll = hDllHandle;
DisableThreadLibraryCalls( hDllHandle );
break;
case DLL_PROCESS_DETACH:
break;
}
return bSuccess;
}
//HookInjDLL.h
#ifndef HOOKLIBAPI
#define HOOKLIBAPI __declspec(dllimport)
#endif
HOOKLIBAPI BOOL InjectHookDll(DWORD dwThreadId);
(2)创建执行注入操作的客户端代码:
//HookInj.cpp
#include <windows.h>
#include <iostream>
#include "..\..\HookInjDLL\HookInjDLL\HookInjDLL.h"
using namespace std;
#pragma comment(lib,"../../HookInjDLL");
int main()
{
char ch;
while((ch = getchar()) != 'e')
{
if(ch == 'i') //输入'i',表示install,安装DLL
{
DWORD dwThreadId = GetWindowThreadProcessId(FindWindow("TXGuiFoundation","QQ2013"),NULL);
if(!InjectHookDll(dwThreadId)) //注入失败
{
cout<< "注入失败" <<endl;
return 0;
}
MSG msg;
GetMessage(&msg,NULL,0,0);
cout<<"注入成功!"<<endl;
}
else if(ch == 'u') //输入'u',表示uninstall,卸载DLL
{
if(!InjectHookDll(0))
cout<<"卸载失败!"<<endl;
else
cout<<"卸载成功!"<<endl;
}
}
return 0;
}
3. 利用远程线程注入DLL
这种方法提供了更大的灵活性,具体步骤如下:
(1)用VirtualAllocEx函数在远程进程的地址空间中分配一块内存。
(2)用WriteProcessMemory函数把DLL的路径名复制到第1步分配的内存中。
(3)用GetProcAddress函数来得到LoadLibrary函数的实际地址。
(4)用CreateRemoteThread函数在远程进程创建一个线程,让新线程调用正确的LoadLibrary函数并在参数中传入第1步分配的内存地址。这时,DLL已经被注入到远程进程的地址空间中。
(5)用VirtualFreeEx来释放第一步分配的内存。
(6)用GetProcAddress来得到FreeLibrary函数的实际地址。
(7)用CreateRemoteThread函数在远程进程创建一个线程,让线程调用FreeLibrary函数并在参数中传入DLL的HMODULE。
示例代码:
//InjLib.cpp
#include <windows.h>
#include <TlHelp32.h>
#include <iostream>
using namespace std;
BOOL InjectLib(DWORD dwProcessId,LPCSTR pszLibFile)
{
BOOL bOk = FALSE;
HANDLE hProcess = NULL,hThread = NULL;
LPSTR pszLibFileRemote = NULL;
__try{
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
PROCESS_CREATE_THREAD |
PROCESS_VM_OPERATION |
PROCESS_VM_WRITE,
FALSE,dwProcessId);
if(hProcess == NULL) __leave;
int cch = 1+strlen(pszLibFile);
int cb = cch * sizeof(char);
//在远程进程分配一块空间
pszLibFileRemote = (LPSTR)VirtualAllocEx(hProcess,NULL,cb,MEM_COMMIT,PAGE_READWRITE);
if(pszLibFileRemote == NULL) __leave;
//拷贝DLL的路径到远程进程地址空间中
if(!WriteProcessMemory(hProcess,pszLibFileRemote,pszLibFile,cb,NULL)) __leave;
//获得LoadLibrary的实际地址
PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)
GetProcAddress(GetModuleHandle("Kernel32"),"LoadLibraryA");
if(pfnThreadRtn == NULL) __leave;
//创建远程线程,让它执行LoadLibray
hThread = CreateRemoteThread(hProcess,NULL,0,pfnThreadRtn,pszLibFileRemote,0,NULL);
WaitForSingleObject(hThread,INFINITE);
bOk = TRUE;
}__finally{
//释放远程进程中包含DLL路径名的内存块
if(pszLibFileRemote != NULL)
VirtualFreeEx(hProcess,pszLibFileRemote,0,MEM_RELEASE);
if(hThread != NULL)
CloseHandle(hThread);
if(hProcess!=NULL)
CloseHandle(hProcess);
}
return bOk;
}
BOOL EjectLib(DWORD dwProcessId, LPCSTR pszLibFile) {
BOOL bOk = FALSE;
HANDLE hthSnapshot = NULL;
HANDLE hProcess = NULL, hThread = NULL;
__try {
// 创建远程进程快照
hthSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
if (hthSnapshot == INVALID_HANDLE_VALUE) __leave;
// 获得指定的库的模块信息
MODULEENTRY32 me = { sizeof(me) };
BOOL bFound = FALSE;
BOOL bMoreMods = Module32First(hthSnapshot, &me);
for (; bMoreMods; bMoreMods = Module32Next(hthSnapshot, &me)) {
bFound = (strcmp(me.szModule, pszLibFile) == 0) ||
(strcmp(me.szExePath, pszLibFile) == 0);
if (bFound) break;
}
if (!bFound) __leave;
// 获得目标进程的句柄
hProcess = OpenProcess(
PROCESS_QUERY_INFORMATION |
PROCESS_CREATE_THREAD |
PROCESS_VM_OPERATION,
FALSE, dwProcessId);
if (hProcess == NULL) __leave;
// 获得FreeLibrary的实际地址
PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)
GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "FreeLibrary");
if (pfnThreadRtn == NULL) __leave;
// 创建远程线程,让它执行FreeLibrary
hThread = CreateRemoteThread(hProcess, NULL, 0,
pfnThreadRtn, me.modBaseAddr, 0, NULL);
if (hThread == NULL) __leave;
// 等待远程线程结束
WaitForSingleObject(hThread, INFINITE);
bOk = TRUE;
}
__finally {
if (hthSnapshot != NULL)
CloseHandle(hthSnapshot);
if (hThread != NULL)
CloseHandle(hThread);
if (hProcess != NULL)
CloseHandle(hProcess);
}
return(bOk);
}
int main()
{
char ch;
while((ch = getchar()) != 'e')
{
if(ch == 'i')
{
DWORD dwProcessId;
DWORD dwThreadId = GetWindowThreadProcessId(FindWindow("TXGuiFoundation","QQ2013"),&dwProcessId);
if(dwProcessId)
InjectLib(dwProcessId,"E:\\ProcDLL\\Debug\\ProcDLL.dll");
}
else if(ch == 'u')
{
DWORD dwProcessId;
DWORD dwThreadId = GetWindowThreadProcessId(FindWindow("TXGuiFoundation","QQ2013"),&dwProcessId);
if(dwProcessId)
EjectLib(dwProcessId,"E:\\ProcDLL\\Debug\\ProcDLL.dll");
}
}
return 0;
}