实现DLL的注入与卸载

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



点击“卸载”按钮:

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


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

获得源码:

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



你可能感兴趣的:(实现DLL的注入与卸载)