InitSystem: uses SystemBasicProcessInformation if available

This commit is contained in:
李通洲
2026-09-02 15:53:16 +08:00
parent ce61783c4b
commit 1128a75d8c
2 changed files with 78 additions and 25 deletions
+23 -3
View File
@@ -13,6 +13,8 @@ enum {
SystemLogicalProcessorAndGroupInformation = 107,
SystemBasicPerformanceInformation = 123,
SystemSecureBootInformation = 146,
SystemBasicProcessInformation = 252,
SystemHandleCountInformation = 253,
};
typedef struct _PROCESSOR_POWER_INFORMATION {
@@ -206,6 +208,20 @@ typedef struct _SYSTEM_BASIC_PERFORMANCE_INFORMATION {
SIZE_T PeakCommitment;
} SYSTEM_BASIC_PERFORMANCE_INFORMATION, *PSYSTEM_BASIC_PERFORMANCE_INFORMATION;
typedef struct _SYSTEM_BASICPROCESS_INFORMATION {
ULONG NextEntryOffset;
HANDLE UniqueProcessId;
HANDLE InheritedFromUniqueProcessId;
ULONG64 SequenceNumber;
UNICODE_STRING ImageName;
} SYSTEM_BASICPROCESS_INFORMATION, *PSYSTEM_BASICPROCESS_INFORMATION;
typedef struct _SYSTEM_HANDLECOUNT_INFORMATION {
ULONG ProcessCount;
ULONG ThreadCount;
ULONG HandleCount;
} SYSTEM_HANDLECOUNT_INFORMATION, *PSYSTEM_HANDLECOUNT_INFORMATION;
NTSYSAPI NTSTATUS NTAPI NtDelayExecution(_In_ BOOLEAN Alertable, _In_ PLARGE_INTEGER DelayInterval);
/**
@@ -691,15 +707,19 @@ static inline uint64_t ffKSystemTimeToUInt64(const volatile KSYSTEM_TIME* pTime)
static inline bool ffIsWindows10OrGreater() {
#if FF_WIN81_COMPAT
return SharedUserData->NtMajorVersion >= 10;
return SharedUserData->NtBuildNumber >= 10240;
#else
return true;
#endif
}
static inline bool ffIsWindows11OrGreater() {
return SharedUserData->NtMajorVersion > 10 ||
(SharedUserData->NtMajorVersion == 10 && SharedUserData->NtBuildNumber >= 22000);
return SharedUserData->NtBuildNumber >= 22000;
}
static inline bool ffIsSystemBasicProcessInfoAvailable() { // Includes HandleCountInformation, which was added together
// https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation#systembasicprocessinformation
return SharedUserData->NtBuildNumber >= 26200; // MSDN says it was added in 26100.4770; ntdoc says 25H2
}
NTSYSAPI NTSTATUS NTAPI NtOpenProcessToken(
+55 -22
View File
@@ -7,32 +7,65 @@
#include <winternl.h>
#include <wchar.h>
static bool fillResult(FFInitSystemResult* result, uint32_t ppid, uint32_t pid, uint16_t len, PCWSTR name) {
if (ppid != 4 /* System */ || len <= 4 || _wcsnicmp(name + len - 4, L".exe", 4) != 0) { // smss.exe
return false;
}
result->pid = pid;
// We have no permission to open the process for querying the full information
wchar_t exePath[MAX_PATH];
_snwprintf(exePath, ARRAY_SIZE(exePath), L"%ls\\system32\\%.*ls", (const wchar_t*) SharedUserData->NtSystemRoot, len, name);
ffGetFileVersion(exePath, NULL, &result->version);
ffStrbufSetWS(&result->exe, exePath);
ffStrbufSetNWS(&result->name, len - 4, name);
return true;
}
const char* ffDetectInitSystem(FFInitSystemResult* result) {
// We only need to find the first user process, so 1024 entries should be enough
SYSTEM_PROCESS_INFORMATION buffer[1024] = {};
ULONG size = sizeof(buffer);
NTSTATUS status = NtQuerySystemInformation(SystemProcessInformation, buffer, size, &size);
if (status != STATUS_INFO_LENGTH_MISMATCH && !NT_SUCCESS(status)) {
return "NtQuerySystemInformation(SystemProcessInformation) failed";
if (ffIsSystemBasicProcessInfoAvailable()) {
// SYSTEM_BASICPROCESS_INFORMATION entries are much smaller than SYSTEM_PROCESS_INFORMATION ones,
// so a modest buffer should be enough to contain all processes
SYSTEM_BASICPROCESS_INFORMATION buffer[1024];
NTSTATUS status = NtQuerySystemInformation(SystemBasicProcessInformation, buffer, sizeof(buffer), NULL);
if (status != STATUS_INFO_LENGTH_MISMATCH && !NT_SUCCESS(status)) {
goto fallback;
}
for (auto ptr = buffer; ;ptr = (PSYSTEM_BASICPROCESS_INFORMATION) ((uint8_t*) ptr + ptr->NextEntryOffset)) {
assert(ptr >= buffer && (uint8_t*) ptr < (uint8_t*) buffer + sizeof(buffer));
if (fillResult(result, (uint32_t)(uintptr_t) ptr->InheritedFromUniqueProcessId,
(uint32_t)(uintptr_t) ptr->UniqueProcessId,
ptr->ImageName.Length / sizeof(*ptr->ImageName.Buffer),
ptr->ImageName.Buffer)) {
return nullptr;
}
// The last process in the list always has a NextEntryOffset of 0, even if the buffer was truncated.
if (!ptr->NextEntryOffset) {
return "Could not find init system process";
}
}
}
for (SYSTEM_PROCESS_INFORMATION* ptr = buffer; ; ptr = (SYSTEM_PROCESS_INFORMATION*) ((uint8_t*) ptr + ptr->NextEntryOffset)) {
assert(ptr >= buffer && (uint8_t*) ptr < (uint8_t*) buffer + sizeof(buffer));
uint16_t len = ptr->ImageName.Length / sizeof(*ptr->ImageName.Buffer);
if (ptr->InheritedFromUniqueProcessId == (HANDLE)(uintptr_t) 4 /* System */ &&
len > 4 && _wcsnicmp(ptr->ImageName.Buffer + len - 4, L".exe", 4) == 0) { // smss.exe
result->pid = (uint32_t)(uintptr_t) ptr->UniqueProcessId;
// We have no permission to open the process for querying the full information
wchar_t exePath[MAX_PATH];
_snwprintf(exePath, ARRAY_SIZE(exePath), L"%ls\\system32\\%.*ls", (const wchar_t*) SharedUserData->NtSystemRoot, len, ptr->ImageName.Buffer);
ffGetFileVersion(exePath, NULL, &result->version);
ffStrbufSetWS(&result->exe, exePath);
ffStrbufSetNWS(&result->name, len - 4, ptr->ImageName.Buffer);
return nullptr;
fallback:
{
// We only need to find the first user process, so 1024 entries should be enough
SYSTEM_PROCESS_INFORMATION buffer[1024] = {};
ULONG size = sizeof(buffer);
NTSTATUS status = NtQuerySystemInformation(SystemProcessInformation, buffer, size, &size);
if (status != STATUS_INFO_LENGTH_MISMATCH && !NT_SUCCESS(status)) {
return "NtQuerySystemInformation(SystemProcessInformation) failed";
}
// The last process in the list always has a NextEntryOffset of 0, even if the buffer was truncated.
if (!ptr->NextEntryOffset) {
return "Could not find init system process";
for (auto ptr = buffer; ; ptr = (SYSTEM_PROCESS_INFORMATION*) ((uint8_t*) ptr + ptr->NextEntryOffset)) {
assert(ptr >= buffer && (uint8_t*) ptr < (uint8_t*) buffer + sizeof(buffer));
uint16_t len = ptr->ImageName.Length / sizeof(*ptr->ImageName.Buffer);
if (fillResult(result, (uint32_t)(uintptr_t) ptr->InheritedFromUniqueProcessId,
(uint32_t)(uintptr_t) ptr->UniqueProcessId, len, ptr->ImageName.Buffer)) {
return nullptr;
}
// The last process in the list always has a NextEntryOffset of 0, even if the buffer was truncated.
if (!ptr->NextEntryOffset) {
return "Could not find init system process";
}
}
}
}