Dll注入:X86/X64 遠程線程CreateRemoteThread 注入


遠線程注入原理是利用Windows 系統中CreateRemoteThread()這個API,其中第4個參數是准備運行的線程,我們可以將LoadLibrary()填入其中,這樣就可以執行遠程進程中的LoadLibrary()函數,進而將我們自己准備的DLL加載到遠程進程空間中執行。

函數原型:

HANDLE
WINAPI
CreateRemoteThread(                                  
    _In_ HANDLE hProcess,                              //遠程線程的句柄
    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, //安全屬性  
    _In_ SIZE_T dwStackSize,                           //棧大小
    _In_ LPTHREAD_START_ROUTINE lpStartAddress,        //進程處理函數
    _In_opt_ LPVOID lpParameter,                       //進程參數
    _In_ DWORD dwCreationFlags,                        //默認創建后的狀態
    _Out_opt_ LPDWORD lpThreadId                       //所創建的線程的ID
    );

 

注入過程:

1.提權

2.根據進程ID打開對方進程OpenProcess,得到進程句柄

3.根據進程句柄在目標進程中申請內存VirtualAllocEx

4.在目標進程中剛剛申請的內存空間中寫入所需參數(Dll的完整路徑)WriteProcessMemory

5.用GetProcAddress得到LoadLibraryW的模塊加載地址

6.啟動遠程線程CreateRemoteThread,並在第四參數傳入該線程需要執行的函數名(即loadlibrary)

BOOL  InjectDllByRemoteThread(ULONG32 ulProcessID, WCHAR* wzDllFullPath)
{
    HANDLE  ProcessHandle = NULL;
    ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ulProcessID);

    if (ProcessHandle==NULL)
    {
        return FALSE;
    }

    WCHAR* VirtualAddress = NULL;

    ULONG32 ulDllLength = (ULONG32)_tcslen(wzDllFullPath) + 1;

    VirtualAddress = (WCHAR*)VirtualAllocEx(ProcessHandle, NULL,
                                            ulDllLength * sizeof(WCHAR),
                                            MEM_COMMIT, PAGE_READWRITE);


    if (VirtualAddress==NULL)
    {
        CloseHandle(ProcessHandle);
        return FALSE;
    }

    // 在目標進程的內存空間中寫入所需參數(模塊名)
    if (!WriteProcessMemory(ProcessHandle, VirtualAddress, (LPVOID)wzDllFullPath, ulDllLength * sizeof(WCHAR), NULL))
    {
        VirtualFreeEx(ProcessHandle, VirtualAddress, ulDllLength, MEM_DECOMMIT);
        CloseHandle(ProcessHandle);
        return FALSE;
    }


    LPTHREAD_START_ROUTINE FunctionAddress = NULL;

    FunctionAddress = (PTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle(_T("Kernel32")), "LoadLibraryW");

    HANDLE ThreadHandle = INVALID_HANDLE_VALUE;
    //啟動遠程線程
    ThreadHandle = ::CreateRemoteThread(ProcessHandle, NULL, 0, FunctionAddress, VirtualAddress, 0, NULL);
    if (ThreadHandle==FALSE)
    {
        VirtualFreeEx(ProcessHandle, VirtualAddress, ulDllLength, MEM_DECOMMIT);
        CloseHandle(ProcessHandle);
        return FALSE;
    }

    // 等待遠程線程結束
    WaitForSingleObject(ThreadHandle, INFINITE);
    // 清理
    VirtualFreeEx(ProcessHandle, VirtualAddress, ulDllLength, MEM_DECOMMIT);
    CloseHandle(ThreadHandle);
    CloseHandle(ProcessHandle);
    return TRUE;
}
BOOL EnableDebugPrivilege()
{

    HANDLE TokenHandle = NULL;
    TOKEN_PRIVILEGES TokenPrivilege;
    LUID uID;

    //打開權限令牌
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle))  
    {
        return FALSE;
    }

    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &uID))
    {

        CloseHandle(TokenHandle);
        TokenHandle = INVALID_HANDLE_VALUE;
        return FALSE;
    }

    TokenPrivilege.PrivilegeCount = 1;
    TokenPrivilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    TokenPrivilege.Privileges[0].Luid = uID;

    //在這里我們進行調整權限
    if (!AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivilege, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
    {
        CloseHandle(TokenHandle);
        TokenHandle = INVALID_HANDLE_VALUE;
        return  FALSE;
    }

    CloseHandle(TokenHandle);
    TokenHandle = INVALID_HANDLE_VALUE;
    return TRUE;
}

但是如此有個問題,如果目標進程為32位,但是注入了一個64的Dll則會出問題。

為此我們應當判斷目標進程的位數。

我們可以通過解析exe文件(magic數)判斷進程是x64還是x86。

根據PE知識,所有的PE文件必須以一個DOS MZ header開始,其實它是一個IMAGE_DOS_HEADER類型的結構,

//DOS頭
typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
    WORD   e_magic;                     // Magic number   ‘MZ’
    WORD   e_cblp;                      // Bytes on last page of file
    WORD   e_cp;                        // Pages in file
    WORD   e_crlc;                      // Relocations
    WORD   e_cparhdr;                   // Size of header in paragraphs
    WORD   e_minalloc;                  // Minimum extra paragraphs needed
    WORD   e_maxalloc;                  // Maximum extra paragraphs needed
    WORD   e_ss;                        // Initial (relative) SS value
    WORD   e_sp;                        // Initial SP value
    WORD   e_csum;                      // Checksum
    WORD   e_ip;                        // Initial IP value
    WORD   e_cs;                        // Initial (relative) CS value
    WORD   e_lfarlc;                    // File address of relocation table
    WORD   e_ovno;                      // Overlay number
    WORD   e_res[4];                    // Reserved words
    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                   // OEM information; e_oemid specific
    WORD   e_res2[10];                  // Reserved words
    LONG   e_lfanew;                    // File address of new exe header   NT頭偏移
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

這是PE文件的大致結構,而我們需要的magic成員在_IMAGE_NT_HEADERS結構體中的選項頭中_IMAGE_OPTIONAL_HEADER

_IMAGE_DOS_HEADER結構體的最后一個元素e_lfanew 便是指向_IMAGE_NT_HEADERS的偏移

//NT頭
typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;                             //‘PE’
    IMAGE_FILE_HEADER FileHeader;            //PE文件頭
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;  //PE選項頭
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
//選項頭
typedef struct _IMAGE_OPTIONAL_HEADER {
    WORD    Magic;                     //我們需要的成員
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;
    DWORD   SizeOfUninitializedData;
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;
    DWORD   ImageBase;
    DWORD   SectionAlignment;
    DWORD   FileAlignment;
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;
    DWORD   SizeOfHeaders;
    DWORD   CheckSum;
    WORD    Subsystem;
    WORD    DllCharacteristics;
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    DWORD   SizeOfHeapReserve;
    DWORD   SizeOfHeapCommit;
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

所以判斷目標進程的代碼為:

enum TargetType
{
    WOW_86,
    WOW_64,
    WOW_ERROR
};


//通過解析exe文件(magic數)判斷進程是x64還是x86
TargetType GetWowByReadFile(ULONG32  ulProcessID)
{
    HANDLE  ProcessHandle = INVALID_HANDLE_VALUE;
    ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ulProcessID);

    if (ProcessHandle == NULL)
    {
        return WOW_ERROR;
    }
    //獲得Exe模塊基地址
    ULONG64 ulModuleBaseAddress = (ULONG64)GetModuleBaseAddressByProcessHandle(ProcessHandle);

    if (ulModuleBaseAddress == NULL)
    {
        CloseHandle(ProcessHandle);
        return WOW_ERROR;
    }

    IMAGE_DOS_HEADER   DosHeader = { 0 };
    //讀取Dos頭
    if (ReadProcessMemory(ProcessHandle, (PVOID)ulModuleBaseAddress, &DosHeader, sizeof(IMAGE_DOS_HEADER), NULL) == FALSE)
    {
        CloseHandle(ProcessHandle);
        return WOW_ERROR;
    }

    WORD  wMagic = 0;
    //模塊加載基地址+Dos頭部e_lfanew成員(PE頭相對於文件的偏移 4字節)+標准PE頭+4字節
    if (ReadProcessMemory(ProcessHandle, (PVOID)(ulModuleBaseAddress + DosHeader.e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER)), &wMagic, sizeof(WORD), NULL) == FALSE)
    {
        CloseHandle(ProcessHandle);
        return WOW_ERROR;
    }

    CloseHandle(ProcessHandle);
    if (wMagic == 0x20b)//x64
    {
        return WOW_64;
    }
    else if (wMagic == 0x10b)//x86
    {
        return WOW_86;
    }
    else
    {
        return WOW_ERROR;
    }
}

 

結合這個功能,主函數為:

int main()
{ 
    if (EnableDebugPrivilege() == FALSE)
    {
        return 0;
    }
    ULONG32 ulProcessID = 0;
    printf("Input A ProcessID to Inject:\r\n");
    scanf_s("%d", &ulProcessID, sizeof(ULONG32));

    DWORD iOk = GetWowByReadFile(ulProcessID);
    switch (iOk)
    {
    case WOW_64:
        if (InjectDllByRemoteThread(ulProcessID, L"InjectDll.dll"))
        {
            printf("Inject Success!\r\n");
            break;
        }
    case WOW_86:
        if (InjectDllByRemoteThread(ulProcessID, L"InjectTest32.dll"))
        {
            printf("Inject Success!\r\n");
            break;
        }
    default:
        break;
    }
    return 0;
}

 


免責聲明!

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



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