如何使用 GetUmsSystemThreadInformation 枚举 UMS 调度程序线程?

问题描述 投票:0回答:1

我正在尝试使用 GetUmsSystemThreadInformation 枚举 UMS 调度程序线程,但我无法使其按预期方式工作。

这是我到目前为止的代码:

#include <windows.h>
#include <tlhelp32.h>
#include <iostream>
#include "UMSEnum.h"

void EnumerateThreads(DWORD tid) {
    // Create a snapshot of all processes
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);

    if (hSnapshot == INVALID_HANDLE_VALUE) {
        std::cerr << "[-] Error creating snapshot: " << GetLastError() << std::endl;
        return;
    }

    // Initialize the THREADENTRY32 structure
    THREADENTRY32 threadEntry;
    threadEntry.dwSize = sizeof(THREADENTRY32);

    // Retrieve information about the first thread
    if (Thread32First(hSnapshot, &threadEntry)) {
        do {
            // Display information about each thread
            // std::cout << "[.] Thread ID: " << threadEntry.th32ThreadID << std::endl;

            // Get the thread handle using the thread ID
            if (threadEntry.th32ThreadID != tid && tid != 0)
                continue;

            HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, threadEntry.th32ThreadID);

            if (hThread != NULL) {
                UMS_SYSTEM_THREAD_INFORMATION tInfo;
                tInfo.UmsVersion = UMS_VERSION;
                tInfo.IsUmsSchedulerThread = 1;
                tInfo.IsUmsWorkerThread = 0;
                // Perform actions on the thread handle if needed
                BOOL found = GetUmsSystemThreadInformation(hThread, &tInfo);
                if ( found && GetLastError() != ERROR_NOT_SUPPORTED) {
                    std::cout << "[.] "
                        << threadEntry.th32OwnerProcessID
                        << "[" << threadEntry.th32ThreadID << "]"
                        << "[" << tInfo.IsUmsSchedulerThread << "]"
                        << "[" << tInfo.IsUmsWorkerThread << "]"
                        << "[" << tInfo.ThreadUmsFlags << "]"
                        << std::endl;
                }
                else {
                    std::cout << "[-] GetUmsSystemThreadInformation failed - " << threadEntry.th32ThreadID << " - " << GetLastError() << std::endl;
                }
                // Close the thread handle
                CloseHandle(hThread);
            }
            //else {
            //    std::cerr << "Error opening thread: " << GetLastError() << std::endl;
            //}

        } while (Thread32Next(hSnapshot, &threadEntry));
    }
    else {
        std::cerr << "[-] Error getting thread information: " << GetLastError() << std::endl;
    }

    // Close the thread snapshot handle
    CloseHandle(hSnapshot);
}

int main(int argc, char** argv) {
    EnumerateThreads(atol(argv[1]));
    return 0;
}

至少对我来说,这似乎是正确的,但是这段代码将列出系统中的每个线程 ID,而不仅仅是 UMS 调度程序的 ID。此外,GetUmsSystemThreadInformation 始终返回 true。

以下是我用来创建 UMS Scheduler 线程的测试应用程序。

#include <iostream>
#include <Windows.h>

PUMS_COMPLETION_LIST g_CompletionList;

RTL_UMS_SCHEDULER_ENTRY_POINT RtlUmsSchedulerEntryPoint;

void RtlUmsSchedulerEntryPoint(
    RTL_UMS_SCHEDULER_REASON Reason,
    ULONG_PTR ActivationPayload,
    PVOID SchedulerParam
)
{
    std::cout << "[+] It works - " << GetCurrentThreadId() << std::endl;
    switch (Reason)
    {
    case UmsSchedulerStartup:
        std::cout << "[+] UmsSchedulerStartup" << std::endl;
        break;
    case UmsSchedulerThreadBlocked:
        std::cout << "[+] UmsSchedulerThreadBlocked" << std::endl;
        break;
    case UmsSchedulerThreadYield:
        std::cout << "[+] UmsSchedulerThreadYield" << std::endl;
        break;
    default:
        break;
    }
}

DWORD WINAPI SchedulerThreadProc(LPVOID lpThreadParameter)
{
    UMS_SCHEDULER_STARTUP_INFO umsStartupInfo;
    umsStartupInfo.UmsVersion = UMS_VERSION;
    umsStartupInfo.SchedulerParam = nullptr;
    umsStartupInfo.SchedulerProc = RtlUmsSchedulerEntryPoint;
    umsStartupInfo.CompletionList = g_CompletionList;
    if (!EnterUmsSchedulingMode(&umsStartupInfo))
    {
        std::cerr << "[-] EnterUmsSchedulingMode failed - " << GetLastError() << std::endl;
        return false;
    }

    Sleep(INFINITE);

    return true;
}

int main()
{
    UMS_SCHEDULER_STARTUP_INFO startupInfo;
    if (!CreateUmsCompletionList(&g_CompletionList)) {
        std::cerr << "[-] CreateUmsCompletionList failed - " << GetLastError() << std::endl;
    }
    HANDLE hThread = CreateThread(NULL, 0, SchedulerThreadProc, NULL, NULL, NULL);
    if (hThread == NULL) {
        std::cerr << "[-] CreateThread failed - " << GetLastError() << std::endl;
        return 1;
    }
    int foo;
    std::cin >> foo;
    return 0;
}

这是运行两者的示例:

我想我不知道如何正确设置SystemThreadInfo。

如有任何帮助,我们将不胜感激。

c++ winapi
1个回答
0
投票
Warning
    
As of Windows 11, user-mode scheduling is not supported. 
All calls fail with the error ERROR_NOT_SUPPORTED.

来自当前的 msdn 文档:

[输入,输出] 系统线程信息

指向已初始化

UMS_SYSTEM_THREAD_INFORMATION
结构的指针 指定查询的线程类型。

如果指定线程与线程类型匹配,则返回

TRUE
SystemThreadInfo 参数指定。否则,函数 返回
FALSE

然而这是非常奇怪且不合逻辑的 api 设计。同样在这种情况下,SystemThreadInfo将只是in参数,但为什么它在api声明中标记为

_Inout_
?以及文档中的 [in, out]

否则,该函数将返回

FALSE
- 但会出现哪个错误?如何区分错误(例如,不正确的参数、不正确的流描述符、没有对 hThread 的访问权限)和“错误”(错误的线程类型)?必须记录错误值,在本例中由
GetLastError()
返回。

接下来是逻辑和本机设计 - IsUmsSchedulerThreadIsUmsWorkerThread 必须是 out 参数。如果

hThread
正确的线程句柄具有
THREAD_QUERY_INFORMATION
访问权限,则 api 必须返回 true。 我们必须在 api 返回后检查 IsUmsSchedulerThreadIsUmsWorkerThread,而不是在 api 调用之前设置它。

让我们寻找

GetUmsSystemThreadInformation

的源代码
BOOL
WINAPI
GetUmsSystemThreadInformation(
    _In_ HANDLE ThreadHandle,
    _Inout_ PUMS_SYSTEM_THREAD_INFORMATION SystemThreadInfo
    )
{
    if (UMS_VERSION != SystemThreadInfo->UmsVersion) return STATUS_INVALID_PARAMETER;
    THREAD_UMS_INFORMATION tui = { UmsInformationCommandQuery };
    ULONG rcb;
    NTSTATUS status = NtQueryInformationThread(hThread, ThreadUmsInformation, &tui, sizeof(tui), &rcb);
    if (0 > status)
    {
      RtlSetLastWin32Error(RtlNtStatusToDosError(status));
      return FALSE;
    }
  
    SystemThreadInfo->ThreadUmsFlags = tui.Flags;
    return TRUE;
}

从中可以清楚地看到,

IsUmsSchedulerThread
IsUmsWorkerThread
作为输入被忽略,并在api调用后设置为仅输出

因此接下来必须是检查线程的正确代码:

void CheckUmsThread(HANDLE hThread, ULONG pid, ULONG tid)
{
    UMS_SYSTEM_THREAD_INFORMATION SystemThreadInfo { UMS_VERSION };

    if (GetUmsSystemThreadInformation(hThread, &SystemThreadInfo))
    {
        if (SystemThreadInfo.ThreadUmsFlags && SystemThreadInfo.ThreadUmsFlags)
        {
            __debugbreak();
        }

        PCSTR pcsz = 0;

        if (SystemThreadInfo.IsUmsSchedulerThread)
        {
            pcsz = "Scheduler";
        }
        else if (SystemThreadInfo.IsUmsWorkerThread)
        {
            pcsz = "Worker";
        }

        if (pcsz)
        {
            DbgPrint("%x.%x: Ums%sThread\n", pid, tid, pcsz);
        }
    }
    else
    {
        GetLastError();
        RtlGetLastNtStatus();
    }
}

完整代码可以在下一个:

NTSTATUS status;
ULONG cb = 0x10000;
do 
{
    status = STATUS_NO_MEMORY;
    if (PVOID buf = LocalAlloc(LMEM_FIXED, cb += 0x1000))
    {
        if (0 <= (status = NtQuerySystemInformation(SystemProcessInformation, buf, cb, &cb)))
        {
            union {
                ULONG_PTR up;
                SYSTEM_PROCESS_INFORMATION* pspi;
                PVOID pv;
            };

            pv = buf;
            ULONG NextEntryOffset = 0;
            OBJECT_ATTRIBUTES oa = { sizeof(oa) };
            do 
            {
                up += NextEntryOffset;

                if (ULONG NumberOfThreads = pspi->NumberOfThreads)
                {
                    SYSTEM_THREAD_INFORMATION* Thread = (SYSTEM_THREAD_INFORMATION*)pspi->Threads;
                    do 
                    {
                        HANDLE hThread;
                        if (0 <= NtOpenThread(&hThread, THREAD_QUERY_INFORMATION, &oa, &Thread->ClientId))
                        {
                            CheckUmsThread(hThread, 
                                (ULONG)(ULONG_PTR)Thread->ClientId.UniqueProcess, 
                                (ULONG)(ULONG_PTR)Thread->ClientId.UniqueThread);

                            NtClose(hThread);
                        }

                    } while (Thread++, --NumberOfThreads);
                }
            } while (NextEntryOffset = pspi->NextEntryOffset);
        }

        LocalFree(buf);
    }

} while (STATUS_INFO_LENGTH_MISMATCH == status);
© www.soinside.com 2019 - 2024. All rights reserved.