windows_version.cc (16217B)
1 // Copyright 2012 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/win/windows_version.h" 6 7 #include <windows.h> 8 9 #include <memory> 10 #include <tuple> 11 #include <utility> 12 13 #include "base/check_op.h" 14 #include "base/file_version_info_win.h" 15 #include "base/files/file_path.h" 16 #include "base/logging.h" 17 #include "base/no_destructor.h" 18 #include "base/notreached.h" 19 #include "base/strings/string_util.h" 20 #include "base/strings/utf_string_conversions.h" 21 #include "base/threading/thread_restrictions.h" 22 #include "base/win/registry.h" 23 #include "build/build_config.h" 24 25 #if !defined(__clang__) && _MSC_FULL_VER < 191125507 26 #error VS 2017 Update 3.2 or higher is required 27 #endif 28 29 #if !defined(NTDDI_WIN10_NI) 30 #error Windows 10.0.22621.0 SDK or higher required. 31 #endif 32 33 namespace base { 34 namespace win { 35 36 namespace { 37 38 // The values under the CurrentVersion registry hive are mirrored under 39 // the corresponding Wow6432 hive. 40 constexpr wchar_t kRegKeyWindowsNTCurrentVersion[] = 41 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"; 42 43 // Returns the "UBR" (Windows 10 patch number) and "DisplayVersion" (or 44 // "ReleaseId" on earlier versions) (Windows 10 release number) from registry. 45 // "UBR" is an undocumented value and will be 0 if the value was not found. 46 // "ReleaseId" will be an empty string if neither new nor old values are found. 47 std::pair<int, std::string> GetVersionData() { 48 DWORD ubr = 0; 49 std::wstring release_id; 50 RegKey key; 51 52 if (key.Open(HKEY_LOCAL_MACHINE, kRegKeyWindowsNTCurrentVersion, 53 KEY_QUERY_VALUE) == ERROR_SUCCESS) { 54 key.ReadValueDW(L"UBR", &ubr); 55 // "DisplayVersion" has been introduced in Windows 10 2009 56 // when naming changed to mixed letters and numbers. 57 key.ReadValue(L"DisplayVersion", &release_id); 58 // Use discontinued "ReleaseId" instead, if the former is unavailable. 59 if (release_id.empty()) 60 key.ReadValue(L"ReleaseId", &release_id); 61 } 62 63 return std::make_pair(static_cast<int>(ubr), WideToUTF8(release_id)); 64 } 65 66 const _SYSTEM_INFO& GetSystemInfoStorage() { 67 static const _SYSTEM_INFO system_info = [] { 68 _SYSTEM_INFO info = {}; 69 ::GetNativeSystemInfo(&info); 70 return info; 71 }(); 72 return system_info; 73 } 74 75 } // namespace 76 77 // static 78 OSInfo** OSInfo::GetInstanceStorage() { 79 // Note: we don't use the Singleton class because it depends on AtExitManager, 80 // and it's convenient for other modules to use this class without it. 81 static OSInfo* info = []() { 82 _OSVERSIONINFOEXW version_info = {sizeof(version_info)}; 83 84 #pragma clang diagnostic push 85 #pragma clang diagnostic ignored "-Wdeprecated-declarations" 86 // GetVersionEx() is deprecated, and the suggested replacement are 87 // the IsWindows*OrGreater() functions in VersionHelpers.h. We can't 88 // use that because: 89 // - For Windows 10, there's IsWindows10OrGreater(), but nothing more 90 // granular. We need to be able to detect different Windows 10 releases 91 // since they sometimes change behavior in ways that matter. 92 // - There is no IsWindows11OrGreater() function yet. 93 ::GetVersionEx(reinterpret_cast<_OSVERSIONINFOW*>(&version_info)); 94 #pragma clang diagnostic pop 95 96 DWORD os_type = 0; 97 ::GetProductInfo(version_info.dwMajorVersion, version_info.dwMinorVersion, 98 0, 0, &os_type); 99 100 return new OSInfo(version_info, GetSystemInfoStorage(), os_type); 101 }(); 102 103 return &info; 104 } 105 106 // static 107 OSInfo* OSInfo::GetInstance() { 108 return *GetInstanceStorage(); 109 } 110 111 // static 112 OSInfo::WindowsArchitecture OSInfo::GetArchitecture() { 113 switch (GetSystemInfoStorage().wProcessorArchitecture) { 114 case PROCESSOR_ARCHITECTURE_INTEL: 115 return X86_ARCHITECTURE; 116 case PROCESSOR_ARCHITECTURE_AMD64: 117 return X64_ARCHITECTURE; 118 case PROCESSOR_ARCHITECTURE_IA64: 119 return IA64_ARCHITECTURE; 120 case PROCESSOR_ARCHITECTURE_ARM64: 121 return ARM64_ARCHITECTURE; 122 default: 123 return OTHER_ARCHITECTURE; 124 } 125 } 126 127 // Returns true if this is an x86/x64 process running on ARM64 through 128 // emulation. 129 // static 130 bool OSInfo::IsRunningEmulatedOnArm64() { 131 #if defined(ARCH_CPU_ARM64) 132 // If we're running native ARM64 then we aren't running emulated. 133 return false; 134 #else 135 using IsWow64Process2Function = decltype(&IsWow64Process2); 136 137 IsWow64Process2Function is_wow64_process2 = 138 reinterpret_cast<IsWow64Process2Function>(::GetProcAddress( 139 ::GetModuleHandleA("kernel32.dll"), "IsWow64Process2")); 140 if (!is_wow64_process2) { 141 return false; 142 } 143 USHORT process_machine; 144 USHORT native_machine; 145 bool retval = is_wow64_process2(::GetCurrentProcess(), &process_machine, 146 &native_machine); 147 if (!retval) { 148 return false; 149 } 150 if (native_machine == IMAGE_FILE_MACHINE_ARM64) { 151 return true; 152 } 153 return false; 154 #endif 155 } 156 157 OSInfo::OSInfo(const _OSVERSIONINFOEXW& version_info, 158 const _SYSTEM_INFO& system_info, 159 DWORD os_type) 160 : version_(Version::PRE_XP), 161 wow_process_machine_(WowProcessMachine::kUnknown), 162 wow_native_machine_(WowNativeMachine::kUnknown) { 163 version_number_.major = version_info.dwMajorVersion; 164 version_number_.minor = version_info.dwMinorVersion; 165 version_number_.build = version_info.dwBuildNumber; 166 std::tie(version_number_.patch, release_id_) = GetVersionData(); 167 version_ = MajorMinorBuildToVersion( 168 version_number_.major, version_number_.minor, version_number_.build); 169 InitializeWowStatusValuesForProcess(GetCurrentProcess()); 170 service_pack_.major = version_info.wServicePackMajor; 171 service_pack_.minor = version_info.wServicePackMinor; 172 service_pack_str_ = WideToUTF8(version_info.szCSDVersion); 173 174 processors_ = static_cast<int>(system_info.dwNumberOfProcessors); 175 allocation_granularity_ = system_info.dwAllocationGranularity; 176 177 if (version_info.dwMajorVersion == 6 || version_info.dwMajorVersion == 10) { 178 // Only present on Vista+. 179 switch (os_type) { 180 case PRODUCT_CLUSTER_SERVER: 181 case PRODUCT_DATACENTER_SERVER: 182 case PRODUCT_DATACENTER_SERVER_CORE: 183 case PRODUCT_ENTERPRISE_SERVER: 184 case PRODUCT_ENTERPRISE_SERVER_CORE: 185 case PRODUCT_ENTERPRISE_SERVER_IA64: 186 case PRODUCT_SMALLBUSINESS_SERVER: 187 case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM: 188 case PRODUCT_STANDARD_SERVER: 189 case PRODUCT_STANDARD_SERVER_CORE: 190 case PRODUCT_WEB_SERVER: 191 version_type_ = SUITE_SERVER; 192 break; 193 case PRODUCT_PROFESSIONAL: 194 case PRODUCT_ULTIMATE: 195 version_type_ = SUITE_PROFESSIONAL; 196 break; 197 case PRODUCT_ENTERPRISE: 198 case PRODUCT_ENTERPRISE_E: 199 case PRODUCT_ENTERPRISE_EVALUATION: 200 case PRODUCT_ENTERPRISE_N: 201 case PRODUCT_ENTERPRISE_N_EVALUATION: 202 case PRODUCT_ENTERPRISE_S: 203 case PRODUCT_ENTERPRISE_S_EVALUATION: 204 case PRODUCT_ENTERPRISE_S_N: 205 case PRODUCT_ENTERPRISE_S_N_EVALUATION: 206 case PRODUCT_BUSINESS: 207 case PRODUCT_BUSINESS_N: 208 version_type_ = SUITE_ENTERPRISE; 209 break; 210 case PRODUCT_PRO_FOR_EDUCATION: 211 case PRODUCT_PRO_FOR_EDUCATION_N: 212 version_type_ = SUITE_EDUCATION_PRO; 213 break; 214 case PRODUCT_EDUCATION: 215 case PRODUCT_EDUCATION_N: 216 version_type_ = SUITE_EDUCATION; 217 break; 218 case PRODUCT_HOME_BASIC: 219 case PRODUCT_HOME_PREMIUM: 220 case PRODUCT_STARTER: 221 default: 222 version_type_ = SUITE_HOME; 223 break; 224 } 225 } else if (version_info.dwMajorVersion == 5 && 226 version_info.dwMinorVersion == 2) { 227 if (version_info.wProductType == VER_NT_WORKSTATION && 228 system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { 229 version_type_ = SUITE_PROFESSIONAL; 230 } else if (version_info.wSuiteMask & VER_SUITE_WH_SERVER) { 231 version_type_ = SUITE_HOME; 232 } else { 233 version_type_ = SUITE_SERVER; 234 } 235 } else if (version_info.dwMajorVersion == 5 && 236 version_info.dwMinorVersion == 1) { 237 if (version_info.wSuiteMask & VER_SUITE_PERSONAL) 238 version_type_ = SUITE_HOME; 239 else 240 version_type_ = SUITE_PROFESSIONAL; 241 } else { 242 // Windows is pre XP so we don't care but pick a safe default. 243 version_type_ = SUITE_HOME; 244 } 245 } 246 247 OSInfo::~OSInfo() = default; 248 249 Version OSInfo::Kernel32Version() { 250 static const Version kernel32_version = 251 MajorMinorBuildToVersion(Kernel32BaseVersion().components()[0], 252 Kernel32BaseVersion().components()[1], 253 Kernel32BaseVersion().components()[2]); 254 return kernel32_version; 255 } 256 257 OSInfo::VersionNumber OSInfo::Kernel32VersionNumber() { 258 DCHECK_EQ(Kernel32BaseVersion().components().size(), 4u); 259 static const VersionNumber version = { 260 .major = Kernel32BaseVersion().components()[0], 261 .minor = Kernel32BaseVersion().components()[1], 262 .build = Kernel32BaseVersion().components()[2], 263 .patch = Kernel32BaseVersion().components()[3]}; 264 return version; 265 } 266 267 // Retrieve a version from kernel32. This is useful because when running in 268 // compatibility mode for a down-level version of the OS, the file version of 269 // kernel32 will still be the "real" version. 270 base::Version OSInfo::Kernel32BaseVersion() { 271 static const NoDestructor<base::Version> version([] { 272 // Allow the calls to `Kernel32BaseVersion()` to block, as they only happen 273 // once (after which the result is cached in `version`), and reading from 274 // kernel32.dll is fast in practice because it is used by all processes and 275 // therefore likely to be in the OS's file cache. 276 base::ScopedAllowBlocking allow_blocking; 277 std::unique_ptr<FileVersionInfoWin> file_version_info = 278 FileVersionInfoWin::CreateFileVersionInfoWin( 279 FilePath(FILE_PATH_LITERAL("kernel32.dll"))); 280 if (!file_version_info) { 281 // crbug.com/912061: on some systems it seems kernel32.dll might be 282 // corrupted or not in a state to get version info. In this case try 283 // kernelbase.dll as a fallback. 284 file_version_info = FileVersionInfoWin::CreateFileVersionInfoWin( 285 FilePath(FILE_PATH_LITERAL("kernelbase.dll"))); 286 } 287 CHECK(file_version_info); 288 return file_version_info->GetFileVersion(); 289 }()); 290 return *version; 291 } 292 293 bool OSInfo::IsWowDisabled() const { 294 return (wow_process_machine_ == WowProcessMachine::kDisabled); 295 } 296 297 bool OSInfo::IsWowX86OnAMD64() const { 298 return (wow_process_machine_ == WowProcessMachine::kX86 && 299 wow_native_machine_ == WowNativeMachine::kAMD64); 300 } 301 302 bool OSInfo::IsWowX86OnARM64() const { 303 return (wow_process_machine_ == WowProcessMachine::kX86 && 304 wow_native_machine_ == WowNativeMachine::kARM64); 305 } 306 307 bool OSInfo::IsWowAMD64OnARM64() const { 308 #if defined(ARCH_CPU_X86_64) 309 // An AMD64 process running on an ARM64 device results in the incorrect 310 // identification of the device architecture (AMD64 is reported). However, 311 // IsWow64Process2 will return the correct device type for the native 312 // machine, even though the OS doesn't consider an AMD64 process on an ARM64 313 // processor a classic Windows-on-Windows setup. 314 return (wow_process_machine_ == WowProcessMachine::kDisabled && 315 wow_native_machine_ == WowNativeMachine::kARM64); 316 #else 317 return false; 318 #endif 319 } 320 321 bool OSInfo::IsWowX86OnOther() const { 322 return (wow_process_machine_ == WowProcessMachine::kX86 && 323 wow_native_machine_ == WowNativeMachine::kOther); 324 } 325 326 std::string OSInfo::processor_model_name() { 327 if (processor_model_name_.empty()) { 328 const wchar_t kProcessorNameString[] = 329 L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"; 330 RegKey key(HKEY_LOCAL_MACHINE, kProcessorNameString, KEY_READ); 331 std::wstring value; 332 key.ReadValue(L"ProcessorNameString", &value); 333 processor_model_name_ = WideToUTF8(value); 334 } 335 return processor_model_name_; 336 } 337 338 // With the exception of Server 2003, server variants are treated the same as 339 // the corresponding workstation release. 340 // static 341 Version OSInfo::MajorMinorBuildToVersion(uint32_t major, 342 uint32_t minor, 343 uint32_t build) { 344 if (major == 11) { 345 // We know nothing about this version of Windows or even if it exists. 346 // Known Windows 11 versions have a major number 10 and are thus handled by 347 // the == 10 block below. 348 return Version::WIN11; 349 } 350 351 if (major == 10) { 352 if (build >= 22621) { 353 return Version::WIN11_22H2; 354 } 355 if (build >= 22000) { 356 return Version::WIN11; 357 } 358 if (build >= 20348) { 359 return Version::SERVER_2022; 360 } 361 if (build >= 19045) { 362 return Version::WIN10_22H2; 363 } 364 if (build >= 19044) { 365 return Version::WIN10_21H2; 366 } 367 if (build >= 19043) { 368 return Version::WIN10_21H1; 369 } 370 if (build >= 19042) { 371 return Version::WIN10_20H2; 372 } 373 if (build >= 19041) { 374 return Version::WIN10_20H1; 375 } 376 if (build >= 18363) { 377 return Version::WIN10_19H2; 378 } 379 if (build >= 18362) { 380 return Version::WIN10_19H1; 381 } 382 if (build >= 17763) { 383 return Version::WIN10_RS5; 384 } 385 if (build >= 17134) { 386 return Version::WIN10_RS4; 387 } 388 if (build >= 16299) { 389 return Version::WIN10_RS3; 390 } 391 if (build >= 15063) { 392 return Version::WIN10_RS2; 393 } 394 if (build >= 14393) { 395 return Version::WIN10_RS1; 396 } 397 if (build >= 10586) { 398 return Version::WIN10_TH2; 399 } 400 return Version::WIN10; 401 } 402 403 if (major > 6) { 404 // Hitting this likely means that it's time for a >11 block above. 405 NOTREACHED() << major << "." << minor << "." << build; 406 return Version::WIN_LAST; 407 } 408 409 if (major == 6) { 410 switch (minor) { 411 case 0: 412 return Version::VISTA; 413 case 1: 414 return Version::WIN7; 415 case 2: 416 return Version::WIN8; 417 default: 418 DCHECK_EQ(minor, 3u); 419 return Version::WIN8_1; 420 } 421 } 422 423 if (major == 5 && minor != 0) { 424 // Treat XP Pro x64, Home Server, and Server 2003 R2 as Server 2003. 425 return minor == 1 ? Version::XP : Version::SERVER_2003; 426 } 427 428 // Win 2000 or older. 429 return Version::PRE_XP; 430 } 431 432 Version GetVersion() { 433 return OSInfo::GetInstance()->version(); 434 } 435 436 OSInfo::WowProcessMachine OSInfo::GetWowProcessMachineArchitecture( 437 const int process_machine) { 438 switch (process_machine) { 439 case IMAGE_FILE_MACHINE_UNKNOWN: 440 return OSInfo::WowProcessMachine::kDisabled; 441 case IMAGE_FILE_MACHINE_I386: 442 return OSInfo::WowProcessMachine::kX86; 443 case IMAGE_FILE_MACHINE_ARM: 444 case IMAGE_FILE_MACHINE_THUMB: 445 case IMAGE_FILE_MACHINE_ARMNT: 446 return OSInfo::WowProcessMachine::kARM32; 447 } 448 return OSInfo::WowProcessMachine::kOther; 449 } 450 451 OSInfo::WowNativeMachine OSInfo::GetWowNativeMachineArchitecture( 452 const int native_machine) { 453 switch (native_machine) { 454 case IMAGE_FILE_MACHINE_ARM64: 455 return OSInfo::WowNativeMachine::kARM64; 456 case IMAGE_FILE_MACHINE_AMD64: 457 return OSInfo::WowNativeMachine::kAMD64; 458 } 459 return OSInfo::WowNativeMachine::kOther; 460 } 461 462 void OSInfo::InitializeWowStatusValuesFromLegacyApi(HANDLE process_handle) { 463 BOOL is_wow64 = FALSE; 464 if (!::IsWow64Process(process_handle, &is_wow64)) 465 return; 466 if (is_wow64) { 467 wow_process_machine_ = WowProcessMachine::kX86; 468 wow_native_machine_ = WowNativeMachine::kAMD64; 469 } else { 470 wow_process_machine_ = WowProcessMachine::kDisabled; 471 } 472 } 473 474 void OSInfo::InitializeWowStatusValuesForProcess(HANDLE process_handle) { 475 static const auto is_wow64_process2 = 476 reinterpret_cast<decltype(&IsWow64Process2)>(::GetProcAddress( 477 ::GetModuleHandle(L"kernel32.dll"), "IsWow64Process2")); 478 if (!is_wow64_process2) { 479 InitializeWowStatusValuesFromLegacyApi(process_handle); 480 return; 481 } 482 483 USHORT process_machine = IMAGE_FILE_MACHINE_UNKNOWN; 484 USHORT native_machine = IMAGE_FILE_MACHINE_UNKNOWN; 485 if (!is_wow64_process2(process_handle, &process_machine, &native_machine)) { 486 return; 487 } 488 wow_process_machine_ = GetWowProcessMachineArchitecture(process_machine); 489 wow_native_machine_ = GetWowNativeMachineArchitecture(native_machine); 490 } 491 492 } // namespace win 493 } // namespace base