15.1 DLL注入
目前公開的DLL注入技巧共有以下幾種:
- 1、注入表注入
- 2、ComRes注入
- 3、APC注入
- 4、消息鈎子注入
- 5、遠線程注入
- 6、依賴可信進程注入
- 7、劫持進程創建注入
- 8、輸入法注入
遠程線程注入的方式在於使用一個Windows API函數CreateRemoteThread,通過它可以在另外一個進程中注入一個線程並執行。
實驗環境
操作系統:win10_64位
被注入程序:系統自帶notepad_32位
工具整理
Procexp : 查看進程模塊是否被注入成功的工具
編程思路
- 1 在目標進程中申請一個空間
- 2 把dll的路徑寫入到對方的進程空間中
- 3 創建一個遠程線程,讓目標進程調用LoadLibrary
- 4 釋放申請的虛擬內存空間
使用的API:
FindWindow
查找處理窗口的類名和窗口名稱匹配指定的字符串
WINUSERAPI
HWND
WINAPI
FindWindow(
//參數指向類名
_In_opt_ LPCSTR lpClassName,
//指向窗口名
_In_opt_ LPCSTR lpWindowName);
WINUSERAPI
- lpClassName
指向一個以NULL字符結尾的、用來指定類名的字符串或一個可以確定類名字符串的原子。如果這個參數是一個原子,那么它必須是一個在調用此函數前已經通過GlobalAddAtom函數創建好的全局原子。這個原子(一個16bit的值),必須被放置在lpClassName的低位字節中,lpClassName的高位字節置零。
如果該參數為null時,將會尋找任何與lpWindowName參數匹配的窗口。
- lpWindowName
指向一個以NULL字符結尾的、用來指定窗口名(即窗口標題)的字符串。如果此參數為NULL,則匹配所有窗口名。
返回值
如果函數執行成功,則返回值是擁有指定窗口類名或窗口名的窗口的句柄。
如果函數執行失敗,則返回值為 NULL。可以通過調用GetLastError函數獲得更加詳細的錯誤信息。
GetWindowThreadProcessId
找出某個進程的PID值。
WINUSERAPI
DWORD
WINAPI
GetWindowThreadProcessId(
//窗口的句柄
_In_ HWND hWnd,
//進程號的存放地址(變量地址)
_Out_opt_ LPDWORD lpdwProcessId);
返回值
返回線程號,注意,lpdwProcessId 是存放進程號的變量。
DWORD dwPID, dwTID;
dwTID = GetWindowThreadProcessId( hWnd, &dwPID );
VirtualAllocEx
可以在其他進程分配或者預定一塊虛擬內存
WINBASEAPI
_Ret_maybenull_ _Post_writable_byte_size_(dwSize)
LPVOID
WINAPI
VirtualAllocEx(
//申請內存所在的進程句柄。
_In_ HANDLE hProcess,
//保留頁面的內存地址;一般用NULL自動分配 。
_In_opt_ LPVOID lpAddress,
//欲分配的內存大小,字節單位;
//注意實際分配的內存大小是頁內存大小的整數倍
_In_ SIZE_T dwSize,
//指定內存分配的方式,預定還是要提交
_In_ DWORD flAllocationType,
//指定應用程序讀寫的權限,內存的保護屬性
_In_ DWORD flProtect
);
flAllocationType 可取下列值:
-
MEM_COMMIT:為特定的頁面區域分配內存中或磁盤的頁面文件中的物理存儲
-
MEM_PHYSICAL :分配物理內存(僅用於地址窗口擴展內存)
-
MEM_RESERVE:保留進程的虛擬地址空間,而不分配任何物理存儲。保留頁面可通過繼續調用VirtualAlloc()而被占用
-
MEM_RESET :指明在內存中由參數lpAddress和dwSize指定的數據無效
-
MEM_TOP_DOWN:在盡可能高的地址上分配內存(Windows 98忽略此標志)
-
MEM_WRITE_WATCH:必須與MEM_RESERVE一起指定,使系統跟蹤那些被寫入分配區域的頁面(僅針對Windows 98)
flProtect可取下列值:
-
PAGE_READONLY: 該區域為只讀。如果應用程序試圖訪問區域中的頁的時候,將會被拒絕訪
-
PAGE_READWRITE 區域可被應用程序讀寫
-
PAGE_EXECUTE: 區域包含可被系統執行的代碼。試圖讀寫該區域的操作將被拒絕。
-
PAGE_EXECUTE_READ :區域包含可執行代碼,應用程序可以讀該區域。
-
PAGE_EXECUTE_READWRITE: 區域包含可執行代碼,應用程序可以讀寫該區域。
-
PAGE_GUARD: 區域第一次被訪問時進入一個STATUS_GUARD_PAGE異常,這個標志要和其他保護標志合並使用,表明區域被第一次訪問的權限
-
PAGE_NOACCESS: 任何訪問該區域的操作將被拒絕
-
PAGE_NOCACHE: RAM中的頁映射到該區域時將不會被微處理器緩存(cached)
注:PAGE_GUARD和PAGE_NOCHACHE標志可以和其他標志合並使用以進一步指定頁的特征。PAGE_GUARD標志指定了一個防護頁(guard page),即當一個頁被提交時會因第一次被訪問而產生一個one-shot異常,接着取得指定的訪問權限。PAGE_NOCACHE防止當它映射到虛擬頁的時候被微處理器緩存。這個標志方便設備驅動使用直接內存訪問方式(DMA)來共享內存塊。
返回值:
執行成功就返回分配內存的首地址,不成功就是NULL。
WriteProcessMemory
此函數能寫入某一進程的內存區域。
WINBASEAPI
_Success_(return != FALSE)
BOOL
WINAPI
WriteProcessMemory(
//由OpenProcess返回的進程句柄。
_In_ HANDLE hProcess,
//要寫的內存首地址
_In_ LPVOID lpBaseAddress,
//指向要寫的數據的指針。
_In_reads_bytes_(nSize) LPCVOID lpBuffer,
//要寫入的字節數。
_In_ SIZE_T nSize,
//可選參數,指向變量的指針接收的字節數轉移到指定的過程
_Out_opt_ SIZE_T * lpNumberOfBytesWritten
);
返回值
非零值代表成功。
可用GetLastError獲取更多的錯誤詳細信息。
CreateRemoteThread
可以創建一個在其它進程地址空間中運行的線程。
WINBASEAPI
_Ret_maybenull_
HANDLE
WINAPI
CreateRemoteThread(
//線程所屬進程的進程句柄.
_In_ HANDLE hProcess,
//一個指向 SECURITY_ATTRIBUTES 結構的指針, 該結指定了線程的安全屬性.
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
//線程初始大小,以字節為單位,如果該值設為0,那么使用系統默認大小.
_In_ SIZE_T dwStackSize,
//在遠程進程的地址空間中,該線程的線程函數的起始地址.
_In_ LPTHREAD_START_ROUTINE lpStartAddress,
//傳給線程函數的參數.
_In_opt_ LPVOID lpParameter,
線程的創建標志.
_In_ DWORD dwCreationFlags,
//指向所創建線程ID的指針,如果創建失敗,該參數為NULL.
_Out_opt_ LPDWORD lpThreadId
);
返回值
如果調用成功,返回新線程句柄.
如果失敗,返回NULL.
WaitForSingleObject
Windows API函數。當等待仍在掛起狀態時,句柄被關閉。
WINBASEAPI
DWORD
WINAPI
WaitForSingleObject(
_In_ HANDLE hHandle,
_In_ DWORD dwMilliseconds
);
- hHandle
對象句柄。可以指定一系列的對象,如Event、Job、Memory resource notification、Mutex、Process、Semaphore、Thread、Waitable timer等。
- dwMilliseconds
定時時間間隔,單位為milliseconds(毫秒).如果指定一個非零值,函數處於等待狀態直到hHandle標記的對象被觸發,或者時間到了。如果dwMilliseconds為0,對象沒有被觸發信號,函數不會進入一個等待狀態,它總是立即返回。如果dwMilliseconds為INFINITE,對象被觸發信號后,函數才會返回。
返回值
執行成功,返回值指示出引發函數返回的事件.
VirtualFreeEx
VirtualFreeEx即為目標進程的句柄,可在其它進程中釋放申請的虛擬內存空間。
WINBASEAPI
BOOL
WINAPI
VirtualFreeEx(
//目標進程的句柄。該句柄必須擁有 PROCESS_VM_OPERATION 權限。
_In_ HANDLE hProcess,
//指向要釋放的虛擬內存空間首地址的指針。
_Pre_notnull_ _When_(dwFreeType == MEM_DECOMMIT, _Post_invalid_) _When_(dwFreeType == MEM_RELEASE, _Post_ptr_invalid_) LPVOID lpAddress,
//虛擬內存空間的字節數。
_In_ SIZE_T dwSize,
//釋放類型
_In_ DWORD dwFreeType
);
- dwFreeType
釋放類型,取值見下表:
-
- MEM_DECOMMIT
表示釋放后內存空間不可用,但是內存頁還存在
-
- MEM_RELEASE
表示內存被釋放,內存頁完全回收
遠程線程注入源碼:
#include "stdafx.h"
#include <Windows.h>
#define path _T("E:\\09核心編程\\_1304~1\\win原理下1\\Debug\\test.dll")
bool Inject(DWORD dwId,WCHAR* szPath)
{
//1 在目標進程中申請一個空間
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwId);
LPVOID pRemoteAddress = VirtualAllocEx(
hProcess,
NULL,
1,
MEM_COMMIT,
PAGE_READWRITE
);
//2 把dll的路徑寫入到對方的進程空間中
DWORD dwWriteSize = 0;
//寫一段數據到指定進程所開辟的內存空間
WriteProcessMemory(hProcess, pRemoteAddress, szPath, wcslen(szPath) * 2 + 2, &dwWriteSize);
//3 創建一個遠程線程,讓目標進程調用LoadLibrary
HANDLE hThread = CreateRemoteThread(
hProcess,
NULL,
0,
(LPTHREAD_START_ROUTINE)LoadLibrary,
pRemoteAddress,
NULL,
NULL
);
WaitForSingleObject(hThread, -1);
//4 釋放申請的虛擬內存空間
VirtualFreeEx(hProcess, pRemoteAddress, 1, MEM_DECOMMIT);
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
DWORD dwId = 0;
printf("請輸入一個ID:");
HWND hCalc = FindWindow(NULL, L"FileCleaner2.0");
DWORD dwPid = 0;
DWORD dwRub = GetWindowThreadProcessId(hCalc, &dwPid);
//選擇自己輸出PID或者自動獲取
// scanf_s("%d", &dwId);
Inject(dwPid, path);
return 0;
}
DLL源碼
#include "stdafx.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"呵呵", L"成功了,ye", NULL);
}
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
注入成果效果圖:
注入后的DLL查看: