Windows DLL 注入技术

本文主要介绍四种常见的 Windows DLL 注入技术。

分别为全局钩子、远线程钩子、突破 SESSION 0 隔离的远线程注入和 APC 注入。

全局钩子注入

Windows 中大部分应用是基于 Windows 的消息机制,Windows提供截获这些消息的钩子函数。

根据钩子作用的不同范围,钩子可以被分为全局和局部钩子。局部钩子是针对某个线程的,全局钩子是只要有使用消息机制的应用。接下来我们主要来看下利用SetWindowsHookEx实现全局钩子。

SetWindowsHookEx函数介绍

HHOOK SetWindowsHookEx(int       idHook, // 要安装的钩子程序的类型  HOOKPROC  lpfn, // 指向钩子函数的指针  HINSTANCE hmod, // 指向钩子过程的DLL的句柄  DWORD     dwThreadId // 与钩子进程关联的线程标识符);// 具体详细介绍可以查阅msdn文档,里面有更为详细的介绍

实现过程

首先我们需要创建一个windowsHookDll DLL,这个DLL在安装全局钩子后只要系统中的其他进程接收到可以发出钩子的消息,这个DLL就会被加载到此进程的地址空间中。这样便实现了Dll注入。

具体实现代码如下:

#include "Hook.h"#include 

extern HMODULE g_hDllModule;// 共享内存#pragma data_seg("mydata") HHOOK g_hHook = NULL;#pragma data_seg()#pragma comment(linker, "/SECTION:mydata,RWS")

// 钩子回调函数LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam){ return ::CallNextHookEx(g_hHook, code, wParam, lParam);}

// 注册钩子BOOL SetHook(){ g_hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hDllModule, 0); //使用WH_GETMESSAGE 确保DLL能够注入所有的进程 if (g_hHook) {  return TRUE; } return FALSE;}

// 卸载钩子BOOL UnSetHook(){ if (g_hHook) {  UnhookWindowsHookEx(g_hHook); } return TRUE;}

试验结果

新建TestHook.exe程序,首先加载windowsHookDll.dll导出函数SetHook并调用。在注册成功后用ProcessExplorer.exe察看explorer.exe中的加载情况。

windowsHookDll成功被explorer.exe加载

如果想要卸载可调用函数UnSetHook,执行完成后再观察explorer.exe是否加载了windowsHookDll.dll。

远线程注入

远线程注入是指一个进程在另一个进程中创建线程的技术。主要是利用LoadLibrary在所有进程空间中的地址是一样,进程在另一个进程中创建线程时传入LoadLibrary的地址和我们要注入的DLL的路径,这样在另一个进程中就能通过LoadLibray加载DLL到进程空间中。

说起来简单,但是实现起来却有几大难点需要注意的。

  • 如何将DLL路径传给另一个进程,因为进程之间是相互隔离的,所以我们要在我们要注入的进程中申请内存。
  • 需要对打开的进程进行提权,这就要求我们的注入程序要有管理员以上的权限。

函数介绍

HANDLE OpenProcess(  DWORD dwDesiredAccess, // 进程访问权限  BOOL  bInheritHandle, // 子进程是否继承此句柄  DWORD dwProcessId // 要打开的进程id);

// 分配指定进程的内存LPVOID VirtualAllocEx(  HANDLE hProcess, // 进程句柄  LPVOID lpAddress, // 要分配内存起始地址,为NULL则自动分配  SIZE_T dwSize, // 要分配的内存大小  DWORD  flAllocationType,// 内存分配类型  DWORD  flProtect // 内存保护(读写));

// 写入内存数据BOOL WriteProcessMemory(  HANDLE  hProcess, // 进程句柄  LPVOID  lpBaseAddress, // 目标进程缓冲区地址  LPCVOID lpBuffer, // 要写入数据的地址  SIZE_T  nSize, // 数据大小  SIZE_T  *lpNumberOfBytesWritten // 写入数据的返回大小, 为NULL则忽略此参数);

// 在另一个进程中创建线程HANDLE CreateRemoteThread(  HANDLE                 hProcess, // 进程句柄  LPSECURITY_ATTRIBUTES  lpThreadAttributes, // 线程安全描述符  SIZE_T                 dwStackSize, // 堆栈的初始大小  LPTHREAD_START_ROUTINE lpStartAddress, // 函数地址(存在于另一个进程中)  LPVOID                 lpParameter, // 函数的参数  DWORD                  dwCreationFlags, // 线程创建标志  LPDWORD                lpThreadId // 线程标识符);

// 详细信息可参阅msdn

代码实现

/*dwProcessId: 目标进程的pid**pszDllFileName: 要注入DLL的路径*/BOOL CreateRemoteThreadInjectDll(DWORD dwProcessId, char *pszDllFileName){ HANDLE hProcess = NULL; SIZE_T dwSize = 0; LPVOID pDllAddr = NULL; FARPROC pFuncProcAddr = NULL;

 // 打开注入进程,获取进程句柄 hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); if (NULL == hProcess) {  EP_ShowError("OpenProcess");  return FALSE; } // 在注入进程中申请内存 dwSize = 1 + ::lstrlenA(pszDllFileName); pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE); if (NULL == pDllAddr) {  EP_ShowError("VirtualAllocEx");  return FALSE; } // 向申请的内存中写入数据 if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL)) {  EP_ShowError("WriteProcessMemory");  return FALSE; } // 获取LoadLibraryA函数地址 pFuncProcAddr = ::GetProcAddress(::GetModuleHandleA("kernel32.dll"), "LoadLibraryA"); if (NULL == pFuncProcAddr) {  EP_ShowError("GetProcAddress_LoadLibraryA");  return FALSE; } // 使用 CreateRemoteThread 创建远线程, 实现 DLL 注入 HANDLE hRemoteThread = ::CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, NULL); if (NULL == hRemoteThread) {  EP_ShowError("CreateRemoteThread");  return FALSE; } // 关闭句柄 ::CloseHandle(hProcess);

 return TRUE;}

试验结果

注入成功后效果图

在explorer.exe中RemoteThreadDll.dll被正确加载

远线程注册进阶 突破SESSION 0隔离

在系统普通进程中可以使用远线程注入DLL,但如果要注入系统服务则不行,因为有SESSION 0隔离。想要突破隔离需要使用ZwCreateThreadEx函数。这也是和远线程注入的区别。

ZwCreateThreadEx 是未公开的函数,在ntdll.dll中,它的函数声明如下:

#ifdef _WIN64 typedef DWORD(WINAPI *typedef_ZwCreateThreadEx)(  PHANDLE ThreadHandle,  ACCESS_MASK DesiredAccess,  LPVOID ObjectAttributes,  HANDLE ProcessHandle,  LPTHREAD_START_ROUTINE lpStartAddress,  LPVOID lpParameter,  ULONG CreateThreadFlags,  SIZE_T ZeroBits,  SIZE_T StackSize,  SIZE_T MaximumStackSize,  LPVOID pUnkown);#else typedef DWORD(WINAPI *typedef_ZwCreateThreadEx)(  PHANDLE ThreadHandle,  ACCESS_MASK DesiredAccess,  LPVOID ObjectAttributes,  HANDLE ProcessHandle,  LPTHREAD_START_ROUTINE lpStartAddress,  LPVOID lpParameter,  BOOL CreateSuspended,  DWORD dwStackSize,  DWORD dw1,  DWORD dw2,  LPVOID pUnkown);#endif

具体实现代码:

// 使用 ZwCreateThreadEx 实现远线程注入BOOL ZwCreateThreadExInjectDll(DWORD dwProcessId, char *pszDllFileName){ HANDLE hProcess = NULL; SIZE_T dwSize = 0; LPVOID pDllAddr = NULL; FARPROC pFuncProcAddr = NULL; HANDLE hRemoteThread = NULL; DWORD dwStatus = 0;

 // 打开注入进程,获取进程句柄 hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); if (NULL == hProcess) {  EP_ShowError("OpenProcess");  return FALSE; } // 在注入进程中申请内存 dwSize = 1 + ::lstrlenA(pszDllFileName); pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE); if (NULL == pDllAddr) {  EP_ShowError("VirtualAllocEx");  return FALSE; } // 向申请的内存中写入数据 if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL)) {  EP_ShowError("WriteProcessMemory");  return FALSE; } // 加载 ntdll.dll HMODULE hNtdllDll = ::LoadLibraryA("ntdll.dll"); if (NULL == hNtdllDll) {  EP_ShowError("LoadLirbary");  return FALSE; } // 获取LoadLibraryA函数地址 pFuncProcAddr = ::GetProcAddress(::GetModuleHandleA("Kernel32.dll"), "LoadLibraryA"); if (NULL == pFuncProcAddr) {  EP_ShowError("GetProcAddress_LoadLibraryA");  return FALSE; } // 获取ZwCreateThread函数地址#ifdef _WIN64 typedef DWORD(WINAPI *typedef_ZwCreateThreadEx)(  PHANDLE ThreadHandle,  ACCESS_MASK DesiredAccess,  LPVOID ObjectAttributes,  HANDLE ProcessHandle,  LPTHREAD_START_ROUTINE lpStartAddress,  LPVOID lpParameter,  ULONG CreateThreadFlags,  SIZE_T ZeroBits,  SIZE_T StackSize,  SIZE_T MaximumStackSize,  LPVOID pUnkown);#else typedef DWORD(WINAPI *typedef_ZwCreateThreadEx)(  PHANDLE ThreadHandle,  ACCESS_MASK DesiredAccess,  LPVOID ObjectAttributes,  HANDLE ProcessHandle,  LPTHREAD_START_ROUTINE lpStartAddress,  LPVOID lpParameter,  BOOL CreateSuspended,  DWORD dwStackSize,  DWORD dw1,  DWORD dw2,  LPVOID pUnkown);#endif typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdllDll, "ZwCreateThreadEx"); if (NULL == ZwCreateThreadEx) {  EP_ShowError("GetProcAddress_ZwCreateThread");  return FALSE; } // 使用 ZwCreateThreadEx 创建远线程, 实现 DLL 注入 dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, 0, 0, 0, NULL); if (NULL == hRemoteThread) {  EP_ShowError("ZwCreateThreadEx");  return FALSE; } // 关闭句柄 ::CloseHandle(hProcess); ::FreeLibrary(hNtdllDll);

 return TRUE;}

试验结果

同远线程注入一致,只是在服务程序中不能弹窗。

APC注入

APC(Asynchronous Procedure Call)为异步过程调用,APC注入是指利用线程本身的APC队列进行DLL注入。

函数介绍

// 将异步调用函数添加到指定线程的APC队列中DWORD QueueUserAPC(  PAPCFUNC  pfnAPC, // 函数指针  HANDLE    hThread, // 线程句柄  ULONG_PTR dwData // 函数参数);

// 详细信息可参阅msdn

实现原理

APC队列中的函数需要等待线程挂起时才会被执行,所以要保证我们注入的程序能被执行需要将我们的函数插入到进程的所有线程中。具体代码实现如下:

// apc.h#include #include 

// 根据进程名称获取PIDDWORD GetProcessIdByProcessName(char *pszProcessName);

// 根据PID获取所有的相应线程IDBOOL GetAllThreadIdByProcessId(DWORD dwProcessId, DWORD **ppThreadId, DWORD *dwThreadIdLength);

// APC注入BOOL ApcInjectDll(char *pszProcessName, char *pszDllName);

//apc.cpp#include "APC.h"

void ShowError(char *pszText){ char szErr[MAX_PATH] = { 0 }; ::wsprintf(szErr, "%s Error[%d]\n", pszText); ::MessageBox(NULL, szErr, "ERROR", MB_OK | MB_ICONERROR);}

// 根据进程名称获取PIDDWORD GetProcessIdByProcessName(char *pszProcessName){ DWORD dwProcessId = 0; PROCESSENTRY32 pe32 = { 0 }; HANDLE hSnapshot = NULL; BOOL bRet = FALSE; ::RtlZeroMemory(&pe32, sizeof(pe32)); pe32.dwSize = sizeof(pe32);

 // 获取进程快照 hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (NULL == hSnapshot) {  ShowError("CreateToolhelp32Snapshot");  return dwProcessId; }

 // 获取第一条进程快照信息 bRet = ::Process32First(hSnapshot, &pe32); while (bRet) {  // 获取快照信息  if (0 == ::lstrcmpi(pe32.szExeFile, pszProcessName))  {   dwProcessId = pe32.th32ProcessID;   break;  }

  // 遍历下一个进程快照信息  bRet = ::Process32Next(hSnapshot, &pe32); }

 return dwProcessId;}

// 根据PID获取所有的相应线程IDBOOL GetAllThreadIdByProcessId(DWORD dwProcessId, DWORD **ppThreadId, DWORD *pdwThreadIdLength){ DWORD *pThreadId = NULL; DWORD dwThreadIdLength = 0; DWORD dwBufferLength = 1000; THREADENTRY32 te32 = { 0 }; HANDLE hSnapshot = NULL; BOOL bRet = TRUE;

 do {  // 申请内存  pThreadId = new DWORD[dwBufferLength];  if (NULL == pThreadId)  {   ShowError("new");   bRet = FALSE;   break;  }  ::RtlZeroMemory(pThreadId, (dwBufferLength * sizeof(DWORD)));

  // 获取线程快照  ::RtlZeroMemory(&te32, sizeof(te32));  te32.dwSize = sizeof(te32);  hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);  if (NULL == hSnapshot)  {   ShowError("CreateToolhelp32Snapshot");   bRet = FALSE;   break;  }

  // 获取第一条线程快照信息  bRet = ::Thread32First(hSnapshot, &te32);  while (bRet)  {   // 获取进程对应的线程ID   if (te32.th32OwnerProcessID == dwProcessId)   {    pThreadId[dwThreadIdLength] = te32.th32ThreadID;    dwThreadIdLength++;   }

   // 遍历下一个线程快照信息   bRet = ::Thread32Next(hSnapshot, &te32);  }

  // 返回  *ppThreadId = pThreadId;  *pdwThreadIdLength = dwThreadIdLength;  bRet = TRUE;

 } while (FALSE);

 if (FALSE == bRet) {  if (pThreadId)  {   delete[]pThreadId;   pThreadId = NULL;  } }

 return bRet;}

// APC注入BOOL ApcInjectDll(char *pszProcessName, char *pszDllName){ BOOL bRet = FALSE; DWORD dwProcessId = 0; DWORD *pThreadId = NULL; DWORD dwThreadIdLength = 0; HANDLE hProcess = NULL, hThread = NULL; PVOID pBaseAddress = NULL; PVOID pLoadLibraryAFunc = NULL; SIZE_T dwRet = 0, dwDllPathLen = 1 + ::lstrlen(pszDllName); DWORD i = 0;

 do {  // 根据进程名称获取PID  dwProcessId = GetProcessIdByProcessName(pszProcessName);  if (0 >= dwProcessId)  {   bRet = FALSE;   break;  }

  // 根据PID获取所有的相应线程ID  bRet = GetAllThreadIdByProcessId(dwProcessId, &pThreadId, &dwThreadIdLength);  if (FALSE == bRet)  {   bRet = FALSE;   break;  }

  // 打开注入进程  hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);  if (NULL == hProcess)  {   ShowError("OpenProcess");   bRet = FALSE;   break;  }

  // 在注入进程空间申请内存  pBaseAddress = ::VirtualAllocEx(hProcess, NULL, dwDllPathLen, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);  if (NULL == pBaseAddress)  {   ShowError("VirtualAllocEx");   bRet = FALSE;   break;  }  // 向申请的空间中写入DLL路径数据   ::WriteProcessMemory(hProcess, pBaseAddress, pszDllName, dwDllPathLen, &dwRet);  if (dwRet != dwDllPathLen)  {   ShowError("WriteProcessMemory");   bRet = FALSE;   break;  }

  // 获取 LoadLibrary 地址  pLoadLibraryAFunc = ::GetProcAddress(::GetModuleHandle("kernel32.dll"), "LoadLibraryA");  if (NULL == pLoadLibraryAFunc)  {   ShowError("GetProcessAddress");   bRet = FALSE;   break;  }

  // 遍历线程, 插入APC  i = dwThreadIdLength;  for (; i > 0; --i)  {   // 打开线程   hThread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, pThreadId[i]);   if (hThread)   {    // 插入APC    ::QueueUserAPC((PAPCFUNC)pLoadLibraryAFunc, hThread, (ULONG_PTR)pBaseAddress);    // 关闭线程句柄    ::CloseHandle(hThread);    hThread = NULL;   }  }

  bRet = TRUE;

 } while (FALSE);

 // 释放内存 if (hProcess) {  ::CloseHandle(hProcess);  hProcess = NULL; } if (pThreadId) {  delete[]pThreadId;  pThreadId = NULL; }

 return bRet;}

总结

Windows注入技术可以方便我们对目标进程作修改,但是也可能使目标进程崩溃,在使用的时候需要小心谨慎。

长按图片自动识别~~~关注我哦~~~~

- END -

redistemplate注入为null_Windows DLL 注入技术相关推荐

  1. DELPHI 键盘HOOK,DLL注入,带窗口DLL注入及释放

    -----------DLL的创建退出过程---------- 新建的DLL内 procedure DllEnterProc(reason:integer); begin case reason of ...

  2. [web安全]深入理解反射式dll注入技术

    一.前言 dll注入技术是让某个进程主动加载指定的dll的技术.恶意软件为了提高隐蔽性,通常会使用dll注入技术将自身的恶意代码以dll的形式注入高可信进程. 常规的dll注入技术使用LoadLibr ...

  3. 深入理解反射式dll注入技术

    前言 dll 注入技术是让某个进程主动加载指定的 dll 的技术.恶意软件为了提高隐蔽性,通常会使用 dll 注入技术将自身的恶意代码以 dll 的形式注入高可信进程. 常规的 dll 注入技术使用 ...

  4. 实现HOOK其他进程的Messagebox(2) DLL注入工具

    DLL注入工具(远程线程技术和简单的MFC CListCtrl控件知识). DLL文件已经编写好.测试程序也很简单.现在就是解决将DLL注入到目标进程中.. 这里采用远程线程注入技术..本来WIN32 ...

  5. 【逆向】【Part 3】DLL注入

    目录 一.通过自制调试器来理解其原理 1.调试器的工作原理 实现反汇编功能(重点) 重点分析exception_debug_event 重点:1.对调试器程序增加异常处理操作功能,核心API, CON ...

  6. DLL注入的8种姿势

    目录 1.远程线程注入 2.APC注入 3.注册表注入 4.ComRes注入 5.劫持进程创建注入 6.输入法注入 7.消息钩子注入 8.依赖可信任进程注入 一.DLL注入--远程线程注入 https ...

  7. bcb dll返回字符_⑩的游戏修改小课堂4——HOOK、DLL注入与游戏乱码修正

    本文采用 CC-BY-NC 协议进行授权. 本文的涉及代码已上传到GITHUB~ https://github.com/rumia-san/hook_finale​github.com 因为是以< ...

  8. 实战dll注入(原理, 踩坑及排雷)

    摘要 使用vs2019编写注入器程序, 在生成的注入器可用前, 踩了不少坑, 因此记录一下. 本文涉及三种恶意代码注入方法: 直接dll注入, 反射式dll注入, 镂空注入. 之所以选这三种注入方法, ...

  9. 系统安全攻防战:DLL注入技术详解

    DLL注入是一种允许攻击者在另一个进程的地址空间的上下文中运行任意代码的技术.攻击者使用DLL注入的过程中如果被赋予过多的运行特权,那么攻击者就很有可能会在DLL文件中嵌入自己的恶意攻击代码以获取更高 ...

最新文章

  1. 图灵奖得主Bengio又出新论文:用强化学习提升模型泛化性!网友崩溃:idea撞车了......
  2. linux安装node js的二进制文件安装方式的注意事项
  3. U盘安装LINUX系统,拔除U盘后无法引导系统
  4. 《 FRIDA系列文章 》
  5. python接口测试_测试大牛都会的接口测试+Python测开+Linux技能+Git命令合集!
  6. 学习python的好处
  7. php 正则匹配 文件,php – 正则表达式匹配.htaccess中的一系列文件类型
  8. Android框架之AsyncHttpClient
  9. Win10中小娜无法搜索本地应用
  10. SCCM2012SP1---资产管理和远程管理
  11. arcgis server 无法识别字体原因
  12. 3.注册后台处理逻辑编写
  13. LeetCode 176 第二高的薪水
  14. 洛谷 3455 (莫比乌斯反演优化)
  15. pyqt5背景色设置使用css或者是调色板
  16. c# 转换Image为Icon
  17. python利用公式计算e的值
  18. 基于HDP版本的YDB安装部署(转)
  19. [实变函数]5.3 非负可测函数的 Lebesgue 积分
  20. 易语言大漠游戏辅助设计一键登录界面

热门文章

  1. smartdns使用指南_Windows10 玩SmartDNS告别污染
  2. android html图片点击事件,Android TextView加载HTMl图文之添加点击事件和查看图片
  3. win7计算机用户名在哪找,win7 c盘里找不到users,用户里也没有C:#92;User...-win7电脑c盘USERS文件夹在哪...
  4. php mysql having_having的用法
  5. oracle数据包对交换机要求,Exalogic Ethernet交换机配置的备份与还原
  6. mac mysql 初始密码_mac下mysql安装后修改默认密码
  7. python安装多少位_python安装流程
  8. mysql实验学生表_数据库实验(学生信息表)
  9. php临时目录没有文件夹里,PHP上传 找不到临时文件夹的解决方法
  10. solr5.3.1 mysql_Solr5.3.1 dataimport 导入mysql数据