我正在尝试使用 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。
如有任何帮助,我们将不胜感激。
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
和
如果指定线程与线程类型匹配,则返回
由 SystemThreadInfo 参数指定。否则,函数 返回TRUE
。FALSE
然而这是非常奇怪且不合逻辑的 api 设计。同样在这种情况下,SystemThreadInfo将只是in参数,但为什么它在api声明中标记为
_Inout_
?以及文档中的 [in, out] ?
否则,该函数将返回
FALSE
- 但会出现哪个错误?如何区分错误(例如,不正确的参数、不正确的流描述符、没有对 hThread 的访问权限)和“错误”(错误的线程类型)?必须记录错误值,在本例中由 GetLastError()
返回。
接下来是逻辑和本机设计 - IsUmsSchedulerThread 和 IsUmsWorkerThread 必须是 out 参数。如果
hThread
正确的线程句柄具有 THREAD_QUERY_INFORMATION
访问权限,则 api 必须返回 true。
我们必须在 api 返回后检查 IsUmsSchedulerThread 和 IsUmsWorkerThread,而不是在 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);