【windows核心編程】遠程線程DLL注入


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;
}

注入成果效果圖:

image

注入后的DLL查看:

image


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM