diff options
| author | sumuel <samuel@yakubos.org> | 2026-08-17 20:44:55 +0000 |
|---|---|---|
| committer | sumuel <samuel@yakubos.org> | 2026-08-17 20:44:55 +0000 |
| commit | 76424950e373d3b04ac3dd13019151bfba3e8423 (patch) | |
| tree | 4c3cfdbda039e592b9186be3e28f8d7cfd439e8a /src/common/windows | |
Add the files
Diffstat (limited to 'src/common/windows')
| -rwxr-xr-x | src/common/windows/c-logo.sh | 10 | ||||
| -rw-r--r-- | src/common/windows/com.c | 67 | ||||
| -rw-r--r-- | src/common/windows/com.h | 23 | ||||
| -rw-r--r-- | src/common/windows/getline.c | 58 | ||||
| -rw-r--r-- | src/common/windows/getline.h | 6 | ||||
| -rw-r--r-- | src/common/windows/logo.ico | bin | 0 -> 107906 bytes | |||
| l--------- | src/common/windows/logo.svg | 1 | ||||
| -rw-r--r-- | src/common/windows/manifest.xml | 18 | ||||
| -rw-r--r-- | src/common/windows/nt.h | 1325 | ||||
| -rw-r--r-- | src/common/windows/perflib_.h | 134 | ||||
| -rw-r--r-- | src/common/windows/registry.c | 388 | ||||
| -rw-r--r-- | src/common/windows/registry.h | 66 | ||||
| -rw-r--r-- | src/common/windows/unicode.c | 44 | ||||
| -rw-r--r-- | src/common/windows/unicode.h | 46 | ||||
| -rw-r--r-- | src/common/windows/unicode.hpp | 27 | ||||
| -rw-r--r-- | src/common/windows/util.hpp | 21 | ||||
| -rw-r--r-- | src/common/windows/variant.cpp | 20 | ||||
| -rw-r--r-- | src/common/windows/variant.hpp | 164 | ||||
| -rw-r--r-- | src/common/windows/version.c | 77 | ||||
| -rw-r--r-- | src/common/windows/version.h | 14 | ||||
| -rw-r--r-- | src/common/windows/version.rc | 37 | ||||
| -rw-r--r-- | src/common/windows/wmi.h | 54 |
22 files changed, 2600 insertions, 0 deletions
diff --git a/src/common/windows/c-logo.sh b/src/common/windows/c-logo.sh new file mode 100755 index 0000000..e4d7863 --- /dev/null +++ b/src/common/windows/c-logo.sh @@ -0,0 +1,10 @@ +#!/bin/sh +# Convert logo.svg to logo.ico +rsvg-convert -w 16 -h 16 logo.svg > logo16.png +rsvg-convert -w 32 -h 32 logo.svg > logo32.png +rsvg-convert -w 48 -h 48 logo.svg > logo48.png +rsvg-convert -w 64 -h 64 logo.svg > logo64.png +rsvg-convert -w 128 -h 128 logo.svg > logo128.png +rsvg-convert -w 256 -h 256 logo.svg > logo256.png +convert logo16.png logo32.png logo48.png logo64.png logo128.png logo256.png logo.ico +rm logo16.png logo32.png logo48.png logo64.png logo128.png logo256.png diff --git a/src/common/windows/com.c b/src/common/windows/com.c new file mode 100644 index 0000000..a400ca9 --- /dev/null +++ b/src/common/windows/com.c @@ -0,0 +1,67 @@ +#include "com.h" + +#include <stdlib.h> + +#if FF_HAVE_WINRT + #include <roapi.h> + +static void RoUninitializeWrap(void) { + RoUninitialize(); +} + +static const char* doInitCom() { + HRESULT res = RoInitialize(RO_INIT_MULTITHREADED); + if (FAILED(res)) { + switch (res) { + case E_INVALIDARG: + return "RoInitialize() failed: invalid argument"; + case E_OUTOFMEMORY: + return "RoInitialize() failed: out of memory"; + case E_UNEXPECTED: + return "RoInitialize() failed: unexpected error"; + case RPC_E_CHANGED_MODE: + // COM was already initialized with a different concurrency model + return NULL; + default: + return "RoInitialize() failed: unknown error"; + } + } + + atexit(RoUninitializeWrap); + return NULL; +} +#else + #include <combaseapi.h> + +static void CoUninitializeWrap(void) { + CoUninitialize(); +} + +static const char* doInitCom() { + HRESULT res = CoInitializeEx(NULL, COINIT_MULTITHREADED); + if (FAILED(res)) { + switch (res) { + case E_INVALIDARG: + return "CoInitializeEx() failed: invalid argument"; + case E_OUTOFMEMORY: + return "CoInitializeEx() failed: out of memory"; + case RPC_E_CHANGED_MODE: + // COM was already initialized with a different concurrency model + return NULL; + default: + return "CoInitializeEx() failed: unknown error"; + } + } + + atexit(CoUninitializeWrap); + return NULL; +} +#endif + +const char* ffInitCom(void) { + static const char* error = ""; + if (error && error[0] == '\0') { + error = doInitCom(); + } + return error; +} diff --git a/src/common/windows/com.h b/src/common/windows/com.h new file mode 100644 index 0000000..57b8c6d --- /dev/null +++ b/src/common/windows/com.h @@ -0,0 +1,23 @@ +#pragma once + +#include "common/attributes.h" +#include <assert.h> +#include <unknwn.h> + +// Initialize COM & WinRT +const char* ffInitCom(void); + +static inline void ffReleaseComObject(void* ppUnknown) { + assert(ppUnknown); + IUnknown* pUnknown = *(IUnknown**) ppUnknown; + if (pUnknown) { +#ifdef __cplusplus + pUnknown->Release(); +#else + pUnknown->lpVtbl->Release(pUnknown); +#endif + *(IUnknown**) ppUnknown = NULL; + } +} + +#define FF_AUTO_RELEASE_COM_OBJECT FF_A_CLEANUP(ffReleaseComObject) diff --git a/src/common/windows/getline.c b/src/common/windows/getline.c new file mode 100644 index 0000000..3d43d55 --- /dev/null +++ b/src/common/windows/getline.c @@ -0,0 +1,58 @@ +#include "getline.h" + +#include <stdlib.h> +#include <errno.h> + +ssize_t getline(char** lineptr, size_t* n, FILE* stream) { + ssize_t pos = -1; + int c; + + if (lineptr == NULL || stream == NULL || n == NULL) { + errno = EINVAL; + return -1; + } + + _lock_file(stream); + + c = _getc_nolock(stream); + if (c == EOF) { + goto exit; + } + + if (*lineptr == NULL) { + *lineptr = malloc(128); + if (*lineptr == NULL) { + goto exit; + } + *n = 128; + } + + pos = 0; + while (c != EOF) { + if ((size_t) (pos + 1) >= *n) { + size_t new_size = *n + (*n >> 2); + if (new_size < 128) { + new_size = 128; + } + char* new_ptr = realloc(*lineptr, new_size); + if (new_ptr == NULL) { + pos = -1; + goto exit; + } + *n = new_size; + *lineptr = new_ptr; + } + + ((char*) (*lineptr))[pos++] = (char) c; + if (c == '\n') { + break; + } + c = _getc_nolock(stream); + } + + (*lineptr)[pos] = '\0'; + +exit: + _unlock_file(stream); + return pos; +} diff --git a/src/common/windows/getline.h b/src/common/windows/getline.h new file mode 100644 index 0000000..49ba5db --- /dev/null +++ b/src/common/windows/getline.h @@ -0,0 +1,6 @@ +#pragma once + +#include <stdint.h> +#include <stdio.h> + +ssize_t getline(char** lineptr, size_t* n, FILE* stream); diff --git a/src/common/windows/logo.ico b/src/common/windows/logo.ico Binary files differnew file mode 100644 index 0000000..cdd89ad --- /dev/null +++ b/src/common/windows/logo.ico diff --git a/src/common/windows/logo.svg b/src/common/windows/logo.svg new file mode 120000 index 0000000..068d3f6 --- /dev/null +++ b/src/common/windows/logo.svg @@ -0,0 +1 @@ +../../logo/logo.svg
\ No newline at end of file diff --git a/src/common/windows/manifest.xml b/src/common/windows/manifest.xml new file mode 100644 index 0000000..2113c07 --- /dev/null +++ b/src/common/windows/manifest.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"> + <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> + <application> + <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/><!-- 8.1 --> + <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/><!-- 10/11 --> + </application> + </compatibility> + <assemblyIdentity type="win32" name="com.github.fastfetch" version="0.0.0.0"/> + <asmv3:application> + <asmv3:windowsSettings> + <activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage> + <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> + <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitor</dpiAwareness> + <heapType xmlns="http://schemas.microsoft.com/SMI/2020/WindowsSettings">SegmentHeap</heapType> + </asmv3:windowsSettings> + </asmv3:application> +</assembly> diff --git a/src/common/windows/nt.h b/src/common/windows/nt.h new file mode 100644 index 0000000..2f476b8 --- /dev/null +++ b/src/common/windows/nt.h @@ -0,0 +1,1325 @@ +#pragma once + +#include <ntdef.h> +#include <winternl.h> +#include <winnt.h> +#include <stdint.h> +#include <assert.h> + +enum { + SystemModuleInformation = 11, + SystemFirmwareTableInformation = 76, + SystemBootEnvironmentInformation = 90, + SystemLogicalProcessorAndGroupInformation = 107, + SystemSecureBootInformation = 146, +}; + +typedef struct _PROCESSOR_POWER_INFORMATION { + ULONG Number; + ULONG MaxMhz; + ULONG CurrentMhz; + ULONG MhzLimit; + ULONG MaxIdleState; + ULONG CurrentIdleState; +} PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION; + +NTSYSAPI NTSTATUS NTAPI NtPowerInformation( + IN POWER_INFORMATION_LEVEL InformationLevel, + IN PVOID InputBuffer OPTIONAL, + IN ULONG InputBufferLength, + OUT PVOID OutputBuffer OPTIONAL, + IN ULONG OutputBufferLength); + +NTSYSAPI NTSTATUS NTAPI RtlGetVersion( + _Inout_ PRTL_OSVERSIONINFOW lpVersionInformation); + +NTSYSAPI NTSTATUS NTAPI NtQueryDirectoryFile( + IN HANDLE FileHandle, + IN HANDLE Event OPTIONAL, + IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, + IN PVOID ApcContext OPTIONAL, + OUT PIO_STATUS_BLOCK IoStatusBlock, + OUT PVOID FileInformation, + IN ULONG Length, + IN FILE_INFORMATION_CLASS FileInformationClass, + IN BOOLEAN ReturnSingleEntry, + IN PUNICODE_STRING FileName OPTIONAL, + IN BOOLEAN RestartScan); + +// https://ntdoc.m417z.com/process_devicemap_information_ex +typedef struct _PROCESS_DEVICEMAP_INFORMATION_EX { + union { + struct + { + HANDLE DirectoryHandle; // A handle to a directory object that can be set as the new device map for the process. This handle must have DIRECTORY_TRAVERSE access. + } Set; + struct + { + ULONG DriveMap; // A bitmask that indicates which drive letters are currently in use in the process's device map. + UCHAR DriveType[32]; // A value that indicates the type of each drive (e.g., local disk, network drive, etc.). // DRIVE_* WinBase.h + } Query; + }; + ULONG Flags; // PROCESS_LUID_DOSDEVICES_ONLY +} PROCESS_DEVICEMAP_INFORMATION_EX, *PPROCESS_DEVICEMAP_INFORMATION_EX; + +#ifndef NtCurrentProcess + #define NtCurrentProcess() ((HANDLE) (LONG_PTR) - 1) +#endif + +typedef struct _CURDIR { + UNICODE_STRING DosPath; + HANDLE Handle; +} CURDIR, *PCURDIR; + +NTSYSAPI PIMAGE_NT_HEADERS NTAPI RtlImageNtHeader(IN PVOID BaseOfImage); + +/** + * The SECTION_IMAGE_INFORMATION structure contains detailed information about an image section. + */ +typedef struct _SECTION_IMAGE_INFORMATION { + PVOID TransferAddress; // The address of the image entry point function. + ULONG ZeroBits; // The number of high-order address bits that must be zero in the image base address. + SIZE_T MaximumStackSize; // The maximum stack size of threads from the PE file header. + SIZE_T CommittedStackSize; // The initial stack size of threads from the PE file header. + ULONG SubSystemType; // The image subsystem from the PE file header (e.g., Windows GUI, Windows CUI, POSIX). + union { + struct + { + USHORT SubSystemMinorVersion; + USHORT SubSystemMajorVersion; + }; + ULONG SubSystemVersion; + }; + union { + struct + { + USHORT MajorOperatingSystemVersion; + USHORT MinorOperatingSystemVersion; + }; + ULONG OperatingSystemVersion; + }; + USHORT ImageCharacteristics; // The image characteristics from the PE file header. + USHORT DllCharacteristics; // The DLL characteristics flags (e.g., ASLR, NX compatibility). + USHORT Machine; // The image architecture (e.g., x86, x64, ARM). + BOOLEAN ImageContainsCode; // The image contains native executable code. + union { + UCHAR ImageFlags; + struct + { + UCHAR ComPlusNativeReady : 1; // The image contains precompiled .NET assembly generated by NGEN (Native Image Generator). + UCHAR ComPlusILOnly : 1; // the image contains only Microsoft Intermediate Language (IL) assembly. + UCHAR ImageDynamicallyRelocated : 1; // The image was mapped using a random base address rather than the preferred base address. + UCHAR ImageMappedFlat : 1; // The image was mapped using a single contiguous region, rather than separate regions for each section. + UCHAR BaseBelow4gb : 1; // The image was mapped using a base address below the 4 GB boundary. + UCHAR ComPlusPrefer32bit : 1; // The image prefers to run as a 32-bit process, even on a 64-bit system. + UCHAR Reserved : 2; + }; + }; + ULONG LoaderFlags; // Reserved by ntdll.dll for the Windows loader. + ULONG ImageFileSize; // The size of the image, in bytes, including all headers. + ULONG CheckSum; // The image file checksum, from the PE optional header. +} SECTION_IMAGE_INFORMATION, *PSECTION_IMAGE_INFORMATION; + +typedef struct _SYSTEM_BOOT_ENVIRONMENT_INFORMATION { + GUID BootIdentifier; + FIRMWARE_TYPE FirmwareType; + union { + ULONGLONG BootFlags; + struct + { + ULONGLONG DbgMenuOsSelection : 1; // REDSTONE4 + ULONGLONG DbgHiberBoot : 1; + ULONGLONG DbgSoftBoot : 1; + ULONGLONG DbgMeasuredLaunch : 1; + ULONGLONG DbgMeasuredLaunchCapable : 1; // 19H1 + ULONGLONG DbgSystemHiveReplace : 1; + ULONGLONG DbgMeasuredLaunchSmmProtections : 1; + ULONGLONG DbgMeasuredLaunchSmmLevel : 7; // 20H1 + ULONGLONG DbgBugCheckRecovery : 1; // 24H2 + ULONGLONG DbgFASR : 1; + ULONGLONG DbgUseCachedBcd : 1; + }; + }; +} SYSTEM_BOOT_ENVIRONMENT_INFORMATION; + +typedef struct _RTL_PROCESS_MODULE_INFORMATION { + PVOID Section; + PVOID MappedBase; + PVOID ImageBase; + ULONG ImageSize; + ULONG Flags; + USHORT LoadOrderIndex; + USHORT InitOrderIndex; + USHORT LoadCount; + USHORT OffsetToFileName; + UCHAR FullPathName[256]; +} RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION; + +typedef struct _RTL_PROCESS_MODULES { + ULONG NumberOfModules; + _Field_size_(NumberOfModules) RTL_PROCESS_MODULE_INFORMATION Modules[1]; +} RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES; + +NTSTATUS NTAPI NtQuerySystemEnvironmentValueEx( + _In_ PCUNICODE_STRING VariableName, + _In_ const GUID* VendorGuid, + _Out_writes_bytes_opt_(*BufferLength) PVOID Buffer, + _Inout_ PULONG BufferLength, + _Out_opt_ PULONG Attributes // EFI_VARIABLE_* +); + +NTSTATUS NTAPI RtlGUIDFromString(IN PCUNICODE_STRING GuidString, OUT GUID* Guid); +NTSTATUS NTAPI RtlStringFromGUIDEx(IN GUID* Guid, OUT PCUNICODE_STRING GuidString, _In_ BOOLEAN AllocateGuidString); + +typedef struct _SYSTEM_SECUREBOOT_INFORMATION { + BOOLEAN SecureBootEnabled; + BOOLEAN SecureBootCapable; +} SYSTEM_SECUREBOOT_INFORMATION, *PSYSTEM_SECUREBOOT_INFORMATION; + +NTSTATUS NTAPI NtQuerySystemInformationEx( + _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, + _In_reads_bytes_(InputBufferLength) PVOID InputBuffer, + _In_ ULONG InputBufferLength, + _Out_writes_bytes_opt_(SystemInformationLength) PVOID SystemInformation, + _In_ ULONG SystemInformationLength, + _Out_opt_ PULONG ReturnLength); + +typedef enum _SYSTEM_FIRMWARE_TABLE_ACTION { + SystemFirmwareTableEnumerate, + SystemFirmwareTableGet, + SystemFirmwareTableMax +} SYSTEM_FIRMWARE_TABLE_ACTION; + +typedef struct _SYSTEM_FIRMWARE_TABLE_INFORMATION { + ULONG ProviderSignature; // (same as the GetSystemFirmwareTable function) + SYSTEM_FIRMWARE_TABLE_ACTION Action; + ULONG TableID; + ULONG TableBufferLength; + _Field_size_bytes_(TableBufferLength) UCHAR TableBuffer[]; +} SYSTEM_FIRMWARE_TABLE_INFORMATION, *PSYSTEM_FIRMWARE_TABLE_INFORMATION; + +NTSYSAPI NTSTATUS NTAPI NtDelayExecution(_In_ BOOLEAN Alertable, _In_ PLARGE_INTEGER DelayInterval); + +/** + * The KSYSTEM_TIME structure represents interrupt time, system time, and time zone bias. + */ +typedef struct _KSYSTEM_TIME { + ULONG LowPart; + LONG High1Time; + LONG High2Time; +} KSYSTEM_TIME, *PKSYSTEM_TIME; + +/** + * PROCESSOR_FEATURE_MAX defines the maximum number of processor feature flags + * that may be reported by the system. + */ +#define PROCESSOR_FEATURE_MAX 64 + +/** + * The ALTERNATIVE_ARCHITECTURE_TYPE enumeration specifies the hardware + * architecture variant used by the system. + * + * \remarks NEC98x86 represents the NEC PC-98 architecture, + * supported only on very early Windows releases. + */ +typedef enum _ALTERNATIVE_ARCHITECTURE_TYPE { + StandardDesign, + NEC98x86, + EndAlternatives +} ALTERNATIVE_ARCHITECTURE_TYPE; + +/** + * The KUSER_SHARED_DATA structure contains information shared with user-mode. + * + * \sa https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data + */ +typedef struct _KUSER_SHARED_DATA { + // + // Current low 32-bit of tick count and tick count multiplier. + // + // N.B. The tick count is updated each time the clock ticks. + // + + ULONG TickCountLowDeprecated; + ULONG TickCountMultiplier; + + // + // Current 64-bit interrupt time in 100ns units. + // + + volatile KSYSTEM_TIME InterruptTime; + + // + // Current 64-bit system time in 100ns units. + // + + volatile KSYSTEM_TIME SystemTime; + + // + // Current 64-bit time zone bias. + // + + volatile KSYSTEM_TIME TimeZoneBias; + + // + // Support image magic number range for the host system. + // + // N.B. This is an inclusive range. + // + + USHORT ImageNumberLow; + USHORT ImageNumberHigh; + + // + // Copy of system root in unicode. + // + // N.B. This field must be accessed via the RtlGetNtSystemRoot API for + // an accurate result. + // + + WCHAR NtSystemRoot[260]; + + // + // Maximum stack trace depth if tracing enabled. + // + + ULONG MaxStackTraceDepth; + + // + // Crypto exponent value. + // + + ULONG CryptoExponent; + + // + // Time zone ID. + // + + ULONG TimeZoneId; + + // + // Minimum size of a large page on the system, in bytes. + // + // N.B. Returned by GetLargePageMinimum() function. + // + + ULONG LargePageMinimum; + + // + // This value controls the Application Impact Telemetry (AIT) Sampling rate. + // + // This value determines how frequently the system records AIT events, + // which are used by the Application Experience and compatibility + // subsystems to evaluate application behavior, performance, and + // potential compatibility issues. + // + // Lower values increase sampling frequency, while higher values reduce it. + // The kernel updates this field as part of its internal telemetry and + // heuristics logic. + // + + ULONG AitSamplingValue; + + // + // This value controls Application Compatibility (AppCompat) switchback processing. + // + + union { + ULONG AppCompatFlag; + struct + { + ULONG SwitchbackEnabled : 1; // Basic switchback processing + ULONG ExtendedHeuristics : 1; // Extended switchback heuristics + ULONG TelemetryFallback : 1; // Telemetry-driven fallback + ULONG Reserved : 29; + } AppCompatFlags; + }; + + // + // Current Kernel Root RNG state seed version + // + + ULONGLONG RNGSeedVersion; + + // + // This value controls assertion failure handling. + // + // Historically (prior to Windows 10), this value was also used by + // Code Integrity (CI), AppLocker, and related security components to + // determine the minimum validation requirements for executable images, + // drivers, and privileged operations. + // + // In modern Windows versions, this field is used primarily by the kernel's + // diagnostic and validation infrastructure to decide how assertion failures + // should be handled (e.g., logging, debugger break-in, or bugcheck). + + ULONG GlobalValidationRunlevel; + + // + // Monotonic stamp incremented by the kernel whenever the system's + // time zone bias value changes. + // + // N.B. This field must be accessed via the RtlGetSystemTimeAndBias API for + // an accurate result. + // This value is read before and after accessing the bias fields to determine + // whether the time zone data changed during the read. If the stamp differs, + // the caller must re-read the bias values to ensure consistency. + // + + volatile LONG TimeZoneBiasStamp; + + // + // The shared collective build number undecorated with C or F. + // GetVersionEx hides the real number + // + + ULONG NtBuildNumber; + + // + // Product type. + // + // N.B. This field must be accessed via the RtlGetNtProductType API for + // an accurate result. + // + + NT_PRODUCT_TYPE NtProductType; + BOOLEAN ProductTypeIsValid; + BOOLEAN Reserved0[1]; + + // + // Native hardware processor architecture of the running system. + // + // N.B. User-mode components read this field to determine the true system + // architecture, especially in WOW64 scenarios where the process architecture + // differs from the native one. + // + + USHORT NativeProcessorArchitecture; + + // + // The NT Version. + // + // N. B. Note that each process sees a version from its PEB, but if the + // process is running with an altered view of the system version, + // the following two fields are used to correctly identify the + // version + // + + ULONG NtMajorVersion; + ULONG NtMinorVersion; + + // + // Processor features. + // + + BOOLEAN ProcessorFeatures[PROCESSOR_FEATURE_MAX]; + + // + // Reserved fields - do not use. + // + + ULONG MaximumUserModeAddressDeprecated; // Deprecated, use SystemBasicInformation instead. + ULONG SystemRangeStartDeprecated; // Deprecated, use SystemRangeStartInformation instead. + + // + // Time slippage while in debugger. + // + + volatile ULONG TimeSlip; + + // + // Alternative system architecture, e.g., NEC PC98xx on x86. + // + + ALTERNATIVE_ARCHITECTURE_TYPE AlternativeArchitecture; + + // + // Boot sequence, incremented for each boot attempt by the OS loader. + // + + ULONG BootId; + + // + // If the system is an evaluation unit, the following field contains the + // date and time that the evaluation unit expires. A value of 0 indicates + // that there is no expiration. A non-zero value is the UTC absolute time + // that the system expires. + // + + LARGE_INTEGER SystemExpirationDate; + + // + // Suite support. + // + // N.B. This field must be accessed via the RtlGetSuiteMask API for + // an accurate result. + // + + ULONG SuiteMask; + + // + // TRUE if a kernel debugger is connected/enabled. + // + + BOOLEAN KdDebuggerEnabled; + + // + // Mitigation policies. + // + + union { + UCHAR MitigationPolicies; + struct + { + UCHAR NXSupportPolicy : 2; + UCHAR SEHValidationPolicy : 2; + UCHAR CurDirDevicesSkippedForDlls : 2; + UCHAR Reserved : 2; + }; + }; + + // + // Measured duration of a single processor yield, in cycles. This is used by + // lock packages to determine how many times to spin waiting for a state + // change before blocking. + // + + USHORT CyclesPerYield; + + // + // Current console session Id. Always zero on non-TS systems. + // + // N.B. This field must be accessed via the RtlGetActiveConsoleId API for an + // accurate result. + // + + volatile ULONG ActiveConsoleId; + + // + // Force-dismounts cause handles to become invalid. Rather than always + // probe handles, a serial number of dismounts is maintained that clients + // can use to see if they need to probe handles. + // + + volatile ULONG DismountCount; + + // + // This field indicates the status of the 64-bit COM+ package on the + // system. It indicates whether the Intermediate Language (IL) COM+ + // images need to use the 64-bit COM+ runtime or the 32-bit COM+ runtime. + // + + ULONG ComPlusPackage; + + // + // Time in tick count for system-wide last user input across all terminal + // sessions. For MP performance, it is not updated all the time (e.g. once + // a minute per session). It is used for idle detection. + // + + ULONG LastSystemRITEventTickCount; + + // + // Number of physical pages in the system. This can dynamically change as + // physical memory can be added or removed from a running system. This + // cell is too small to hold the non-truncated value on very large memory + // machines so code that needs the full value should access + // FullNumberOfPhysicalPages instead. + // + + ULONG NumberOfPhysicalPages; + + // + // True if the system was booted in safe boot mode. + // + + BOOLEAN SafeBootMode; + + // + // Virtualization flags. + // + + union { + UCHAR VirtualizationFlags; + +#if defined(_ARM64_) + + // + // N.B. Keep this bitfield in sync with the one in arc.w. + // + + struct + { + UCHAR ArchStartedInEl2 : 1; + UCHAR QcSlIsSupported : 1; + UCHAR : 6; + }; + +#endif + }; + + // + // Reserved (available for reuse). + // + + UCHAR Reserved12[2]; + + // + // This is a packed bitfield that contains various flags concerning + // the system state. They must be manipulated using interlocked + // operations. + // + // N.B. DbgMultiSessionSku must be accessed via the RtlIsMultiSessionSku + // API for an accurate result + // + + union { + ULONG SharedDataFlags; + struct + { + // + // The following bit fields are for the debugger only. Do not use. + // Use the bit definitions instead. + // + + ULONG DbgErrorPortPresent : 1; + ULONG DbgElevationEnabled : 1; + ULONG DbgVirtEnabled : 1; + ULONG DbgInstallerDetectEnabled : 1; + ULONG DbgLkgEnabled : 1; + ULONG DbgDynProcessorEnabled : 1; + ULONG DbgConsoleBrokerEnabled : 1; + ULONG DbgSecureBootEnabled : 1; + ULONG DbgMultiSessionSku : 1; + ULONG DbgMultiUsersInSessionSku : 1; + ULONG DbgStateSeparationEnabled : 1; + ULONG DbgSplitTokenEnabled : 1; + ULONG DbgShadowAdminEnabled : 1; + ULONG SpareBits : 19; + }; + }; + + // ... more fields follow, but we don't need them +} KUSER_SHARED_DATA, *PKUSER_SHARED_DATA; + +#ifdef __aarch64__ + #define SharedUserData ({ \ + __auto_type shared_user_data = (const volatile KUSER_SHARED_DATA*) (uintptr_t) 0x7FFE0000UL; \ + __asm__("" : "+r"(shared_user_data)); /* https://github.com/lhmouse/mcfgthread/issues/330 */ \ + shared_user_data; \ + }) +#else + #define SharedUserData ((const volatile KUSER_SHARED_DATA*) (uintptr_t) 0x7FFE0000UL) +#endif + +static inline uint64_t ffKSystemTimeToUInt64(const volatile KSYSTEM_TIME* pTime) { +#if _WIN64 + + // This is safe even if pTime is not 8-byte aligned + // See https://learn.microsoft.com/en-us/windows/win32/winprog64/fault-alignments + return *(const volatile uint64_t*) pTime; + +#else + + uint32_t low, high1, high2; + + do { + high1 = (uint32_t) pTime->High1Time; + low = (uint32_t) pTime->LowPart; + high2 = (uint32_t) pTime->High2Time; + } while (high1 != high2); + + return ((uint64_t) high1 << 32) | low; +#endif +} + +static inline bool ffIsWindows10OrGreater() { +#if FF_WIN81_COMPAT + return SharedUserData->NtMajorVersion >= 10; +#else + return true; +#endif +} + +static inline bool ffIsWindows11OrGreater() { + return SharedUserData->NtMajorVersion > 10 || + (SharedUserData->NtMajorVersion == 10 && SharedUserData->NtBuildNumber >= 22000); +} + +NTSYSAPI NTSTATUS NTAPI NtOpenProcessToken( + _In_ HANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE TokenHandle); +NTSYSAPI NTSTATUS NTAPI NtAdjustPrivilegesToken( + _In_ HANDLE TokenHandle, + _In_ BOOLEAN DisableAllPrivileges, + _In_opt_ PTOKEN_PRIVILEGES NewState, + _In_ ULONG BufferLength, + _Out_writes_bytes_to_opt_(BufferLength, *ReturnLength) PTOKEN_PRIVILEGES PreviousState, + _Out_opt_ PULONG ReturnLength); +NTSYSAPI NTSTATUS NTAPI NtQueryInformationToken( + _In_ HANDLE TokenHandle, + _In_ TOKEN_INFORMATION_CLASS TokenInformationClass, + _Out_writes_bytes_to_opt_(TokenInformationLength, *ReturnLength) PVOID TokenInformation, + _In_ ULONG TokenInformationLength, + _Out_ PULONG ReturnLength); +#define NtCurrentProcessToken() ((HANDLE) (LONG_PTR) - 4) // for NtQueryInformationToken only; Windows 8+ + +NTSYSAPI NTSTATUS NTAPI NtReadFile( + _In_ HANDLE FileHandle, + _In_opt_ HANDLE Event, + _In_opt_ PIO_APC_ROUTINE ApcRoutine, + _In_opt_ PVOID ApcContext, + _Out_ PIO_STATUS_BLOCK IoStatusBlock, + _Out_writes_bytes_(Length) PVOID Buffer, + _In_ ULONG Length, + _In_opt_ PLARGE_INTEGER ByteOffset, + _In_opt_ PULONG Key); + +NTSYSAPI NTSTATUS NTAPI NtCreateEvent( + _Out_ PHANDLE EventHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ EVENT_TYPE EventType, + _In_ BOOLEAN InitialState); + +NTSYSAPI NTSTATUS NTAPI NtQueryAttributesFile( + _In_ PCOBJECT_ATTRIBUTES ObjectAttributes, + _Out_ PFILE_BASIC_INFORMATION FileInformation); + +NTSYSAPI NTSTATUS NTAPI RtlUnicodeToUTF8N( + _Out_writes_bytes_to_(UTF8StringMaxByteCount, *UTF8StringActualByteCount) PCHAR UTF8StringDestination, + _In_ ULONG UTF8StringMaxByteCount, + _Out_opt_ PULONG UTF8StringActualByteCount, + _In_reads_bytes_(UnicodeStringByteCount) PCWCH UnicodeStringSource, + _In_ ULONG UnicodeStringByteCount); + +NTSYSAPI NTSTATUS NTAPI RtlUTF8ToUnicodeN( + _Out_writes_bytes_to_(UnicodeStringMaxByteCount, *UnicodeStringActualByteCount) PWSTR UnicodeStringDestination, + _In_ ULONG UnicodeStringMaxByteCount, + _Out_opt_ PULONG UnicodeStringActualByteCount, + _In_reads_bytes_(UTF8StringByteCount) PCCH UTF8StringSource, + _In_ ULONG UTF8StringByteCount); + +#define RTL_MAX_DRIVE_LETTERS 32 +typedef struct _RTL_DRIVE_LETTER_CURDIR { + USHORT Flags; + USHORT Length; + ULONG TimeStamp; + STRING DosPath; +} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR; + +typedef struct _RTL_USER_PROCESS_PARAMETERS_FULL { + ULONG MaximumLength; + ULONG Length; + + ULONG Flags; + ULONG DebugFlags; + + HANDLE ConsoleHandle; + ULONG ConsoleFlags; + HANDLE StandardInput; + HANDLE StandardOutput; + HANDLE StandardError; + + CURDIR CurrentDirectory; + UNICODE_STRING DllPath; + UNICODE_STRING ImagePathName; + UNICODE_STRING CommandLine; + PVOID Environment; + + ULONG StartingX; + ULONG StartingY; + ULONG CountX; + ULONG CountY; + ULONG CountCharsX; + ULONG CountCharsY; + ULONG FillAttribute; + + ULONG WindowFlags; + ULONG ShowWindowFlags; + UNICODE_STRING WindowTitle; + UNICODE_STRING DesktopInfo; + UNICODE_STRING ShellInfo; + UNICODE_STRING RuntimeData; + RTL_DRIVE_LETTER_CURDIR CurrentDirectories[RTL_MAX_DRIVE_LETTERS]; + + // Windows Vista + ULONG_PTR EnvironmentSize; + // Windows 7 + ULONG_PTR EnvironmentVersion; + + // Windows 8 + PVOID PackageDependencyData; + ULONG ProcessGroupId; + + // ... +} RTL_USER_PROCESS_PARAMETERS_FULL, *PRTL_USER_PROCESS_PARAMETERS_FULL; + +typedef struct KERNEL_CALLBACK_TABLE* PKERNEL_CALLBACK_TABLE; +typedef struct API_SET_NAMESPACE* PAPI_SET_NAMESPACE; +typedef struct RTL_BITMAP* PRTL_BITMAP; +typedef struct SILO_USER_SHARED_DATA* PSILO_USER_SHARED_DATA; +typedef struct CPTABLEINFO* PCPTABLEINFO; +typedef struct NLSTABLEINFO* PNLSTABLEINFO; +typedef struct GDI_HANDLE_ENTRY* PGDI_HANDLE_ENTRY; + +typedef struct _PEB_FULL { + // + // The process was cloned with an inherited address space. + // + BOOLEAN InheritedAddressSpace; + + // + // The process has image file execution options (IFEO). + // + BOOLEAN ReadImageFileExecOptions; + + // + // The process has a debugger attached. + // + BOOLEAN BeingDebugged; + + union { + BOOLEAN BitField; + struct + { + BOOLEAN ImageUsesLargePages : 1; // The process uses large image regions (4 MB). + BOOLEAN IsProtectedProcess : 1; // The process is a protected process. + BOOLEAN IsImageDynamicallyRelocated : 1; // The process image base address was relocated. + BOOLEAN SkipPatchingUser32Forwarders : 1; // The process skipped forwarders for User32.dll functions. 1 for 64-bit, 0 for 32-bit. + BOOLEAN IsPackagedProcess : 1; // The process is a packaged store process (APPX/MSIX). + BOOLEAN IsAppContainerProcess : 1; // The process has an AppContainer token. + BOOLEAN IsProtectedProcessLight : 1; // The process is a protected process (light). + BOOLEAN IsLongPathAwareProcess : 1; // The process is long path aware. + }; + }; + + // + // Handle to a mutex for synchronization. + // + HANDLE Mutant; + + // + // Pointer to the base address of the process image. + // + PVOID ImageBaseAddress; + + // + // Pointer to the process loader data. + // + PPEB_LDR_DATA Ldr; + + // + // Pointer to the process parameters. + // + PRTL_USER_PROCESS_PARAMETERS_FULL ProcessParameters; + + // + // Reserved. + // + PVOID SubSystemData; + + // + // Pointer to the process default heap. + // + PVOID ProcessHeap; + + // + // Pointer to a critical section used to synchronize access to the PEB. + // + PRTL_CRITICAL_SECTION FastPebLock; + + // + // Pointer to a singly linked list used by ATL. + // + PSLIST_HEADER AtlThunkSListPtr; + + // + // Handle to the Image File Execution Options key. + // + HANDLE IFEOKey; + + // + // Cross process flags. + // + union { + ULONG CrossProcessFlags; + struct + { + ULONG ProcessInJob : 1; // The process is part of a job. + ULONG ProcessInitializing : 1; // The process is initializing. + ULONG ProcessUsingVEH : 1; // The process is using VEH. + ULONG ProcessUsingVCH : 1; // The process is using VCH. + ULONG ProcessUsingFTH : 1; // The process is using FTH. + ULONG ProcessPreviouslyThrottled : 1; // The process was previously throttled. + ULONG ProcessCurrentlyThrottled : 1; // The process is currently throttled. + ULONG ProcessImagesHotPatched : 1; // The process images are hot patched. // RS5 + ULONG ReservedBits0 : 24; + }; + }; + + // + // User32 KERNEL_CALLBACK_TABLE (ntuser.h) + // + union { + PKERNEL_CALLBACK_TABLE KernelCallbackTable; + PVOID UserSharedInfoPtr; + }; + + // + // Reserved. + // + ULONG SystemReserved; + + // + // Pointer to the Active Template Library (ATL) singly linked list (32-bit) + // + ULONG AtlThunkSListPtr32; + + // + // Pointer to the API Set Schema. + // + PAPI_SET_NAMESPACE ApiSetMap; + + // + // Counter for TLS expansion. + // + ULONG TlsExpansionCounter; + + // + // Pointer to the TLS bitmap. + // + PRTL_BITMAP TlsBitmap; + + // + // Bits for the TLS bitmap. + // + ULONG TlsBitmapBits[2]; + + // + // Reserved for CSRSS. + // + PVOID ReadOnlySharedMemoryBase; + + // + // Pointer to the USER_SHARED_DATA for the current SILO. + // + PSILO_USER_SHARED_DATA SharedData; + + // + // Reserved for CSRSS. + // + PVOID* ReadOnlyStaticServerData; + + // + // Pointer to the ANSI code page data. + // + PCPTABLEINFO AnsiCodePageData; + + // + // Pointer to the OEM code page data. + // + PCPTABLEINFO OemCodePageData; + + // + // Pointer to the Unicode case table data. + // + PNLSTABLEINFO UnicodeCaseTableData; + + // + // The total number of system processors. + // + ULONG NumberOfProcessors; + + // + // Global flags for the system. + // + union { + ULONG NtGlobalFlag; + struct + { + ULONG StopOnException : 1; // FLG_STOP_ON_EXCEPTION + ULONG ShowLoaderSnaps : 1; // FLG_SHOW_LDR_SNAPS + ULONG DebugInitialCommand : 1; // FLG_DEBUG_INITIAL_COMMAND + ULONG StopOnHungGUI : 1; // FLG_STOP_ON_HUNG_GUI + ULONG HeapEnableTailCheck : 1; // FLG_HEAP_ENABLE_TAIL_CHECK + ULONG HeapEnableFreeCheck : 1; // FLG_HEAP_ENABLE_FREE_CHECK + ULONG HeapValidateParameters : 1; // FLG_HEAP_VALIDATE_PARAMETERS + ULONG HeapValidateAll : 1; // FLG_HEAP_VALIDATE_ALL + ULONG ApplicationVerifier : 1; // FLG_APPLICATION_VERIFIER + ULONG MonitorSilentProcessExit : 1; // FLG_MONITOR_SILENT_PROCESS_EXIT + ULONG PoolEnableTagging : 1; // FLG_POOL_ENABLE_TAGGING + ULONG HeapEnableTagging : 1; // FLG_HEAP_ENABLE_TAGGING + ULONG UserStackTraceDb : 1; // FLG_USER_STACK_TRACE_DB + ULONG KernelStackTraceDb : 1; // FLG_KERNEL_STACK_TRACE_DB + ULONG MaintainObjectTypeList : 1; // FLG_MAINTAIN_OBJECT_TYPELIST + ULONG HeapEnableTagByDll : 1; // FLG_HEAP_ENABLE_TAG_BY_DLL + ULONG DisableStackExtension : 1; // FLG_DISABLE_STACK_EXTENSION + ULONG EnableCsrDebug : 1; // FLG_ENABLE_CSRDEBUG + ULONG EnableKDebugSymbolLoad : 1; // FLG_ENABLE_KDEBUG_SYMBOL_LOAD + ULONG DisablePageKernelStacks : 1; // FLG_DISABLE_PAGE_KERNEL_STACKS + ULONG EnableSystemCritBreaks : 1; // FLG_ENABLE_SYSTEM_CRIT_BREAKS + ULONG HeapDisableCoalescing : 1; // FLG_HEAP_DISABLE_COALESCING + ULONG EnableCloseExceptions : 1; // FLG_ENABLE_CLOSE_EXCEPTIONS + ULONG EnableExceptionLogging : 1; // FLG_ENABLE_EXCEPTION_LOGGING + ULONG EnableHandleTypeTagging : 1; // FLG_ENABLE_HANDLE_TYPE_TAGGING + ULONG HeapPageAllocs : 1; // FLG_HEAP_PAGE_ALLOCS + ULONG DebugInitialCommandEx : 1; // FLG_DEBUG_INITIAL_COMMAND_EX + ULONG DisableDbgPrint : 1; // FLG_DISABLE_DBGPRINT + ULONG CritSecEventCreation : 1; // FLG_CRITSEC_EVENT_CREATION + ULONG LdrTopDown : 1; // FLG_LDR_TOP_DOWN + ULONG EnableHandleExceptions : 1; // FLG_ENABLE_HANDLE_EXCEPTIONS + ULONG DisableProtDlls : 1; // FLG_DISABLE_PROTDLLS + } NtGlobalFlags; + }; + + // + // Timeout for critical sections. + // + LARGE_INTEGER CriticalSectionTimeout; + + // + // Reserved size for heap segments. + // + SIZE_T HeapSegmentReserve; + + // + // Committed size for heap segments. + // + SIZE_T HeapSegmentCommit; + + // + // Threshold for decommitting total free heap. + // + SIZE_T HeapDeCommitTotalFreeThreshold; + + // + // Threshold for decommitting free heap blocks. + // + SIZE_T HeapDeCommitFreeBlockThreshold; + + // + // Number of process heaps. + // + ULONG NumberOfHeaps; + + // + // Maximum number of process heaps. + // + ULONG MaximumNumberOfHeaps; + + // + // Pointer to an array of process heaps. ProcessHeaps is initialized + // to point to the first free byte after the PEB and MaximumNumberOfHeaps + // is computed from the page size used to hold the PEB, less the fixed + // size of this data structure. + // + PVOID* ProcessHeaps; + + // + // Pointer to the system GDI shared handle table. + // + PGDI_HANDLE_ENTRY GdiSharedHandleTable; + + // + // Pointer to the process starter helper. + // + PVOID ProcessStarterHelper; + + // + // The maximum number of GDI function calls during batch operations (GdiSetBatchLimit) + // + ULONG GdiDCAttributeList; + + // + // Pointer to the loader lock critical section. + // + PRTL_CRITICAL_SECTION LoaderLock; + + // + // Major version of the operating system. + // + ULONG OSMajorVersion; + + // + // Minor version of the operating system. + // + ULONG OSMinorVersion; + + // + // Build number of the operating system. + // + USHORT OSBuildNumber; + + // + // CSD version of the operating system. + // + USHORT OSCSDVersion; + + // + // Platform ID of the operating system. + // + ULONG OSPlatformId; + + // + // Subsystem version of the current process image (PE Headers). + // + ULONG ImageSubsystem; + + // + // Major version of the current process image subsystem (PE Headers). + // + ULONG ImageSubsystemMajorVersion; + + // + // Minor version of the current process image subsystem (PE Headers). + // + ULONG ImageSubsystemMinorVersion; + + // ... +} PEB_FULL, *PPEB_FULL; + +typedef struct _TEB_FULL { + // + // Thread Information Block (TIB) contains the thread's stack, base and limit addresses, the current stack pointer, and the exception list. + // + NT_TIB NtTib; + + // + // Reserved. + // + PVOID EnvironmentPointer; + + // + // Client ID for this thread. + // + CLIENT_ID ClientId; + + // + // A handle to an active Remote Procedure Call (RPC) if the thread is currently involved in an RPC operation. + // + PVOID ActiveRpcHandle; + + // + // A pointer to the __declspec(thread) local storage array. + // + PVOID ThreadLocalStoragePointer; + + // + // A pointer to the Process Environment Block (PEB), which contains information about the process. + // + PPEB_FULL ProcessEnvironmentBlock; + + // + // The previous Win32 error value for this thread. + // + ULONG LastErrorValue; + + // + // The number of critical sections currently owned by this thread. + // + ULONG CountOfOwnedCriticalSections; + + // + // Reserved. + // + PVOID CsrClientThread; + + // + // Reserved for win32k.sys + // + PVOID Win32ThreadInfo; + + // + // Reserved for user32.dll + // + ULONG User32Reserved[26]; + + // + // Reserved for winsrv.dll + // + ULONG UserReserved[5]; + + // + // Reserved. + // + PVOID WOW32Reserved; + + // + // The LCID of the current thread. (Kernel32!GetThreadLocale) + // + LCID CurrentLocale; +} TEB_FULL, *PTEB_FULL; + +static inline PTEB_FULL ffGetTeb() { + return (PTEB_FULL) NtCurrentTeb(); +} + +static inline PPEB_FULL ffGetPeb() { + return ffGetTeb()->ProcessEnvironmentBlock; +} + +NTSYSAPI NTSTATUS NTAPI RtlExpandEnvironmentStrings( + _In_opt_ PVOID Environment, + _In_reads_(SourceLength) PCWSTR Source, + _In_ SIZE_T SourceLength, + _Out_writes_(DestinationLength) PWSTR Destination, + _In_ SIZE_T DestinationLength, + _Out_opt_ PSIZE_T ReturnLength); + +NTSYSAPI NTSTATUS NTAPI NtOpenKey( + _Out_ PHANDLE KeyHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes); + +typedef enum _KEY_VALUE_INFORMATION_CLASS { + KeyValueBasicInformation, // KEY_VALUE_BASIC_INFORMATION + KeyValueFullInformation, // KEY_VALUE_FULL_INFORMATION + KeyValuePartialInformation, // KEY_VALUE_PARTIAL_INFORMATION + KeyValueFullInformationAlign64, // KEY_VALUE_FULL_INFORMATION_ALIGN64 + KeyValuePartialInformationAlign64, // KEY_VALUE_PARTIAL_INFORMATION_ALIGN64 + KeyValueLayerInformation, // KEY_VALUE_LAYER_INFORMATION + MaxKeyValueInfoClass +} KEY_VALUE_INFORMATION_CLASS; + +NTSYSAPI NTSTATUS NTAPI NtQueryValueKey( + _In_ HANDLE KeyHandle, + _In_ PCUNICODE_STRING ValueName, + _In_ KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, + _Out_writes_bytes_to_opt_(Length, *ResultLength) PVOID KeyValueInformation, + _In_ ULONG Length, + _Out_ PULONG ResultLength); + +NTSYSAPI NTSTATUS NTAPI RtlOpenCurrentUser( + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE CurrentUserKey); + +typedef struct _KEY_VALUE_PARTIAL_INFORMATION { + ULONG TitleIndex; + ULONG Type; + ULONG DataLength; + _Field_size_bytes_(DataLength) UCHAR Data[]; +} KEY_VALUE_PARTIAL_INFORMATION, *PKEY_VALUE_PARTIAL_INFORMATION; + +typedef enum _KEY_INFORMATION_CLASS { + KeyBasicInformation, // KEY_BASIC_INFORMATION + KeyNodeInformation, // KEY_NODE_INFORMATION + KeyFullInformation, // KEY_FULL_INFORMATION + KeyNameInformation, // KEY_NAME_INFORMATION + KeyCachedInformation, // KEY_CACHED_INFORMATION + KeyFlagsInformation, // KEY_FLAGS_INFORMATION + KeyVirtualizationInformation, // KEY_VIRTUALIZATION_INFORMATION + KeyHandleTagsInformation, // KEY_HANDLE_TAGS_INFORMATION + KeyTrustInformation, // KEY_TRUST_INFORMATION + KeyLayerInformation, // KEY_LAYER_INFORMATION + MaxKeyInfoClass +} KEY_INFORMATION_CLASS; + +NTSYSAPI NTSTATUS NTAPI NtEnumerateKey( + _In_ HANDLE KeyHandle, + _In_ ULONG Index, + _In_ KEY_INFORMATION_CLASS KeyInformationClass, + _Out_writes_bytes_to_opt_(Length, *ResultLength) PVOID KeyInformation, + _In_ ULONG Length, + _Out_ PULONG ResultLength); + +typedef struct _KEY_BASIC_INFORMATION { + LARGE_INTEGER LastWriteTime; // Number of 100-nanosecond intervals since this key or any of its values changed. + ULONG TitleIndex; // Reserved // A legacy field originally intended for use with localization such as an index of a resource table. + ULONG NameLength; // The size, in bytes, of the key name string in the Name array. + _Field_size_bytes_(NameLength) WCHAR Name[]; // The name of the registry key. This string is not null-terminated. +} KEY_BASIC_INFORMATION, *PKEY_BASIC_INFORMATION; + +typedef struct _KEY_FULL_INFORMATION { + LARGE_INTEGER LastWriteTime; + ULONG TitleIndex; + ULONG ClassOffset; + ULONG ClassLength; + ULONG SubKeys; + ULONG MaxNameLength; + ULONG MaxClassLength; + ULONG Values; + ULONG MaxValueNameLength; + ULONG MaxValueDataLength; + WCHAR Class[]; +} KEY_FULL_INFORMATION, *PKEY_FULL_INFORMATION; + +NTSYSAPI NTSTATUS NTAPI NtQueryKey( + _In_ HANDLE KeyHandle, + _In_ KEY_INFORMATION_CLASS KeyInformationClass, + _Out_writes_bytes_to_opt_(Length, *ResultLength) PVOID KeyInformation, + _In_ ULONG Length, + _Out_ PULONG ResultLength); + +NTSYSAPI NTSTATUS NTAPI NtOpenProcess( + _Out_ PHANDLE ProcessHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ PCOBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ PCLIENT_ID ClientId); + +NTSYSAPI NTSTATUS NTAPI LdrLoadDll( + _In_opt_ PCWSTR DllPath, + _In_opt_ PULONG DllCharacteristics, + _In_ PCUNICODE_STRING DllName, + _Out_ PVOID* DllHandle); + +NTSYSAPI NTSTATUS NTAPI LdrUnloadDll( + _In_ PVOID DllHandle); + +NTSYSAPI NTSTATUS NTAPI LdrGetDllHandle( + _In_opt_ PCWSTR DllPath, + _In_opt_ PULONG DllCharacteristics, + _In_ PCUNICODE_STRING DllName, + _Out_ PVOID* DllHandle); + +NTSYSAPI NTSTATUS NTAPI LdrGetProcedureAddress( + _In_ PVOID DllHandle, + _In_opt_ PCANSI_STRING ProcedureName, + _In_opt_ ULONG ProcedureNumber, + _Out_ PVOID* ProcedureAddress); + +typedef enum _SECTION_INHERIT { + ViewShare = 1, + ViewUnmap = 2 +} SECTION_INHERIT; + +NTSYSAPI NTSTATUS NTAPI NtCreateSection( + _Out_ PHANDLE SectionHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_opt_ PLARGE_INTEGER MaximumSize, + _In_ ULONG SectionPageProtection, + _In_ ULONG AllocationAttributes, + _In_opt_ HANDLE FileHandle); + +NTSYSAPI NTSTATUS NTAPI NtMapViewOfSection( + _In_ HANDLE SectionHandle, + _In_ HANDLE ProcessHandle, + _Inout_ _At_(*BaseAddress, _Readable_bytes_(*ViewSize) _Writable_bytes_(*ViewSize) _Post_readable_byte_size_(*ViewSize)) PVOID* BaseAddress, + _In_ ULONG_PTR ZeroBits, + _In_ SIZE_T CommitSize, + _Inout_opt_ PLARGE_INTEGER SectionOffset, + _Inout_ PSIZE_T ViewSize, + _In_ SECTION_INHERIT InheritDisposition, + _In_ ULONG AllocationType, + _In_ ULONG PageProtection); + +NTSYSAPI NTSTATUS NTAPI NtUnmapViewOfSection( + _In_ HANDLE ProcessHandle, + _In_opt_ PVOID BaseAddress); + +NTSYSAPI LOGICAL NTAPI RtlQueryPerformanceCounter( + _Out_ PLARGE_INTEGER PerformanceCounter); + +NTSYSAPI LOGICAL NTAPI RtlQueryPerformanceFrequency( + _Out_ PLARGE_INTEGER PerformanceFrequency); + +NTSYSAPI NTSTATUS NTAPI NtCancelIoFileEx( + _In_ HANDLE FileHandle, + _In_opt_ PIO_STATUS_BLOCK IoRequestToCancel, + _Out_ PIO_STATUS_BLOCK IoStatusBlock); + +NTSYSAPI NTSTATUS NTAPI NtTerminateProcess( + _In_opt_ HANDLE ProcessHandle, + _In_ NTSTATUS ExitStatus); diff --git a/src/common/windows/perflib_.h b/src/common/windows/perflib_.h new file mode 100644 index 0000000..f30ed54 --- /dev/null +++ b/src/common/windows/perflib_.h @@ -0,0 +1,134 @@ +#pragma once + +#include <windows.h> +#include <perflib.h> + +// Missing from <perflib.h> of MinGW-w64 SDK + +#define PERF_WILDCARD_COUNTER 0xFFFFFFFF +#define PERF_WILDCARD_INSTANCE L"*" +#define PERF_AGGREGATE_INSTANCE L"_Total" +#define PERF_MAX_INSTANCE_NAME 1024 + +typedef struct _PERF_INSTANCE_HEADER { + ULONG Size; // = sizeof(PERF_INSTANCE_HEADER) + sizeof(InstanceName) + sizeof(Padding) + ULONG InstanceId; // Instance ID. + // Followed by: + // WCHAR InstanceName[]; // Nul-terminated. + // WCHAR Padding[]; // Pad to a multiple of 8 bytes +} PERF_INSTANCE_HEADER, *PPERF_INSTANCE_HEADER; + +typedef struct _PERF_COUNTER_IDENTIFIER { + GUID CounterSetGuid; // The GUID of the counterset. + ULONG Status; // Win32 error code indicating success/failure of the add/delete operation. + ULONG Size; // sizeof(PERF_COUNTER_IDENTIFIER) + sizeof(InstanceName) + sizeof(Padding) + ULONG CounterId; // CounterId, or PERF_WILDCARD_COUNTER for all counters. + ULONG InstanceId; // InstanceId, or 0xFFFFFFFF to not filter on instance ID. + ULONG Index; // Set by PerfQueryCounterInfo to the position in which the corresponding counter data is returned. + ULONG Reserved; // Reserved. + // Followed by: + // WCHAR InstanceName[]; + // WCHAR Padding[]; +} PERF_COUNTER_IDENTIFIER, *PPERF_COUNTER_IDENTIFIER; + +typedef struct _PERF_DATA_HEADER { + ULONG dwTotalSize; // = sizeof(PERF_DATA_HEADER) + sizeof(PERF_COUNTER_HEADER blocks...) + ULONG dwNumCounters; // The number of PERF_COUNTER_HEADER blocks. + LONGLONG PerfTimeStamp; // Timestamp from a high-resolution clock. + LONGLONG PerfTime100NSec; // The number of 100 nanosecond intervals since January 1, 1601, in Coordinated Universal Time (UTC). + LONGLONG PerfFreq; // The frequency of a high-resolution clock. + SYSTEMTIME SystemTime; // The time at which data is collected on the provider side. + // Followed by: + // PERF_COUNTER_HEADER blocks...; +} PERF_DATA_HEADER, *PPERF_DATA_HEADER; + +typedef enum _PerfCounterDataType { + PERF_ERROR_RETURN = 0, /* An error occurred when the performance counter value was queried. */ + PERF_SINGLE_COUNTER = 1, /* Query returned a single counter from a single-instance. */ + PERF_MULTIPLE_COUNTERS = 2, /* Query returned multiple counters from a single instance. */ + PERF_MULTIPLE_INSTANCES = 4, /* Query returned a single counter from each of multiple instances. */ + PERF_COUNTERSET = 6 /* Query returned multiple counters from each of multiple instances. */ +} PerfCounterDataType; + +typedef struct _PERF_COUNTER_HEADER { + ULONG dwStatus; // Win32 error code indicating success/failure of the query operation. + PerfCounterDataType dwType; // Result type - error, single/single, multi/single, single/multi, multi/multi. + ULONG dwSize; // = sizeof(PERF_COUNTER_HEADER) + sizeof(Additional data) + ULONG Reserved; // Reserved. + // Followed by additional data: + // If dwType == PERF_ERROR_RETURN: nothing. + // If dwType == PERF_SINGLE_COUNTER: PERF_COUNTER_DATA block. + // If dwType == PERF_MULTIPLE_COUNTERS: PERF_MULTI_COUNTERS block + PERF_COUNTER_DATA blocks. + // If dwType == PERF_MULTIPLE_INSTANCES: PERF_MULTI_INSTANCES block. + // If dwType == PERF_COUNTERSET: PERF_MULTI_COUNTERS block + PERF_MULTI_INSTANCES block. +} PERF_COUNTER_HEADER, *PPERF_COUNTER_HEADER; + +typedef struct _PERF_MULTI_INSTANCES { + ULONG dwTotalSize; // = sizeof(PERF_MULTI_INSTANCES) + sizeof(instance data blocks...) + ULONG dwInstances; // Number of instance data blocks. + // Followed by: + // Instance data blocks...; +} PERF_MULTI_INSTANCES, *PPERF_MULTI_INSTANCES; + +typedef struct _PERF_MULTI_COUNTERS { + ULONG dwSize; // sizeof(PERF_MULTI_COUNTERS) + sizeof(CounterIds) + ULONG dwCounters; // Number of counter ids. + // Followed by: + // DWORD CounterIds[dwCounters]; +} PERF_MULTI_COUNTERS, *PPERF_MULTI_COUNTERS; + +typedef struct _PERF_COUNTER_DATA { + ULONG dwDataSize; // Size of the counter data, in bytes. + ULONG dwSize; // = sizeof(PERF_COUNTER_DATA) + sizeof(Data) + sizeof(Padding) + // Followed by: + // BYTE Data[dwDataSize]; + // BYTE Padding[]; +} PERF_COUNTER_DATA, *PPERF_COUNTER_DATA; + +_Success_(return == ERROR_SUCCESS) + ULONG + WINAPI + PerfEnumerateCounterSetInstances( + _In_opt_z_ LPCWSTR szMachine, + _In_ LPCGUID pCounterSetId, + _Out_opt_bytecap_post_bytecount_(cbInstances, *pcbInstancesActual) PPERF_INSTANCE_HEADER pInstances, + DWORD cbInstances, + _Out_ LPDWORD pcbInstancesActual); + +_Success_(return == ERROR_SUCCESS) + ULONG + WINAPI + PerfOpenQueryHandle( + _In_opt_z_ LPCWSTR szMachine, + _Out_ HANDLE* phQuery); + +_Success_(return == ERROR_SUCCESS) + ULONG + WINAPI + PerfCloseQueryHandle( + _In_ HANDLE hQuery); + +_Success_(return == ERROR_SUCCESS) + ULONG + WINAPI + PerfAddCounters( + _In_ HANDLE hQuery, + _Inout_bytecount_(cbCounters) PPERF_COUNTER_IDENTIFIER pCounters, + DWORD cbCounters); + +_Success_(return == ERROR_SUCCESS) + ULONG + WINAPI + PerfDeleteCounters( + _In_ HANDLE hQuery, + _Inout_bytecount_(cbCounters) PPERF_COUNTER_IDENTIFIER pCounters, + DWORD cbCounters); + +_Success_(return == ERROR_SUCCESS) + ULONG + WINAPI + PerfQueryCounterData( + _In_ HANDLE hQuery, + _Out_opt_bytecap_post_bytecount_(cbCounterBlock, *pcbCounterBlockActual) PPERF_DATA_HEADER pCounterBlock, + DWORD cbCounterBlock, + _Out_ LPDWORD pcbCounterBlockActual); diff --git a/src/common/windows/registry.c b/src/common/windows/registry.c new file mode 100644 index 0000000..3618760 --- /dev/null +++ b/src/common/windows/registry.c @@ -0,0 +1,388 @@ +#include "registry.h" +#include "unicode.h" +#include "common/mallocHelper.h" +#include "common/debug.h" +#include "common/windows/nt.h" + +#include <stdalign.h> +#include <ntstatus.h> + +static HANDLE hRootKeys[8 /*(uintptr_t) HKEY_CURRENT_USER_LOCAL_SETTINGS - (uintptr_t) HKEY_CLASSES_ROOT + 1*/]; + +static const char* hKey2Str(HANDLE hRootKey) { +#define HKEY_CASE(compareKey) \ + if (hRootKey == hRootKeys[(uintptr_t) compareKey - (uintptr_t) HKEY_CLASSES_ROOT]) return #compareKey; + HKEY_CASE(HKEY_CLASSES_ROOT) + HKEY_CASE(HKEY_CURRENT_USER) + HKEY_CASE(HKEY_LOCAL_MACHINE) + HKEY_CASE(HKEY_USERS) + HKEY_CASE(HKEY_PERFORMANCE_DATA) + HKEY_CASE(HKEY_CURRENT_CONFIG) + HKEY_CASE(HKEY_DYN_DATA) + HKEY_CASE(HKEY_CURRENT_USER_LOCAL_SETTINGS) +#undef HKEY_CASE + + return "UNKNOWN"; +} + +HANDLE ffRegGetRootKeyHandle(HKEY hKey) { + assert(hKey); + assert((uintptr_t) hKey >= (uintptr_t) HKEY_CLASSES_ROOT && (uintptr_t) hKey <= (uintptr_t) HKEY_CURRENT_USER_LOCAL_SETTINGS); + + FF_DEBUG("Getting root key handle for HKEY %08llx", (uint64_t) (uintptr_t) hKey); + + HANDLE result = hRootKeys[(uintptr_t) hKey - (uintptr_t) HKEY_CLASSES_ROOT]; + if (result) { + FF_DEBUG("Found cached root key handle for %s -> %p", hKey2Str(result), result); + return result; + } + + switch ((uintptr_t) hKey) { + case (uintptr_t) HKEY_CURRENT_USER: { + NTSTATUS status = RtlOpenCurrentUser(KEY_READ, &result); + if (!NT_SUCCESS(status)) { + FF_DEBUG("RtlOpenCurrentUser() failed: %s", ffDebugNtStatus(status)); + return NULL; + } + break; + } + + case (uintptr_t) HKEY_LOCAL_MACHINE: { + UNICODE_STRING path = RTL_CONSTANT_STRING(L"\\Registry\\Machine"); + NTSTATUS status = NtOpenKey(&result, KEY_READ, &(OBJECT_ATTRIBUTES) { + .Length = sizeof(OBJECT_ATTRIBUTES), + .RootDirectory = NULL, + .ObjectName = &path, + .Attributes = OBJ_CASE_INSENSITIVE, + }); + if (!NT_SUCCESS(status)) { + FF_DEBUG("NtOpenKey(%ls) failed: %s (0x%08lx)", path.Buffer, ffDebugNtStatus(status), status); + return NULL; + } + break; + } + default: + // Unsupported + FF_DEBUG("Unsupported root key: %p", hKey); + assert(false); + return NULL; + } + hRootKeys[(uintptr_t) hKey - (uintptr_t) HKEY_CLASSES_ROOT] = result; + FF_DEBUG("Opened root key %s -> %p", hKey2Str(result), result); + return result; +} + +bool ffRegOpenSubkeyForRead(HANDLE hKey, const wchar_t* subKeyW, HANDLE* result, FFstrbuf* error) { + assert(hKey); + assert(subKeyW); + assert(result); + + FF_DEBUG("Opening subkey %s\\%ls for read", hKey2Str(hKey), subKeyW); + + USHORT subKeyLen = (USHORT) (wcslen(subKeyW) * sizeof(wchar_t)); + if (!NT_SUCCESS(NtOpenKey(result, KEY_READ, &(OBJECT_ATTRIBUTES) { + .Length = sizeof(OBJECT_ATTRIBUTES), + .RootDirectory = hKey, + .ObjectName = &(UNICODE_STRING) { + .Length = subKeyLen, + .MaximumLength = subKeyLen + (USHORT) sizeof(wchar_t), + .Buffer = (wchar_t*) subKeyW, + }, + }))) { + FF_DEBUG("NtOpenKey(%s\\<subkey>) failed", hKey2Str(hKey)); + if (error) { + FF_STRBUF_AUTO_DESTROY subKeyA = ffStrbufCreateWS(subKeyW); + ffStrbufAppendF(error, "NtOpenKey(%s\\%s) failed", hKey2Str(hKey), subKeyA.chars); + } + return false; + } + FF_DEBUG("Opened subkey under %s -> %p", hKey2Str(hKey), *result); + return true; +} + +static bool processRegValue(const FFRegValueArg* arg, const ULONG regType, const void* regData, ULONG regDataLen, FFstrbuf* error) { + switch (arg->type) { + case FF_ARG_TYPE_STRBUF: { + if (regType != REG_SZ && regType != REG_EXPAND_SZ) { + goto type_mismatch; + } + + FFstrbuf* strbuf = (FFstrbuf*) arg->value; + uint32_t strLen = regDataLen / sizeof(wchar_t); + if (strLen == 0) { + ffStrbufClear(strbuf); + } else { + const wchar_t* ws = (const wchar_t*) regData; + if (ws[strLen - 1] == L'\0') { + --strLen; + } + ffStrbufSetNWS(strbuf, strLen, ws); + } + break; + } + + case FF_ARG_TYPE_UINT: + case FF_ARG_TYPE_UINT64: + case FF_ARG_TYPE_UINT16: + case FF_ARG_TYPE_UINT8: + case FF_ARG_TYPE_BOOL: { + uint64_t value = 0; + + if (regType == REG_DWORD) { + if (regDataLen < sizeof(uint32_t)) { + goto type_mismatch; + } + value = *(uint32_t*) regData; + } else if (regType == REG_QWORD) { + if (regDataLen < sizeof(uint64_t)) { + goto type_mismatch; + } + value = *(uint64_t*) regData; + } else { + goto type_mismatch; + } + + if (arg->type == FF_ARG_TYPE_UINT) { + *(uint32_t*) arg->value = (uint32_t) value; + } else if (arg->type == FF_ARG_TYPE_UINT64) { + *(uint64_t*) arg->value = (uint64_t) value; + } else if (arg->type == FF_ARG_TYPE_UINT16) { + *(uint16_t*) arg->value = (uint16_t) value; + } else if (arg->type == FF_ARG_TYPE_UINT8) { + *(uint8_t*) arg->value = (uint8_t) value; + } else if (arg->type == FF_ARG_TYPE_BOOL) { + *(bool*) arg->value = value != 0; + } + break; + } + + case FF_ARG_TYPE_FLOAT: { + if (regDataLen < sizeof(float)) { + goto type_mismatch; + } + *(float*) arg->value = *(float*) regData; + break; + } + + case FF_ARG_TYPE_DOUBLE: { + if (regDataLen < sizeof(double)) { + goto type_mismatch; + } + *(double*) arg->value = *(double*) regData; + break; + } + + case FF_ARG_TYPE_LIST: { + if (regType != REG_MULTI_SZ) { + goto type_mismatch; + } + + FFlist* list = (FFlist*) arg->value; + ffListClear(list); + + for ( + const wchar_t* ptr = (const wchar_t*) regData; + (const uint8_t*) ptr < (const uint8_t*) regData + regDataLen && *ptr; + ptr++) { + uint32_t strLen = (uint32_t) wcsnlen(ptr, regDataLen / sizeof(wchar_t) - (size_t) (ptr - (const wchar_t*) regData)); + ffStrbufInitNWS(FF_LIST_ADD(FFstrbuf, *list), strLen, ptr); + ptr += strLen; + } + break; + } + + case FF_ARG_TYPE_BUFFER: { + FFArgBuffer* buffer = (FFArgBuffer*) arg->value; + if (buffer->length == 0) { + buffer->data = malloc(regDataLen); + } else if (buffer->length < regDataLen) { + if (error) { + FF_STRBUF_AUTO_DESTROY nameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)"); + ffStrbufAppendF(error, "ffRegReadValues(%s) buffer too small (%u): expected %u", nameA.chars, (unsigned) buffer->length, (unsigned) regDataLen); + } + return false; + } + buffer->length = regDataLen; + memcpy(buffer->data, regData, regDataLen); + break; + } + + case FF_ARG_TYPE_INT: // Use UINT instead + case FF_ARG_TYPE_STRING: + case FF_ARG_TYPE_NULL: + default: + if (error) { + FF_STRBUF_AUTO_DESTROY nameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)"); + ffStrbufAppendF(error, "processRegValue(%s) unsupported FFArgType %u", nameA.chars, (unsigned) arg->type); + } + return false; + } + + return true; + +type_mismatch: + FF_DEBUG("ffRegReadValues(%ls) type mismatch: regType=%u, argType=%u, dataLen=%u", + arg->name ?: L"(default)", + (unsigned) regType, + (unsigned) arg->type, + (unsigned) regDataLen); + if (error) { + FF_STRBUF_AUTO_DESTROY nameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)"); + ffStrbufAppendF(error, "ffRegReadValues(%s) type mismatch: regType=%u, argType=%u, dataLen=%u", nameA.chars, (unsigned) regType, (unsigned) arg->type, (unsigned) regDataLen); + } + return false; +} + +bool ffRegReadValue(HANDLE hKey, const FFRegValueArg* arg, FFstrbuf* error) { + UNICODE_STRING* valueNameU = &(UNICODE_STRING) { + .Length = arg->name ? (USHORT) (wcslen(arg->name) * sizeof(wchar_t)) : 0 /*(default)*/, + .MaximumLength = 0, + .Buffer = (wchar_t*) arg->name, + }; + + alignas(KEY_VALUE_PARTIAL_INFORMATION) uint8_t staticBuffer[128 + sizeof(KEY_VALUE_PARTIAL_INFORMATION)]; + FF_AUTO_FREE uint8_t* dynamicBuffer = NULL; + + KEY_VALUE_PARTIAL_INFORMATION* buffer = (KEY_VALUE_PARTIAL_INFORMATION*) &staticBuffer; + DWORD bufSize = sizeof(staticBuffer); + if (NT_SUCCESS(NtQueryValueKey(hKey, valueNameU, KeyValuePartialInformation, buffer, bufSize, &bufSize))) { + goto process_value; + } + + if (bufSize == 0) { + FF_DEBUG("NtQueryValueKey(%p, %ls) failed (bufSize=0)", hKey, arg->name ?: L"(default)"); + if (error) { + FF_STRBUF_AUTO_DESTROY valueNameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)"); + ffStrbufAppendF(error, "NtQueryValueKey(%p, %s) failed", hKey, valueNameA.chars); + } + return false; + } + + dynamicBuffer = (uint8_t*) malloc(bufSize); + buffer = (KEY_VALUE_PARTIAL_INFORMATION*) dynamicBuffer; + + if (!NT_SUCCESS(NtQueryValueKey(hKey, valueNameU, KeyValuePartialInformation, buffer, bufSize, &bufSize))) { + FF_DEBUG("NtQueryValueKey(%p, %ls, buffer=%u) failed", hKey, arg->name ?: L"(default)", (unsigned) bufSize); + if (error) { + FF_STRBUF_AUTO_DESTROY valueNameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)"); + ffStrbufAppendF(error, "NtQueryValueKey(%p, %s, buffer) failed", hKey, valueNameA.chars); + } + return false; + } + +process_value: + FF_DEBUG("Read value from %p (%ls), type=%u, len=%u", hKey, arg->name ?: L"(default)", (unsigned) buffer->Type, (unsigned) buffer->DataLength); + return processRegValue(arg, buffer->Type, buffer->Data, buffer->DataLength, error); +} + +bool ffRegReadValues(HANDLE hKey, uint32_t argc, const FFRegValueArg argv[], FFstrbuf* error) { + if (__builtin_expect(argc == 0, false)) { + return true; + } + + assert(argv); + + FF_AUTO_FREE UNICODE_STRING* names = (UNICODE_STRING*) calloc(argc, sizeof(*names)); + FF_AUTO_FREE KEY_VALUE_ENTRY* entries = (KEY_VALUE_ENTRY*) calloc(argc, sizeof(*entries)); + + for (uint32_t i = 0; i < argc; ++i) { + if (__builtin_expect(!argv[i].value, false)) { + FF_DEBUG("ffRegReadValues(argv[%u].value) is NULL", (unsigned) i); + if (error) { + ffStrbufAppendF(error, "ffRegReadValues(argv[%u].pVar) is NULL", (unsigned) i); + } + return false; + } + + names[i] = (UNICODE_STRING) { + .Length = argv[i].name ? (USHORT) (wcslen(argv[i].name) * sizeof(wchar_t)) : 0 /*(default)*/, + .MaximumLength = 0, + .Buffer = (wchar_t*) argv[i].name, + }; + entries[i].ValueName = &names[i]; + } + + ULONG bufferSize = argc * 128; + if (bufferSize < 512) { + bufferSize = 512; + } + + FF_AUTO_FREE uint8_t* buffer = NULL; + + while (true) { + buffer = (uint8_t*) realloc(buffer, bufferSize); + + ULONG writtenSize = bufferSize; + ULONG requiredSize = 0; + NTSTATUS status = NtQueryMultipleValueKey(hKey, entries, argc, buffer, &writtenSize, &requiredSize); + + if (!NT_SUCCESS(status)) { + // Buffer too small: docs guarantee requiredSize is returned when provided. + if (requiredSize > bufferSize) { + FF_DEBUG("NtQueryMultipleValueKey(%p) resize buffer: %u -> %u", hKey, (unsigned) bufferSize, (unsigned) requiredSize); + bufferSize = requiredSize; + continue; + } + + FF_DEBUG("NtQueryMultipleValueKey(%p, argc=%u) failed, status=0x%08X", hKey, (unsigned) argc, (unsigned) status); + if (error) { + ffStrbufAppendF(error, "NtQueryMultipleValueKey(%p, argc=%u) failed, status=0x%08X", hKey, (unsigned) argc, (unsigned) status); + } + return false; + } + + break; + } + + for (uint32_t i = 0; i < argc; ++i) { + const FFRegValueArg* arg = &argv[i]; + const KEY_VALUE_ENTRY* entry = &entries[i]; + + FF_DEBUG("Read value[%u] from %p: type=%u, len=%u", (unsigned) i, hKey, (unsigned) entry->Type, (unsigned) entry->DataLength); + if (!processRegValue(arg, entry->Type, buffer + entry->DataOffset, entry->DataLength, error)) { + return false; + } + } + + return true; +} + +bool ffRegGetSubKey(HANDLE hKey, uint32_t index, FFstrbuf* result, FFstrbuf* error) { + assert(hKey); + assert(result); + + alignas(KEY_BASIC_INFORMATION) uint8_t buffer[sizeof(KEY_BASIC_INFORMATION) + MAX_PATH * sizeof(wchar_t)]; + ULONG bufSize = (ULONG) sizeof(buffer); + KEY_BASIC_INFORMATION* keyInfo = (KEY_BASIC_INFORMATION*) buffer; + + if (!NT_SUCCESS(NtEnumerateKey(hKey, index, KeyBasicInformation, keyInfo, bufSize, &bufSize))) { + FF_DEBUG("NtEnumerateKey(hKey=%p, index=%u) failed", hKey, (unsigned) index); + if (error) { + ffStrbufAppendF(error, "NtEnumerateKey(hKey=%p, %u, keyInfo) failed", hKey, (unsigned) index); + } + return false; + } + + ffStrbufSetNWS(result, keyInfo->NameLength / sizeof(wchar_t), keyInfo->Name); + return true; +} + +bool ffRegGetNSubKeys(HANDLE hKey, uint32_t* result, FFstrbuf* error) { + assert(hKey); + assert(result); + + alignas(KEY_FULL_INFORMATION) uint8_t buffer[sizeof(KEY_FULL_INFORMATION) + MAX_PATH * sizeof(wchar_t)]; + ULONG bufSize = sizeof(buffer); + KEY_FULL_INFORMATION* keyInfo = (KEY_FULL_INFORMATION*) buffer; + + if (!NT_SUCCESS(NtQueryKey(hKey, KeyFullInformation, keyInfo, bufSize, &bufSize))) { + FF_DEBUG("NtQueryKey(hKey=%p, KeyFullInformation) failed", hKey); + if (error) { + ffStrbufAppendF(error, "NtQueryKey(hKey=%p, KeyFullInformation, keyInfo) failed", hKey); + } + return false; + } + + *result = (uint32_t) keyInfo->SubKeys; + return true; +} diff --git a/src/common/windows/registry.h b/src/common/windows/registry.h new file mode 100644 index 0000000..9745372 --- /dev/null +++ b/src/common/windows/registry.h @@ -0,0 +1,66 @@ +#pragma once + +#include "fastfetch.h" +#include "common/argType.h" +#include "common/io.h" + +#ifndef HKEY_CURRENT_USER + #define HKEY_CLASSES_ROOT ((HKEY) (ULONG_PTR) ((LONG) 0x80000000)) + #define HKEY_CURRENT_USER ((HKEY) (ULONG_PTR) ((LONG) 0x80000001)) + #define HKEY_LOCAL_MACHINE ((HKEY) (ULONG_PTR) ((LONG) 0x80000002)) + #define HKEY_USERS ((HKEY) (ULONG_PTR) ((LONG) 0x80000003)) + #define HKEY_PERFORMANCE_DATA ((HKEY) (ULONG_PTR) ((LONG) 0x80000004)) + #define HKEY_CURRENT_CONFIG ((HKEY) (ULONG_PTR) ((LONG) 0x80000005)) + #define HKEY_DYN_DATA ((HKEY) (ULONG_PTR) ((LONG) 0x80000006)) + #define HKEY_CURRENT_USER_LOCAL_SETTINGS ((HKEY) (ULONG_PTR) ((LONG) 0x80000007)) +#endif + +typedef struct FFRegValueArg { + FFArgType type; + const void* value; + const wchar_t* name; +} FFRegValueArg; + +HANDLE ffRegGetRootKeyHandle(HKEY hKey); +bool ffRegOpenSubkeyForRead(HANDLE hKey, const wchar_t* subKeyW, HANDLE* result, FFstrbuf* error); +bool ffRegReadValue(HANDLE hKey, const FFRegValueArg* arg, FFstrbuf* error); +bool ffRegReadValues(HANDLE hKey, uint32_t argc, const FFRegValueArg argv[], FFstrbuf* error); +bool ffRegGetSubKey(HANDLE hKey, uint32_t index, FFstrbuf* result, FFstrbuf* error); +bool ffRegGetNSubKeys(HANDLE hKey, uint32_t* result, FFstrbuf* error); + +static inline bool ffRegOpenKeyForRead(HKEY hRootKey, const wchar_t* subKeyW, HANDLE* result, FFstrbuf* error) { + return ffRegOpenSubkeyForRead(ffRegGetRootKeyHandle(hRootKey), subKeyW, result, error); +} + +static inline bool ffRegReadStrbuf(HANDLE hKey, const wchar_t* valueNameW, FFstrbuf* result, FFstrbuf* error) { + return ffRegReadValue(hKey, &(FFRegValueArg) { + .type = FF_ARG_TYPE_STRBUF, + .value = result, + .name = valueNameW, + }, + error); +} +static inline bool ffRegReadUint(HANDLE hKey, const wchar_t* valueNameW, uint32_t* result, FFstrbuf* error) { + return ffRegReadValue(hKey, &(FFRegValueArg) { + .type = FF_ARG_TYPE_UINT, + .value = result, + .name = valueNameW, + }, + error); +} +static inline bool ffRegReadUint64(HANDLE hKey, const wchar_t* valueNameW, uint64_t* result, FFstrbuf* error) { + return ffRegReadValue(hKey, &(FFRegValueArg) { + .type = FF_ARG_TYPE_UINT64, + .value = result, + .name = valueNameW, + }, + error); +} +static inline bool ffRegReadData(HANDLE hKey, const wchar_t* valueNameW, FFArgBuffer* buffer, FFstrbuf* error) { + return ffRegReadValue(hKey, &(FFRegValueArg) { + .type = FF_ARG_TYPE_BUFFER, + .value = buffer, + .name = valueNameW, + }, + error); +} diff --git a/src/common/windows/unicode.c b/src/common/windows/unicode.c new file mode 100644 index 0000000..06008ba --- /dev/null +++ b/src/common/windows/unicode.c @@ -0,0 +1,44 @@ +#include "unicode.h" + +#include "common/windows/nt.h" + +void ffStrbufSetNWS(FFstrbuf* result, uint32_t length, const wchar_t* source) { + if (!length) { + ffStrbufClear(result); + return; + } + + ULONG size_needed = 0; + NTSTATUS status = RtlUnicodeToUTF8N(NULL, 0, &size_needed, source, length * sizeof(wchar_t)); + + if (size_needed == 0) { + ffStrbufSetF(result, "RtlUnicodeToUTF8N failed: %X", (unsigned) status); + return; + } + + ffStrbufEnsureFixedLengthFree(result, size_needed); + RtlUnicodeToUTF8N(result->chars, size_needed, &size_needed, source, length * sizeof(wchar_t)); + + result->length = size_needed; + result->chars[size_needed] = '\0'; +} + +void ffStrbufAppendNWS(FFstrbuf* result, uint32_t length, const wchar_t* source) { + if (!length) { + return; + } + + ULONG size_needed = 0; + NTSTATUS status = RtlUnicodeToUTF8N(NULL, 0, &size_needed, source, length * sizeof(wchar_t)); + + if (size_needed == 0) { + ffStrbufAppendF(result, "RtlUnicodeToUTF8N failed: %X", (unsigned) status); + return; + } + + ffStrbufEnsureFree(result, size_needed); + RtlUnicodeToUTF8N(result->chars + result->length, size_needed, &size_needed, source, length * sizeof(wchar_t)); + + result->length += size_needed; + result->chars[result->length] = '\0'; +} diff --git a/src/common/windows/unicode.h b/src/common/windows/unicode.h new file mode 100644 index 0000000..f58b425 --- /dev/null +++ b/src/common/windows/unicode.h @@ -0,0 +1,46 @@ +#pragma once + +#include "common/FFstrbuf.h" +#include <wchar.h> + +void ffStrbufSetNWS(FFstrbuf* result, uint32_t length, const wchar_t* source); +void ffStrbufAppendNWS(FFstrbuf* result, uint32_t length, const wchar_t* source); + +static inline void ffStrbufSetWS(FFstrbuf* result, const wchar_t* source) { + if (!source) { + return ffStrbufClear(result); + } + return ffStrbufSetNWS(result, (uint32_t) wcslen(source), source); +} + +static inline void ffStrbufAppendWS(FFstrbuf* result, const wchar_t* source) { + if (!source) { + return; + } + return ffStrbufAppendNWS(result, (uint32_t) wcslen(source), source); +} + +static inline void ffStrbufInitNWS(FFstrbuf* result, uint32_t length, const wchar_t* source) { + ffStrbufInit(result); + return ffStrbufSetNWS(result, length, source); +} + +static inline void ffStrbufInitWS(FFstrbuf* result, const wchar_t* source) { + if (!source) { + return ffStrbufInit(result); + } + return ffStrbufInitNWS(result, (uint32_t) wcslen(source), source); +} + +static inline FFstrbuf ffStrbufCreateNWS(uint32_t length, const wchar_t* source) { + FFstrbuf result; + ffStrbufInitNWS(&result, length, source); + return result; +} + +static inline FFstrbuf ffStrbufCreateWS(const wchar_t* source) { + if (!source) { + return ffStrbufCreate(); + } + return ffStrbufCreateNWS((uint32_t) wcslen(source), source); +} diff --git a/src/common/windows/unicode.hpp b/src/common/windows/unicode.hpp new file mode 100644 index 0000000..f9c355a --- /dev/null +++ b/src/common/windows/unicode.hpp @@ -0,0 +1,27 @@ +#pragma once + +#ifdef __cplusplus + +extern "C" { + #include "unicode.h" +} + + #include <string_view> + +static inline void ffStrbufInitWSV(FFstrbuf* result, const std::wstring_view source) { + return ffStrbufInitNWS(result, (uint32_t) source.size(), source.data()); +} + +static inline FFstrbuf ffStrbufCreateWSV(const std::wstring_view source) { + return ffStrbufCreateNWS((uint32_t) source.size(), source.data()); +} + +static inline void ffStrbufSetWSV(FFstrbuf* result, const std::wstring_view source) { + return ffStrbufSetNWS(result, (uint32_t) source.size(), source.data()); +} + +#else + + #error Must be included in C++ source file + +#endif diff --git a/src/common/windows/util.hpp b/src/common/windows/util.hpp new file mode 100644 index 0000000..939f6bb --- /dev/null +++ b/src/common/windows/util.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include <utility> +#include <type_traits> + +template <typename Fn> +struct on_scope_exit { + static_assert(std::is_nothrow_move_constructible<Fn>::value, + "Fn must be nothrow move constructible"); + + explicit on_scope_exit(Fn&& fn) noexcept + : _fn(std::move(fn)) {}; + on_scope_exit(const on_scope_exit&) = delete; + on_scope_exit& operator=(const on_scope_exit&) = delete; + ~on_scope_exit() noexcept { + this->_fn(); + } + + private: + Fn _fn; +}; diff --git a/src/common/windows/variant.cpp b/src/common/windows/variant.cpp new file mode 100644 index 0000000..d5610d3 --- /dev/null +++ b/src/common/windows/variant.cpp @@ -0,0 +1,20 @@ +#include "variant.hpp" + +#include <oleauto.h> + +FFWmiVariant::FFWmiVariant(std::initializer_list<PCWSTR> strings) : FFWmiVariant() { + SAFEARRAYBOUND bound = { + .cElements = (ULONG) strings.size(), + .lLbound = 0, + }; + SAFEARRAY* psa = SafeArrayCreate(VT_BSTR, 1, &bound); + + LONG i = 0; + for (PCWSTR str : strings) { + SafeArrayPutElement(psa, &i, bstr_t(str)); + ++i; + } + + this->vt = VT_ARRAY | VT_BSTR; + this->parray = psa; +} diff --git a/src/common/windows/variant.hpp b/src/common/windows/variant.hpp new file mode 100644 index 0000000..58e46b7 --- /dev/null +++ b/src/common/windows/variant.hpp @@ -0,0 +1,164 @@ +#include <oaidl.h> +#include <propidl.h> +#include <type_traits> +#include <utility> +#include <string_view> +#include <cassert> +#include <cstdint> + +template <typename TVariant> +struct FFBaseVariant : TVariant { + bool hasValue() { + return this->vt != VT_EMPTY; + } + + explicit operator bool() { + return this->hasValue(); + } + + template <typename T> + T get() { + // boolean + if constexpr (std::is_same_v<T, bool>) { + assert(this->vt == VT_BOOL); + return this->boolVal != VARIANT_FALSE; + } + + // signed + else if constexpr (std::is_same_v<T, int8_t>) { + assert(this->vt == VT_I1); + return this->cVal; + } else if constexpr (std::is_same_v<T, int16_t>) { + assert(this->vt == VT_I2); + return this->iVal; + } else if constexpr (std::is_same_v<T, int32_t>) { + assert(this->vt == VT_I4 || this->vt == VT_INT); + return this->intVal; + } else if constexpr (std::is_same_v<T, int64_t>) { + assert(this->vt == VT_I8); + return this->llVal; + } + + // unsigned + else if constexpr (std::is_same_v<T, uint8_t>) { + assert(this->vt == VT_UI1); + return this->bVal; + } else if constexpr (std::is_same_v<T, uint16_t>) { + assert(this->vt == VT_UI2); + return this->uiVal; + } else if constexpr (std::is_same_v<T, uint32_t>) { + assert(this->vt == VT_UI4 || this->vt == VT_UINT); + return this->uintVal; + } else if constexpr (std::is_same_v<T, uint64_t>) { + assert(this->vt == VT_UI8); + return this->ullVal; + } + + // decimal + else if constexpr (std::is_same_v<T, float>) { + assert(this->vt == VT_R4); + return this->fltVal; + } else if constexpr (std::is_same_v<T, double>) { + assert(this->vt == VT_R8); + return this->dblVal; + } + + // string + else if constexpr (std::is_same_v<T, std::string_view>) { + assert(this->vt == VT_LPSTR); + return this->pcVal; + } else if constexpr (std::is_same_v<T, std::wstring_view>) { + assert(this->vt == VT_BSTR || this->vt == VT_LPWSTR); + if (this->vt == VT_LPWSTR) { + return this->bstrVal; + } else { + return { this->bstrVal, SysStringLen(this->bstrVal) }; + } + } + + // array signed + else if constexpr (std::is_same_v<T, std::pair<const int8_t*, uint32_t>>) { + assert(this->vt & VT_ARRAY); + assert((this->vt & ~VT_ARRAY) == VT_I1); + return std::make_pair((int8_t*) this->parray->pvData, this->parray->cDims); + } else if constexpr (std::is_same_v<T, std::pair<const int16_t*, uint32_t>>) { + assert(this->vt & VT_ARRAY); + assert((this->vt & ~VT_ARRAY) == VT_I2); + return std::make_pair((int16_t*) this->parray->pvData, this->parray->cDims); + } else if constexpr (std::is_same_v<T, std::pair<const int32_t*, uint32_t>>) { + assert(this->vt & VT_ARRAY); + assert((this->vt & ~VT_ARRAY) == VT_I4); + return std::make_pair((int32_t*) this->parray->pvData, this->parray->cDims); + } else if constexpr (std::is_same_v<T, std::pair<const int64_t*, uint32_t>>) { + assert(this->vt & VT_ARRAY); + assert((this->vt & ~VT_ARRAY) == VT_I8); + return std::make_pair((int64_t*) this->parray->pvData, this->parray->cDims); + } + + // array unsigned + else if constexpr (std::is_same_v<T, std::pair<const uint8_t*, uint32_t>>) { + assert(this->vt & VT_ARRAY); + assert((this->vt & ~VT_ARRAY) == VT_UI1); + return std::make_pair((uint8_t*) this->parray->pvData, this->parray->cDims); + } else if constexpr (std::is_same_v<T, std::pair<const uint16_t*, uint32_t>>) { + assert(this->vt & VT_ARRAY); + assert((this->vt & ~VT_ARRAY) == VT_UI2); + return std::make_pair((uint16_t*) this->parray->pvData, this->parray->cDims); + } else if constexpr (std::is_same_v<T, std::pair<const uint32_t*, uint32_t>>) { + assert(this->vt & VT_ARRAY); + assert((this->vt & ~VT_ARRAY) == VT_UI4); + return std::make_pair((uint32_t*) this->parray->pvData, this->parray->cDims); + } else if constexpr (std::is_same_v<T, std::pair<const uint64_t*, uint32_t>>) { + assert(this->vt & VT_ARRAY); + assert((this->vt & ~VT_ARRAY) == VT_UI8); + return std::make_pair((uint64_t*) this->parray->pvData, this->parray->cDims); + } else { + assert(false && "unsupported type"); + __builtin_unreachable(); + } + } +}; + +struct FFWmiVariant : FFBaseVariant<VARIANT> { + FFWmiVariant(const FFWmiVariant&) = delete; + FFWmiVariant(FFWmiVariant&&); // don't define it to enforce NRVO optimization + explicit FFWmiVariant() { + VariantInit(this); + } + explicit FFWmiVariant(std::initializer_list<PCWSTR> strings); + ~FFWmiVariant() { + VariantClear(this); + } +}; +static_assert(sizeof(FFWmiVariant) == sizeof(VARIANT), ""); + +struct FFPropVariant : FFBaseVariant<PROPVARIANT> { + FFPropVariant(const FFPropVariant&) = delete; + FFPropVariant(FFPropVariant&&); // don't define it to enforce NRVO optimization + explicit FFPropVariant() { + PropVariantInit(this); + } + ~FFPropVariant() { + PropVariantClear(this); + } +}; +static_assert(sizeof(FFPropVariant) == sizeof(PROPVARIANT), ""); + +namespace { +// Provide our bstr_t to avoid libstdc++ dependency +struct bstr_t { + explicit bstr_t(const wchar_t* str) noexcept : _bstr(SysAllocString(str)) {} + ~bstr_t(void) noexcept { + SysFreeString(_bstr); + } + explicit operator const wchar_t*(void) const noexcept { + return _bstr; + } + operator BSTR(void) const noexcept { + return _bstr; + } + + private: + BSTR _bstr; +}; +} // namespace diff --git a/src/common/windows/version.c b/src/common/windows/version.c new file mode 100644 index 0000000..4d61c78 --- /dev/null +++ b/src/common/windows/version.c @@ -0,0 +1,77 @@ +#include "common/debug.h" +#include "common/mallocHelper.h" +#include "common/windows/version.h" +#include "common/windows/unicode.h" + +#include <windows.h> + +bool ffGetFileVersion(const wchar_t* filePath, const wchar_t* stringName, FFstrbuf* version) { + FF_DEBUG("ffGetFileVersion: enter filePath=%ls stringName=%ls", filePath, stringName); + + DWORD handle; + DWORD size = GetFileVersionInfoSizeW(filePath, &handle); + if (size == 0) { + FF_DEBUG("GetFileVersionInfoSizeW failed: %s", ffDebugWin32Error(GetLastError())); + return false; + } + + FF_DEBUG("GetFileVersionInfoSizeW ok: size=%lu handle=%lu", + (unsigned long) size, + (unsigned long) handle); + + FF_AUTO_FREE void* versionData = malloc(size); + if (!versionData) { + FF_DEBUG("malloc failed: size=%lu", (unsigned long) size); + return false; + } + + if (!GetFileVersionInfoW(filePath, handle, size, versionData)) { + FF_DEBUG("GetFileVersionInfoW failed: %s", ffDebugWin32Error(GetLastError())); + return false; + } + + FF_DEBUG("GetFileVersionInfoW ok"); + + if (!stringName) { + VS_FIXEDFILEINFO* verInfo; + UINT len; + if (VerQueryValueW(versionData, L"\\", (void**) &verInfo, &len) && + len && + verInfo->dwSignature == 0xFEEF04BD) { + ffStrbufSetF(version, "%u.%u.%u.%u", (unsigned) ((verInfo->dwProductVersionMS >> 16) & 0xffff), (unsigned) ((verInfo->dwProductVersionMS >> 0) & 0xffff), (unsigned) ((verInfo->dwProductVersionLS >> 16) & 0xffff), (unsigned) ((verInfo->dwProductVersionLS >> 0) & 0xffff)); + FF_DEBUG("fixed version resolved: %s", version->chars); + return true; + } + + FF_DEBUG("fixed version query failed or invalid signature"); + return false; + } + + struct { + WORD language; + WORD codePage; + }* translations; + UINT translationsLen; + + if (VerQueryValueW(versionData, L"\\VarFileInfo\\Translation", (void**) &translations, &translationsLen) && + translationsLen >= sizeof(*translations)) { + wchar_t subBlock[128]; + snwprintf(subBlock, ARRAY_SIZE(subBlock), L"\\StringFileInfo\\%04x%04x\\%ls", translations[0].language, translations[0].codePage, stringName); + FF_DEBUG("query version string with translation: %ls", subBlock); + + wchar_t* value; + UINT valueLen; // Number of characters, including null terminator + if (VerQueryValueW(versionData, subBlock, (void**) &value, &valueLen) && valueLen > 0) { + ffStrbufSetNWS(version, valueLen - 1, value); + FF_DEBUG("version string resolved (translation fallback): %s", version->chars); + return true; + } + + FF_DEBUG("translation fallback query failed"); + } else { + FF_DEBUG("no translation table found in version resource"); + } + + FF_DEBUG("ffGetFileVersion failed"); + return false; +} diff --git a/src/common/windows/version.h b/src/common/windows/version.h new file mode 100644 index 0000000..457c053 --- /dev/null +++ b/src/common/windows/version.h @@ -0,0 +1,14 @@ +#include "fastfetch.h" + +/** + * @brief Retrieves a specific version string for a Windows file. + * + * This function gets a version string from a Windows file's version information. + * + * @param filePath The path to the file for which version information is requested. + * @param stringName The name of the specific version string to retrieve (e.g., "FileVersion", "ProductVersion"). + * @param version Pointer to an FFstrbuf where the version string will be stored. + * + * @return true if the version string was successfully retrieved, false otherwise. + */ +bool ffGetFileVersion(const wchar_t* filePath, const wchar_t* stringName, FFstrbuf* version); diff --git a/src/common/windows/version.rc b/src/common/windows/version.rc new file mode 100644 index 0000000..f01ca7f --- /dev/null +++ b/src/common/windows/version.rc @@ -0,0 +1,37 @@ +#ifdef RC_INVOKED + +#include <winuser.h> +#include <winver.h> +#include <ntdef.h> +#include "fastfetch_config.h" + +#define FF_TO_STR1(str) #str +#define FF_TO_STR(str) FF_TO_STR1(str) + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "manifest.xml" +id ICON "logo.ico" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION FASTFETCH_PROJECT_VERSION_MAJOR,FASTFETCH_PROJECT_VERSION_MINOR,FASTFETCH_PROJECT_VERSION_PATCH,FASTFETCH_PROJECT_VERSION_TWEAK_NUM + PRODUCTVERSION FASTFETCH_PROJECT_VERSION_MAJOR,FASTFETCH_PROJECT_VERSION_MINOR,FASTFETCH_PROJECT_VERSION_PATCH,FASTFETCH_PROJECT_VERSION_TWEAK_NUM + FILEOS VOS_NT + FILETYPE VFT_APP +{ + BLOCK "StringFileInfo" { + BLOCK "040904b0" { + VALUE "Comments", FASTFETCH_PROJECT_DESCRIPTION + VALUE "FileDescription", FF_TO_STR(FASTFETCH_TARGET_BINARY_NAME) + VALUE "FileVersion", FASTFETCH_PROJECT_VERSION FASTFETCH_PROJECT_VERSION_TWEAK + VALUE "InternalName", FF_TO_STR(FASTFETCH_TARGET_BINARY_NAME) + VALUE "LegalCopyright", FASTFETCH_PROJECT_LICENSE + VALUE "OriginalFilename", FF_TO_STR(FASTFETCH_TARGET_BINARY_NAME) ".exe" + VALUE "ProductName", FASTFETCH_PROJECT_NAME " - " FASTFETCH_PROJECT_DESCRIPTION + VALUE "ProductVersion", FASTFETCH_PROJECT_VERSION FASTFETCH_PROJECT_VERSION_TWEAK + VALUE "CompanyName", FASTFETCH_PROJECT_HOMEPAGE_URL + } + } + BLOCK "VarFileInfo" { + VALUE "Translation", 0x0409, 1200 + } +} +#endif diff --git a/src/common/windows/wmi.h b/src/common/windows/wmi.h new file mode 100644 index 0000000..aa50c00 --- /dev/null +++ b/src/common/windows/wmi.h @@ -0,0 +1,54 @@ +#pragma once + +#include <windef.h> +#include <wmistr.h> +#include <assert.h> + +/** + * The WmiOpenBlock function opens the WMI data block object for the specified WMI class. + * + * \param Guid Specifies the GUID for WMI class. + * \param DesiredAccess Specifies the desired access rights to the data block object. + * \param DataBlockHandle Pointer to a memory location where the routine returns a handle to the data block object. + * \return ULONG Successful or errant status. + */ +NTSYSAPI ULONG NTAPI +WmiOpenBlock( + _In_ LPCGUID Guid, + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE DataBlockHandle); + +/** + * The WmiQueryAllDataW function returns all WMI data blocks that implement a given WMI class (Unicode). + * + * \param DataBlockHandle Handle to a WMI data block object. + * \param BufferLength Pointer to a memory location that specifies the size of the buffer. + * \param Buffer Pointer to the buffer where the routine returns the WMI data. + * \return ULONG Successful or errant status. + */ +NTSYSAPI ULONG NTAPI +WmiQueryAllDataW( + _In_ HANDLE DataBlockHandle, + _Inout_ PULONG BufferLength, + _Out_writes_bytes_opt_(*BufferLength) PVOID Buffer); + +/** + * The WmiCloseBlock function closes a WMI data block object. + * + * \param DataBlockHandle Handle to the data block object to be closed. + * \return ULONG Successful or errant status. + */ +NTSYSAPI ULONG NTAPI +WmiCloseBlock( + _In_ HANDLE DataBlockHandle); + +static inline void ffCloseWmiBlock(HANDLE* hBlock) { + assert(hBlock); + if (*hBlock) { + WmiCloseBlock(*hBlock); + } +} + +#define FF_AUTO_CLOSE_WMI_BLOCK __attribute__((cleanup(ffCloseWmiBlock))) + +// MOF: https://github.com/tpn/winsdk-10/blob/master/Include/10.0.16299.0/km/wmicore.mof |