From 516e3c20635fbe39539f7ed361a15c51fec29cd8 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Tue, 18 Jun 2019 12:54:32 +0200 Subject: [PATCH] windows: allow determining real version number Other functions, like GetVersion(), will lie about the OS version depending on various win32 and manifest compatibility shims in place. Calling RtlGetVersion is the proper way to retrieve the true OS version. Change-Id: I2bd6d097dd763df51617cd825dc0ad300abf6212 Reviewed-on: https://go-review.googlesource.com/c/sys/+/182718 Run-TryBot: Jason A. Donenfeld TryBot-Result: Gobot Gobot Reviewed-by: Matt Layher Reviewed-by: Jason A. Donenfeld --- windows/syscall_windows.go | 14 ++++++++++++++ windows/syscall_windows_test.go | 7 +++++++ windows/types_windows.go | 14 ++++++++++++++ windows/zsyscall_windows.go | 10 ++++++++++ 4 files changed, 45 insertions(+) diff --git a/windows/syscall_windows.go b/windows/syscall_windows.go index 92ce02bb..be8c50f2 100644 --- a/windows/syscall_windows.go +++ b/windows/syscall_windows.go @@ -295,6 +295,7 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys stringFromGUID2(rguid *GUID, lpsz *uint16, cchMax int32) (chars int32) = ole32.StringFromGUID2 //sys coCreateGuid(pguid *GUID) (ret error) = ole32.CoCreateGuid //sys coTaskMemFree(address unsafe.Pointer) = ole32.CoTaskMemFree +//sys rtlGetVersion(info *OsVersionInfoEx) (ret error) = ntdll.RtlGetVersion // syscall interface implementation for other packages @@ -1304,3 +1305,16 @@ func (t Token) KnownFolderPath(folderID *KNOWNFOLDERID, flags uint32) (string, e defer coTaskMemFree(unsafe.Pointer(p)) 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. +func RtlGetVersion() *OsVersionInfoEx { + info := &OsVersionInfoEx{} + info.osVersionInfoSize = uint32(unsafe.Sizeof(*info)) + // According to documentation, this function always succeeds. + // The function doesn't even check the validity of the + // osVersionInfoSize member. Disassembling ntdll.dll indicates + // that the documentation is indeed correct about that. + _ = rtlGetVersion(info) + return info +} diff --git a/windows/syscall_windows_test.go b/windows/syscall_windows_test.go index f1d5ec18..ff9689a2 100644 --- a/windows/syscall_windows_test.go +++ b/windows/syscall_windows_test.go @@ -216,3 +216,10 @@ func TestKnownFolderPath(t *testing.T) { t.Fatalf("Path = %q; want %q", got, want) } } + +func TestRtlGetVersion(t *testing.T) { + version := windows.RtlGetVersion() + if version.MajorVersion < 6 { + t.Fatalf("MajorVersion = %d; want >= 6", version.MajorVersion) + } +} diff --git a/windows/types_windows.go b/windows/types_windows.go index 1cba11ed..8a563f92 100644 --- a/windows/types_windows.go +++ b/windows/types_windows.go @@ -1649,3 +1649,17 @@ const ( KF_FLAG_SIMPLE_IDLIST = 0x00000100 KF_FLAG_ALIAS_ONLY = 0x80000000 ) + +type OsVersionInfoEx struct { + osVersionInfoSize uint32 + MajorVersion uint32 + MinorVersion uint32 + BuildNumber uint32 + PlatformId uint32 + CsdVersion [128]uint16 + ServicePackMajor uint16 + ServicePackMinor uint16 + SuiteMask uint16 + ProductType byte + _ byte +} diff --git a/windows/zsyscall_windows.go b/windows/zsyscall_windows.go index e66ab049..9e43e966 100644 --- a/windows/zsyscall_windows.go +++ b/windows/zsyscall_windows.go @@ -43,6 +43,7 @@ var ( modcrypt32 = NewLazySystemDLL("crypt32.dll") moduser32 = NewLazySystemDLL("user32.dll") modole32 = NewLazySystemDLL("ole32.dll") + modntdll = NewLazySystemDLL("ntdll.dll") modws2_32 = NewLazySystemDLL("ws2_32.dll") moddnsapi = NewLazySystemDLL("dnsapi.dll") modiphlpapi = NewLazySystemDLL("iphlpapi.dll") @@ -232,6 +233,7 @@ var ( procStringFromGUID2 = modole32.NewProc("StringFromGUID2") procCoCreateGuid = modole32.NewProc("CoCreateGuid") procCoTaskMemFree = modole32.NewProc("CoTaskMemFree") + procRtlGetVersion = modntdll.NewProc("RtlGetVersion") procWSAStartup = modws2_32.NewProc("WSAStartup") procWSACleanup = modws2_32.NewProc("WSACleanup") procWSAIoctl = modws2_32.NewProc("WSAIoctl") @@ -2520,6 +2522,14 @@ func coTaskMemFree(address unsafe.Pointer) { return } +func rtlGetVersion(info *OsVersionInfoEx) (ret error) { + r0, _, _ := syscall.Syscall(procRtlGetVersion.Addr(), 1, uintptr(unsafe.Pointer(info)), 0, 0) + if r0 != 0 { + ret = syscall.Errno(r0) + } + 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 {