From fb81701db80f1745f51259b1f286de3fe2ec80c8 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Tue, 30 Jul 2019 23:46:11 +0200 Subject: [PATCH] windows: allow retrieving true version with RtlGetNtVersionNumbers While RtlGetVersion was added so that users can get the Windows version that isn't affected by manifesting, RtlGetVersion is still stubbed out by the application compatibility layer (aclayers.dll and apphelp.dll) for certain processes, such as msiexec.exe, rendering these functions useless for actually determining the underlying operating system. This matters in the case of msiexec.exe using a custom action DLL to install a kernel driver, which of course is version specific. This is also useful, it turns out, for the C runtime library, in which Microsoft uses this function too. It's existed as a stable interface since Windows XP, has Wine support, and is used in a decent amount of software. Change-Id: If391e43bc6d798eff6803d5a7aa6a179f2b31d88 Reviewed-on: https://go-review.googlesource.com/c/sys/+/188119 Run-TryBot: Jason A. Donenfeld TryBot-Result: Gobot Gobot Reviewed-by: Alex Brainman --- windows/syscall_windows.go | 13 +++++++++++-- windows/syscall_windows_test.go | 7 +++++-- windows/zsyscall_windows.go | 6 ++++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/windows/syscall_windows.go b/windows/syscall_windows.go index b2305092..452d4412 100644 --- a/windows/syscall_windows.go +++ b/windows/syscall_windows.go @@ -296,6 +296,7 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys coCreateGuid(pguid *GUID) (ret error) = ole32.CoCreateGuid //sys CoTaskMemFree(address unsafe.Pointer) = ole32.CoTaskMemFree //sys rtlGetVersion(info *OsVersionInfoEx) (ret error) = ntdll.RtlGetVersion +//sys rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) = ntdll.RtlGetNtVersionNumbers // syscall interface implementation for other packages @@ -1306,8 +1307,8 @@ func (t Token) KnownFolderPath(folderID *KNOWNFOLDERID, flags uint32) (string, e return UTF16ToString((*[(1 << 30) - 1]uint16)(unsafe.Pointer(p))[:]), nil } -// RtlGetVersion returns the true version of the underlying operating system, ignoring -// any manifesting or compatibility layers on top of the win32 layer. +// RtlGetVersion returns the version of the underlying operating system, ignoring +// manifest semantics but is affected by the application compatibility layer. func RtlGetVersion() *OsVersionInfoEx { info := &OsVersionInfoEx{} info.osVersionInfoSize = uint32(unsafe.Sizeof(*info)) @@ -1318,3 +1319,11 @@ func RtlGetVersion() *OsVersionInfoEx { _ = rtlGetVersion(info) return info } + +// RtlGetNtVersionNumbers returns the version of the underlying operating system, +// ignoring manifest semantics and the application compatibility layer. +func RtlGetNtVersionNumbers() (majorVersion, minorVersion, buildNumber uint32) { + rtlGetNtVersionNumbers(&majorVersion, &minorVersion, &buildNumber) + buildNumber &= 0xffff + return +} diff --git a/windows/syscall_windows_test.go b/windows/syscall_windows_test.go index ff9689a2..d21d33f0 100644 --- a/windows/syscall_windows_test.go +++ b/windows/syscall_windows_test.go @@ -219,7 +219,10 @@ func TestKnownFolderPath(t *testing.T) { func TestRtlGetVersion(t *testing.T) { version := windows.RtlGetVersion() - if version.MajorVersion < 6 { - t.Fatalf("MajorVersion = %d; want >= 6", version.MajorVersion) + major, minor, build := windows.RtlGetNtVersionNumbers() + // Go is not explictly added to the application compatibility database, so + // these two functions should return the same thing. + if version.MajorVersion != major || version.MinorVersion != minor || version.BuildNumber != build { + t.Fatalf("%d.%d.%d != %d.%d.%d", version.MajorVersion, version.MinorVersion, version.BuildNumber, major, minor, build) } } diff --git a/windows/zsyscall_windows.go b/windows/zsyscall_windows.go index d461bed9..e5d62f3b 100644 --- a/windows/zsyscall_windows.go +++ b/windows/zsyscall_windows.go @@ -234,6 +234,7 @@ var ( procCoCreateGuid = modole32.NewProc("CoCreateGuid") procCoTaskMemFree = modole32.NewProc("CoTaskMemFree") procRtlGetVersion = modntdll.NewProc("RtlGetVersion") + procRtlGetNtVersionNumbers = modntdll.NewProc("RtlGetNtVersionNumbers") procWSAStartup = modws2_32.NewProc("WSAStartup") procWSACleanup = modws2_32.NewProc("WSACleanup") procWSAIoctl = modws2_32.NewProc("WSAIoctl") @@ -2530,6 +2531,11 @@ func rtlGetVersion(info *OsVersionInfoEx) (ret error) { return } +func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) { + syscall.Syscall(procRtlGetNtVersionNumbers.Addr(), 3, uintptr(unsafe.Pointer(majorVersion)), uintptr(unsafe.Pointer(minorVersion)), uintptr(unsafe.Pointer(buildNumber))) + return +} + func WSAStartup(verreq uint32, data *WSAData) (sockerr error) { r0, _, _ := syscall.Syscall(procWSAStartup.Addr(), 2, uintptr(verreq), uintptr(unsafe.Pointer(data)), 0) if r0 != 0 {