...

Source file src/golang.org/x/sys/windows/syscall_windows_test.go

Documentation: golang.org/x/sys/windows

     1  // Copyright 2012 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package windows_test
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"debug/pe"
    11  	"errors"
    12  	"fmt"
    13  	"os"
    14  	"os/exec"
    15  	"path/filepath"
    16  	"runtime"
    17  	"strconv"
    18  	"strings"
    19  	"syscall"
    20  	"testing"
    21  	"time"
    22  	"unicode/utf8"
    23  	"unsafe"
    24  
    25  	"golang.org/x/sys/windows"
    26  )
    27  
    28  func TestWin32finddata(t *testing.T) {
    29  	path := filepath.Join(t.TempDir(), "long_name.and_extension")
    30  	f, err := os.Create(path)
    31  	if err != nil {
    32  		t.Fatalf("failed to create %v: %v", path, err)
    33  	}
    34  	f.Close()
    35  
    36  	type X struct {
    37  		fd  windows.Win32finddata
    38  		got byte
    39  		pad [10]byte // to protect ourselves
    40  
    41  	}
    42  	var want byte = 2 // it is unlikely to have this character in the filename
    43  	x := X{got: want}
    44  
    45  	pathp, _ := windows.UTF16PtrFromString(path)
    46  	h, err := windows.FindFirstFile(pathp, &(x.fd))
    47  	if err != nil {
    48  		t.Fatalf("FindFirstFile failed: %v", err)
    49  	}
    50  	err = windows.FindClose(h)
    51  	if err != nil {
    52  		t.Fatalf("FindClose failed: %v", err)
    53  	}
    54  
    55  	if x.got != want {
    56  		t.Fatalf("memory corruption: want=%d got=%d", want, x.got)
    57  	}
    58  }
    59  
    60  func TestFormatMessage(t *testing.T) {
    61  	dll := windows.MustLoadDLL("netevent.dll")
    62  
    63  	const TITLE_SC_MESSAGE_BOX uint32 = 0xC0001B75
    64  	const flags uint32 = syscall.FORMAT_MESSAGE_FROM_HMODULE | syscall.FORMAT_MESSAGE_ARGUMENT_ARRAY | syscall.FORMAT_MESSAGE_IGNORE_INSERTS
    65  	buf := make([]uint16, 300)
    66  	_, err := windows.FormatMessage(flags, uintptr(dll.Handle), TITLE_SC_MESSAGE_BOX, 0, buf, nil)
    67  	if err != nil {
    68  		t.Fatalf("FormatMessage for handle=%x and errno=%x failed: %v", dll.Handle, TITLE_SC_MESSAGE_BOX, err)
    69  	}
    70  }
    71  
    72  func abort(funcname string, err error) {
    73  	panic(funcname + " failed: " + err.Error())
    74  }
    75  
    76  func ExampleLoadLibrary() {
    77  	h, err := windows.LoadLibrary("kernel32.dll")
    78  	if err != nil {
    79  		abort("LoadLibrary", err)
    80  	}
    81  	defer windows.FreeLibrary(h)
    82  	proc, err := windows.GetProcAddress(h, "GetVersion")
    83  	if err != nil {
    84  		abort("GetProcAddress", err)
    85  	}
    86  	r, _, _ := syscall.Syscall(uintptr(proc), 0, 0, 0, 0)
    87  	major := byte(r)
    88  	minor := uint8(r >> 8)
    89  	build := uint16(r >> 16)
    90  	print("windows version ", major, ".", minor, " (Build ", build, ")\n")
    91  }
    92  
    93  func TestTOKEN_ALL_ACCESS(t *testing.T) {
    94  	if windows.TOKEN_ALL_ACCESS != 0xF01FF {
    95  		t.Errorf("TOKEN_ALL_ACCESS = %x, want 0xF01FF", windows.TOKEN_ALL_ACCESS)
    96  	}
    97  }
    98  
    99  func TestCreateWellKnownSid(t *testing.T) {
   100  	sid, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid)
   101  	if err != nil {
   102  		t.Fatalf("Unable to create well known sid for administrators: %v", err)
   103  	}
   104  	if got, want := sid.String(), "S-1-5-32-544"; got != want {
   105  		t.Fatalf("Builtin Administrators SID = %s, want %s", got, want)
   106  	}
   107  }
   108  
   109  func TestPseudoTokens(t *testing.T) {
   110  	version, err := windows.GetVersion()
   111  	if err != nil {
   112  		t.Fatal(err)
   113  	}
   114  	if ((version&0xffff)>>8)|((version&0xff)<<8) < 0x0602 {
   115  		return
   116  	}
   117  
   118  	realProcessToken, err := windows.OpenCurrentProcessToken()
   119  	if err != nil {
   120  		t.Fatal(err)
   121  	}
   122  	defer realProcessToken.Close()
   123  	realProcessUser, err := realProcessToken.GetTokenUser()
   124  	if err != nil {
   125  		t.Fatal(err)
   126  	}
   127  
   128  	pseudoProcessToken := windows.GetCurrentProcessToken()
   129  	pseudoProcessUser, err := pseudoProcessToken.GetTokenUser()
   130  	if err != nil {
   131  		t.Fatal(err)
   132  	}
   133  	if !windows.EqualSid(realProcessUser.User.Sid, pseudoProcessUser.User.Sid) {
   134  		t.Fatal("The real process token does not have the same as the pseudo process token")
   135  	}
   136  
   137  	runtime.LockOSThread()
   138  	defer runtime.UnlockOSThread()
   139  
   140  	err = windows.RevertToSelf()
   141  	if err != nil {
   142  		t.Fatal(err)
   143  	}
   144  
   145  	pseudoThreadToken := windows.GetCurrentThreadToken()
   146  	_, err = pseudoThreadToken.GetTokenUser()
   147  	if err != windows.ERROR_NO_TOKEN {
   148  		t.Fatal("Expected an empty thread token")
   149  	}
   150  	pseudoThreadEffectiveToken := windows.GetCurrentThreadEffectiveToken()
   151  	pseudoThreadEffectiveUser, err := pseudoThreadEffectiveToken.GetTokenUser()
   152  	if err != nil {
   153  		t.Fatal(nil)
   154  	}
   155  	if !windows.EqualSid(realProcessUser.User.Sid, pseudoThreadEffectiveUser.User.Sid) {
   156  		t.Fatal("The real process token does not have the same as the pseudo thread effective token, even though we aren't impersonating")
   157  	}
   158  
   159  	err = windows.ImpersonateSelf(windows.SecurityImpersonation)
   160  	if err != nil {
   161  		t.Fatal(err)
   162  	}
   163  	defer windows.RevertToSelf()
   164  	pseudoThreadUser, err := pseudoThreadToken.GetTokenUser()
   165  	if err != nil {
   166  		t.Fatal(err)
   167  	}
   168  	if !windows.EqualSid(realProcessUser.User.Sid, pseudoThreadUser.User.Sid) {
   169  		t.Fatal("The real process token does not have the same as the pseudo thread token after impersonating self")
   170  	}
   171  }
   172  
   173  func TestGUID(t *testing.T) {
   174  	guid, err := windows.GenerateGUID()
   175  	if err != nil {
   176  		t.Fatal(err)
   177  	}
   178  	if guid.Data1 == 0 && guid.Data2 == 0 && guid.Data3 == 0 && guid.Data4 == [8]byte{} {
   179  		t.Fatal("Got an all zero GUID, which is overwhelmingly unlikely")
   180  	}
   181  	want := fmt.Sprintf("{%08X-%04X-%04X-%04X-%012X}", guid.Data1, guid.Data2, guid.Data3, guid.Data4[:2], guid.Data4[2:])
   182  	got := guid.String()
   183  	if got != want {
   184  		t.Fatalf("String = %q; want %q", got, want)
   185  	}
   186  	guid2, err := windows.GUIDFromString(got)
   187  	if err != nil {
   188  		t.Fatal(err)
   189  	}
   190  	if guid2 != guid {
   191  		t.Fatalf("Did not parse string back to original GUID = %q; want %q", guid2, guid)
   192  	}
   193  	_, err = windows.GUIDFromString("not-a-real-guid")
   194  	if err != syscall.Errno(windows.CO_E_CLASSSTRING) {
   195  		t.Fatalf("Bad GUID string error = %v; want CO_E_CLASSSTRING", err)
   196  	}
   197  }
   198  
   199  func TestKnownFolderPath(t *testing.T) {
   200  	token, err := windows.OpenCurrentProcessToken()
   201  	if err != nil {
   202  		t.Fatal(err)
   203  	}
   204  	defer token.Close()
   205  	profileDir, err := token.GetUserProfileDirectory()
   206  	if err != nil {
   207  		t.Fatal(err)
   208  	}
   209  	want := filepath.Join(profileDir, "Desktop")
   210  	got, err := windows.KnownFolderPath(windows.FOLDERID_Desktop, windows.KF_FLAG_DEFAULT)
   211  	if err != nil {
   212  		t.Fatal(err)
   213  	}
   214  	if want != got {
   215  		t.Fatalf("Path = %q; want %q", got, want)
   216  	}
   217  }
   218  
   219  func TestRtlGetVersion(t *testing.T) {
   220  	version := windows.RtlGetVersion()
   221  	major, minor, build := windows.RtlGetNtVersionNumbers()
   222  	// Go is not explicitly added to the application compatibility database, so
   223  	// these two functions should return the same thing.
   224  	if version.MajorVersion != major || version.MinorVersion != minor || version.BuildNumber != build {
   225  		t.Fatalf("%d.%d.%d != %d.%d.%d", version.MajorVersion, version.MinorVersion, version.BuildNumber, major, minor, build)
   226  	}
   227  }
   228  
   229  func TestGetNamedSecurityInfo(t *testing.T) {
   230  	path, err := windows.GetSystemDirectory()
   231  	if err != nil {
   232  		t.Fatal(err)
   233  	}
   234  	sd, err := windows.GetNamedSecurityInfo(path, windows.SE_FILE_OBJECT, windows.OWNER_SECURITY_INFORMATION)
   235  	if err != nil {
   236  		t.Fatal(err)
   237  	}
   238  	if !sd.IsValid() {
   239  		t.Fatal("Invalid security descriptor")
   240  	}
   241  	sdOwner, _, err := sd.Owner()
   242  	if err != nil {
   243  		t.Fatal(err)
   244  	}
   245  	if !sdOwner.IsValid() {
   246  		t.Fatal("Invalid security descriptor owner")
   247  	}
   248  }
   249  
   250  func TestGetSecurityInfo(t *testing.T) {
   251  	sd, err := windows.GetSecurityInfo(windows.CurrentProcess(), windows.SE_KERNEL_OBJECT, windows.DACL_SECURITY_INFORMATION)
   252  	if err != nil {
   253  		t.Fatal(err)
   254  	}
   255  	if !sd.IsValid() {
   256  		t.Fatal("Invalid security descriptor")
   257  	}
   258  	sdStr := sd.String()
   259  	if !strings.HasPrefix(sdStr, "D:(A;") {
   260  		t.Fatalf("DACL = %q; want D:(A;...", sdStr)
   261  	}
   262  }
   263  
   264  func TestSddlConversion(t *testing.T) {
   265  	sd, err := windows.SecurityDescriptorFromString("O:BA")
   266  	if err != nil {
   267  		t.Fatal(err)
   268  	}
   269  	if !sd.IsValid() {
   270  		t.Fatal("Invalid security descriptor")
   271  	}
   272  	sdOwner, _, err := sd.Owner()
   273  	if err != nil {
   274  		t.Fatal(err)
   275  	}
   276  	if !sdOwner.IsValid() {
   277  		t.Fatal("Invalid security descriptor owner")
   278  	}
   279  	if !sdOwner.IsWellKnown(windows.WinBuiltinAdministratorsSid) {
   280  		t.Fatalf("Owner = %q; want S-1-5-32-544", sdOwner)
   281  	}
   282  }
   283  
   284  func TestBuildSecurityDescriptor(t *testing.T) {
   285  	const want = "O:SYD:(A;;GA;;;BA)"
   286  
   287  	adminSid, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid)
   288  	if err != nil {
   289  		t.Fatal(err)
   290  	}
   291  	systemSid, err := windows.CreateWellKnownSid(windows.WinLocalSystemSid)
   292  	if err != nil {
   293  		t.Fatal(err)
   294  	}
   295  
   296  	access := []windows.EXPLICIT_ACCESS{{
   297  		AccessPermissions: windows.GENERIC_ALL,
   298  		AccessMode:        windows.GRANT_ACCESS,
   299  		Trustee: windows.TRUSTEE{
   300  			TrusteeForm:  windows.TRUSTEE_IS_SID,
   301  			TrusteeType:  windows.TRUSTEE_IS_GROUP,
   302  			TrusteeValue: windows.TrusteeValueFromSID(adminSid),
   303  		},
   304  	}}
   305  	owner := &windows.TRUSTEE{
   306  		TrusteeForm:  windows.TRUSTEE_IS_SID,
   307  		TrusteeType:  windows.TRUSTEE_IS_USER,
   308  		TrusteeValue: windows.TrusteeValueFromSID(systemSid),
   309  	}
   310  
   311  	sd, err := windows.BuildSecurityDescriptor(owner, nil, access, nil, nil)
   312  	if err != nil {
   313  		t.Fatal(err)
   314  	}
   315  	sd, err = sd.ToAbsolute()
   316  	if err != nil {
   317  		t.Fatal(err)
   318  	}
   319  	err = sd.SetSACL(nil, false, false)
   320  	if err != nil {
   321  		t.Fatal(err)
   322  	}
   323  	if got := sd.String(); got != want {
   324  		t.Fatalf("SD = %q; want %q", got, want)
   325  	}
   326  	sd, err = sd.ToSelfRelative()
   327  	if err != nil {
   328  		t.Fatal(err)
   329  	}
   330  	if got := sd.String(); got != want {
   331  		t.Fatalf("SD = %q; want %q", got, want)
   332  	}
   333  
   334  	sd, err = windows.NewSecurityDescriptor()
   335  	if err != nil {
   336  		t.Fatal(err)
   337  	}
   338  	acl, err := windows.ACLFromEntries(access, nil)
   339  	if err != nil {
   340  		t.Fatal(err)
   341  	}
   342  	err = sd.SetDACL(acl, true, false)
   343  	if err != nil {
   344  		t.Fatal(err)
   345  	}
   346  	err = sd.SetOwner(systemSid, false)
   347  	if err != nil {
   348  		t.Fatal(err)
   349  	}
   350  	if got := sd.String(); got != want {
   351  		t.Fatalf("SD = %q; want %q", got, want)
   352  	}
   353  	sd, err = sd.ToSelfRelative()
   354  	if err != nil {
   355  		t.Fatal(err)
   356  	}
   357  	if got := sd.String(); got != want {
   358  		t.Fatalf("SD = %q; want %q", got, want)
   359  	}
   360  }
   361  
   362  // getEntriesFromACL returns a list of explicit access control entries associated with the given ACL.
   363  func getEntriesFromACL(acl *windows.ACL) (aces []*windows.ACCESS_ALLOWED_ACE, err error) {
   364  	aces = make([]*windows.ACCESS_ALLOWED_ACE, acl.AceCount)
   365  
   366  	for i := uint16(0); i < acl.AceCount; i++ {
   367  		err = windows.GetAce(acl, uint32(i), &aces[i])
   368  		if err != nil {
   369  			return nil, err
   370  		}
   371  	}
   372  
   373  	return aces, nil
   374  }
   375  
   376  func TestGetACEsFromACL(t *testing.T) {
   377  	// Create a temporary file to set ACLs on and test getting the ACEs from the ACL.
   378  	f, err := os.CreateTemp("", "foo.lish")
   379  	defer os.Remove(f.Name())
   380  
   381  	if err = f.Close(); err != nil {
   382  		t.Fatal(err)
   383  	}
   384  
   385  	// Well-known SID Strings:
   386  	// https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems
   387  	ownerSid, err := windows.StringToSid("S-1-3-2")
   388  	if err != nil {
   389  		t.Fatal(err)
   390  	}
   391  	groupSid, err := windows.StringToSid("S-1-3-3")
   392  	if err != nil {
   393  		t.Fatal(err)
   394  	}
   395  	worldSid, err := windows.StringToSid("S-1-1-0")
   396  	if err != nil {
   397  		t.Fatal(err)
   398  	}
   399  
   400  	ownerPermissions := windows.ACCESS_MASK(windows.GENERIC_ALL)
   401  	groupPermissions := windows.ACCESS_MASK(windows.GENERIC_READ | windows.GENERIC_EXECUTE)
   402  	worldPermissions := windows.ACCESS_MASK(windows.GENERIC_READ)
   403  
   404  	access := []windows.EXPLICIT_ACCESS{
   405  		{
   406  			AccessPermissions: ownerPermissions,
   407  			AccessMode:        windows.GRANT_ACCESS,
   408  			Trustee: windows.TRUSTEE{
   409  				TrusteeForm:  windows.TRUSTEE_IS_SID,
   410  				TrusteeValue: windows.TrusteeValueFromSID(ownerSid),
   411  			},
   412  		},
   413  		{
   414  			AccessPermissions: groupPermissions,
   415  			AccessMode:        windows.GRANT_ACCESS,
   416  			Trustee: windows.TRUSTEE{
   417  				TrusteeForm:  windows.TRUSTEE_IS_SID,
   418  				TrusteeType:  windows.TRUSTEE_IS_GROUP,
   419  				TrusteeValue: windows.TrusteeValueFromSID(groupSid),
   420  			},
   421  		},
   422  		{
   423  			AccessPermissions: worldPermissions,
   424  			AccessMode:        windows.GRANT_ACCESS,
   425  			Trustee: windows.TRUSTEE{
   426  				TrusteeForm:  windows.TRUSTEE_IS_SID,
   427  				TrusteeType:  windows.TRUSTEE_IS_GROUP,
   428  				TrusteeValue: windows.TrusteeValueFromSID(worldSid),
   429  			},
   430  		},
   431  	}
   432  
   433  	acl, err := windows.ACLFromEntries(access, nil)
   434  	if err != nil {
   435  		t.Fatal(err)
   436  	}
   437  
   438  	// Set new ACL.
   439  	err = windows.SetNamedSecurityInfo(
   440  		f.Name(),
   441  		windows.SE_FILE_OBJECT,
   442  		windows.DACL_SECURITY_INFORMATION|windows.PROTECTED_DACL_SECURITY_INFORMATION,
   443  		nil,
   444  		nil,
   445  		acl,
   446  		nil,
   447  	)
   448  	if err != nil {
   449  		t.Fatal(err)
   450  	}
   451  
   452  	descriptor, err := windows.GetNamedSecurityInfo(
   453  		f.Name(),
   454  		windows.SE_FILE_OBJECT,
   455  		windows.DACL_SECURITY_INFORMATION|windows.PROTECTED_DACL_SECURITY_INFORMATION|windows.OWNER_SECURITY_INFORMATION|windows.GROUP_SECURITY_INFORMATION,
   456  	)
   457  	if err != nil {
   458  		t.Fatal(err)
   459  	}
   460  
   461  	dacl, _, err := descriptor.DACL()
   462  	if err != nil {
   463  		t.Fatal(err)
   464  	}
   465  
   466  	owner, _, err := descriptor.Owner()
   467  	if err != nil {
   468  		t.Fatal(err)
   469  	}
   470  
   471  	group, _, err := descriptor.Group()
   472  	if err != nil {
   473  		t.Fatal(err)
   474  	}
   475  
   476  	entries, err := getEntriesFromACL(dacl)
   477  	if err != nil {
   478  		t.Fatal(err)
   479  	}
   480  
   481  	if len(entries) != 3 {
   482  		t.Fatalf("Expected newly set ACL to only have 3 entries.")
   483  	}
   484  
   485  	// https://docs.microsoft.com/en-us/windows/win32/fileio/file-access-rights-constants
   486  	read := uint32(windows.FILE_READ_DATA | windows.FILE_READ_ATTRIBUTES)
   487  	write := uint32(windows.FILE_WRITE_DATA | windows.FILE_APPEND_DATA | windows.FILE_WRITE_ATTRIBUTES | windows.FILE_WRITE_EA)
   488  	execute := uint32(windows.FILE_READ_DATA | windows.FILE_EXECUTE)
   489  
   490  	// Check the set ACEs. We should have the equivalent of 754.
   491  	for _, entry := range entries {
   492  		mask := uint32(entry.Mask)
   493  		actual := 0
   494  
   495  		if mask&read == read {
   496  			actual |= 4
   497  		}
   498  		if mask&write == write {
   499  			actual |= 2
   500  		}
   501  		if mask&execute == execute {
   502  			actual |= 1
   503  		}
   504  
   505  		entrySid := (*windows.SID)(unsafe.Pointer(&entry.SidStart))
   506  		if owner.Equals(entrySid) {
   507  			if actual != 7 {
   508  				t.Fatalf("Expected owner to have FullAccess permissions.")
   509  			}
   510  		} else if group.Equals(entrySid) {
   511  			if actual != 5 {
   512  				t.Fatalf("Expected group to have only Read and Execute permissions.")
   513  			}
   514  		} else if worldSid.Equals(entrySid) {
   515  			if actual != 4 {
   516  				t.Fatalf("Expected the World to have only Read permissions.")
   517  			}
   518  		} else {
   519  			t.Fatalf("Unexpected SID in ACEs: %s", entrySid.String())
   520  		}
   521  	}
   522  }
   523  
   524  func TestGetDiskFreeSpaceEx(t *testing.T) {
   525  	cwd, err := windows.UTF16PtrFromString(".")
   526  	if err != nil {
   527  		t.Fatalf(`failed to call UTF16PtrFromString("."): %v`, err)
   528  	}
   529  	var freeBytesAvailableToCaller, totalNumberOfBytes, totalNumberOfFreeBytes uint64
   530  	if err := windows.GetDiskFreeSpaceEx(cwd, &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes); err != nil {
   531  		t.Fatalf("failed to call GetDiskFreeSpaceEx: %v", err)
   532  	}
   533  
   534  	if freeBytesAvailableToCaller == 0 {
   535  		t.Errorf("freeBytesAvailableToCaller: got 0; want > 0")
   536  	}
   537  	if totalNumberOfBytes == 0 {
   538  		t.Errorf("totalNumberOfBytes: got 0; want > 0")
   539  	}
   540  	if totalNumberOfFreeBytes == 0 {
   541  		t.Errorf("totalNumberOfFreeBytes: got 0; want > 0")
   542  	}
   543  }
   544  
   545  func TestGetPreferredUILanguages(t *testing.T) {
   546  	tab := map[string]func(flags uint32) ([]string, error){
   547  		"GetProcessPreferredUILanguages": windows.GetProcessPreferredUILanguages,
   548  		"GetThreadPreferredUILanguages":  windows.GetThreadPreferredUILanguages,
   549  		"GetUserPreferredUILanguages":    windows.GetUserPreferredUILanguages,
   550  		"GetSystemPreferredUILanguages":  windows.GetSystemPreferredUILanguages,
   551  	}
   552  	for fName, f := range tab {
   553  		lang, err := f(windows.MUI_LANGUAGE_ID)
   554  		if err != nil {
   555  			t.Errorf(`failed to call %v(MUI_LANGUAGE_ID): %v`, fName, err)
   556  		}
   557  		for _, l := range lang {
   558  			_, err := strconv.ParseUint(l, 16, 16)
   559  			if err != nil {
   560  				t.Errorf(`%v(MUI_LANGUAGE_ID) returned unexpected LANGID: %v`, fName, l)
   561  			}
   562  		}
   563  
   564  		lang, err = f(windows.MUI_LANGUAGE_NAME)
   565  		if err != nil {
   566  			t.Errorf(`failed to call %v(MUI_LANGUAGE_NAME): %v`, fName, err)
   567  		}
   568  	}
   569  }
   570  
   571  func TestProcessWorkingSetSizeEx(t *testing.T) {
   572  	// Grab a handle to the current process
   573  	hProcess := windows.CurrentProcess()
   574  
   575  	// Allocate memory to store the result of the query
   576  	var minimumWorkingSetSize, maximumWorkingSetSize uintptr
   577  
   578  	// Make the system-call
   579  	var flag uint32
   580  	windows.GetProcessWorkingSetSizeEx(hProcess, &minimumWorkingSetSize, &maximumWorkingSetSize, &flag)
   581  
   582  	// Set the new limits to the current ones
   583  	if err := windows.SetProcessWorkingSetSizeEx(hProcess, minimumWorkingSetSize, maximumWorkingSetSize, flag); err != nil {
   584  		t.Error(err)
   585  	}
   586  }
   587  
   588  func TestJobObjectInfo(t *testing.T) {
   589  	jo, err := windows.CreateJobObject(nil, nil)
   590  	if err != nil {
   591  		t.Fatalf("CreateJobObject failed: %v", err)
   592  	}
   593  	defer windows.CloseHandle(jo)
   594  
   595  	var info windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION
   596  
   597  	err = windows.QueryInformationJobObject(jo, windows.JobObjectExtendedLimitInformation,
   598  		uintptr(unsafe.Pointer(&info)), uint32(unsafe.Sizeof(info)), nil)
   599  	if err != nil {
   600  		t.Fatalf("QueryInformationJobObject failed: %v", err)
   601  	}
   602  
   603  	const wantMemLimit = 4 * 1024
   604  
   605  	info.BasicLimitInformation.LimitFlags |= windows.JOB_OBJECT_LIMIT_PROCESS_MEMORY
   606  	info.ProcessMemoryLimit = wantMemLimit
   607  	_, err = windows.SetInformationJobObject(jo, windows.JobObjectExtendedLimitInformation,
   608  		uintptr(unsafe.Pointer(&info)), uint32(unsafe.Sizeof(info)))
   609  	if err != nil {
   610  		t.Fatalf("SetInformationJobObject failed: %v", err)
   611  	}
   612  
   613  	err = windows.QueryInformationJobObject(jo, windows.JobObjectExtendedLimitInformation,
   614  		uintptr(unsafe.Pointer(&info)), uint32(unsafe.Sizeof(info)), nil)
   615  	if err != nil {
   616  		t.Fatalf("QueryInformationJobObject failed: %v", err)
   617  	}
   618  
   619  	if have := info.ProcessMemoryLimit; wantMemLimit != have {
   620  		t.Errorf("ProcessMemoryLimit is wrong: want %v have %v", wantMemLimit, have)
   621  	}
   622  }
   623  
   624  func TestIsWow64Process2(t *testing.T) {
   625  	var processMachine, nativeMachine uint16
   626  	err := windows.IsWow64Process2(windows.CurrentProcess(), &processMachine, &nativeMachine)
   627  	if errors.Is(err, windows.ERROR_PROC_NOT_FOUND) {
   628  		maj, min, build := windows.RtlGetNtVersionNumbers()
   629  		if maj < 10 || (maj == 10 && min == 0 && build < 17763) {
   630  			t.Skip("not available on older versions of Windows")
   631  			return
   632  		}
   633  	}
   634  	if err != nil {
   635  		t.Fatalf("IsWow64Process2 failed: %v", err)
   636  	}
   637  	if processMachine == pe.IMAGE_FILE_MACHINE_UNKNOWN {
   638  		processMachine = nativeMachine
   639  	}
   640  	switch {
   641  	case processMachine == pe.IMAGE_FILE_MACHINE_AMD64 && runtime.GOARCH == "amd64":
   642  	case processMachine == pe.IMAGE_FILE_MACHINE_I386 && runtime.GOARCH == "386":
   643  	case processMachine == pe.IMAGE_FILE_MACHINE_ARMNT && runtime.GOARCH == "arm":
   644  	case processMachine == pe.IMAGE_FILE_MACHINE_ARM64 && runtime.GOARCH == "arm64":
   645  	default:
   646  		t.Errorf("IsWow64Process2 is wrong: want %v have %v", runtime.GOARCH, processMachine)
   647  	}
   648  }
   649  
   650  func TestNTStatusString(t *testing.T) {
   651  	want := "The name limit for the local computer network adapter card was exceeded."
   652  	got := windows.STATUS_TOO_MANY_NAMES.Error()
   653  	if want != got {
   654  		t.Errorf("NTStatus.Error did not return an expected error string - want %q; got %q", want, got)
   655  	}
   656  }
   657  
   658  func TestNTStatusConversion(t *testing.T) {
   659  	want := windows.ERROR_TOO_MANY_NAMES
   660  	got := windows.STATUS_TOO_MANY_NAMES.Errno()
   661  	if want != got {
   662  		t.Errorf("NTStatus.Errno = %q (0x%x); want %q (0x%x)", got.Error(), got, want.Error(), want)
   663  	}
   664  }
   665  
   666  func TestPEBFilePath(t *testing.T) {
   667  	peb := windows.RtlGetCurrentPeb()
   668  	if peb == nil || peb.Ldr == nil {
   669  		t.Error("unable to retrieve PEB with valid Ldr")
   670  	}
   671  	var entry *windows.LDR_DATA_TABLE_ENTRY
   672  	for cur := peb.Ldr.InMemoryOrderModuleList.Flink; cur != &peb.Ldr.InMemoryOrderModuleList; cur = cur.Flink {
   673  		e := (*windows.LDR_DATA_TABLE_ENTRY)(unsafe.Pointer(uintptr(unsafe.Pointer(cur)) - unsafe.Offsetof(windows.LDR_DATA_TABLE_ENTRY{}.InMemoryOrderLinks)))
   674  		if e.DllBase == peb.ImageBaseAddress {
   675  			entry = e
   676  			break
   677  		}
   678  	}
   679  	if entry == nil {
   680  		t.Error("unable to find Ldr entry for current process")
   681  	}
   682  	osPath, err := os.Executable()
   683  	if err != nil {
   684  		t.Errorf("unable to get path to current executable: %v", err)
   685  	}
   686  	pebPath := entry.FullDllName.String()
   687  	if osPath != pebPath {
   688  		t.Errorf("peb.Ldr.{entry}.FullDllName = %#q; want %#q", pebPath, osPath)
   689  	}
   690  	paramPath := peb.ProcessParameters.ImagePathName.String()
   691  	if osPath != paramPath {
   692  		t.Errorf("peb.ProcessParameters.ImagePathName.{entry}.ImagePathName = %#q; want %#q", paramPath, osPath)
   693  	}
   694  	osCwd, err := os.Getwd()
   695  	if err != nil {
   696  		t.Errorf("unable to get working directory: %v", err)
   697  	}
   698  	osCwd = filepath.Clean(osCwd)
   699  	paramCwd := filepath.Clean(peb.ProcessParameters.CurrentDirectory.DosPath.String())
   700  	if paramCwd != osCwd {
   701  		t.Errorf("peb.ProcessParameters.CurrentDirectory.DosPath = %#q; want %#q", paramCwd, osCwd)
   702  	}
   703  }
   704  
   705  func TestResourceExtraction(t *testing.T) {
   706  	system32, err := windows.GetSystemDirectory()
   707  	if err != nil {
   708  		t.Errorf("unable to find system32 directory: %v", err)
   709  	}
   710  	cmd, err := windows.LoadLibrary(filepath.Join(system32, "cmd.exe"))
   711  	if err != nil {
   712  		t.Errorf("unable to load cmd.exe: %v", err)
   713  	}
   714  	defer windows.FreeLibrary(cmd)
   715  	rsrc, err := windows.FindResource(cmd, windows.CREATEPROCESS_MANIFEST_RESOURCE_ID, windows.RT_MANIFEST)
   716  	if err != nil {
   717  		t.Errorf("unable to find cmd.exe manifest resource: %v", err)
   718  	}
   719  	manifest, err := windows.LoadResourceData(cmd, rsrc)
   720  	if err != nil {
   721  		t.Errorf("unable to load cmd.exe manifest resource data: %v", err)
   722  	}
   723  	if !bytes.Contains(manifest, []byte("</assembly>")) {
   724  		t.Errorf("did not find </assembly> in manifest")
   725  	}
   726  }
   727  
   728  func FuzzComposeCommandLine(f *testing.F) {
   729  	f.Add(`C:\foo.exe /bar /baz "-bag qux"`)
   730  	f.Add(`"C:\Program Files\Go\bin\go.exe" env`)
   731  	f.Add(`C:\"Program Files"\Go\bin\go.exe env`)
   732  	f.Add(`C:\"Program Files"\Go\bin\go.exe env`)
   733  	f.Add(`C:\"Pro"gram Files\Go\bin\go.exe env`)
   734  	f.Add(``)
   735  	f.Add(` `)
   736  	f.Add(`W\"0`)
   737  	f.Add("\"\f")
   738  	f.Add("\f")
   739  	f.Add("\x16")
   740  	f.Add(`"" ` + strings.Repeat("a", 8193))
   741  	f.Add(strings.Repeat(`"" `, 8193))
   742  
   743  	f.Add("\x00abcd")
   744  	f.Add("ab\x00cd")
   745  	f.Add("abcd\x00")
   746  	f.Add("\x00abcd\x00")
   747  	f.Add("\x00ab\x00cd\x00")
   748  	f.Add("\x00\x00\x00")
   749  	f.Add("\x16\x00\x16")
   750  	f.Add(`C:\Program Files\Go\bin\go.exe` + "\x00env")
   751  	f.Add(`"C:\Program Files\Go\bin\go.exe"` + "\x00env")
   752  	f.Add(`C:\"Program Files"\Go\bin\go.exe` + "\x00env")
   753  	f.Add(`C:\"Pro"gram Files\Go\bin\go.exe` + "\x00env")
   754  	f.Add("\x00" + strings.Repeat("a", 8192))
   755  	f.Add("\x00" + strings.Repeat("a", 8193))
   756  	f.Add(strings.Repeat("\x00"+strings.Repeat("a", 8192), 4))
   757  
   758  	f.Fuzz(func(t *testing.T, s string) {
   759  		// DecomposeCommandLine is the “control” for our experiment:
   760  		// if it returns a particular list of arguments, then we know
   761  		// it must be possible to create an input string that produces
   762  		// exactly those arguments.
   763  		//
   764  		// However, DecomposeCommandLine returns an error if the string
   765  		// contains a NUL byte. In that case, we will fall back to
   766  		// strings.Split, and be a bit more permissive about the results.
   767  		args, err := windows.DecomposeCommandLine(s)
   768  		argsFromSplit := false
   769  
   770  		if err == nil {
   771  			if testing.Verbose() {
   772  				t.Logf("DecomposeCommandLine(%#q) = %#q", s, args)
   773  			}
   774  		} else {
   775  			t.Logf("DecomposeCommandLine: %v", err)
   776  			if !strings.Contains(s, "\x00") {
   777  				// The documentation for CommandLineToArgv takes for granted that
   778  				// the first argument is a valid file path, and doesn't describe any
   779  				// specific behavior for malformed arguments. Empirically it seems to
   780  				// tolerate anything we throw at it, but if we discover cases where it
   781  				// actually returns an error we might need to relax this check.
   782  				t.Fatal("(error unexpected)")
   783  			}
   784  
   785  			// Since DecomposeCommandLine can't handle this string,
   786  			// interpret it as the raw arguments to ComposeCommandLine.
   787  			args = strings.Split(s, "\x00")
   788  			argsFromSplit = true
   789  			for i, arg := range args {
   790  				if !utf8.ValidString(arg) {
   791  					// We need to encode the arguments as UTF-16 to pass them to
   792  					// CommandLineToArgvW, so skip inputs that are not valid: they might
   793  					// have one or more runes converted to replacement characters.
   794  					t.Skipf("skipping: input %d is not valid UTF-8", i)
   795  				}
   796  			}
   797  			if testing.Verbose() {
   798  				t.Logf("using input: %#q", args)
   799  			}
   800  		}
   801  
   802  		// It's ok if we compose a different command line than what was read.
   803  		// Just check that we are able to compose something that round-trips
   804  		// to the same results as the original.
   805  		commandLine := windows.ComposeCommandLine(args)
   806  		t.Logf("ComposeCommandLine(_) = %#q", commandLine)
   807  
   808  		got, err := windows.DecomposeCommandLine(commandLine)
   809  		if err != nil {
   810  			t.Fatalf("DecomposeCommandLine: unexpected error: %v", err)
   811  		}
   812  		if testing.Verbose() {
   813  			t.Logf("DecomposeCommandLine(_) = %#q", got)
   814  		}
   815  
   816  		var badMatches []int
   817  		for i := range args {
   818  			if i >= len(got) {
   819  				badMatches = append(badMatches, i)
   820  				continue
   821  			}
   822  			want := args[i]
   823  			if got[i] != want {
   824  				if i == 0 && argsFromSplit {
   825  					// It is possible that args[0] cannot be encoded exactly, because
   826  					// CommandLineToArgvW doesn't unescape that argument in the same way
   827  					// as the others: since the first argument is assumed to be the name
   828  					// of the program itself, we only have the option of quoted or not.
   829  					//
   830  					// If args[0] contains a space or control character, we must quote it
   831  					// to avoid it being split into multiple arguments.
   832  					// If args[0] already starts with a quote character, we have no way
   833  					// to indicate that character is part of the literal argument.
   834  					// In either case, if the string already contains a quote character
   835  					// we must avoid misinterpreting that character as the end of the
   836  					// quoted argument string.
   837  					//
   838  					// Unfortunately, ComposeCommandLine does not return an error, so we
   839  					// can't report existing quote characters as errors.
   840  					// Instead, we strip out the problematic quote characters from the
   841  					// argument, and quote the remainder.
   842  					// For paths like C:\"Program Files"\Go\bin\go.exe that is arguably
   843  					// what the caller intended anyway, and for other strings it seems
   844  					// less harmful than corrupting the subsequent arguments.
   845  					if got[i] == strings.ReplaceAll(want, `"`, ``) {
   846  						continue
   847  					}
   848  				}
   849  				badMatches = append(badMatches, i)
   850  			}
   851  		}
   852  		if len(badMatches) != 0 {
   853  			t.Errorf("Incorrect decomposition at indices: %v", badMatches)
   854  		}
   855  	})
   856  }
   857  
   858  func TestWinVerifyTrust(t *testing.T) {
   859  	evsignedfile := `.\testdata\ev-signed-file.exe`
   860  	evsignedfile16, err := windows.UTF16PtrFromString(evsignedfile)
   861  	if err != nil {
   862  		t.Fatalf("unable to get utf16 of %s: %v", evsignedfile, err)
   863  	}
   864  	data := &windows.WinTrustData{
   865  		Size:             uint32(unsafe.Sizeof(windows.WinTrustData{})),
   866  		UIChoice:         windows.WTD_UI_NONE,
   867  		RevocationChecks: windows.WTD_REVOKE_NONE, // No revocation checking, in case the tests don't have network connectivity.
   868  		UnionChoice:      windows.WTD_CHOICE_FILE,
   869  		StateAction:      windows.WTD_STATEACTION_VERIFY,
   870  		FileOrCatalogOrBlobOrSgnrOrCert: unsafe.Pointer(&windows.WinTrustFileInfo{
   871  			Size:     uint32(unsafe.Sizeof(windows.WinTrustFileInfo{})),
   872  			FilePath: evsignedfile16,
   873  		}),
   874  	}
   875  	verifyErr := windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data)
   876  	data.StateAction = windows.WTD_STATEACTION_CLOSE
   877  	closeErr := windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data)
   878  	if verifyErr != nil {
   879  		t.Errorf("%s did not verify: %v", evsignedfile, verifyErr)
   880  	}
   881  	if closeErr != nil {
   882  		t.Errorf("unable to free verification resources: %v", closeErr)
   883  	}
   884  
   885  	// Now that we've verified the legitimate file verifies, let's corrupt it and see if it correctly fails.
   886  
   887  	corruptedEvsignedfile := filepath.Join(t.TempDir(), "corrupted-file")
   888  	evsignedfileBytes, err := os.ReadFile(evsignedfile)
   889  	if err != nil {
   890  		t.Fatalf("unable to read %s bytes: %v", evsignedfile, err)
   891  	}
   892  	if len(evsignedfileBytes) > 0 {
   893  		evsignedfileBytes[len(evsignedfileBytes)/2-1]++
   894  	}
   895  	err = os.WriteFile(corruptedEvsignedfile, evsignedfileBytes, 0755)
   896  	if err != nil {
   897  		t.Fatalf("unable to write corrupted ntoskrnl.exe bytes: %v", err)
   898  	}
   899  	evsignedfile16, err = windows.UTF16PtrFromString(corruptedEvsignedfile)
   900  	if err != nil {
   901  		t.Fatalf("unable to get utf16 of ntoskrnl.exe: %v", err)
   902  	}
   903  	data = &windows.WinTrustData{
   904  		Size:             uint32(unsafe.Sizeof(windows.WinTrustData{})),
   905  		UIChoice:         windows.WTD_UI_NONE,
   906  		RevocationChecks: windows.WTD_REVOKE_NONE, // No revocation checking, in case the tests don't have network connectivity.
   907  		UnionChoice:      windows.WTD_CHOICE_FILE,
   908  		StateAction:      windows.WTD_STATEACTION_VERIFY,
   909  		FileOrCatalogOrBlobOrSgnrOrCert: unsafe.Pointer(&windows.WinTrustFileInfo{
   910  			Size:     uint32(unsafe.Sizeof(windows.WinTrustFileInfo{})),
   911  			FilePath: evsignedfile16,
   912  		}),
   913  	}
   914  	verifyErr = windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data)
   915  	data.StateAction = windows.WTD_STATEACTION_CLOSE
   916  	closeErr = windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data)
   917  	if verifyErr != windows.Errno(windows.TRUST_E_BAD_DIGEST) {
   918  		t.Errorf("%s did not fail to verify as expected: %v", corruptedEvsignedfile, verifyErr)
   919  	}
   920  	if closeErr != nil {
   921  		t.Errorf("unable to free verification resources: %v", closeErr)
   922  	}
   923  
   924  }
   925  
   926  func TestEnumProcesses(t *testing.T) {
   927  	var (
   928  		pids    [2]uint32
   929  		outSize uint32
   930  	)
   931  	err := windows.EnumProcesses(pids[:], &outSize)
   932  	if err != nil {
   933  		t.Fatalf("unable to enumerate processes: %v", err)
   934  	}
   935  
   936  	// Regression check for go.dev/issue/60223
   937  	if outSize != 8 {
   938  		t.Errorf("unexpected bytes returned: %d", outSize)
   939  	}
   940  	// Most likely, this should be [0, 4].
   941  	// 0 is the system idle pseudo-process. 4 is the initial system process ID.
   942  	// This test expects that at least one of the PIDs is not 0.
   943  	if pids[0] == 0 && pids[1] == 0 {
   944  		t.Errorf("all PIDs are 0")
   945  	}
   946  }
   947  
   948  func TestProcessModules(t *testing.T) {
   949  	process, err := windows.GetCurrentProcess()
   950  	if err != nil {
   951  		t.Fatalf("unable to get current process: %v", err)
   952  	}
   953  	// NB: Assume that we're always the first module. This technically isn't documented anywhere (that I could find), but seems to always hold.
   954  	var module windows.Handle
   955  	var cbNeeded uint32
   956  	err = windows.EnumProcessModules(process, &module, uint32(unsafe.Sizeof(module)), &cbNeeded)
   957  	if err != nil {
   958  		t.Fatalf("EnumProcessModules failed: %v", err)
   959  	}
   960  
   961  	var moduleEx windows.Handle
   962  	err = windows.EnumProcessModulesEx(process, &moduleEx, uint32(unsafe.Sizeof(moduleEx)), &cbNeeded, windows.LIST_MODULES_DEFAULT)
   963  	if err != nil {
   964  		t.Fatalf("EnumProcessModulesEx failed: %v", err)
   965  	}
   966  	if module != moduleEx {
   967  		t.Fatalf("module from EnumProcessModules does not match EnumProcessModulesEx: %v != %v", module, moduleEx)
   968  	}
   969  
   970  	exePath, err := os.Executable()
   971  	if err != nil {
   972  		t.Fatalf("unable to get current executable path: %v", err)
   973  	}
   974  
   975  	modulePathUTF16 := make([]uint16, len(exePath)+1)
   976  	err = windows.GetModuleFileNameEx(process, module, &modulePathUTF16[0], uint32(len(modulePathUTF16)))
   977  	if err != nil {
   978  		t.Fatalf("GetModuleFileNameEx failed: %v", err)
   979  	}
   980  
   981  	modulePath := windows.UTF16ToString(modulePathUTF16)
   982  	if modulePath != exePath {
   983  		t.Fatalf("module does not match executable for GetModuleFileNameEx: %s != %s", modulePath, exePath)
   984  	}
   985  
   986  	err = windows.GetModuleBaseName(process, module, &modulePathUTF16[0], uint32(len(modulePathUTF16)))
   987  	if err != nil {
   988  		t.Fatalf("GetModuleBaseName failed: %v", err)
   989  	}
   990  
   991  	modulePath = windows.UTF16ToString(modulePathUTF16)
   992  	baseExePath := filepath.Base(exePath)
   993  	if modulePath != baseExePath {
   994  		t.Fatalf("module does not match executable for GetModuleBaseName: %s != %s", modulePath, baseExePath)
   995  	}
   996  
   997  	var moduleInfo windows.ModuleInfo
   998  	err = windows.GetModuleInformation(process, module, &moduleInfo, uint32(unsafe.Sizeof(moduleInfo)))
   999  	if err != nil {
  1000  		t.Fatalf("GetModuleInformation failed: %v", err)
  1001  	}
  1002  
  1003  	peFile, err := pe.Open(exePath)
  1004  	if err != nil {
  1005  		t.Fatalf("unable to open current executable: %v", err)
  1006  	}
  1007  	defer peFile.Close()
  1008  
  1009  	var peSizeOfImage uint32
  1010  	switch runtime.GOARCH {
  1011  	case "amd64", "arm64":
  1012  		peSizeOfImage = peFile.OptionalHeader.(*pe.OptionalHeader64).SizeOfImage
  1013  	case "386", "arm":
  1014  		peSizeOfImage = peFile.OptionalHeader.(*pe.OptionalHeader32).SizeOfImage
  1015  	default:
  1016  		t.Fatalf("unable to test GetModuleInformation on arch %v", runtime.GOARCH)
  1017  	}
  1018  
  1019  	if moduleInfo.SizeOfImage != peSizeOfImage {
  1020  		t.Fatalf("module size does not match executable: %v != %v", moduleInfo.SizeOfImage, peSizeOfImage)
  1021  	}
  1022  }
  1023  
  1024  func TestQueryWorkingSetEx(t *testing.T) {
  1025  	var a int
  1026  
  1027  	process := windows.CurrentProcess()
  1028  	information := windows.PSAPI_WORKING_SET_EX_INFORMATION{
  1029  		VirtualAddress: windows.Pointer(unsafe.Pointer(&a)),
  1030  	}
  1031  	infos := []windows.PSAPI_WORKING_SET_EX_INFORMATION{information}
  1032  
  1033  	cb := uint32(uintptr(len(infos)) * unsafe.Sizeof(infos[0]))
  1034  	if err := windows.QueryWorkingSetEx(process, uintptr(unsafe.Pointer(&infos[0])), cb); err != nil {
  1035  		t.Fatalf("%+v", err)
  1036  	}
  1037  
  1038  	if !infos[0].VirtualAttributes.Valid() {
  1039  		t.Errorf("memory location not valid")
  1040  	}
  1041  }
  1042  
  1043  func TestReadWriteProcessMemory(t *testing.T) {
  1044  	testBuffer := []byte{0xBA, 0xAD, 0xF0, 0x0D}
  1045  
  1046  	process, err := windows.GetCurrentProcess()
  1047  	if err != nil {
  1048  		t.Fatalf("unable to get current process: %v", err)
  1049  	}
  1050  
  1051  	buffer := make([]byte, len(testBuffer))
  1052  	err = windows.ReadProcessMemory(process, uintptr(unsafe.Pointer(&testBuffer[0])), &buffer[0], uintptr(len(buffer)), nil)
  1053  	if err != nil {
  1054  		t.Errorf("ReadProcessMemory failed: %v", err)
  1055  	}
  1056  	if !bytes.Equal(testBuffer, buffer) {
  1057  		t.Errorf("bytes read does not match buffer: 0x%X != 0x%X", testBuffer, buffer)
  1058  	}
  1059  
  1060  	buffer = []byte{0xDE, 0xAD, 0xBE, 0xEF}
  1061  	err = windows.WriteProcessMemory(process, uintptr(unsafe.Pointer(&testBuffer[0])), &buffer[0], uintptr(len(buffer)), nil)
  1062  	if err != nil {
  1063  		t.Errorf("WriteProcessMemory failed: %v", err)
  1064  	}
  1065  	if !bytes.Equal(testBuffer, buffer) {
  1066  		t.Errorf("bytes written does not match buffer: 0x%X != 0x%X", testBuffer, buffer)
  1067  	}
  1068  }
  1069  
  1070  func TestSystemModuleVersions(t *testing.T) {
  1071  	var modules []windows.RTL_PROCESS_MODULE_INFORMATION
  1072  	for bufferSize := uint32(128 * 1024); ; {
  1073  		moduleBuffer := make([]byte, bufferSize)
  1074  		err := windows.NtQuerySystemInformation(windows.SystemModuleInformation, unsafe.Pointer(&moduleBuffer[0]), bufferSize, &bufferSize)
  1075  		switch err {
  1076  		case windows.STATUS_INFO_LENGTH_MISMATCH:
  1077  			continue
  1078  		case nil:
  1079  			break
  1080  		default:
  1081  			t.Error(err)
  1082  			return
  1083  		}
  1084  		mods := (*windows.RTL_PROCESS_MODULES)(unsafe.Pointer(&moduleBuffer[0]))
  1085  		modules = unsafe.Slice(&mods.Modules[0], int(mods.NumberOfModules))
  1086  		break
  1087  	}
  1088  	for i := range modules {
  1089  		moduleName := windows.ByteSliceToString(modules[i].FullPathName[modules[i].OffsetToFileName:])
  1090  		driverPath := `\\?\GLOBALROOT` + windows.ByteSliceToString(modules[i].FullPathName[:])
  1091  		var zero windows.Handle
  1092  		infoSize, err := windows.GetFileVersionInfoSize(driverPath, &zero)
  1093  		if err != nil {
  1094  			if err != windows.ERROR_FILE_NOT_FOUND && err != windows.ERROR_RESOURCE_TYPE_NOT_FOUND {
  1095  				t.Errorf("%v: %v", moduleName, err)
  1096  			}
  1097  			continue
  1098  		}
  1099  		versionInfo := make([]byte, infoSize)
  1100  		if err = windows.GetFileVersionInfo(driverPath, 0, infoSize, unsafe.Pointer(&versionInfo[0])); err != nil {
  1101  			t.Errorf("%v: %v", moduleName, err)
  1102  			continue
  1103  		}
  1104  		var fixedInfo *windows.VS_FIXEDFILEINFO
  1105  		fixedInfoLen := uint32(unsafe.Sizeof(*fixedInfo))
  1106  		err = windows.VerQueryValue(unsafe.Pointer(&versionInfo[0]), `\`, (unsafe.Pointer)(&fixedInfo), &fixedInfoLen)
  1107  		if err != nil {
  1108  			t.Errorf("%v: %v", moduleName, err)
  1109  			continue
  1110  		}
  1111  		t.Logf("%s: v%d.%d.%d.%d", moduleName,
  1112  			(fixedInfo.FileVersionMS>>16)&0xff,
  1113  			(fixedInfo.FileVersionMS>>0)&0xff,
  1114  			(fixedInfo.FileVersionLS>>16)&0xff,
  1115  			(fixedInfo.FileVersionLS>>0)&0xff)
  1116  	}
  1117  }
  1118  
  1119  type fileRenameInformation struct {
  1120  	ReplaceIfExists uint32
  1121  	RootDirectory   windows.Handle
  1122  	FileNameLength  uint32
  1123  	FileName        [1]uint16
  1124  }
  1125  
  1126  func TestNtCreateFileAndNtSetInformationFile(t *testing.T) {
  1127  	var iosb windows.IO_STATUS_BLOCK
  1128  	var allocSize int64 = 0
  1129  	// Open test directory with NtCreateFile.
  1130  	testDirPath := t.TempDir()
  1131  	objectName, err := windows.NewNTUnicodeString("\\??\\" + testDirPath)
  1132  	if err != nil {
  1133  		t.Fatal(err)
  1134  	}
  1135  	oa := &windows.OBJECT_ATTRIBUTES{
  1136  		ObjectName: objectName,
  1137  	}
  1138  	oa.Length = uint32(unsafe.Sizeof(*oa))
  1139  	var testDirHandle windows.Handle
  1140  	err = windows.NtCreateFile(&testDirHandle, windows.FILE_GENERIC_READ|windows.FILE_GENERIC_WRITE, oa, &iosb,
  1141  		&allocSize, 0, windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE, windows.FILE_OPEN,
  1142  		windows.FILE_DIRECTORY_FILE, 0, 0)
  1143  	if err != nil {
  1144  		t.Fatalf("NtCreateFile(%v) failed: %v", testDirPath, err)
  1145  	}
  1146  	defer windows.CloseHandle(testDirHandle)
  1147  	// Create a file in test directory with NtCreateFile.
  1148  	fileName := "filename"
  1149  	filePath := filepath.Join(testDirPath, fileName)
  1150  	objectName, err = windows.NewNTUnicodeString(fileName)
  1151  	if err != nil {
  1152  		t.Fatal(err)
  1153  	}
  1154  	oa.RootDirectory = testDirHandle
  1155  	oa.ObjectName = objectName
  1156  	var fileHandle windows.Handle
  1157  	err = windows.NtCreateFile(&fileHandle, windows.FILE_GENERIC_READ|windows.FILE_GENERIC_WRITE|windows.DELETE, oa, &iosb,
  1158  		&allocSize, 0, windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE, windows.FILE_CREATE,
  1159  		0, 0, 0)
  1160  	if err != nil {
  1161  		t.Fatalf("NtCreateFile(%v) failed: %v", filePath, err)
  1162  	}
  1163  	defer windows.CloseHandle(fileHandle)
  1164  	_, err = os.Stat(filePath)
  1165  	if err != nil {
  1166  		t.Fatalf("cannot stat file created with NtCreatefile: %v", err)
  1167  	}
  1168  	// Rename file with NtSetInformationFile.
  1169  	newName := "newname"
  1170  	newPath := filepath.Join(testDirPath, newName)
  1171  	newNameUTF16, err := windows.UTF16FromString(newName)
  1172  	if err != nil {
  1173  		t.Fatal(err)
  1174  	}
  1175  	fileNameLen := len(newNameUTF16)*2 - 2
  1176  	var dummyFileRenameInfo fileRenameInformation
  1177  	bufferSize := int(unsafe.Offsetof(dummyFileRenameInfo.FileName)) + fileNameLen
  1178  	buffer := make([]byte, bufferSize)
  1179  	typedBufferPtr := (*fileRenameInformation)(unsafe.Pointer(&buffer[0]))
  1180  	typedBufferPtr.ReplaceIfExists = windows.FILE_RENAME_REPLACE_IF_EXISTS | windows.FILE_RENAME_POSIX_SEMANTICS
  1181  	typedBufferPtr.FileNameLength = uint32(fileNameLen)
  1182  	copy((*[windows.MAX_LONG_PATH]uint16)(unsafe.Pointer(&typedBufferPtr.FileName[0]))[:fileNameLen/2:fileNameLen/2], newNameUTF16)
  1183  	err = windows.NtSetInformationFile(fileHandle, &iosb, &buffer[0], uint32(bufferSize), windows.FileRenameInformation)
  1184  	if err != nil {
  1185  		t.Fatalf("NtSetInformationFile(%v) failed: %v", newPath, err)
  1186  	}
  1187  	_, err = os.Stat(newPath)
  1188  	if err != nil {
  1189  		t.Fatalf("cannot stat rename target %v: %v", newPath, err)
  1190  	}
  1191  }
  1192  
  1193  var deviceClassNetGUID = &windows.GUID{0x4d36e972, 0xe325, 0x11ce, [8]byte{0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}}
  1194  var deviceInterfaceNetGUID = &windows.GUID{0xcac88484, 0x7515, 0x4c03, [8]byte{0x82, 0xe6, 0x71, 0xa8, 0x7a, 0xba, 0xc3, 0x61}}
  1195  
  1196  func TestListLoadedNetworkDevices(t *testing.T) {
  1197  	devInfo, err := windows.SetupDiGetClassDevsEx(deviceClassNetGUID, "", 0, windows.DIGCF_PRESENT, 0, "")
  1198  	if err != nil {
  1199  		t.Fatal(err)
  1200  	}
  1201  	defer devInfo.Close()
  1202  	for i := 0; ; i++ {
  1203  		devInfoData, err := devInfo.EnumDeviceInfo(i)
  1204  		if err != nil {
  1205  			if err == windows.ERROR_NO_MORE_ITEMS {
  1206  				break
  1207  			}
  1208  			continue
  1209  		}
  1210  		friendlyName, err := devInfo.DeviceRegistryProperty(devInfoData, windows.SPDRP_DEVICEDESC)
  1211  		if err != nil {
  1212  			t.Fatal(err)
  1213  		}
  1214  		var status, problemCode uint32
  1215  		err = windows.CM_Get_DevNode_Status(&status, &problemCode, devInfoData.DevInst, 0)
  1216  		if err != nil || (status&windows.DN_DRIVER_LOADED|windows.DN_STARTED) != windows.DN_DRIVER_LOADED|windows.DN_STARTED {
  1217  			continue
  1218  		}
  1219  		instanceId, err := devInfo.DeviceInstanceID(devInfoData)
  1220  		if err != nil {
  1221  			t.Fatal(err)
  1222  		}
  1223  		interfaces, err := windows.CM_Get_Device_Interface_List(instanceId, deviceInterfaceNetGUID, windows.CM_GET_DEVICE_INTERFACE_LIST_PRESENT)
  1224  		if err != nil || len(interfaces) == 0 {
  1225  			continue
  1226  		}
  1227  		t.Logf("%s - %s", friendlyName, interfaces[0])
  1228  	}
  1229  }
  1230  
  1231  func TestListWireGuardDrivers(t *testing.T) {
  1232  	devInfo, err := windows.SetupDiCreateDeviceInfoListEx(deviceClassNetGUID, 0, "")
  1233  	if err != nil {
  1234  		t.Fatal(err)
  1235  	}
  1236  	defer devInfo.Close()
  1237  	devInfoData, err := devInfo.CreateDeviceInfo("WireGuard", deviceClassNetGUID, "", 0, windows.DICD_GENERATE_ID)
  1238  	if err != nil {
  1239  		t.Fatal(err)
  1240  	}
  1241  	err = devInfo.SetDeviceRegistryProperty(devInfoData, windows.SPDRP_HARDWAREID, []byte("W\x00i\x00r\x00e\x00G\x00u\x00a\x00r\x00d\x00\x00\x00\x00\x00"))
  1242  	if err != nil {
  1243  		t.Fatal(err)
  1244  	}
  1245  	err = devInfo.BuildDriverInfoList(devInfoData, windows.SPDIT_COMPATDRIVER)
  1246  	if err != nil {
  1247  		t.Fatal(err)
  1248  	}
  1249  	defer devInfo.DestroyDriverInfoList(devInfoData, windows.SPDIT_COMPATDRIVER)
  1250  	for i := 0; ; i++ {
  1251  		drvInfoData, err := devInfo.EnumDriverInfo(devInfoData, windows.SPDIT_COMPATDRIVER, i)
  1252  		if err != nil {
  1253  			if err == windows.ERROR_NO_MORE_ITEMS {
  1254  				break
  1255  			}
  1256  			continue
  1257  		}
  1258  		drvInfoDetailData, err := devInfo.DriverInfoDetail(devInfoData, drvInfoData)
  1259  		if err != nil {
  1260  			t.Error(err)
  1261  			continue
  1262  		}
  1263  		t.Logf("%s - %s", drvInfoData.Description(), drvInfoDetailData.InfFileName())
  1264  	}
  1265  }
  1266  
  1267  func TestProcThreadAttributeHandleList(t *testing.T) {
  1268  	const sentinel = "the gopher dance"
  1269  	system32, err := windows.GetSystemDirectory()
  1270  	if err != nil {
  1271  		t.Fatal(err)
  1272  	}
  1273  	executable16, err := windows.UTF16PtrFromString(filepath.Join(system32, "cmd.exe"))
  1274  	if err != nil {
  1275  		t.Fatal(err)
  1276  	}
  1277  	args16, err := windows.UTF16PtrFromString(windows.ComposeCommandLine([]string{"/c", "echo " + sentinel}))
  1278  	if err != nil {
  1279  		t.Fatal(err)
  1280  	}
  1281  	attributeList, err := windows.NewProcThreadAttributeList(1)
  1282  	if err != nil {
  1283  		t.Fatal(err)
  1284  	}
  1285  	defer attributeList.Delete()
  1286  	si := &windows.StartupInfoEx{
  1287  		StartupInfo:             windows.StartupInfo{Cb: uint32(unsafe.Sizeof(windows.StartupInfoEx{}))},
  1288  		ProcThreadAttributeList: attributeList.List(),
  1289  	}
  1290  	pipeR, pipeW, err := os.Pipe()
  1291  	if err != nil {
  1292  		t.Fatal(err)
  1293  	}
  1294  	defer pipeR.Close()
  1295  	defer pipeW.Close()
  1296  	func() {
  1297  		// We allocate handles in a closure to provoke a UaF in the case of attributeList.Update being buggy.
  1298  		handles := []windows.Handle{windows.Handle(pipeW.Fd())}
  1299  		attributeList.Update(windows.PROC_THREAD_ATTRIBUTE_HANDLE_LIST, unsafe.Pointer(&handles[0]), uintptr(len(handles))*unsafe.Sizeof(handles[0]))
  1300  		si.Flags |= windows.STARTF_USESTDHANDLES
  1301  		si.StdOutput = handles[0]
  1302  		// Go 1.16's pipe handles aren't inheritable, so mark it explicitly as such here.
  1303  		windows.SetHandleInformation(handles[0], windows.HANDLE_FLAG_INHERIT, windows.HANDLE_FLAG_INHERIT)
  1304  	}()
  1305  	pi := new(windows.ProcessInformation)
  1306  	err = windows.CreateProcess(executable16, args16, nil, nil, true, windows.CREATE_DEFAULT_ERROR_MODE|windows.CREATE_UNICODE_ENVIRONMENT|windows.EXTENDED_STARTUPINFO_PRESENT, nil, nil, &si.StartupInfo, pi)
  1307  	if err != nil {
  1308  		t.Fatal(err)
  1309  	}
  1310  	defer windows.CloseHandle(pi.Thread)
  1311  	defer windows.CloseHandle(pi.Process)
  1312  	pipeR.SetReadDeadline(time.Now().Add(time.Minute))
  1313  	out, _, err := bufio.NewReader(pipeR).ReadLine()
  1314  	if err != nil {
  1315  		t.Fatal(err)
  1316  	}
  1317  	if string(out) != sentinel {
  1318  		t.Fatalf("got %q; want %q", out, sentinel)
  1319  	}
  1320  }
  1321  
  1322  func TestWSALookupService(t *testing.T) {
  1323  	var flags uint32 = windows.LUP_CONTAINERS
  1324  	flags |= windows.LUP_RETURN_NAME
  1325  	flags |= windows.LUP_RETURN_ADDR
  1326  
  1327  	var querySet windows.WSAQUERYSET
  1328  	querySet.NameSpace = windows.NS_BTH
  1329  	querySet.Size = uint32(unsafe.Sizeof(windows.WSAQUERYSET{}))
  1330  
  1331  	var handle windows.Handle
  1332  	err := windows.WSALookupServiceBegin(&querySet, flags, &handle)
  1333  	if err != nil {
  1334  		if errors.Is(err, windows.WSASERVICE_NOT_FOUND) {
  1335  			t.Skip("WSA Service not found, so skip this test")
  1336  		}
  1337  		t.Fatal(err)
  1338  	}
  1339  
  1340  	defer windows.WSALookupServiceEnd(handle)
  1341  
  1342  	n := int32(unsafe.Sizeof(windows.WSAQUERYSET{}))
  1343  	buf := make([]byte, n)
  1344  items_loop:
  1345  	for {
  1346  		q := (*windows.WSAQUERYSET)(unsafe.Pointer(&buf[0]))
  1347  		err := windows.WSALookupServiceNext(handle, flags, &n, q)
  1348  		switch err {
  1349  		case windows.WSA_E_NO_MORE, windows.WSAENOMORE:
  1350  			// no more data available - break the loop
  1351  			break items_loop
  1352  		case windows.WSAEFAULT:
  1353  			// buffer is too small - reallocate and try again
  1354  			buf = make([]byte, n)
  1355  		case nil:
  1356  			// found a record - display the item and fetch next item
  1357  			var addr string
  1358  			for _, e := range q.SaBuffer.RemoteAddr.Sockaddr.Addr.Data {
  1359  				if e != 0 {
  1360  					addr += fmt.Sprintf("%x", e)
  1361  				}
  1362  			}
  1363  			t.Logf("%s -> %s\n", windows.UTF16PtrToString(q.ServiceInstanceName), addr)
  1364  
  1365  		default:
  1366  			t.Fatal(err)
  1367  		}
  1368  	}
  1369  }
  1370  
  1371  func TestTimePeriod(t *testing.T) {
  1372  	if err := windows.TimeBeginPeriod(1); err != nil {
  1373  		t.Fatal(err)
  1374  	}
  1375  	if err := windows.TimeEndPeriod(1); err != nil {
  1376  		t.Fatal(err)
  1377  	}
  1378  }
  1379  
  1380  func TestGetStartupInfo(t *testing.T) {
  1381  	var si windows.StartupInfo
  1382  	err := windows.GetStartupInfo(&si)
  1383  	if err != nil {
  1384  		// see https://go.dev/issue/31316
  1385  		t.Fatalf("GetStartupInfo: got error %v, want nil", err)
  1386  	}
  1387  }
  1388  
  1389  func TestAddRemoveDllDirectory(t *testing.T) {
  1390  	if _, err := exec.LookPath("gcc"); err != nil {
  1391  		t.Skip("skipping test: gcc is missing")
  1392  	}
  1393  	dllSrc := `#include <stdint.h>
  1394  #include <windows.h>
  1395  
  1396  uintptr_t beep(void) {
  1397     return 5;
  1398  }`
  1399  	tmpdir := t.TempDir()
  1400  	srcname := "beep.c"
  1401  	err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(dllSrc), 0)
  1402  	if err != nil {
  1403  		t.Fatal(err)
  1404  	}
  1405  	name := "beep.dll"
  1406  	cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", name, srcname)
  1407  	cmd.Dir = tmpdir
  1408  	out, err := cmd.CombinedOutput()
  1409  	if err != nil {
  1410  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
  1411  	}
  1412  
  1413  	if _, err := windows.LoadLibraryEx("beep.dll", 0, windows.LOAD_LIBRARY_SEARCH_USER_DIRS); err == nil {
  1414  		t.Fatal("LoadLibraryEx unexpectedly found beep.dll")
  1415  	}
  1416  
  1417  	dllCookie, err := windows.AddDllDirectory(windows.StringToUTF16Ptr(tmpdir))
  1418  	if err != nil {
  1419  		t.Fatalf("AddDllDirectory failed: %s", err)
  1420  	}
  1421  
  1422  	handle, err := windows.LoadLibraryEx("beep.dll", 0, windows.LOAD_LIBRARY_SEARCH_USER_DIRS)
  1423  	if err != nil {
  1424  		t.Fatalf("LoadLibraryEx failed: %s", err)
  1425  	}
  1426  
  1427  	if err := windows.FreeLibrary(handle); err != nil {
  1428  		t.Fatalf("FreeLibrary failed: %s", err)
  1429  	}
  1430  
  1431  	if err := windows.RemoveDllDirectory(dllCookie); err != nil {
  1432  		t.Fatalf("RemoveDllDirectory failed: %s", err)
  1433  	}
  1434  
  1435  	_, err = windows.LoadLibraryEx("beep.dll", 0, windows.LOAD_LIBRARY_SEARCH_USER_DIRS)
  1436  	if err == nil {
  1437  		t.Fatal("LoadLibraryEx unexpectedly found beep.dll")
  1438  	}
  1439  }
  1440  
  1441  func TestToUnicodeEx(t *testing.T) {
  1442  	var utf16Buf [16]uint16
  1443  
  1444  	// Arabic (101) Keyboard Identifier
  1445  	// See https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/windows-language-pack-default-values
  1446  	ara, err := windows.UTF16PtrFromString("00000401")
  1447  	if err != nil {
  1448  		t.Fatalf("UTF16PtrFromString failed: %v", err)
  1449  	}
  1450  	araLayout, err := windows.LoadKeyboardLayout(ara, windows.KLF_ACTIVATE)
  1451  	if err != nil {
  1452  		t.Fatalf("LoadKeyboardLayout failed: %v", err)
  1453  	}
  1454  
  1455  	var keyState [256]byte
  1456  	ret := windows.ToUnicodeEx(
  1457  		0x41, // 'A' vkCode
  1458  		0x1e, // 'A' scanCode
  1459  		&keyState[0],
  1460  		&utf16Buf[0],
  1461  		int32(len(utf16Buf)),
  1462  		0x4, // don't change keyboard state
  1463  		araLayout,
  1464  	)
  1465  
  1466  	if ret != 1 {
  1467  		t.Errorf("ToUnicodeEx failed, wanted 1, got %d", ret)
  1468  	}
  1469  	if utf16Buf[0] != 'ش' {
  1470  		t.Errorf("ToUnicodeEx failed, wanted 'ش', got %q", utf16Buf[0])
  1471  	}
  1472  	if err := windows.UnloadKeyboardLayout(araLayout); err != nil {
  1473  		t.Errorf("UnloadKeyboardLayout failed: %v", err)
  1474  	}
  1475  }
  1476  

View as plain text