实现DLL的注入与卸载

在Windows系统下,为了避免各个进程相互影响,每个进程的地址空间都是被隔离的。在执行DLL注入时需要通过创建“远程线程”来实现。所谓“远程线程”,并不是跨计算机,而是跨进程的。简而言之,就是进程A在进程B中创建一个线程,这个线程就叫“远程线程”。

要向其它进程中“注入”DLL就需要在目标进程中调用相应的API函数(LoadLibrary),可是目标进程不会自己“乖乖地”调用加载函数,这时候要怎么办呢?有一个API可以帮我们强制让某个进程加载DLL文件到它的地址空间中,这个手段就是利用远程线程。

该函数的原型如下:

HANDLE CreateRemoteThread(
     HANDLE hProcess,
     LPSECURITY_ATTRIBUTES lpThreadAttributes,
     SIZE_T dwStackSize,
     LPTHREAD_START_ROUTINE lpStartAddress,
     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文件的完整路径写入目标进程中。这里需要用到以下函数:

BOOL WriteProcessMemory(
HANDLE hProcess,
LPVOID lpBaseAddress,
LPVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesWritten
);

参数说明:

hProcess:指定目标进程的句柄

lpBaseAddress:要写的内存首地址

lpBuffer:要写入目标进程内存的缓冲区起始地址

nSize:指定写入目标进程内存中的缓冲区的长度

lpNumberOfBytesWritten:接收实际写入内存的长度

要在目标进程中写入数据,首先要有可以写入的内存。对于目标进程是不会事先准备好等着我们的,所以需要自己在目标进程中申请一块内存,然后把dll的路径写入。在目标进程中申请内存,我们可以调用以下函数:

LPVOID VirtualAllocEx(
HANDLE hProcess,
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);

hProcess:指定目标进程句柄

lpAddress:在目标进程中申请内存的起始地址

dwSize:目标进程中申请内存的长度

flAllocationType:指定申请内存的状态类型

flProtect:指定申请内存的属性

到此主要的API函数和编程逻辑已经介绍完了,下面是一个简单的例子。

新建一个基于对话框的MFC项目,界面如下:

实现DLL的注入与卸载_第1张图片

下面列出了几个关键函数的实现:

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路径和目标进程,点击“注入”按钮:

实现DLL的注入与卸载_第2张图片


点击“卸载”按钮:

实现DLL的注入与卸载_第3张图片


写到这里已经感觉手有点酸酸的了。

获得源码:

http://pan.baidu.com/s/1bHCLcy



你可能感兴趣的:(C++,windows,MFC,Windows九阳神功)