use crate::arch::segment;
use crate::error::{Result, WraithError};
use core::cmp::Ordering;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct WindowsVersion {
pub major: u32,
pub minor: u32,
pub build: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum WindowsRelease {
Windows7, Windows8, Windows81, Windows10_1507, Windows10_1511, Windows10_1607, Windows10_1703, Windows10_1709, Windows10_1803, Windows10_1809, Windows10_1903, Windows10_1909, Windows10_2004, Windows10_20H2, Windows10_21H1, Windows10_21H2, Windows10_22H2, Windows11_21H2, Windows11_22H2, Windows11_23H2, Windows11_24H2, Unknown,
}
impl WindowsVersion {
pub const MIN_SUPPORTED: Self = Self {
major: 6,
minor: 1,
build: 7601,
};
pub fn current() -> Result<Self> {
let peb = unsafe { segment::get_peb() };
if peb.is_null() {
return Err(WraithError::InvalidPebAccess);
}
#[cfg(target_arch = "x86_64")]
let (major, minor, build) = unsafe {
let major = (peb.add(0x118) as *const u32).read_unaligned();
let minor = (peb.add(0x11C) as *const u32).read_unaligned();
let build = (peb.add(0x120) as *const u16).read_unaligned() as u32;
(major, minor, build)
};
#[cfg(target_arch = "x86")]
let (major, minor, build) = unsafe {
let major = (peb.add(0xA4) as *const u32).read_unaligned();
let minor = (peb.add(0xA8) as *const u32).read_unaligned();
let build = (peb.add(0xAC) as *const u16).read_unaligned() as u32;
(major, minor, build)
};
let version = Self { major, minor, build };
if version < Self::MIN_SUPPORTED {
return Err(WraithError::UnsupportedWindowsVersion {
major,
minor,
build,
});
}
Ok(version)
}
pub fn release(&self) -> WindowsRelease {
match (self.major, self.minor, self.build) {
(6, 1, _) => WindowsRelease::Windows7,
(6, 2, _) => WindowsRelease::Windows8,
(6, 3, _) => WindowsRelease::Windows81,
(10, 0, b) if b >= 26100 => WindowsRelease::Windows11_24H2,
(10, 0, b) if b >= 22631 => WindowsRelease::Windows11_23H2,
(10, 0, b) if b >= 22621 => WindowsRelease::Windows11_22H2,
(10, 0, b) if b >= 22000 => WindowsRelease::Windows11_21H2,
(10, 0, b) if b >= 19045 => WindowsRelease::Windows10_22H2,
(10, 0, b) if b >= 19044 => WindowsRelease::Windows10_21H2,
(10, 0, b) if b >= 19043 => WindowsRelease::Windows10_21H1,
(10, 0, b) if b >= 19042 => WindowsRelease::Windows10_20H2,
(10, 0, b) if b >= 19041 => WindowsRelease::Windows10_2004,
(10, 0, b) if b >= 18363 => WindowsRelease::Windows10_1909,
(10, 0, b) if b >= 18362 => WindowsRelease::Windows10_1903,
(10, 0, b) if b >= 17763 => WindowsRelease::Windows10_1809,
(10, 0, b) if b >= 17134 => WindowsRelease::Windows10_1803,
(10, 0, b) if b >= 16299 => WindowsRelease::Windows10_1709,
(10, 0, b) if b >= 15063 => WindowsRelease::Windows10_1703,
(10, 0, b) if b >= 14393 => WindowsRelease::Windows10_1607,
(10, 0, b) if b >= 10586 => WindowsRelease::Windows10_1511,
(10, 0, b) if b >= 10240 => WindowsRelease::Windows10_1507,
_ => WindowsRelease::Unknown,
}
}
pub fn is_at_least(&self, release: WindowsRelease) -> bool {
self.release() >= release
}
pub fn supports_hash_table(&self) -> bool {
self.is_at_least(WindowsRelease::Windows8)
}
pub fn supports_base_address_index(&self) -> bool {
self.is_at_least(WindowsRelease::Windows8)
}
pub fn is_windows_11(&self) -> bool {
self.build >= 22000
}
}
impl PartialOrd for WindowsVersion {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for WindowsVersion {
fn cmp(&self, other: &Self) -> Ordering {
match self.major.cmp(&other.major) {
Ordering::Equal => match self.minor.cmp(&other.minor) {
Ordering::Equal => self.build.cmp(&other.build),
ord => ord,
},
ord => ord,
}
}
}
impl core::fmt::Display for WindowsVersion {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}.{}.{}", self.major, self.minor, self.build)
}
}
impl core::fmt::Display for WindowsRelease {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Windows7 => write!(f, "Windows 7"),
Self::Windows8 => write!(f, "Windows 8"),
Self::Windows81 => write!(f, "Windows 8.1"),
Self::Windows10_1507 => write!(f, "Windows 10 1507"),
Self::Windows10_1511 => write!(f, "Windows 10 1511"),
Self::Windows10_1607 => write!(f, "Windows 10 1607"),
Self::Windows10_1703 => write!(f, "Windows 10 1703"),
Self::Windows10_1709 => write!(f, "Windows 10 1709"),
Self::Windows10_1803 => write!(f, "Windows 10 1803"),
Self::Windows10_1809 => write!(f, "Windows 10 1809"),
Self::Windows10_1903 => write!(f, "Windows 10 1903"),
Self::Windows10_1909 => write!(f, "Windows 10 1909"),
Self::Windows10_2004 => write!(f, "Windows 10 2004"),
Self::Windows10_20H2 => write!(f, "Windows 10 20H2"),
Self::Windows10_21H1 => write!(f, "Windows 10 21H1"),
Self::Windows10_21H2 => write!(f, "Windows 10 21H2"),
Self::Windows10_22H2 => write!(f, "Windows 10 22H2"),
Self::Windows11_21H2 => write!(f, "Windows 11 21H2"),
Self::Windows11_22H2 => write!(f, "Windows 11 22H2"),
Self::Windows11_23H2 => write!(f, "Windows 11 23H2"),
Self::Windows11_24H2 => write!(f, "Windows 11 24H2"),
Self::Unknown => write!(f, "Unknown"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_version_comparison() {
let win10 = WindowsVersion {
major: 10,
minor: 0,
build: 19041,
};
let win11 = WindowsVersion {
major: 10,
minor: 0,
build: 22000,
};
assert!(win10 < win11);
}
#[test]
fn test_release_mapping() {
let win11 = WindowsVersion {
major: 10,
minor: 0,
build: 22621,
};
assert_eq!(win11.release(), WindowsRelease::Windows11_22H2);
}
}