遠執行緒注入
遠執行緒(RemoteThread)注入是指一個程序在另一個程序中建立執行緒的技術,這是一種很經典的DLL注入技術。
雖然比較古老,但是很實用。透過遠執行緒注入,再配合api函式的hook技術,可以實現很多有意思的功能。
實現遠執行緒注入的關鍵函式
OpenProcess
開啟現有的本地程序,函式宣告如下:
1 WINAPI 2 OpenProcess( 3 _In_ DWORD dwDesiredAccess, 4 _In_ BOOL bInheritHandle, 5 _In_ DWORD dwProcessId 6 );
引數:
dwDesiredAccess:
訪問程序物件。此訪問許可權為針對程序的安全描述符進行檢查,此引數可以是一個或多個程序訪問許可權。如果呼叫該函式的程序啟用了SeDebugPrivilege許可權,則無論安全描述符的內容是什麼,它都會授予所請求的訪問許可權。
bInheritHandle:
若此值為TRUE,則此程序建立的程序將繼承該控制代碼。否則,程序不會繼承此控制代碼。
dwProcessId:
要開啟的本地程序的PID
返回值:
成功,返回程序的控制代碼
失敗,返回NULL,要獲取錯誤資訊,呼叫GetLastError
VirtualAllocEx
在指定程序的虛擬地址空間內保留、提交或更改記憶體的狀態。宣告如下:
1 WINAPI 2 VirtualAllocEx( 3 _In_ HANDLE hProcess, 4 _In_opt_ LPVOID lpAddress, 5 _In_ SIZE_T dwSize, 6 _In_ DWORD flAllocationType, 7 _In_ DWORD flProtect 8 );
hProcess
程序的控制代碼。VirtualAllocEx函式在該程序的虛擬地址空間內分配記憶體,控制代碼必有具有PROCESS_VM_OPERATION許可權。
lpAddress
指定要分配頁面所需起始地址的指標。如果lpAddress為NULL,則該函式自動分配記憶體。
dwSize
要分配的記憶體大小,以位元組為單位。
flAllocationType
記憶體分配型別。此引數必須為以下值之一
此引數還可以按指示指定以下值。
flProtect
要分配的頁面區域的記憶體保護。如果頁面已提交,則可以指定任何一個如下記憶體保護常量。
常量/值 | 說明 |
---|---|
|
啟用對頁面已提交區域的執行訪問。 嘗試寫入已提交區域會導致訪問衝突。 CreateFileMapping 函式不支援此標誌。 |
|
啟用對頁面已提交區域的執行或只讀訪問。 嘗試寫入已提交區域會導致訪問衝突。 Windows Server 2003 和 Windows XP: 在 Windows XP SP2 和 Windows Server 2003 SP1 之前, CreateFileMapping 函式不支援此屬性。 |
|
啟用對已提交頁面區域的執行、只讀或讀/寫訪問許可權。 Windows Server 2003 和 Windows XP: 在 Windows XP SP2 和 Windows Server 2003 SP1 之前, CreateFileMapping 函式不支援此屬性。 |
|
啟用對檔案對映物件的對映檢視的執行、只讀或寫入時複製訪問許可權。 嘗試寫入已提交的寫入時複製頁會導致為程序建立頁面的專用副本。 專用頁面標記為 PAGE_EXECUTE_READWRITE,更改將寫入新頁面。 VirtualAlloc 或 VirtualAllocEx 函式不支援此標誌。 Windows Vista、Windows Server 2003 和 Windows XP: 在具有 SP1 和 Windows Server 2008 的 Windows Vista 之前, CreateFileMapping 函式不支援此屬性。 |
|
禁用對已提交頁面區域的所有訪問。 嘗試從提交區域讀取、寫入或執行區域會導致訪問衝突。 CreateFileMapping 函式不支援此標誌。 |
|
啟用對已提交頁面區域的只讀訪問。 嘗試寫入已提交區域會導致訪問衝突。 如果啟用了 資料執行防護 ,則嘗試在已提交的區域中執行程式碼會導致訪問衝突。 |
|
啟用對已提交頁面區域的只讀或讀/寫訪問。 如果啟用了 資料執行保護 ,則嘗試在提交的區域中執行程式碼會導致訪問衝突。 |
|
啟用對檔案對映物件的對映檢視的只讀或寫入時複製訪問許可權。 嘗試寫入已提交的寫入時複製頁會導致為程序建立頁面的專用副本。 專用頁面標記為 PAGE_READWRITE,更改將寫入新頁面。 如果啟用了 資料執行保護 ,則嘗試在提交的區域中執行程式碼會導致訪問衝突。 VirtualAlloc 或 VirtualAllocEx 函式不支援此標誌。 |
|
將頁面中的所有位置設定為 CFG 的無效目標。 與任何執行頁保護(如 PAGE_EXECUTE、 PAGE_EXECUTE_READ、 PAGE_EXECUTE_READWRITE 和 PAGE_EXECUTE_WRITECOPY)一起使用。 對這些頁面中的位置的任何間接呼叫都將失敗 CFG 檢查,並且程序將終止。 分配的可執行頁面的預設行為是標記為 CFG 的有效呼叫目標。 VirtualProtect 或 CreateFileMapping 函式不支援此標誌。 |
|
當 VirtualProtect 的保護髮生更改時,區域中的頁面將不會更新其 CFG 資訊。 例如,如果區域中的頁面是使用 PAGE_TARGETS_INVALID 分配的,則在頁面保護更改時將保留無效資訊。 僅當保護更改為可執行型別(如 PAGE_EXECUTE、PAGE_EXECUTE_READ、PAGE_EXECUTE_READWRITE 和 PAGE_EXECUTE_WRITECOPY)時,此標誌才有效。 VirtualProtect 保護更改為可執行檔案的預設行為是將所有位置標記為 CFG 的有效呼叫目標。 |
如果lpAddress指定了一個地址,則flProtect不能是以下值之一:
PAGE_NOACCESS
PAGE_GUARD
PAGE_NOCACHE
PAGE_WRITECOMBINE
返回值:
成功,返回分配頁面的基址
失敗,返回NULL,要獲得更多的錯誤資訊,請呼叫 GetLastError。
WriteProcessMemory
在指定的程序中將資料寫入記憶體區域,要寫入的整個區域必須可訪問,否則操作失敗。函式宣告如下:
1 BOOL 2 WINAPI 3 WriteProcessMemory( 4 _In_ HANDLE hProcess, 5 _In_ LPVOID lpBaseAddress, 6 _In_reads_bytes_(nSize) LPCVOID lpBuffer, 7 _In_ SIZE_T nSize, 8 _Out_opt_ SIZE_T* lpNumberOfBytesWritten 9 );
hProcess
要修改的程序記憶體的控制代碼,控制代碼必有具有 PROCESS_VM_WRITE和PROCESS_VM_OPERATION訪問許可權
lpBaseAddress
指向指定程序中寫入資料的基地址指標。在資料傳輸發生之前,系統會驗證指定大小的基地址和記憶體中的所有資料是否可以進行寫入訪問,如果不可以訪問,則該函式將失敗。
lpBuffer
指向緩衝區的指標,其中包含要寫入指定程序的地址空間中的資料。
nSize
要寫入指定程序的位元組數。
lpNumberOfBytesWritten
指向變數的指標,該變數接收傳輸到指定程序的位元組數。如果lpNumberOfBytes Written為NULL,則忽略該引數。
返回值:
成功,返回值不為0,
失敗,返回值為0
CreateRemoteThread
在另一個程序的虛擬地址空間中建立執行的執行緒。宣告如下:
1 HANDLE 2 WINAPI 3 CreateRemoteThread( 4 _In_ HANDLE hProcess, 5 _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, 6 _In_ SIZE_T dwStackSize, 7 _In_ LPTHREAD_START_ROUTINE lpStartAddress, 8 _In_opt_ LPVOID lpParameter, 9 _In_ DWORD dwCreationFlags, 10 _Out_opt_ LPDWORD lpThreadId 11 );
引數說明:
hProcess
要建立執行緒的程序的控制代碼。控制代碼必須具有
PROCESS_CREATE_THREAD、
PROCESS_QUERY_INFORMATION、
PROCESS_VM_OPERATION、
PROCESS_VM_WRITE
PROCESS_VM_READ
訪問許可權
lpThreadAttributes
指向SECURITY_ATTRIBUTES結構的指標,該結構指定新執行緒的安全描述符,並確定子程序是否可以繼承返回的控制代碼。如果lpThreadAttributes為NULL,則執行緒將獲得預設的安全描述符,並且不能繼承該控制代碼。
dwStackSize
堆疊的初始大小,以位元組為單位。如果此引數為o,則新執行緒使用可執行檔案的預設大小。
lpStartAddress
指向由執行緒執﹐行型別為LPTHREAD_START_ROUTINE的應用程式定義的函式指標,並表示遠端程序中執行緒的起始地址,該函式必須存在於遠端程序中。
lpParameter
指向要傳遞給執行緒函式的變數的指標。
dwCreationFlags
控制執行緒建立的標誌。若是0,則表示執行緒在建立後立即執行。
lpThreadId
指向接收執行緒識別符號的變數的指標。如果此引數為NULL,則不返回執行緒識別符號。
返回值:
成功,返回新執行緒的控制代碼,
失敗,返回NULL,如果要獲取錯誤資訊,請呼叫GetLastError
遠執行緒注入實現原理
程式在載入DLL時,通常呼叫LoadLibrary函式來實現DLL的動態載入。LoadLibrary函式的宣告如下:
1 HMODULE WINAPI LoadLibrary(LPCTSTR lpFileName);
LoadLibrary函式只有一個引數,傳遞的是要載入的DLL路徑字串。
而CreateRemoteThread需要傳遞的是目標程序空間中的多執行緒函式地址,以及多執行緒引數。
如果程式能夠獲取目標程序LoadLibrary函式的地址,而且還能夠獲取目標程序空間中某個DLL路徑字串的地址,那麼,可將LoadLibrary函式的地址作為多執行緒函式的地址,某個DLL路徑字串作為多執行緒函式的引數,並傳遞給CreateRemoteThread函式在目標程序空間中建立一個多執行緒,這樣能不能成功呢?答案是可以的。這樣就可以在目標程序空間中建立一個多執行緒,這個多執行緒就是LoadLibrary函式載入DLL。
遠執行緒注入的原理就大致清晰了。那麼要想實現遠執行緒注入DLL,還需要解決以下兩個問題:
1、是目標程序空間中LoadLibrary函式的地址是多少
2、是如何向目標程序空間中寫入DLL路徑字串函式