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 <tchar.h>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中的加载情况。

如果想要卸载可调用函数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;
}

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

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

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

#ifdef _WIN64typedef 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);
#elsetypedef 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.dllHMODULE 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 _WIN64typedef 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);
#elsetypedef 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);
#endiftypedef_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 <Windows.h>
#include <TlHelp32.h>// 根据进程名称获取PID
DWORD GetProcessIdByProcessName(char *pszProcessName);// 根据PID获取所有的相应线程ID
BOOL 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);
}// 根据进程名称获取PID
DWORD 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获取所有的相应线程ID
BOOL 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){// 获取进程对应的线程IDif (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{// 根据进程名称获取PIDdwProcessId = GetProcessIdByProcessName(pszProcessName);if (0 >= dwProcessId){bRet = FALSE;break;}// 根据PID获取所有的相应线程IDbRet = 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;}// 遍历线程, 插入APCi = 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注入技术可以方便我们对目标进程作修改,但是也可能使目标进程崩溃,在使用的时候需要小心谨慎。

Windows DLL 注入技术相关推荐

  1. redistemplate注入为null_Windows DLL 注入技术

    Windows DLL 注入技术 本文主要介绍四种常见的 Windows DLL 注入技术. 分别为全局钩子.远线程钩子.突破 SESSION 0 隔离的远线程注入和 APC 注入. 全局钩子注入 W ...

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

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

  3. Dll注入技术之劫持注入

    Dll注入技术之劫持注入 测试环境 系统:Windows 7 32bit 工具:FileCleaner2.0 和 lpk.dll 主要思路 利用Window可以先加载当前目录下的dll特性,仿造系统的 ...

  4. DLL 注入技术的 N 种姿势

    DLL 注入技术的 N 种姿势 本文中我将介绍DLL注入的相关知识.不算太糟的是,DLL注入技术可以被正常软件用来添加/扩展其他程序,调试或逆向工程的功能性:该技术也常被恶意软件以多种方式利用.这意味 ...

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

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

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

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

  7. Windows Dll注入与API HOOK

    DLL注入: 1.  使用注册表注入dll HKEY_LOCAL_MACHINE\Software\Microsoft\WindowsNT\CurrentVersion\Windows\AppInit ...

  8. Windows 下的 7 种 DLL 劫持技术

    本文讲的是Windows 下的 7 种 DLL 劫持技术,在本文中,我将列出半打可以在Windows运行用户模式的进程中使用DLL注入技术.也许可能会有更多类似的技术,但我正在和你分享的是我所拥有的第 ...

  9. 2020-11-23(Windows系统的dll注入 )

    一.什么是dll注入 在Windows操作系统中,运行的每一个进程都生活在自己的程序空间中(保护模式),每一个进程都认为自己拥有整个机器的控制权,每个进程都认为自己拥有计算机的整个内存空间,这些假象都 ...

  10. Windows Dll Injection、Process Injection、API Hook、DLL后门/恶意程序入侵技术

    catalogue 1. 引言 2. 使用注册表注入DLL 3. 使用Windows挂钩来注入DLL 4. 使用远程线程来注入DLL 5. 使用木马DLL来注入DLL 6. 把DLL作为调试器来注入 ...

最新文章

  1. ISA系列之ISA Server 2004 中的新增功能--利用多个网络
  2. 调试css的新装备--IETester
  3. 【黑客免杀攻防】读书笔记6 - PE文件知识在免杀中的应用
  4. java基础篇---网络编程(IP与URL)
  5. python读取windows系统的文件后print乱码问题的解决
  6. HDU 6581 Vacation
  7. PHP中||与or的区别
  8. 基于密集卷积神经网络的多类植物病害检测与分类
  9. python网站开发实例 flask_Python Web开发之——构建基于Flask框架的web后端项目
  10. 金蝶KIS专业版13.0视频教程
  11. Spyder使用教程
  12. MacBook Pro(13 英寸,2011 年末)A1278 官方最高支持macOS High Sierra,使用macOS Catalina Patcher成功安装macOS Catalina
  13. 【网络协议】IPV4协议介绍
  14. 提出辞职可以用的五个理由推荐给大家
  15. 形式化验证工具——prism(1-安装)
  16. 有什么好玩的网页小游戏网站推荐么?
  17. 宝塔面板linux账号密码忘记,宝塔面板忘记账号或密码的解决办法
  18. 【镜像取证篇】常见镜像文件类型
  19. 控制面板打印机显示不出来的解决办法
  20. 【uni-app】解决iPhone X “刘海屏”兼容性问题

热门文章

  1. python win32 窗口截图
  2. 阿里fastjson 对象转JSON
  3. java 中文车牌识别_EasyPR-Java开源中文车牌识别系统工程部署
  4. 锌离子荧光探针Zinquin 乙酯
  5. 离散数学编程输出主析取范式(二进制排列转十进制输出)
  6. 西门子plc cpu228 4路模拟量输入 2路模拟量输出
  7. SQL优化案例-自定义函数索引(五)
  8. ubuntu+i3wm桌面
  9. android 获取机顶盒ip,在电视机顶盒端查看IP地址 - 零成本让你的电视盒子变身无线路由器...
  10. Grad-CAM在语义分割中的pytorch实现