📜 ⬆️ ⬇️

How to find out the real version of Windows from compatibility mode

I think everyone at least once faced with a situation where it was not possible to run the old program on a modern OS, and in this case Windows compatibility mode helped.



At the heart of this mechanism is the interception of various functions and emulation of their behavior inherent in the specified version of Windows, for example, emulated registry keys, directories of documents and stuff. All this is necessary in order for the program to think that it is running in the chosen environment.
')
If the application is running in compatibility mode, the call to GetVersionEx will return a dummy version of Windows, which is probably not suitable for system programs such as OS tweakers. How to be in this case?

Analysis of exported functions


In the open spaces of the network, I came across a detection method based on the presence / absence of exported functions in system libraries. Code example:
TDSiWindowsVersion = (wvUnknown, wvWin31, wvWin95, wvWin95OSR2, wvWin98, wvWin98SE, wvWinME, wvWin9x, wvWinNT3, wvWinNT4, wvWin2000, wvWinXP, wvWinNT, wvWinServer2003, wvWinVista); function DSiGetTrueWindowsVersion: TDSiWindowsVersion; function ExportsAPI(module: HMODULE; const apiName: string): boolean; begin Result := GetProcAddress(module, PChar(apiName)) <> nil; end; { ExportsAPI } var hKernel32: HMODULE; begin { DSiGetTrueWindowsVersion } hKernel32 := GetModuleHandle('kernel32'); Win32Check(hKernel32 <> 0); if ExportsAPI(hKernel32, 'GetLocaleInfoEx') then Result := wvWinVista else if ExportsAPI(hKernel32, 'GetLargePageMinimum') then Result := wvWinServer2003 else if ExportsAPI(hKernel32, 'GetNativeSystemInfo') then Result := wvWinXP else if ExportsAPI(hKernel32, 'ReplaceFile') then Result := wvWin2000 else if ExportsAPI(hKernel32, 'OpenThread') then Result := wvWinME else if ExportsAPI(hKernel32, 'GetThreadPriorityBoost') then Result := wvWinNT4 else if ExportsAPI(hKernel32, 'IsDebuggerPresent') then //is also in NT4! Result := wvWin98 else if ExportsAPI(hKernel32, 'GetDiskFreeSpaceEx') then //is also in NT4! Result := wvWin95OSR2 else if ExportsAPI(hKernel32, 'ConnectNamedPipe') then Result := wvWinNT3 else if ExportsAPI(hKernel32, 'Beep') then Result := wvWin95 else // we have no idea Result := DSiGetWindowsVersion; end; { DSiGetTrueWindowsVersion } 


The solution is interesting, but I do not consider it acceptable, since with the release of each version of Windows, nontrivial support is required.

Using WMI


From wikipedia
The literal translation of Windows Management Instrumentation (WMI) is a Windows management toolkit. More generally, WMI is one of the core technologies for centralized management and monitoring of various parts of the computer infrastructure running on the Windows platform.

Windows version can also be obtained from WMI. From the documentation it follows that this can be done by such a request:

SELECT Version FROM Win32_OperatingSystem

By running WMI Explorer in compatibility mode for Windows XP, you can see that this value is not emulated:


The method works, moreover, it is fully documented, but slow, and requires a lot of code to work with WMI into the project.

We are looking in the registry


Perhaps the most elegant and correct way found on the net is to look at the value in the registry:
HLKM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\CurrentVersion
Well, let's try:
 with TRegistry.Create do try RootKey := HKEY_LOCAL_MACHINE; if OpenKeyReadOnly('SOFTWARE\Microsoft\Windows NT\CurrentVersion') then Edit1.Text := ReadString('CurrentVersion'); finally Free; end; 

Unfortunately, checking it on Windows 7 turned out that this registry key is emulated. It seems that in previous versions of Windows this method worked, but, alas, now this trick will not work .

Analysis of kernel32.dll version


I did not check it myself, but they say that the file version of kernel32.dll coincides with the version of Windows. On my Windows 7 computer, it’s like this:

It is a good way, but for me, for unknown reasons, I don’t like it, since there is still an alternative.

Analyzing the PEB process


Each Windows process has a structure describing it, it is called PEB. It is filled at the start of the process and contains the download address, the list of loaded modules, command line parameters, and, in particular, the Windows version. Below is an example of a module, using which you can get a real version of Windows (tested on Delphi 2010 Win32):
 unit RealWindowsVerUnit; interface uses Windows; var //  ,         //   Win32MajorVersionReal: Integer; Win32MinorVersionReal: Integer; implementation type PPEB=^PEB; PEB = record InheritedAddressSpace: Boolean; ReadImageFileExecOptions: Boolean; BeingDebugged: Boolean; Spare: Boolean; Mutant: Cardinal; ImageBaseAddress: Pointer; LoaderData: Pointer; ProcessParameters: Pointer; //PRTL_USER_PROCESS_PARAMETERS; SubSystemData: Pointer; ProcessHeap: Pointer; FastPebLock: Pointer; FastPebLockRoutine: Pointer; FastPebUnlockRoutine: Pointer; EnvironmentUpdateCount: Cardinal; KernelCallbackTable: PPointer; EventLogSection: Pointer; EventLog: Pointer; FreeList: Pointer; //PPEB_FREE_BLOCK; TlsExpansionCounter: Cardinal; TlsBitmap: Pointer; TlsBitmapBits: array[0..1] of Cardinal; ReadOnlySharedMemoryBase: Pointer; ReadOnlySharedMemoryHeap: Pointer; ReadOnlyStaticServerData: PPointer; AnsiCodePageData: Pointer; OemCodePageData: Pointer; UnicodeCaseTableData: Pointer; NumberOfProcessors: Cardinal; NtGlobalFlag: Cardinal; Spare2: array[0..3] of Byte; CriticalSectionTimeout: LARGE_INTEGER; HeapSegmentReserve: Cardinal; HeapSegmentCommit: Cardinal; HeapDeCommitTotalFreeThreshold: Cardinal; HeapDeCommitFreeBlockThreshold: Cardinal; NumberOfHeaps: Cardinal; MaximumNumberOfHeaps: Cardinal; ProcessHeaps: Pointer; GdiSharedHandleTable: Pointer; ProcessStarterHelper: Pointer; GdiDCAttributeList: Pointer; LoaderLock: Pointer; OSMajorVersion: Cardinal; OSMinorVersion: Cardinal; OSBuildNumber: Cardinal; OSPlatformId: Cardinal; ImageSubSystem: Cardinal; ImageSubSystemMajorVersion: Cardinal; ImageSubSystemMinorVersion: Cardinal; GdiHandleBuffer: array [0..33] of Cardinal; PostProcessInitRoutine: Cardinal; TlsExpansionBitmap: Cardinal; TlsExpansionBitmapBits: array [0..127] of Byte; SessionId: Cardinal; end; //  PEB   function GetPDB: PPEB; stdcall; asm MOV EAX, DWORD PTR FS:[30h] end; initialization //    Win32MajorVersionReal := GetPDB^.OSMajorVersion; Win32MinorVersionReal := GetPDB^.OSMinorVersion; end. 


The speed of work is immediate, nothing superfluous, the only BUT is the undocumented PEB structure, but as you know, Microsoft is very concerned about backward compatibility, so with a large degree of optimism we can assume that since the structure description has been browsing the Internet for a long time, then Microsoft already considers it documented.

findings


And the conclusions are simple, if you really need to get the real version, then WMI is a great option, if you need a lightweight solution - see PEB.

Source: https://habr.com/ru/post/227453/


All Articles