高級遠程線程注入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位進程。需要特殊方式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章