高级远程线程注入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位进程。需要特殊方式。

posted @ 2022-03-19 13:40  iBinary  阅读(2547)  评论(3编辑  收藏  举报