Browse Source

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 <Jason@zx2c4.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matt Layher <mdlayher@gmail.com>
Reviewed-by: Jason A. Donenfeld <Jason@zx2c4.com>
Jason A. Donenfeld 6 years ago
parent
commit
516e3c2063

+ 14 - 0
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
+}

+ 7 - 0
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)
+	}
+}

+ 14 - 0
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
+}

+ 10 - 0
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 {