tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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