ohmu
·
2026-10-15
1#include "initsystem.h"
2#include "common/debug.h"
3#include "common/windows/unicode.h"
4#include "common/windows/nt.h"
5#include "common/windows/version.h"
6
7#include <ntstatus.h>
8#include <winternl.h>
9#include <wchar.h>
10
11static bool fillResult(FFInitSystemResult* result, uint32_t ppid, uint32_t pid, uint16_t len, PCWSTR name) {
12 if (ppid != 4 /* System */ || len <= 4 || _wcsnicmp(name + len - 4, L".exe", 4) != 0) { // smss.exe
13 return false;
14 }
15 result->pid = pid;
16 // We have no permission to open the process for querying the full information
17 wchar_t exePath[MAX_PATH];
18 _snwprintf(exePath, ARRAY_SIZE(exePath), L"%ls\\system32\\%.*ls", (const wchar_t*) SharedUserData->NtSystemRoot, len, name);
19 ffGetFileVersion(exePath, NULL, &result->version);
20 ffStrbufSetWS(&result->exe, exePath);
21 ffStrbufSetNWS(&result->name, len - 4, name);
22 return true;
23}
24
25const char* ffDetectInitSystem(FFInitSystemResult* result) {
26 if (ffIsSystemBasicProcessInfoAvailable()) {
27 // SYSTEM_BASICPROCESS_INFORMATION entries are much smaller than SYSTEM_PROCESS_INFORMATION ones,
28 // so a modest buffer should be enough to contain all processes
29 SYSTEM_BASICPROCESS_INFORMATION buffer[1024] = {};
30 NTSTATUS status = NtQuerySystemInformation(SystemBasicProcessInformation, buffer, sizeof(buffer), NULL);
31 if (status != STATUS_INFO_LENGTH_MISMATCH && !NT_SUCCESS(status)) {
32 goto fallback;
33 }
34 for (auto ptr = buffer; ;ptr = (PSYSTEM_BASICPROCESS_INFORMATION) ((uint8_t*) ptr + ptr->NextEntryOffset)) {
35 assert(ptr >= buffer && (uint8_t*) ptr < (uint8_t*) buffer + sizeof(buffer));
36 if (fillResult(result, (uint32_t)(uintptr_t) ptr->InheritedFromUniqueProcessId,
37 (uint32_t)(uintptr_t) ptr->UniqueProcessId,
38 ptr->ImageName.Length / sizeof(*ptr->ImageName.Buffer),
39 ptr->ImageName.Buffer)) {
40 return nullptr;
41 }
42 // The last process in the list always has a NextEntryOffset of 0, even if the buffer was truncated.
43 if (!ptr->NextEntryOffset) {
44 return "Could not find init system process";
45 }
46 }
47 }
48
49fallback:
50 {
51 // We only need to find the first user process, so 1024 entries should be enough
52 SYSTEM_PROCESS_INFORMATION buffer[1024] = {};
53 ULONG size = sizeof(buffer);
54 NTSTATUS status = NtQuerySystemInformation(SystemProcessInformation, buffer, size, &size);
55 if (status != STATUS_INFO_LENGTH_MISMATCH && !NT_SUCCESS(status)) {
56 FF_DEBUG("NtQuerySystemInformation(SystemProcessInformation) failed: %s", ffDebugNtStatus(status));
57 return "NtQuerySystemInformation(SystemProcessInformation) failed";
58 }
59
60 for (auto ptr = buffer; ; ptr = (SYSTEM_PROCESS_INFORMATION*) ((uint8_t*) ptr + ptr->NextEntryOffset)) {
61 assert(ptr >= buffer && (uint8_t*) ptr < (uint8_t*) buffer + sizeof(buffer));
62 uint16_t len = ptr->ImageName.Length / sizeof(*ptr->ImageName.Buffer);
63 if (fillResult(result, (uint32_t)(uintptr_t) ptr->InheritedFromUniqueProcessId,
64 (uint32_t)(uintptr_t) ptr->UniqueProcessId, len, ptr->ImageName.Buffer)) {
65 return nullptr;
66 }
67 // The last process in the list always has a NextEntryOffset of 0, even if the buffer was truncated.
68 if (!ptr->NextEntryOffset) {
69 return "Could not find init system process";
70 }
71 }
72 }
73}