国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Windows系統版本判定那些事兒

2019-11-08 19:49:51
字體:
來源:轉載
供稿:網友

轉自:http://blog.csdn.net/magictong/article/details/40753519

前言

本文并不是討論Windows操作系統的版本來歷和特點,也不是討論為什么沒有Win9,而是從程序員角度討論下Windows獲取系統版本的方法和遇到的一些問題。在Win8和Win10出來之后,在獲取系統版本時,可能很多人都碰到了類似的問題,為什么以前工作得很好的API,突然開始說謊了?

 

我們一般怎么獲取系統版本

我想用的最多的可能就是這兩個API了吧。

DWord WINAPI GetVersion (VOID);

BOOL WINAPI GetVersionExW(__inout LPOSVERSIONINFOW lpVersionInformation);

其實GetVersion和GetVersionExW的實現是類似的,內部都是調用的NtCurrentPeb這個函數,還有一個GetVersionExA內部則是調用的GetVersionExW來實現。

GetVersionExW大概是這么實現的(這只是Windows2000的源碼,后面的新系統,OSVERSIONINFOW這個結構多了幾倍的成員)。

WINBASEAPI BOOL WINAPI GetVersionExW(

    LPOSVERSIONINFOW lpVersionInformation)

{

    PPEB Peb;

    if (lpVersionInformation->dwOSVersionInfoSize != sizeof( *lpVersionInformation )) {

        SetLastError( ERROR_INSUFFICIENT_BUFFER );

       return FALSE;

        }

 

    Peb = NtCurrentPeb();

    lpVersionInformation->dwMajorVersion = Peb->OSMajorVersion;

    lpVersionInformation->dwMinorVersion = Peb->OSMinorVersion;

    lpVersionInformation->dwBuildNumber  =Peb->OSBuildNumber;

    lpVersionInformation->dwPlatformId  = Peb->OSPlatformId;

    wcscpy(lpVersionInformation->szCSDVersion,BaseCSDVersion );

    return TRUE;

}

其中BaseCSDVersion是個全局變量,存放的是系統SP的字符串信息,在DLL初始化的時候就已經賦值了,由BaseDllInitialize來初始化。重點看下NtCurrentPeb這個函數,其實很顯然,GetVersionExW就是從PEB里面去拷貝版本信息。NtCurrentPeb是一個調用比較頻繁的函數,它返回當前進程的PEB結構地址,也就是通過fs寄存器去定位PEB,然后在GetVersionExW里面把PEB里面的系統版本信息拷貝給GetVersionExW的傳出參數,也就是上面的OSMajorVersion等成員。

 

現在為什么不行了

但是從Windows8.1出來之后,GetVersionExW這個API被微軟明文給廢棄了,這個坑下得可夠大的(參考[1])。也就是說從Windows8.1開始之后(包括Windows10),這個API常規情況下就是返回6.2了。

“In Windows 8.1, the GetVersion(Ex)APIs have been dePRecated. That means that while you can still call the APIs,if your app does not specifically target Windows 8.1, you will getWindows 8 versioning (6.2.0.0).”

但是此時你去查看應用軟件PEB的信息,發現PEB里面的系統版本還是正確的,在Windows10下面調試了一下,發現但是GetVersionExW確實返回的是6.2,但是PEB里面的版本則是6.4。也就是說微軟更改了這個API的實現。

 

去調試微軟對這個API做了什么改動意義不大,反正現在的結果就是這個API返回的值不對了,API也開始說謊了~不過在[1]里面,微軟同時給出一個解決方案,嗯,一邊跟你說,這個API已經被廢棄了,一邊又說還是可以用的,這不是坑爹是什么……解決方案是什么呢?修改manifest文件。加一段compatibility節點。

<?xml version="1.0"encoding="UTF-8" standalone="yes"?>

<assembly manifestVersion="1.0"xmlns="urn:schemas-microsoft-com:asm.v1"xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">

    <description> my appexe </description>

    <trustInfoxmlns="urn:schemas-microsoft-com:asm.v3">

       <security>

           <requestedPrivileges>

               <requestedExecutionLevel

                   level="asInvoker"

                   uiaccess="false"

               />    

           </requestedPrivileges>

       </security>

    </trustInfo>

    <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">

       <application>

           <!-- Windows 8.1 -->

           <supportedOSId="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>

           <!-- Windows Vista -->

           <supportedOSId="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>

           <!-- Windows 7 -->

           <supportedOSId="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>

           <!-- Windows 8 -->

           <supportedOSId="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>

       </application>

    </compatibility>

</assembly>

主要就是compatibility部分了,如果你已經有manifest文件了,只需要添加compatibility部分即可。對了Windows10怎么辦?貌似[1]里面還沒有說啊,別急,用

<!-- Windows 10 -->

<supportedOSId="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>

就好了,怎么知道的?請叫我雷鋒!在Windows10下面測試一把的結果如圖。

 

兼容模式的影響

還有一個可能的情況會造成GetVersionExW返回的系統版本和實際的系統版本不一樣。這個與Windows8.1,Windows10沒有什么關系。純粹是為了兼容考慮,在設置兼容模式之后,GetVersionExW返回的是兼容的目標版本的系統版本。啟動調試去查看應用程序的PEB是不是被修改過了,結果發現,并沒有修改過PEB。那么問題來了,為什么GetVersionExW的值發生變化了呢?

直接調試GetVersionExW發現,在設置兼容模式之后,微軟使用IATHook的方式,Hook了一堆的(嗯,不是1-2個,而是一堆)系統API,其中GetVersionExW就被AcLayers.dll里面的一個函數給Hook了,然后Hook函數里面返回了兼容系統版本號。

 

怎樣判斷兼容模式

一般來說,應用程序不需要判斷當前是否處于兼容模式下運行,微軟實現這個機制的目的本意就是想對應用程序透明。主要是很多“古老的”程序內部嚴格限定只能在某個具體的系統下運行,譬如限定在WindowsXP SP3下運行(因為當時微軟的系統最高版本可能就是XP),這樣當用戶操作系統升級之后,譬如升級到了Windows7,這個時候問題來了!本來一般情況下微軟的系統是可以前向兼容的,結果應用程序自己主動不兼容,發現不是XP,主動退出,導致用戶用不了了,因此微軟發明了一個兼容模式,高版本的系統可以模擬一個低版本的系統運行環境,這樣就解決大量的類似問題。

在兼容模式下,當應用程序調用GetVersionExW等API時,返回的是兼容的目標系統的系統版本,當然這只是兼容模式技術解決的一個問題而已,但是是較重要的一個問題(兼容模式還解決了很多其它問題)。

一般的應用程序不需要關心這個兼容模式。但是某些特殊的應用程序卻恰恰需要,應用程序可能會根據不同的系統版本做不同的事情,而一個可能性是用戶誤把應用程序設置為某個低版本操作系統兼容運行,導致整個程序運行反而異常。

舉個例子,像系統補丁修復程序,一般來說漏洞補丁都是和系統版本一一對應,如果程序使用GetVersionExW來獲取系統版本,那么程序運行在Windows7下面,因為兼容模式的影響,導致補丁修復程序推送了一大批WindowsXP下面的補丁,想想這個場景,也是有點尷尬的。

從大部分的使用場景上面來說,放棄使用GetVersionExW也許是一個更好的選擇。通過其它方式拿到更精確的系統版本,不用考慮兼容模式的副作用,也不用擔心Windows8(主要是指Window8.1和Window10)以上的系統獲取到錯誤的系統版本。

那么怎么判斷當成程序正在兼容模式運行呢?方法應該有很多,比較簡單的方法,[4]里面介紹過一種,不過這種方法要注意,在Windows8.1之后,它可能給出錯誤的結果,要按照上面提到的辦法,讓GetVersionExW返回正確的值。

另外一種更好的方法是判斷注冊表里面的應用程序兼容模式記錄列表,當把一個應用程序設置為兼容模式或者管理員權限啟動之后,系統會在HKEY_CURRENT_USER/Software/Microsoft/Windows NT/CurrentVersion/AppCompatFlags/Layers下面記錄相應的信息,如果想所有用戶起效,則修改HKEY_LOCAL_MACHINE//Software/Microsoft/Windows NT/CurrentVersion/AppCompatFlags/Layers即可。我們可以試試設置之后的效果。我在Windows7SP1下面隨意設置了幾個。

可以很清楚的看到兩個程序被設置為兼容Win7RTM運行和兼容WINXPSP3運行,如果你去掉這兩個注冊表值,則應用程序就不再以兼容模式運行,因此實際上可以檢測這個位置判斷哪些程序被設置為兼容模式運行,甚至可以通過刪除這里的內容,去掉某些應用程序的兼容模式設置。同時可以發現的是微軟用很容易識別的字符串來描述兼容的目標系統,更多的兼容描述字符串可以參考[3],另外要注意的是從Windows8開始,這些字符串前面多了一個波浪線和空格(~ ),譬如兼容WINXPSP3,在Windows10下面是~ WINXPSP3。

 

判斷系統版本更好的辦法

GetVersionExW既然被微軟廢棄了,再使用總覺得拔涼拔涼的,有什么更好的判斷系統版本的方法嗎?答案是肯定的!下面給出幾種實踐中用過的方法。

1、首先從原理上來說,GetVersionExW是讀取的PEB里面的版本信息,其實我們自己也可以讀取PEB嘛,只是麻煩一點。這個就不給例子了。有興趣可以自己實現一下。

2、微軟在[1]里面其實推薦過一批更好的API([7]),號稱接口名更人性化,從名字上面看確實含義更清晰了,不過使用起來是否方便就仁者見仁智者見智了,隨意羅列幾個,不過這套API聲明在<VersionHelpers.h>里面,比較新的SDK才有。

VERSIONHELPERAPI IsWindows7OrGreater()

VERSIONHELPERAPIIsWindows7SP1OrGreater()

VERSIONHELPERAPI IsWindows8OrGreater()

VERSIONHELPERAPI IsWindows8_1OrGreater()

VERSIONHELPERAPI IsWindowsServer()

 

3、使用VerifyVersionInfo來進行版本判斷(參考[8]),這個API聲明在winbase.h里面,從Windows2000系統就已經開始提供了,但是我們可能很少使用,說實話,使用起來不是特別方便。我們先看看是怎么使用的,它本質是進行版本比較。

BOOL WINAPI VerifyVersionInfo(

 _In_  LPOSVERSIONINFOEX lpVersionInfo,

 _In_  DWORD dwTypeMask,

 _In_  DWORDLONG dwlConditionMask

);

這個函數的原型里面第一個參數是熟悉的OSVERSIONINFOEX,但是這里是做為傳入參數使用,第二個參數dwTypeMask用于指定要比較哪些項,可以比較主版本,次版本,Build號等等,可以使用位組合。第三個參數則是比較的方法,是>、=還是<,或者>=,<=等等,可以通過VER_SET_CONDITION來設置,可以進行各種組合來判斷,還是比較靈活的。看兩個例子吧。

BOOL IsWinVerGreaterThan(DWORDdwMajorVersion, DWORD dwMinorVersion)

{

    OSVERSIONINFOEXW osvi = {0};

    DWORDLONG dwlConditionMask = 0;

 

    ZeroMemory(&osvi, sizeof(osvi));

    osvi.dwOSVersionInfoSize= sizeof(osvi);

    osvi.dwMajorVersion= dwMajorVersion;

    osvi.dwMinorVersion= dwMinorVersion;

 

    // 主版本號判斷

    VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER);

    if (::VerifyVersionInfoW(&osvi, VER_MAJORVERSION, dwlConditionMask))

       return TRUE;

 

    // 次版本號判斷

    VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_EQUAL);

    VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER);

 

    return ::VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, dwlConditionMask);

}

 

//-------------------------------------------------------------------------

// 函數    : IsWinVerEqualTo

// 功能    : 判斷是否=某個特定的系統版本

// 返回值  : BOOL

// 參數    : DWORD dwMajorVersion

// 參數    : DWORD dwMinorVersion

// 附注    :

//-------------------------------------------------------------------------

BOOL IsWinVerEqualTo(DWORDdwMajorVersion, DWORD dwMinorVersion)

{

    OSVERSIONINFOEXW osvi = {0};

    DWORDLONG dwlConditionMask = 0;

 

    // 1、初始化系統版本信息數據結構

    ZeroMemory(&osvi, sizeof(osvi));

    osvi.dwOSVersionInfoSize= sizeof(osvi);

    osvi.dwMajorVersion= dwMajorVersion;

    osvi.dwMinorVersion= dwMinorVersion;

 

    // 2、初始化條件掩碼

    VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_EQUAL);

    VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_EQUAL);

 

    return ::VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION, dwlConditionMask);

}

封裝一下使用就更方便了,譬如要判斷當前是Window7,用IsWinVerEqualTo(6,1)即可。或者你不想暴露一些“惡心”的MagicNumber,可以再封裝一個IsWindows7()嘛。

 

4、還有一個我個人比較喜歡的方法是使用一個未文檔化的函數來獲取系統版本,也就是RtlGetNtVersionNumbers,這個是NTDLL里面的一個未文檔化函數。但是這個函數微軟把它導出了,因此我們就有辦法使用了。

使用方法:

//-------------------------------------------------------------------------

// 函數    : GetNtVersionNumbers

// 功能    : 調用RtlGetNtVersionNumbers獲取系統版本信息

// 返回值  : BOOL

// 參數    : DWORD& dwMajorVer 主版本

// 參數    : DWORD& dwMinorVer 次版本

// 參數    : DWORD& dwBuildNumber build號

// 附注    :

//-------------------------------------------------------------------------

BOOL GetNtVersionNumbers(DWORD&dwMajorVer, DWORD& dwMinorVer,DWORD& dwBuildNumber)

{

    BOOL bRet= FALSE;

    HMODULE hModNtdll= NULL;

    if (hModNtdll= ::LoadLibraryW(L"ntdll.dll"))

    {

        typedef void (WINAPI *pfRTLGETNTVERSIONNUMBERS)(DWORD*,DWORD*, DWORD*);

        pfRTLGETNTVERSIONNUMBERS pfRtlGetNtVersionNumbers;

        pfRtlGetNtVersionNumbers = (pfRTLGETNTVERSIONNUMBERS)::GetProcAddress(hModNtdll, "RtlGetNtVersionNumbers");

        if (pfRtlGetNtVersionNumbers)

        {

           pfRtlGetNtVersionNumbers(&dwMajorVer, &dwMinorVer,&dwBuildNumber);

           dwBuildNumber&= 0x0ffff;

           bRet = TRUE;

        }

 

        ::FreeLibrary(hModNtdll);

        hModNtdll = NULL;

    }

 

    return bRet;

}

使用未文檔化的函數要注意的一個點是,需要分析清楚函數的傳入參數的類型,否則傳錯了類型,如果類型大小不一樣,輕則函數出錯,重則程序崩潰(尤其是傳出參數)。我們可以看下RtlGetNtVersionNumbers這個函數是怎么實現的(調試用的ntdll.dll的版本是6.1.7601.18247,其它系統的也差不多的,僅僅是Hardcode的數字不一樣),下面是它的實現偽碼(IDA生成)。

int __stdcall RtlGetNtVersionNumbers(int a1, int a2, int a3)

{

 int result; // eax@5

 

 if ( a1 )

    *(_DWORD *)a1 = 6;

 if ( a2 )

    *(_DWORD *)a2 = 1;

 result = a3;

 if ( a3 )

    *(_DWORD *)a3 = 0xF0001DB1u;

 return result;

}

我只能說微軟,你干得漂亮!直接Hardcode處理,簡單干凈!

 

5、還有一種方法是直接去獲取NTDLL這個系統關鍵文件(其它的文件也可行,但是實踐證明NTDLL最好)的文件版本號,一般來說,該文件的版本基本上就是系統的版本。像[4]里面用到的判斷兼容的方法就是通過對比GetVersionEx的返回值和關鍵系統文件的版本,來判斷是否當前應用程序處理兼容模式下

 

注:建議不要使用RtlGetVersion來進行版本判斷。Windows2003之前它的行為在兼容模式下和GetVersionExW不一致,Vista之后在兼容模式下它的行為和GetVersionExW一致。

 

效果展示

分別在WindowsXP,Windows7,Windows10下面測試了這些方法。注意左邊的是常規模式運行,右邊的是兼容模式運行。

 

參考文獻

[1] Operating system version changes inWindows 8.1 and Windows Server 2012 R2 http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074(v=vs.85).aspx

[2] GetVersionExhttp://msdn.microsoft.com/en-us/library/ms724451(VS.85).aspx

[3] Running an Application asAdministrator or in Compatibility Mode http://www.verboon.info/2011/03/running-an-application-as-administrator-or-in-compatibility-mode/

[4] http://blog.csdn.NET/magictong/article/details/5829065怎樣判定應用程序自身運行在“兼容模式”下?

[5] http://blogs.msdn.com/b/chuckw/archive/2013/09/10/manifest-madness.aspxManifestMadness

[6] OSVERSIONINFOEX structure http://msdn.microsoft.com/en-us/library/ms724833(v=vs.85).aspx

[7] Version Helper functions http://msdn.microsoft.com/en-us/library/windows/desktop/dn424972(v=vs.85).aspx

[8] VerifyVersionInfofunction http://msdn.microsoft.com/en-us/ms725492(VS.85).aspx


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 万盛区| 永州市| 介休市| 阿克苏市| 夹江县| 华坪县| 黄大仙区| 上虞市| 龙陵县| 威远县| 延津县| 德惠市| 德兴市| 平阳县| 合作市| 沅陵县| 前郭尔| 伊吾县| 宜黄县| 新蔡县| 漾濞| 顺义区| 原阳县| 汶川县| 宣汉县| 登封市| 贵德县| 九江县| 涟源市| 建水县| 临沭县| 枣阳市| 红原县| 渝北区| 西林县| 上栗县| 湘潭市| 清河县| 虞城县| 阿拉尔市| 资阳市|