Kaynağa Gözat

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 <Jason@zx2c4.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
Jason A. Donenfeld 6 yıl önce
ebeveyn
işleme
fb81701db8

+ 11 - 2
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
+}

+ 5 - 2
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)
 	}
 }

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