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

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



The operation of this mechanism is based on the interception of various functions and emulation of their behavior characteristic of the specified version of Windows, for example, registry keys, directories with documents, etc. are emulated. All this is necessary for the program to think that it is running in the selected environment.

If the application is running in compatibility mode, calling 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 vastness of the network, I came across a way of detecting by 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 think it is acceptable, since the release of each version of Windows requires non-trivial support.

Using WMI


From Wikipedia
Windows Management Instrumentation (WMI) in literal translation is a Windows management toolkit. More to the point, WMI is one of the basic technologies for centralized management and monitoring the operation of various parts of the computer infrastructure running the Windows platform.

You can also get a version of Windows from WMI. From the documentation it follows that this can be done with such a request:

SELECT Version FROM Win32_OperatingSystem

Starting WMI Explorer in compatibility mode with 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 be pulled into the project by work with WMI.

We are looking in the registry


Perhaps the most elegant and correct way found on the network is to peek 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, having tested it on Windows 7, it 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 the kernel32.dll version


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

It’s a very suitable method, but for some reason I personally don’t like it, since there is still an alternative.

We analyze the PEB process


Every Windows process has a structure that describes it; it is called PEB. It is filled at the start of the process and contains the download address, a list of loaded modules, command line parameters, and, including, the version of Windows. 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 instantaneous, nothing superfluous, the only BUT is the undocumented PEB structure, but as you know Microsoft is very concerned about backward compatibility, so with a lot of optimism we can assume that since the description of the structure has been surfing the Internet for a long time, then it is already considered to be documented in Microsoft.

conclusions


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

Also popular now: