1. 程式人生 > 其它 >高階遠端執行緒注入NtCreateThreadEx

高階遠端執行緒注入NtCreateThreadEx

目錄

高階遠端執行緒注入NtCreateThreadEx

一丶簡介

在Windows下NtCreateThreadExCreateRemoteThread的底層函式。RtlCreateUserThread 也是對 NtCreateThreadEx的一層包裝

所以著重一下研究NtCreateThreadEx函式

二丶原型

2.1 函式原型

NtCreateThreadEx在32位下和64位下函式原型不一致。

結構如下:

#ifdef _AMD64_
typedef DWORD(WINAPI* PfnZwCreateThreadEx)(
    PHANDLE ThreadHandle,
    ACCESS_MASK DesiredAccess,
    LPVOID ObjectAttributes,
    HANDLE ProcessHandle,
    LPTHREAD_START_ROUTINE lpStartAddress,
    LPVOID lpParameter,
    ULONG CreateThreadFlags,
    SIZE_T ZeroBits,
    SIZE_T StackSize,
    SIZE_T MaximunStackSize,
    LPVOID pUnkown);


#else

typedef DWORD(WINAPI *PfnZwCreateThreadEx)(
    PHANDLE ThreadHandle,
    ACCESS_MASK DesiredAccess,
    LPVOID ObjectAttributes,
    HANDLE ProcessHandle,
    LPTHREAD_START_ROUTINE lpStartAddress,
    LPVOID lpParameter,
    BOOL CreateThreadFlags,
    DWORD  ZeroBits,
    DWORD  StackSize,
    DWORD  MaximumStackSize,
    LPVOID pUnkown);

#endif // DEBUG

如果要想使用 NtCreateThreadEx函式。那麼就需要從NtDll中以動態的方式匯出使用。

2.2 遠端執行緒注入程式碼

遠端執行緒程式碼注入分為如下幾個步驟

  • OpenProcess 開啟要注入的程序
  • VirtualAllocEx 在被注入的程序中申請讀寫記憶體
  • WriteProcessMemory 寫入DLL路徑到申請的記憶體中
  • VirtualProtectEx 修改記憶體保護屬性,這一步可以不需要使用。
  • CreateRemoteThread 建立遠端執行緒,高階遠端執行緒注入可以 將此函式 替換為 NtCreateThreadEx
  • WaitForSingleObject 等待過程完成

完整虛擬碼如下:

BOOLEAN RemoteInject(DWORD pid, LPWSTR wszInjectDllPathName,ULONG uDllPathSize)
{
    HANDLE hProc = NULL;
    LPVOID lpBuffer = NULL;
    SIZE_T dwWriteBytes = 0;
    DWORD dwRetErrorCode = 0;
    HANDLE hThreadHandle = NULL;;
    PVOID pfnLoadLibraryW = NULL;
    HMODULE ntdll = NULL;
    HMODULE k32 = NULL;
    bool bIsOk = FALSE;

    do {
        ntdll = LoadLibrary(TEXT("ntdll.dll"));
        if (ntdll == NULL)
        {
            break;
        }
        k32 = LoadLibrary(TEXT("kernel32.dll"));
        if (k32 == NULL)
        {
            break;
        }
        m_ZwCreateThreadEx = reinterpret_cast<PfnZwCreateThreadEx>(GetProcAddress(ntdll, "ZwCreateThreadEx"));
        if (NULL == m_ZwCreateThreadEx)
        {
            break;
        }
        pfnLoadLibraryW = reinterpret_cast<PVOID>(::GetProcAddress(k32, "LoadLibraryW"));
        if (pfnLoadLibraryW == NULL)
        {
            break;
        }
        hProc = OpenProcess(PROCESS_ALL_ACCESS, false, pid);
        if (hProc == NULL)
        {
            break;
        }
        lpBuffer = VirtualAllocEx(hProc, 0, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
        if (NULL == lpBuffer)
        {
            break;
        }
        dwRetErrorCode = WriteProcessMemory(hProc, lpBuffer, wszInjectDllPathName, uDllPathSize, &dwWriteBytes);
        if (0 == dwRetErrorCode)
        {
            break;
        }

        m_ZwCreateThreadEx(&hThreadHandle, PROCESS_ALL_ACCESS, NULL, hProc, (LPTHREAD_START_ROUTINE)pfnLoadLibraryW, lpBuffer, 0, 0, 0, 0, NULL);
        WaitForSingleObject(hProc, 2000);
        if (NULL == hThreadHandle)
        {
            break;
        }
        bIsOk = TRUE;
    } while (FALSE);

    if (NULL != lpBuffer)
    {
        VirtualFreeEx(hProc, lpBuffer, 0, MEM_RELEASE);
        lpBuffer = NULL;
    }
    if (NULL != hProc)
    {
        CloseHandle(hProc);
        hProc = NULL;
    }
    if (NULL != hThreadHandle)
    {
        CloseHandle(hThreadHandle);
        hThreadHandle = NULL;
    }
    if (k32 != NULL)
    {
        FreeLibrary(k32);
        k32 = NULL;
    }
    if (ntdll != NULL)
    {
        FreeLibrary(ntdll);
        ntdll = NULL;
    }
    return bIsOk;
}

注意: uDllPathSize 是DLL全路徑的空間長度。 如果是寬字元一定要 wcslen(str) * 2 才可以。

程式碼經過驗證 32位程式可以注入DLL到32位的程序。 64位程序可以注入dll到64位程序。

32位程序不可注入DLL到64位程序。需要特殊方式。