实现DLL的注入与卸载

20 篇文章 1 订阅
17 篇文章 1 订阅

在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项目,界面如下:


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

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



  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
DLL注入是指在目标进程中加载一个外部的DLL文件,并且在目标进程的地址空间中执行该DLL中的函数。在进行DLL注入时,我们可以利用钩子函数技术来实现一些特殊的功能。 下面是一个简单的DLL注入钩子函数的实现过程: 1. 定义一个全局的钩子函数: ```c++ LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode < 0) { return CallNextHookEx(NULL, nCode, wParam, lParam); } // 进行一些特殊的处理 return CallNextHookEx(NULL, nCode, wParam, lParam); } ``` 2. 在DLL的入口函数中,使用SetWindowsHookEx函数安装钩子函数: ```c++ // 安装钩子函数 HHOOK hHook = SetWindowsHookEx(WH_CALLWNDPROC, HookProc, hDll, 0); if (hHook == NULL) { // 处理错误信息 } // 进行消息循环 MSG msg; while (GetMessage(&msg, NULL, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); } // 卸载钩子函数 UnhookWindowsHookEx(hHook); ``` 3. 在目标进程中,调用LoadLibrary函数加载DLL文件: ```c++ // 加载DLL文件 HMODULE hDll = LoadLibrary(L"path/to/DLL"); if (hDll == NULL) { // 处理错误信息 } ``` 通过以上步骤,我们可以在目标进程中成功注入一个DLL,并且在其中安装一个钩子函数。在钩子函数中,我们可以进行一些特殊的处理,例如记录键盘输入、拦截鼠标消息等等。需要注意的是,钩子函数的处理需要尽可能地简洁和高效,以避免对目标进程造成不必要的影响。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值