CreateRemoteThread和RtlCreateUserThread進程創建之后注入DLL(簡單的注入器)


1. CreateRemoteThread

本文參考前輩的文章https://www.cnblogs.com/wf751620780/,對原理有了很大了解

首先CreateRemoteThread函數的原型如下,它很像CreateThread函數。不同的是,前者是遠程創建線程,后者是在自己的進程下創建線程。我們用前者來注入DLL:

HANDLE WINAPI CreateRemoteThread(
  _In_  HANDLE                 hProcess,
  _In_  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  _In_  SIZE_T                 dwStackSize,
  _In_  LPTHREAD_START_ROUTINE lpStartAddress,
  _In_  LPVOID                 lpParameter,
  _In_  DWORD                  dwCreationFlags,
  _Out_ LPDWORD                lpThreadId
);

兩個函數的區別就是第一個句柄行參,hProcess表示創建的新線程屬於哪一個進程。

 

注入的過程如下:

(1).用VirtualAllocEx函數在目標進程的地址空間中分配一塊足夠大的內存用於保存被注入的dll的路徑。
(2).用WriteProcessMemory函數把本進程中保存dll路徑的內存中的數據拷貝到第(1)步得到的目標進程的內存中。
(3).用GetProcAddress函數獲得LoadLibraryW函數的起始地址。LoadLibraryW函數位於Kernel32.dll中。
(4).用CreateRemoteThread函數讓目標進程執行LoadLibraryW來加載被注入的dll。函數結束將返回載入dll后的模塊句柄。
(5).用VirtualFreeEx釋放第(1)步開辟的內存。
在需要卸載dll時我們可以在上述第(5)步的基礎上繼續執行以下步驟:
(6).用GetProcAddress函數獲得FreeLibrary函數的起始地址。FreeLibrary函數位於Kernel32.dll中。
(7).用CreateRemoteThread函數讓目標進程執行FreeLibrary來卸載被注入的dll。(其參數是第(4)步返回的模塊句柄)。
如果不在上述步驟基礎上執行操作,卸載dll時你需要這么做:
(1).獲得被注入的dll在目標進程的模塊句柄。
(2).重復上述步驟的第(6)、(7)兩步。

 

需要明確幾點,首先用的調用dll函數是LoadLibraryW,因為不管是LoadLibrary還是LoadLibraryA,翻譯到底層用的都是寬字符函數LoadLibraryW。第二使用GetProcAddress獲得LoadLibraryW函數起始位置。

第三,DLL的絕對路徑需要放在用WriteProcessMemory寫到目標程序的內存空間下面,而分配這個內存空間使用的函數是VirtualAllocEx

這兩個函數原型是:

LPVOID WINAPI VirtualAllocEx(
  _In_     HANDLE hProcess,
  _In_opt_ LPVOID lpAddress,
  _In_     SIZE_T dwSize,
  _In_     DWORD  flAllocationType,
  _In_     DWORD  flProtect
);

BOOL WINAPI WriteProcessMemory(
  _In_  HANDLE  hProcess,
  _In_  LPVOID  lpBaseAddress,
  _In_  LPCVOID lpBuffer,
  _In_  SIZE_T  nSize,
  _Out_ SIZE_T  *lpNumberOfBytesWritten
);

 

運行環境win10+vs2019。代碼如下:

#include "windows.h"
#include "stdio.h"
#include "tlhelp32.h"
#include "io.h"
#include "tchar.h"

//判斷某模塊(dll)是否在相應的進程中
//dwPID         進程的PID
//szDllPath     查詢的dll的完整路徑
BOOL CheckDllInProcess(DWORD dwPID, LPCTSTR szDllPath)
{
    BOOL                    bMore = FALSE;
    HANDLE                  hSnapshot = INVALID_HANDLE_VALUE;
    MODULEENTRY32           me = { sizeof(me), };

    if (INVALID_HANDLE_VALUE ==
        (hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID)))//獲得進程的快照
    {
        _tprintf(L"CheckDllInProcess() : CreateToolhelp32Snapshot(%d) failed!!! [%d]\n",
            dwPID, GetLastError());
        return FALSE;
    }
    bMore = Module32First(hSnapshot, &me);//遍歷進程內得的所有模塊
    for (; bMore; bMore = Module32Next(hSnapshot, &me))
    {
        if (!_tcsicmp(me.szModule, szDllPath) || !_tcsicmp(me.szExePath, szDllPath))//模塊名或含路徑的名相符
        {
            CloseHandle(hSnapshot);
            return TRUE;
        }
    }
    CloseHandle(hSnapshot);
    return FALSE;
}

//向指定的進程注入相應的模塊
//dwPID         目標進程的PID
//szDllPath     被注入的dll的完整路徑
BOOL InjectDll(DWORD dwPID, LPCTSTR szDllPath)
{
    HANDLE                  hProcess = NULL;//保存目標進程的句柄
    LPVOID                  pRemoteBuf = NULL;//目標進程開辟的內存的起始地址
    DWORD                   dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR);//開辟的內存的大小
    LPTHREAD_START_ROUTINE  pThreadProc = NULL;//loadLibreayW函數的起始地址
    HMODULE                 hMod = NULL;//kernel32.dll模塊的句柄
    BOOL                    bRet = FALSE;
    if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))//打開目標進程,獲得句柄
    {
        _tprintf(L"目標進程打開失敗  OpenProcess(%d)!!! [%d]\n",
            dwPID, GetLastError());
        goto INJECTDLL_EXIT;
    }
    pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize,
        MEM_COMMIT, PAGE_READWRITE);//在目標進程空間開辟一塊內存
    if (pRemoteBuf == NULL)
    {
        _tprintf(L"分配空間失敗VirtualAllocEx() failed!!! [%d]\n",
            GetLastError());
        goto INJECTDLL_EXIT;
    }
    if (!WriteProcessMemory(hProcess, pRemoteBuf,
        (LPVOID)szDllPath, dwBufSize, NULL))//向開辟的內存復制dll的路徑
    {
        _tprintf(L"向目標空間復制路徑失敗 WriteProcessMemory() failed!!! [%d]\n",
            GetLastError());
        goto INJECTDLL_EXIT;
    }
    hMod = GetModuleHandle(L"kernel32.dll");//獲得本進程kernel32.dll的模塊句柄
    if (hMod == NULL)
    {
        _tprintf(L"InjectDll() : GetModuleHandle(\"kernel32.dll\") failed!!! [%d]\n",
            GetLastError());
        goto INJECTDLL_EXIT;
    }
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");//獲得LoadLibraryW函數的起始地址
    if (pThreadProc == NULL)
    {
        _tprintf(L"InjectDll() : GetProcAddress(\"LoadLibraryW\") failed!!! [%d]\n",
            GetLastError());
        goto INJECTDLL_EXIT;
    }
    if (!CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL))//執行遠程線程
    {
        _tprintf(L"InjectDll() : MyCreateRemoteThread() failed!!!\n");
        goto INJECTDLL_EXIT;
    }
INJECTDLL_EXIT:
    bRet = CheckDllInProcess(dwPID, szDllPath);//確認結果
    if (pRemoteBuf) {
        MessageBox(NULL,L"注入成功",L"注入成功",NULL);
        VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE);
    }
    if (hProcess)
        CloseHandle(hProcess);
    return bRet;
}

//讓指定的進程卸載相應的模塊
//dwPID         目標進程的PID
//szDllPath     被注入的dll的完整路徑,注意:路徑不要用“/”來代替“\\”
BOOL EjectDll(DWORD dwPID, LPCTSTR szDllPath)
{
    BOOL                    bMore = FALSE, bFound = FALSE, bRet = FALSE;
    HANDLE                  hSnapshot = INVALID_HANDLE_VALUE;
    HANDLE                  hProcess = NULL;
    MODULEENTRY32           me = { sizeof(me), };
    LPTHREAD_START_ROUTINE  pThreadProc = NULL;
    HMODULE                 hMod = NULL;
    TCHAR                   szProcName[MAX_PATH] = { 0, };
    if (INVALID_HANDLE_VALUE == (hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID)))
    {
        _tprintf(L"EjectDll() : CreateToolhelp32Snapshot(%d) failed!!! [%d]\n",
            dwPID, GetLastError());
        goto EJECTDLL_EXIT;
    }
    bMore = Module32First(hSnapshot, &me);
    for (; bMore; bMore = Module32Next(hSnapshot, &me))//查找模塊句柄
    {
        if (!_tcsicmp(me.szModule, szDllPath) ||
            !_tcsicmp(me.szExePath, szDllPath))
        {
            bFound = TRUE;
            break;
        }
    }
    if (!bFound)
    {
        _tprintf(L"EjectDll() : There is not %s module in process(%d) memory!!!\n",
            szDllPath, dwPID);
        goto EJECTDLL_EXIT;
    }
    if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))
    {
        _tprintf(L"EjectDll() : OpenProcess(%d) failed!!! [%d]\n",
            dwPID, GetLastError());
        goto EJECTDLL_EXIT;
    }
    hMod = GetModuleHandle(L"kernel32.dll");
    if (hMod == NULL)
    {
        _tprintf(L"EjectDll() : GetModuleHandle(\"kernel32.dll\") failed!!! [%d]\n",
            GetLastError());
        goto EJECTDLL_EXIT;
    }
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "FreeLibrary");
    if (pThreadProc == NULL)
    {
        _tprintf(L"EjectDll() : GetProcAddress(\"FreeLibrary\") failed!!! [%d]\n",
            GetLastError());
        goto EJECTDLL_EXIT;
    }
    if (!CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL))
    {
        _tprintf(L"EjectDll() : MyCreateRemoteThread() failed!!!\n");
        goto EJECTDLL_EXIT;
    }
    bRet = TRUE;
EJECTDLL_EXIT:
    if (hProcess)
        CloseHandle(hProcess);
    if (hSnapshot != INVALID_HANDLE_VALUE)
        CloseHandle(hSnapshot);
    return bRet;
}

int main()
{
    InjectDll(19220, L"S:\\panny_dll.dll");
    EjectDll(19220, L"S:\\panny_dll.dll");
    return 0;
}

 

我們啟動一個demo並獲得它的pid:

 

然后獲得我們需要注入的DLL的絕對路徑,開始攻擊:

用的查詢注入是否成功的方法是用進程快照搜索進程空間是否有目標dll的方式,同反調試技術,運行就會彈出注入成功框:

 

 

同時Process Explore可以看到目標dll:

 

 2. RtlCreateUserThread

 不同於前面直接調用win api,  RtlCreateUserThread是CreateRemoteThread的底層實現,所以使用RtlCreateUserThread的原理是和使用CreateRemoteThread的原理是一樣的,這個函數可以實現跨會話創建線程。唯一的問題就是:當我們在Vista 之前的操作系統調用此函數所創建的線程並沒有通知給csrss 進程,它沒有完整的初始化,在調用一些網絡或者其它的系統函數的時候他可能返回失敗,而通過CreateRemoteThread 創建的線程沒有任何問題,在Vista+的操作系統上調用此函數也沒有問題。因此我們需要判斷系統版本,當目標系統為Vista-的時候,我們應該通過RtlCreateUserThread創建一個掛起的線程,然后調用CsrClientCallServer通知csrss 這個線程的創建,然后csrss 做相應的記錄及初始化工作之后,我們再恢復線程的執行。最后,我們新創建的線程必須自己調用ExitThread退出,否則也會產生崩潰的情況。

 

LoadLibraryW函數調用原型:
typedef DWORD(WINAPI* pRtlCreateUserThread)(    //函數原型
    IN HANDLE                     ProcessHandle,
    IN PSECURITY_DESCRIPTOR     SecurityDescriptor,
    IN BOOL                     CreateSuspended,
    IN ULONG                    StackZeroBits,
    IN OUT PULONG                StackReserved,
    IN OUT PULONG                StackCommit,
    IN LPVOID                    StartAddress,
    IN LPVOID                    StartParameter,
    OUT HANDLE                     ThreadHandle,
    OUT LPVOID                    ClientID
    );

 

LoadLibraryW

函數shellcode:

 BYTE StaticShellCode[31] = { 0xE8,0,0,0,0,  // call (5字節)           4
        0x5D,           // pop ebp              5
        0x8B,0xC5,  // mov eax,ebp          7
        0x83,0xC0,0x1A, // add eax,1a       10
        0x50,       // push eax             11
        0xB8,0,0,0,0,   // mov eax,LoadLibraryW 16
        0xFF,0xD0,  // call eax             18
        0x6A,0,     // push 0               20
        0xB8,0,0,0,0,   // mov eax,ExitThread   25
        0xFF,0xD0,  // call eax             27
        0xC3,   // ret 4                28
        0};

 

 

代碼如下:

(整體邏輯一樣,就不詳細備注)

#include <Windows.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <iostream>
#include <string>
DWORD findPidByName(const char* pname) //獲得pid函數,對上面的查找進行改進
{
    HANDLE h;
    PROCESSENTRY32 procSnapshot;
    h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    procSnapshot.dwSize = sizeof(PROCESSENTRY32);

    do
    {
        if (!_stricmp(procSnapshot.szExeFile, pname))
        {
            DWORD pid = procSnapshot.th32ProcessID;
            CloseHandle(h);
#ifdef _DEBUG
            printf(("[+]進程地址: %ld\n"), pid);
#endif
            return pid;
        }
    } while (Process32Next(h, &procSnapshot));

    CloseHandle(h);
    return 0;
}

typedef DWORD(WINAPI* pRtlCreateUserThread)(    //函數申明
    IN HANDLE                     ProcessHandle,
    IN PSECURITY_DESCRIPTOR     SecurityDescriptor,
    IN BOOL                     CreateSuspended,
    IN ULONG                    StackZeroBits,
    IN OUT PULONG                StackReserved,
    IN OUT PULONG                StackCommit,
    IN LPVOID                    StartAddress,
    IN LPVOID                    StartParameter,
    OUT HANDLE                     ThreadHandle,
    OUT LPVOID                    ClientID
    );

DWORD RtlCreateUserThread(LPCSTR pszLibFile, DWORD dwProcessId)
{
    pRtlCreateUserThread RtlCreateUserThread = NULL;
    HANDLE  hRemoteThread = NULL;

    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); //打開目標進程pid
    if (hProcess == NULL)
    {
        printf("[-] Error: Could not open process for PID (%d).\n", dwProcessId);
        exit(1);
    }

    LPVOID LoadLibraryAddress = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");  //load函數地址
    if (LoadLibraryAddress == NULL)
    {
        printf(("[-] Error: Could not find LoadLibraryA function inside kernel32.dll library.\n"));
        exit(1);
    }

    RtlCreateUserThread = (pRtlCreateUserThread)GetProcAddress(GetModuleHandle(("ntdll.dll")), ("RtlCreateUserThread"));//獲取Rtl函數地址
    if (RtlCreateUserThread == NULL)
    {
        exit(1);
    }

#ifdef _DEBUG
    printf(("[+]RtlCreateUserThread函數地址: 0x%08x\n"), (UINT)RtlCreateUserThread);
    printf(("[+]LoadLibraryA函數地址: 0x%08x\n"), (UINT)LoadLibraryAddress);
#endif

    DWORD dwSize = (strlen(pszLibFile) + 1) * sizeof(char);

    LPVOID lpBaseAddress = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);//開辟一段地址
    if (lpBaseAddress == NULL)
    {
        printf(("[-] Error: Could not allocate memory inside PID (%d).\n"), dwProcessId);
        exit(1);
    }

    BOOL bStatus = WriteProcessMemory(hProcess, lpBaseAddress, pszLibFile, dwSize, NULL);  //目標dll寫入
    if (bStatus == 0)
    {
        printf(("[-] Error: Could not write any bytes into the PID (%d) address space.\n"), dwProcessId);
        return(1);
    }

    bStatus = (BOOL)RtlCreateUserThread(
        hProcess,
        NULL,
        0,
        0,
        0,
        0,
        LoadLibraryAddress,  //shellcode
        lpBaseAddress,
        &hRemoteThread,
        NULL);
    if (bStatus < 0)
    {
        printf(("[-]注入失敗\n"));
        return(1);
    }
    else
    {
        printf(("[+]注入成功...\n"));
        WaitForSingleObject(hRemoteThread, INFINITE);

        CloseHandle(hProcess);
        VirtualFreeEx(hProcess, lpBaseAddress, dwSize, MEM_RELEASE);
        return(0);
    }

    return(0);
}
int main()
{
    const char* name = ("菜單dome.exe");

    DWORD pId = findPidByName(name);
    LPCSTR location = ("S:\\panny_dll.dll");

    RtlCreateUserThread(location, pId);
}

還是運行菜單dome.exe,:

 

 

 

 (其實變藍就已經在提示有dll注入了)

 

 

 

 

 


免責聲明!

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



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