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

15.1 DLL注入

目前公開的DLL注入技巧共有如下幾種:編程

  • 一、注入表注入
  • 二、ComRes注入
  • 三、APC注入
  • 四、消息鉤子注入
  • 五、遠線程注入
  • 六、依賴可信進程注入
  • 七、劫持進程建立注入
  • 八、輸入法注入

遠程線程注入的方式在於使用一個Windows API函數CreateRemoteThread,經過它能夠在另一個進程中注入一個線程並執行。api

實驗環境

操做系統: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

能夠在其餘進程分配或者預約一塊虛擬內存code

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

相關文章
相關標籤/搜索