找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1186

积分

0

好友

210

主题
发表于 3 天前 | 查看: 7| 回复: 0

在Windows系统开发或运维脚本编写中,准确获取当前操作系统的版本信息是一个常见需求。然而,许多开发者会发现,使用常见的GetVersionEx函数或直接查询注册表ProductName得到的结果并不准确。本文将介绍一种通过底层ntdll.dll调用获取真实版本信息的方法,并提供完整的C++实现代码。

为何常规方法会失效?

在尝试获取Windows版本时,开发者常会遇到以下痛点:

注册表查询不准确

通过读取注册表路径 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion 下的 ProductName 值,得到的信息可能与实际系统不符。例如:

  • Windows 11 可能显示为 “Windows 10”
  • Windows 8 早期版本可能显示为 “Windows 6.2”

传统API被废弃

GetVersionEx API 自 Windows 8.1 起,其行为发生了变化。为了促进应用程序兼容性,未正确声明目标操作系统版本的应用程序将收到兼容性版本号(例如,在未清单的应用程序中,Windows 8.1 和 10 可能返回 Windows 8 的版本号 6.2)。微软已不鼓励使用此函数。

构建号(Build Number)的混淆

虽然注册表中也存在 CurrentBuildNumber,但其信息有时也不可靠。为了获得最核心、最准确的构建版本号,最佳实践是直接与系统底层交互。

解决方案:调用 ntdll.dll 中的 RtlGetVersion

最可靠的方法是动态调用位于 ntdll.dll 中的 RtlGetVersion 函数。这是GetVersionEx等API的底层实现,能够绕过应用程序兼容性劫持,返回操作系统真实的版本信息。

兼容性说明RtlGetVersion 函数最低支持 Windows 2000 Professional 操作系统。

核心函数实现

下面的C++函数演示了如何安全地调用 RtlGetVersion 来获取 RTL_OSVERSIONINFOEXW 结构体,其中包含了系统的主版本、次版本和构建号。

typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOEXW);

RTL_OSVERSIONINFOEXW GetRealOSVersion() {
    HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
    if (hMod) {
        RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion");
        if (fxPtr != nullptr) {
            RTL_OSVERSIONINFOEXW rovi = { 0 };
            rovi.dwOSVersionInfoSize = sizeof(rovi);
            if (STATUS_SUCCESS == fxPtr(&rovi)) {
                return rovi;
            }
        }
    }
    RTL_OSVERSIONINFOEXW rovi = { 0 };
    return rovi;
}

该函数首先获取 ntdll.dll 的模块句柄,然后找到 RtlGetVersion 函数的地址并调用它。通过这种方式,我们可以确保获取到的是操作系统内核报告的真实数据,这对于需要精确版本判定的系统编程场景至关重要。

获取Windows系统信息的完整实践

仅获取构建号还不够,一个完整的版本信息工具通常需要整合多种数据源。以下程序综合了注册表查询、传统API和 RtlGetVersion 方法,并提供了一个将构建号映射到已知Windows版本名称的查表功能。

完整代码解析

程序主要包含以下几个功能模块:

  1. 注册表信息读取:获取 DisplayVersion (如22H2)、ProductNameCurrentBuildNumberUBR (更新构建版本)。
  2. 传统API调用:演示 GetVersionEx 的调用(结果可能受清单影响)。
  3. 真实版本获取:使用上述 GetRealOSVersion 函数。
  4. 版本号映射:通过一个包含从Windows 3.1到Windows 11 24H2的构建号映射表,将数字构建号转换为可读的系统名称。
#include <iostream>
#include <string>
#include <windows.h>
#include <map>

typedef LONG NTSTATUS, * PNTSTATUS;
#define STATUS_SUCCESS (0x00000000)

typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOEXW);

// ... (此处省略上文已列出的 GetRealOSVersion() 函数及注册表操作辅助函数)

class WindowsVersions {
public:
    static std::map<int, std::string> getWindowsVersions() {
        std::map<int, std::string> windowsVersions;
        // Windows NT 系列
        windowsVersions[2195] = "Windows 2000 Professional";
        windowsVersions[2600] = "Windows XP";
        windowsVersions[3790] = "Windows XP Professional x64 Edition/Windows Server 2003";
        windowsVersions[6000] = "Windows Vista/Windows Server 2008";
        windowsVersions[7601] = "Windows 7, Service Pack 1";
        windowsVersions[9200] = "Windows 8/Windows Server 2012";
        windowsVersions[9600] = "Windows 8.1";
        windowsVersions[10240] = "Windows 10 version 1507";
        windowsVersions[19045] = "Windows 10 version 22H2";
        windowsVersions[22000] = "Windows 11 21H2";
        windowsVersions[22621] = "Windows 11 22H2";
        windowsVersions[26100] = "Windows 11 24H2";
        // 可在此处继续添加更新版本的映射
        return windowsVersions;
    }
};

int main() {
    HKEY hKey;
    // 打开注册表项
    if (!OpenRegistryKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", &hKey)) {
        std::cerr << "无法打开注册表项。" << std::endl;
        return 1;
    }

    // 1. 读取注册表中的各种信息
    char szDisplayVersion[256];
    if (GetDisplayVersion(hKey, szDisplayVersion, sizeof(szDisplayVersion))) {
        printf("DisplayVersion: %s\n", szDisplayVersion);
    }

    char szProductName[256];
    if (GetProductName(hKey, szProductName, sizeof(szProductName))) {
        printf("ProductName: %s\n", szProductName);
    }

    char szCurrentBuildNumber[256];
    if (GetCurrentBuildNumber(hKey, szCurrentBuildNumber, sizeof(szCurrentBuildNumber))) {
        printf("CurrentBuildNumber: %s\n", szCurrentBuildNumber);
    }

    DWORD ubr = 0;
    if (GetUBR(hKey, ubr)) {
        printf("UBR: %d\n", ubr); // UBR 代表本次构建内的更新版本,用于区分安全更新
    }

    // 2. 调用可能被劫持的 GetVersionEx
    OSVERSIONINFO osvi;
    ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    GetVersionEx(&osvi);
    printf("\nGetVersionEx 获取的信息 (可能不准确):\n");
    printf("  版本: %d.%d (Build: %d, CSD: %s)\n", osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber, osvi.szCSDVersion);

    // 3. 调用底层的 RtlGetVersion 获取真实版本
    printf("\nRtlGetVersion 获取的真实信息:\n");
    RTL_OSVERSIONINFOEXW osInfo = GetRealOSVersion();
    if (osInfo.dwOSVersionInfoSize != 0) {
        if (wcscmp(osInfo.szCSDVersion, L"") == 0)
            printf("  核心版本: %d.%d (Build: %d)\n", osInfo.dwMajorVersion, osInfo.dwMinorVersion, osInfo.dwBuildNumber);
        else
            printf("  核心版本: %d.%d (Build: %d, CSD: %ws)\n", osInfo.dwMajorVersion, osInfo.dwMinorVersion, osInfo.dwBuildNumber, osInfo.szCSDVersion);

        // 4. 通过构建号查表确定系统名称
        int currentBuildNumber = osInfo.dwBuildNumber;
        std::map<int, std::string> windowsVersions = WindowsVersions::getWindowsVersions();
        auto it = windowsVersions.find(currentBuildNumber);
        if (it != windowsVersions.end()) {
            printf("  对应系统: %s\n", it->second.c_str());
        } else {
            printf("  构建号 %d 未在已知版本列表中,可能为更新版本。\n", currentBuildNumber);
        }
    }

    // 关闭注册表项
    RegCloseKey(hKey);
    getchar();
    return 0;
}

程序输出示例

编译并运行上述程序,你可能会看到类似下面的输出,它清晰地对比了不同方法得到的结果:

DisplayVersion: 22H2
ProductName: Windows 10 Pro
CurrentBuildNumber: 19045
UBR: 3086

GetVersionEx 获取的信息 (可能不准确):
  版本: 10.0 (Build: 19045, CSD: )

RtlGetVersion 获取的真实信息:
  核心版本: 10.0 (Build: 19045)
  对应系统: Windows 10 version 22H2

在这个例子中,尽管注册表ProductName显示为“Windows 10 Pro”,但通过构建号19045查表,我们准确地确定了系统是“Windows 10 version 22H2”。GetVersionExRtlGetVersion在本例中结果一致,但这取决于应用程序的清单配置。

![]https://static1.yunpan.plus/attachment/d94c275a2646.png)

![]https://static1.yunpan.plus/attachment/d94c275a2646.png)

对比不同方法获取的Windows版本信息(示意图)

总结与建议

在需要精确判断Windows版本的场景下(例如软件兼容性检查、系统运维脚本或驱动开发),建议优先使用 RtlGetVersion 方法获取构建号,并结合构建号映射表来确定具体版本。

  • 注册表 ProductName:适用于向用户展示,但不适合用于逻辑判断。
  • GetVersionEx:已过时,其行为受应用程序清单影响,结果不可靠。
  • RtlGetVersion + 构建号映射:是当前获取真实、准确系统版本信息的最可靠方法。

你可以根据上述提供的完整代码,轻松地将其集成到自己的项目或工具中,实现精准的Windows版本检测。




上一篇:U-Net模型压缩实战:基于稀疏化(Sparsity)与量化(Quantization)的PyTorch实现
下一篇:Python requests库实现HTTP接口测试与mitmproxy响应拦截
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-17 17:28 , Processed in 0.349774 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表