在Windows系统下,为了避免各个进程相互影响,每个进程的地址空间都是被隔离的。在执行DLL注入时需要通过创建“远程线程”来实现。所谓“远程线程”,并不是跨计算机,而是跨进程的。简而言之,就是进程A在进程B中创建一个线程,这个线程就叫“远程线程”。
要向其它进程中“注入”DLL就需要在目标进程中调用相应的API函数(LoadLibrary),可是目标进程不会自己“乖乖地”调用加载函数,这时候要怎么办呢?有一个API可以帮我们强制让某个进程加载DLL文件到它的地址空间中,这个手段就是利用远程线程。
该函数的原型如下:
HANDLECreateRemoteThread(
HANDLE hProcess,
LPSECURITY_ATTRIBUTESlpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINElpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
参数说明:
hProcess:目标进程的句柄
lpThreadAttributes:指向线程的安全描述结构体的指针,一般设置为NULL,表示使用默认的安全级别
dwStackSize:线程堆栈大小,一般设置为0,表示使用默认的大小,一般为1M
lpStartAddress:线程函数的地址
lpParameter:线程参数
dwCreationFlags:线程的创建方式
CREATE_SUSPENDED 线程以挂起方式创建
lpThreadId:输出参数,记录创建的远程线程的ID
我们知道,每个进程的地址空间是隔离的,那么新创建的线程函数的地址也应该在目标进程中,而不应该在调用CreateRemoteThread函数的进程中。同理,传递给新创建线程的参数也应该在目标进程中。鉴于此,我们可以把LoadLibrary()函数作为线程函数创建到指定的进程中,而将要加载的DLL的完整路径作为线程参数。
那么如何将LoadLibrary()函数的地址放到目标进程的空间中。LoadLibrary()函数是系统中的Kernel32.dll的导出函数,Kernel32.dll在任何进程中加载的位置都是相同的,也就是说LoadLibrary()函数的地址在任何进程中也相同!所以,CreateRemoteThread函数直接传递本进程中的LoadLibrary()函数地址即可。
下一步就是将要加载的DLL文件的完整路径写入目标进程中。这里需要用到以下函数:
BOOLWriteProcessMemory(
HANDLEhProcess,
LPVOIDlpBaseAddress,
LPVOIDlpBuffer,
DWORD nSize,
LPDWORDlpNumberOfBytesWritten
);
参数说明:
hProcess:指定目标进程的句柄
lpBaseAddress:要写的内存首地址
lpBuffer:要写入目标进程内存的缓冲区起始地址
nSize:指定写入目标进程内存中的缓冲区的长度
lpNumberOfBytesWritten:接收实际写入内存的长度
要在目标进程中写入数据,首先要有可以写入的内存。对于目标进程是不会事先准备好等着我们的,所以需要自己在目标进程中申请一块内存,然后把dll的路径写入。在目标进程中申请内存,我们可以调用以下函数:
LPVOIDVirtualAllocEx(
HANDLEhProcess,
LPVOID lpAddress,
SIZE_T dwSize,
DWORDflAllocationType,
DWORDflProtect
);
hProcess:指定目标进程句柄
lpAddress:在目标进程中申请内存的起始地址
dwSize:目标进程中申请内存的长度
flAllocationType:指定申请内存的状态类型
flProtect:指定申请内存的属性
到此主要的API函数和编程逻辑已经介绍完了,下面是一个简单的例子。
新建一个基于对话框的MFC项目,界面如下:
下面列出了几个关键函数的实现:
voidCDLLDlg::OnBtnInject()
{
char szDllName[MAX_PATH] = {0};
char szProcessName[MAX_PATH] = {0};
DWORD dwPid = 0;
GetDlgItemText(IDC_EDIT_DLLFILE,szDllName, MAX_PATH);
GetDlgItemText(IDC_EDIT_PROCESSNAME,szProcessName, MAX_PATH);
//MessageBox(szDllName);
//MessageBox(szProcessName);
//由进程名获得pid
dwPid = GetProcId(szProcessName);
/*
if(dwPid == 0)
{
MessageBox("Error to getpid!");
}
*/
//注入szDllName到dwPid
InjectDll(dwPid, szDllName);
}
DWORDCDLLDlg::GetProcId(char *szProcessName)
{
BOOL bRet;
PROCESSENTRY32 pe32;
HANDLE hSnap;
hSnap =CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
pe32.dwSize = sizeof(pe32);
bRet = Process32First(hSnap, &pe32);
while(bRet)
{
//strupr()函数是将字符转化为大写
if(lstrcmp(strupr(pe32.szExeFile),strupr(szProcessName)) == 0)
{
return pe32.th32ProcessID;
}
bRet = Process32Next(hSnap,&pe32);
}
return 0;
}
VOIDCDLLDlg::InjectDll(DWORD dwPid, char *szDllName)
{
if(dwPid == 0 || lstrlen(szDllName) == 0)
{
return;
}
char *pFunName = "LoadLibraryA";
//打开目标进程
HANDLE hProcess =OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if(hProcess == NULL)
{
return;
}
//计算欲注入DLL文件完整路径的长度
int nDllLen = lstrlen(szDllName) +sizeof(char);
//在目标进程申请一块长度为nDllLen大小的内存空间
PVOID pDllAddr = VirtualAllocEx(hProcess,NULL, nDllLen, MEM_COMMIT, PAGE_READWRITE);
if(pDllAddr == NULL)
{
CloseHandle(hProcess);
return;
}
DWORD dwWriteNum = 0;
//将欲注入DLL文件的完整路径写入目标进程中申请的空间内
WriteProcessMemory(hProcess, pDllAddr,szDllName, nDllLen, &dwWriteNum);
//获得LoadLibraryA()函数的地址
FARPROC pFunAddr =GetProcAddress(GetModuleHandle("kernel32.dll"), pFunName);
//创建远程线程
HANDLE hThread =CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunAddr,pDllAddr, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
}
voidCDLLDlg::OnBtnUninject()
{
char szDllName[MAX_PATH] = {0};
char szProcessName[MAX_PATH] = {0};
DWORD dwPid = 0;
GetDlgItemText(IDC_EDIT_DLLFILE,szDllName, MAX_PATH);
GetDlgItemText(IDC_EDIT_PROCESSNAME,szProcessName, MAX_PATH);
//由进程名获得pid
dwPid = GetProcId(szProcessName);
//注入szDllName到dwPid
UninjectDll(dwPid, szDllName);
}
voidCDLLDlg::UninjectDll(DWORD dwPid, char *szDllName)
{
if(dwPid == 0 || lstrlen(szDllName) == 0)
{
return;
}
HANDLE hSnap =CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPid);
MODULEENTRY32 me32;
me32.dwSize = sizeof(me32);
//查找匹配的进程名称
BOOL bRet = Module32First(hSnap,&me32);
while(bRet)
{
if(lstrcmp(strupr(me32.szExePath),strupr(szDllName)) == 0)
{
break;
}
bRet = Module32Next(hSnap,&me32);
}
CloseHandle(hSnap);
char *pFunName = "FreeLibrary";
HANDLE hProcess =OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if(hProcess == NULL)
{
return;
}
FARPROC pFunAddr =GetProcAddress(GetModuleHandle("kernel32.dll"), pFunName);
HANDLE hThread =CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunAddr,me32.hModule, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
}
运行该项目可以看到效果。
填写正确的dll路径和目标进程,点击“注入”按钮:
点击“卸载”按钮:
写到这里已经感觉手有点酸酸的了。
获得源码:
http://pan.baidu.com/s/1bHCLcy