Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions litebox_shim_windows/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1744,6 +1744,15 @@ impl<Platform: ShimPlatform, FS: ShimFS> Task<Platform, FS> {
performance_counter,
performance_frequency,
} => self.sys_nt_query_performance_counter(performance_counter, performance_frequency),
SyscallRequest::NtQueryTimerResolution {
minimum_resolution,
maximum_resolution,
current_resolution,
} => Self::sys_nt_query_timer_resolution(
minimum_resolution,
maximum_resolution,
current_resolution,
),
SyscallRequest::NtQueryLicenseValue {
value_name,
value_type,
Expand Down
10 changes: 10 additions & 0 deletions litebox_shim_windows/src/syscalls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,11 @@ pub(crate) enum SyscallRequest<Platform: RawPointerProvider> {
performance_counter: Platform::RawMutPointer<i64>,
performance_frequency: Option<Platform::RawMutPointer<i64>>,
},
NtQueryTimerResolution {
minimum_resolution: Platform::RawMutPointer<u32>,
maximum_resolution: Platform::RawMutPointer<u32>,
current_resolution: Platform::RawMutPointer<u32>,
},
NtQueryLicenseValue {
value_name: Platform::RawConstPointer<nt_types::UnicodeString>,
value_type: Platform::RawMutPointer<u32>,
Expand Down Expand Up @@ -1333,6 +1338,11 @@ impl<Platform: RawPointerProvider> SyscallRequest<Platform> {
performance_counter:*,
performance_frequency:*,
})),
NtSysno::NtQueryTimerResolution => Some(sys_req!(NtQueryTimerResolution {
minimum_resolution:*,
maximum_resolution:*,
current_resolution:*,
})),
NtSysno::NtQueryLicenseValue => Some(sys_req!(NtQueryLicenseValue {
value_name:*,
value_type:*,
Expand Down
175 changes: 175 additions & 0 deletions litebox_shim_windows/src/syscalls/sysinfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const QPC_FREQUENCY_HZ: i64 = 1_000_000_000;
// They avoid leaking host topology while satisfying Windows CRT/environment
// probes that require plausible system-information success outputs.
const TIMER_RESOLUTION_100NS: u32 = 156_250;
const MINIMUM_TIMER_RESOLUTION_100NS: u32 = 5_000;
const DEFAULT_PHYSICAL_PAGES: u32 = 1024 * 1024;
const NUMBER_OF_PROCESSORS: u8 = 1;
const PROCESSOR_AFFINITY_MASK: usize = (1usize << NUMBER_OF_PROCESSORS) - 1;
Expand All @@ -34,6 +35,25 @@ const NUMA_NODE_COUNT: usize = NUMBER_OF_PROCESSORS as usize;
const PROCESSOR_ARCHITECTURE_AMD64: u16 = 9;
const SYSTEM_VERIFIER_INFORMATION_LENGTH: u32 = 0x90;
const SYSTEM_VERIFIER_INFORMATION_LENGTH_USIZE: usize = 0x90;
// Synthetic generation for the immutable feature table. Increment if the table changes.
const FEATURE_CONFIGURATION_CHANGE_STAMP: u64 = 1;
// Feature configurations observed from host ntdll on Windows 11 build 26200.
// This is the subset found in the scanned ID ranges, not an exhaustive feature list.
// Keep entries sorted by feature ID for binary search.
const OBSERVED_FEATURE_CONFIGURATIONS: &[(u32, u32)] = &[
(58_599_532, 0x2f),
(58_599_550, 0x2f),
(58_599_553, 0x2f),
(58_599_559, 0x2f),
(58_599_563, 0x2f),
(58_599_570, 0x2f),
(58_988_972, 0x2f),
(58_989_002, 0x2f),
(58_989_021, 0x2f),
(58_989_070, 0x64),
(58_989_092, 0x64),
(58_989_177, 0x64),
];
const X64_SYSTEM_RANGE_START: usize = 0xffff_8000_0000_0000;

#[repr(C)]
Expand Down Expand Up @@ -67,6 +87,7 @@ enum SystemInformationClass {
LogicalProcessorAndGroup = 107,
Flush = 192,
HypervisorSharedPage = 197,
FeatureConfiguration = 210,
FeatureConfigurationSection = 211,
ProcessorFeaturesBitMap = 250,
}
Expand All @@ -83,6 +104,13 @@ enum LogicalProcessorRelationship {
All = 0xffff,
}

#[repr(u32)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, IntEnum)]
enum FeatureConfigurationType {
Boot = 0,
Runtime = 1,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, FromBytes, Immutable, IntoBytes)]
struct SystemBasicInformation {
Expand Down Expand Up @@ -145,6 +173,22 @@ struct SystemProcessorFeaturesBitMapInformation {
feature_bits: [u64; 2],
}

#[repr(C)]
#[derive(Clone, Copy, Debug, FromBytes, Immutable, IntoBytes)]
struct SystemFeatureConfigurationQuery {
configuration_type: u32,
feature_id: u32,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, Default, FromBytes, Immutable, IntoBytes)]
struct SystemFeatureConfigurationInformation {
change_stamp: u64,
feature_id: u32,
configuration: u32,
variant_payload: u64,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, FromBytes, Immutable, IntoBytes)]
struct ProcessorRelationship {
Expand Down Expand Up @@ -324,6 +368,7 @@ impl<Platform: ShimPlatform, FS: ShimFS> Task<Platform, FS> {
},
),
SystemInformationClass::LogicalProcessorAndGroup
| SystemInformationClass::FeatureConfiguration
| SystemInformationClass::FeatureConfigurationSection => NtStatus::INVALID_INFO_CLASS,
};

Expand Down Expand Up @@ -372,6 +417,13 @@ impl<Platform: ShimPlatform, FS: ShimFS> Task<Platform, FS> {
return_length,
)
}
SystemInformationClass::FeatureConfiguration => Self::query_feature_configuration(
input_buffer,
input_buffer_length,
system_information,
system_information_length,
return_length,
),
// TODO: Windows returns section handles for this class. LiteBox does not yet model those
// NT section objects, so do not publish a fabricated success payload.
SystemInformationClass::FeatureConfigurationSection => NtStatus::INVALID_INFO_CLASS,
Expand All @@ -395,6 +447,64 @@ impl<Platform: ShimPlatform, FS: ShimFS> Task<Platform, FS> {
status
}

fn query_feature_configuration(
input_buffer: ConstPtr<Platform, u8>,
input_buffer_length: u32,
system_information: MutPtr<Platform, u8>,
system_information_length: u32,
return_length: Option<MutPtr<Platform, u32>>,
) -> NtStatus {
if input_buffer_length != size_of::<SystemFeatureConfigurationQuery>().trunc() {
return Self::write_return_length_for_short_buffer(return_length, 0);
}

let input_buffer = ConstPtr::<Platform, SystemFeatureConfigurationQuery>::from_usize(
input_buffer.as_usize(),
);
let Some(query) = input_buffer.read_at_offset(0) else {
return NtStatus::ACCESS_VIOLATION;
};
// Boot and Runtime returned identical values for every observed feature. Validate the
// selector to match Windows, but use the same deterministic table for both.
if FeatureConfigurationType::try_from(query.configuration_type).is_err() {
if Self::write_return_length(return_length, 0).is_err() {
return NtStatus::ACCESS_VIOLATION;
}
return NtStatus::INVALID_PARAMETER;
}

let required_len = size_of::<SystemFeatureConfigurationInformation>().trunc();
if system_information_length != required_len {
return Self::write_return_length_for_short_buffer(return_length, required_len);
}

let configuration = OBSERVED_FEATURE_CONFIGURATIONS
.binary_search_by_key(&query.feature_id, |&(feature_id, _)| feature_id)
.ok()
.map(|index| OBSERVED_FEATURE_CONFIGURATIONS[index].1);
if configuration.is_none() {
litebox_util_log::debug!(
feature_id = query.feature_id;
"No observed feature configuration"
);
}
let information = SystemFeatureConfigurationInformation {
change_stamp: FEATURE_CONFIGURATION_CHANGE_STAMP,
feature_id: configuration.map_or(0, |_| query.feature_id),
configuration: configuration.unwrap_or(0),
variant_payload: 0,
};
if system_information
.write_slice_at_offset(0, information.as_bytes())
.is_none()
|| Self::write_return_length(return_length, required_len).is_err()
{
return NtStatus::ACCESS_VIOLATION;
}

configuration.map_or(NtStatus::NOT_FOUND, |_| NtStatus::SUCCESS)
}

fn write_logical_processor_and_group_information(
input_buffer: ConstPtr<Platform, u8>,
system_information: MutPtr<Platform, u8>,
Expand Down Expand Up @@ -657,6 +767,33 @@ impl<Platform: ShimPlatform, FS: ShimFS> Task<Platform, FS> {
NtStatus::SUCCESS
}

pub(crate) fn sys_nt_query_timer_resolution(
minimum_resolution: MutPtr<Platform, u32>,
maximum_resolution: MutPtr<Platform, u32>,
current_resolution: MutPtr<Platform, u32>,
) -> NtStatus {
if minimum_resolution
.write_at_offset(0, TIMER_RESOLUTION_100NS)
.is_none()
{
return NtStatus::ACCESS_VIOLATION;
}
if maximum_resolution
.write_at_offset(0, MINIMUM_TIMER_RESOLUTION_100NS)
.is_none()
{
return NtStatus::ACCESS_VIOLATION;
}
if current_resolution
.write_at_offset(0, TIMER_RESOLUTION_100NS)
.is_none()
{
return NtStatus::ACCESS_VIOLATION;
}

NtStatus::SUCCESS
}

pub(crate) fn sys_nt_convert_between_auxiliary_counter_and_performance_counter(
_flag: u32,
source: ConstPtr<Platform, u64>,
Expand Down Expand Up @@ -990,6 +1127,44 @@ mod tests {
});
}

#[test]
fn nt_query_system_information_ex_reports_configured_features() {
run_with_test_platform_pointers(|| {
assert!(
OBSERVED_FEATURE_CONFIGURATIONS
.windows(2)
.all(|entries| entries[0].0 < entries[1].0)
);

for configuration_type in [
FeatureConfigurationType::Boot,
FeatureConfigurationType::Runtime,
] {
for &(feature_id, expected_configuration) in OBSERVED_FEATURE_CONFIGURATIONS {
let query = SystemFeatureConfigurationQuery {
configuration_type: configuration_type as u32,
feature_id,
};
let mut output = SystemFeatureConfigurationInformation::default();

assert_eq!(
TestTask::sys_nt_query_system_information_ex(
SystemInformationClass::FeatureConfiguration as u32,
Some(const_byte_ptr(&query)),
size_of::<SystemFeatureConfigurationQuery>().trunc(),
mut_byte_ptr(&mut output),
size_of::<SystemFeatureConfigurationInformation>().trunc(),
None,
),
NtStatus::SUCCESS
);
assert_eq!(output.feature_id, feature_id);
assert_eq!(output.configuration, expected_configuration);
}
}
});
}

#[test]
fn nt_query_system_information_ex_reports_required_logical_processor_length() {
run_with_test_platform_pointers(|| {
Expand Down