Windows DLL 注入技术
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 注入技术的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python项目方案书模板格式_项目计划
- 下一篇: 斐讯路由刷华硕固件后指示灯颜色修改