heaths / msiverca-rs

Windows Installer custom actions to get real version information
MIT License
0 stars 0 forks source link

Even RtlGetVersion() is lied to #5

Open heaths opened 1 year ago

heaths commented 1 year ago

Despite having tested this in a standalone executable, in Windows Installer it seems we're still getting lied to:

Action start 1:14:18: VerSetVersionInfo.
MSI (c) (C8:80) [01:14:18:498]: Creating MSIHANDLE (1) of type 790542 for thread 3456
MSI (c) (C8:B8) [01:14:18:499]: Invoking remote custom action. DLL: C:\Users\heaths\AppData\Local\Temp\MSIB25C.tmp, Entrypoint: SetVersionInfo
MSI (c) (C8:1C) [01:14:18:500]: Cloaking enabled.
MSI (c) (C8:1C) [01:14:18:500]: Attempting to enable all disabled privileges before calling Install on Server
MSI (c) (C8:1C) [01:14:18:500]: Connected to service for CA interface.
MSI (c) (C8!0C) [01:14:18:546]: PROPERTY CHANGE: Adding VER_WINDOWS_MAJOR property. Its value is '6'.
MSI (c) (C8!0C) [01:14:18:546]: PROPERTY CHANGE: Adding VER_WINDOWS_MINOR property. Its value is '0'.
MSI (c) (C8!0C) [01:14:18:546]: PROPERTY CHANGE: Adding VER_WINDOWS_BUILD property. Its value is '6000'.
MSI (c) (C8:B8) [01:14:18:547]: Closing MSIHANDLE (1) of type 790542 for thread 3456
Action ended 1:14:18: VerSetVersionInfo. Return value 1.

I suspect this is because msiexec.exe - both the client, service (with "/v"), and custom action host - is manifested as supporting Windows 8.1 even on Windows 11:

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> 
  <application> 
    <!-- Windows BLUE -->
    <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
  </application> 
</compatibility>
heaths commented 1 year ago

After additional testing, it appears it's not the manifest of msiexec.exe, to which I tried using it both as-is and with minor modifications to change the identity:

// main.cpp
#define _WIN32_WINNT 0x0603
#include <ntddk.h>
#include <iostream>

void main()
{
    RTL_OSVERSIONINFOW info = { sizeof(RTL_OSVERSIONINFOW) };
    auto status = RtlGetVersion(&info);
    if (status == STATUS_SUCCESS)
    {
        printf("%d.%d.%d\n", info.dwMajorVersion, info.dwMinorVersion, info.dwBuildNumber);
    }
}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- manifest.xml -->
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
    <assemblyIdentity 
        type="win32" 
        name="main"
        version="4.0.0.0"
        processorArchitecture="amd64"
    />
    <description> Test </description>
    <trustInfo xmlns="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 BLUE -->
            <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
        </application> 
    </compatibility>
    <asmv3:application>
        <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
            <dpiAware>true</dpiAware>
        </asmv3:windowsSettings>
    </asmv3:application>
</assembly>
cl main.cpp /I'C:\Program Files (x86)\Windows Kits\10\Include\10.0.22621.0\km' /D_AMD64_ /link 'C:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\km\x64\ntoskrnl.lib' /MANIFEST:EMBED /MANIFESTINPUT:manifest.xml

This still outputs "10.0.22621".

heaths commented 1 year ago

@tydunkel what is VSSetup currently doing to detect Windows 10 and above, or is it? Even the newer helpers based on VerifyVersionInfo are deprecated as of Windows 10.

...though, this likely might be some AppCompat shim applied to Windows Installer that otherwise wouldn't apply to other executables like the test above that is only manifested for Windows 8.1 and uses RtlGetVersion() directly, while the custom action uses GetProcAddress(), ultimately, to import it from ntdll.dll as WiX 4 is doing as well.

heaths commented 1 year ago

Presumably, custom bootstrappers like Burn could still call RtlGetVersion() and get the real value - manifested or not - but this would necessitate writing a custom bootstrapper just to appropriate version information.

Seems Burn, at least, is already doing this.

/cc @barnson