1use crate::arch::segment;
4use crate::error::{Result, WraithError};
5use core::cmp::Ordering;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub struct WindowsVersion {
10 pub major: u32,
11 pub minor: u32,
12 pub build: u32,
13}
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
17pub enum WindowsRelease {
18 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,
40}
41
42impl WindowsVersion {
43 pub const MIN_SUPPORTED: Self = Self {
45 major: 6,
46 minor: 1,
47 build: 7601,
48 };
49
50 pub fn current() -> Result<Self> {
54 let peb = unsafe { segment::get_peb() };
56 if peb.is_null() {
57 return Err(WraithError::InvalidPebAccess);
58 }
59
60 #[cfg(target_arch = "x86_64")]
65 let (major, minor, build) = unsafe {
66 let major = (peb.add(0x118) as *const u32).read_unaligned();
67 let minor = (peb.add(0x11C) as *const u32).read_unaligned();
68 let build = (peb.add(0x120) as *const u16).read_unaligned() as u32;
69 (major, minor, build)
70 };
71
72 #[cfg(target_arch = "x86")]
73 let (major, minor, build) = unsafe {
74 let major = (peb.add(0xA4) as *const u32).read_unaligned();
75 let minor = (peb.add(0xA8) as *const u32).read_unaligned();
76 let build = (peb.add(0xAC) as *const u16).read_unaligned() as u32;
77 (major, minor, build)
78 };
79
80 let version = Self { major, minor, build };
81
82 if version < Self::MIN_SUPPORTED {
83 return Err(WraithError::UnsupportedWindowsVersion {
84 major,
85 minor,
86 build,
87 });
88 }
89
90 Ok(version)
91 }
92
93 pub fn release(&self) -> WindowsRelease {
95 match (self.major, self.minor, self.build) {
96 (6, 1, _) => WindowsRelease::Windows7,
97 (6, 2, _) => WindowsRelease::Windows8,
98 (6, 3, _) => WindowsRelease::Windows81,
99 (10, 0, b) if b >= 26100 => WindowsRelease::Windows11_24H2,
100 (10, 0, b) if b >= 22631 => WindowsRelease::Windows11_23H2,
101 (10, 0, b) if b >= 22621 => WindowsRelease::Windows11_22H2,
102 (10, 0, b) if b >= 22000 => WindowsRelease::Windows11_21H2,
103 (10, 0, b) if b >= 19045 => WindowsRelease::Windows10_22H2,
104 (10, 0, b) if b >= 19044 => WindowsRelease::Windows10_21H2,
105 (10, 0, b) if b >= 19043 => WindowsRelease::Windows10_21H1,
106 (10, 0, b) if b >= 19042 => WindowsRelease::Windows10_20H2,
107 (10, 0, b) if b >= 19041 => WindowsRelease::Windows10_2004,
108 (10, 0, b) if b >= 18363 => WindowsRelease::Windows10_1909,
109 (10, 0, b) if b >= 18362 => WindowsRelease::Windows10_1903,
110 (10, 0, b) if b >= 17763 => WindowsRelease::Windows10_1809,
111 (10, 0, b) if b >= 17134 => WindowsRelease::Windows10_1803,
112 (10, 0, b) if b >= 16299 => WindowsRelease::Windows10_1709,
113 (10, 0, b) if b >= 15063 => WindowsRelease::Windows10_1703,
114 (10, 0, b) if b >= 14393 => WindowsRelease::Windows10_1607,
115 (10, 0, b) if b >= 10586 => WindowsRelease::Windows10_1511,
116 (10, 0, b) if b >= 10240 => WindowsRelease::Windows10_1507,
117 _ => WindowsRelease::Unknown,
118 }
119 }
120
121 pub fn is_at_least(&self, release: WindowsRelease) -> bool {
123 self.release() >= release
124 }
125
126 pub fn supports_hash_table(&self) -> bool {
128 self.is_at_least(WindowsRelease::Windows8)
129 }
130
131 pub fn supports_base_address_index(&self) -> bool {
133 self.is_at_least(WindowsRelease::Windows8)
134 }
135
136 pub fn is_windows_11(&self) -> bool {
138 self.build >= 22000
139 }
140}
141
142impl PartialOrd for WindowsVersion {
143 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
144 Some(self.cmp(other))
145 }
146}
147
148impl Ord for WindowsVersion {
149 fn cmp(&self, other: &Self) -> Ordering {
150 match self.major.cmp(&other.major) {
151 Ordering::Equal => match self.minor.cmp(&other.minor) {
152 Ordering::Equal => self.build.cmp(&other.build),
153 ord => ord,
154 },
155 ord => ord,
156 }
157 }
158}
159
160impl core::fmt::Display for WindowsVersion {
161 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
162 write!(f, "{}.{}.{}", self.major, self.minor, self.build)
163 }
164}
165
166impl core::fmt::Display for WindowsRelease {
167 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
168 match self {
169 Self::Windows7 => write!(f, "Windows 7"),
170 Self::Windows8 => write!(f, "Windows 8"),
171 Self::Windows81 => write!(f, "Windows 8.1"),
172 Self::Windows10_1507 => write!(f, "Windows 10 1507"),
173 Self::Windows10_1511 => write!(f, "Windows 10 1511"),
174 Self::Windows10_1607 => write!(f, "Windows 10 1607"),
175 Self::Windows10_1703 => write!(f, "Windows 10 1703"),
176 Self::Windows10_1709 => write!(f, "Windows 10 1709"),
177 Self::Windows10_1803 => write!(f, "Windows 10 1803"),
178 Self::Windows10_1809 => write!(f, "Windows 10 1809"),
179 Self::Windows10_1903 => write!(f, "Windows 10 1903"),
180 Self::Windows10_1909 => write!(f, "Windows 10 1909"),
181 Self::Windows10_2004 => write!(f, "Windows 10 2004"),
182 Self::Windows10_20H2 => write!(f, "Windows 10 20H2"),
183 Self::Windows10_21H1 => write!(f, "Windows 10 21H1"),
184 Self::Windows10_21H2 => write!(f, "Windows 10 21H2"),
185 Self::Windows10_22H2 => write!(f, "Windows 10 22H2"),
186 Self::Windows11_21H2 => write!(f, "Windows 11 21H2"),
187 Self::Windows11_22H2 => write!(f, "Windows 11 22H2"),
188 Self::Windows11_23H2 => write!(f, "Windows 11 23H2"),
189 Self::Windows11_24H2 => write!(f, "Windows 11 24H2"),
190 Self::Unknown => write!(f, "Unknown"),
191 }
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198
199 #[test]
200 fn test_version_comparison() {
201 let win10 = WindowsVersion {
202 major: 10,
203 minor: 0,
204 build: 19041,
205 };
206 let win11 = WindowsVersion {
207 major: 10,
208 minor: 0,
209 build: 22000,
210 };
211 assert!(win10 < win11);
212 }
213
214 #[test]
215 fn test_release_mapping() {
216 let win11 = WindowsVersion {
217 major: 10,
218 minor: 0,
219 build: 22621,
220 };
221 assert_eq!(win11.release(), WindowsRelease::Windows11_22H2);
222 }
223}