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
5package windows_test
6
7import (
8	"bytes"
9	"debug/pe"
10	"errors"
11	"fmt"
12	"io/ioutil"
13	"math/rand"
14	"os"
15	"path/filepath"
16	"runtime"
17	"strconv"
18	"strings"
19	"syscall"
20	"testing"
21	"unsafe"
22
23	"golang.org/x/sys/internal/unsafeheader"
24	"golang.org/x/sys/windows"
25)
26
27func TestWin32finddata(t *testing.T) {
28	dir, err := ioutil.TempDir("", "go-build")
29	if err != nil {
30		t.Fatalf("failed to create temp directory: %v", err)
31	}
32	defer os.RemoveAll(dir)
33
34	path := filepath.Join(dir, "long_name.and_extension")
35	f, err := os.Create(path)
36	if err != nil {
37		t.Fatalf("failed to create %v: %v", path, err)
38	}
39	f.Close()
40
41	type X struct {
42		fd  windows.Win32finddata
43		got byte
44		pad [10]byte // to protect ourselves
45
46	}
47	var want byte = 2 // it is unlikely to have this character in the filename
48	x := X{got: want}
49
50	pathp, _ := windows.UTF16PtrFromString(path)
51	h, err := windows.FindFirstFile(pathp, &(x.fd))
52	if err != nil {
53		t.Fatalf("FindFirstFile failed: %v", err)
54	}
55	err = windows.FindClose(h)
56	if err != nil {
57		t.Fatalf("FindClose failed: %v", err)
58	}
59
60	if x.got != want {
61		t.Fatalf("memory corruption: want=%d got=%d", want, x.got)
62	}
63}
64
65func TestFormatMessage(t *testing.T) {
66	dll := windows.MustLoadDLL("netevent.dll")
67
68	const TITLE_SC_MESSAGE_BOX uint32 = 0xC0001B75
69	const flags uint32 = syscall.FORMAT_MESSAGE_FROM_HMODULE | syscall.FORMAT_MESSAGE_ARGUMENT_ARRAY | syscall.FORMAT_MESSAGE_IGNORE_INSERTS
70	buf := make([]uint16, 300)
71	_, err := windows.FormatMessage(flags, uintptr(dll.Handle), TITLE_SC_MESSAGE_BOX, 0, buf, nil)
72	if err != nil {
73		t.Fatalf("FormatMessage for handle=%x and errno=%x failed: %v", dll.Handle, TITLE_SC_MESSAGE_BOX, err)
74	}
75}
76
77func abort(funcname string, err error) {
78	panic(funcname + " failed: " + err.Error())
79}
80
81func ExampleLoadLibrary() {
82	h, err := windows.LoadLibrary("kernel32.dll")
83	if err != nil {
84		abort("LoadLibrary", err)
85	}
86	defer windows.FreeLibrary(h)
87	proc, err := windows.GetProcAddress(h, "GetVersion")
88	if err != nil {
89		abort("GetProcAddress", err)
90	}
91	r, _, _ := syscall.Syscall(uintptr(proc), 0, 0, 0, 0)
92	major := byte(r)
93	minor := uint8(r >> 8)
94	build := uint16(r >> 16)
95	print("windows version ", major, ".", minor, " (Build ", build, ")\n")
96}
97
98func TestTOKEN_ALL_ACCESS(t *testing.T) {
99	if windows.TOKEN_ALL_ACCESS != 0xF01FF {
100		t.Errorf("TOKEN_ALL_ACCESS = %x, want 0xF01FF", windows.TOKEN_ALL_ACCESS)
101	}
102}
103
104func TestCreateWellKnownSid(t *testing.T) {
105	sid, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid)
106	if err != nil {
107		t.Fatalf("Unable to create well known sid for administrators: %v", err)
108	}
109	if got, want := sid.String(), "S-1-5-32-544"; got != want {
110		t.Fatalf("Builtin Administrators SID = %s, want %s", got, want)
111	}
112}
113
114func TestPseudoTokens(t *testing.T) {
115	version, err := windows.GetVersion()
116	if err != nil {
117		t.Fatal(err)
118	}
119	if ((version&0xffff)>>8)|((version&0xff)<<8) < 0x0602 {
120		return
121	}
122
123	realProcessToken, err := windows.OpenCurrentProcessToken()
124	if err != nil {
125		t.Fatal(err)
126	}
127	defer realProcessToken.Close()
128	realProcessUser, err := realProcessToken.GetTokenUser()
129	if err != nil {
130		t.Fatal(err)
131	}
132
133	pseudoProcessToken := windows.GetCurrentProcessToken()
134	pseudoProcessUser, err := pseudoProcessToken.GetTokenUser()
135	if err != nil {
136		t.Fatal(err)
137	}
138	if !windows.EqualSid(realProcessUser.User.Sid, pseudoProcessUser.User.Sid) {
139		t.Fatal("The real process token does not have the same as the pseudo process token")
140	}
141
142	runtime.LockOSThread()
143	defer runtime.UnlockOSThread()
144
145	err = windows.RevertToSelf()
146	if err != nil {
147		t.Fatal(err)
148	}
149
150	pseudoThreadToken := windows.GetCurrentThreadToken()
151	_, err = pseudoThreadToken.GetTokenUser()
152	if err != windows.ERROR_NO_TOKEN {
153		t.Fatal("Expected an empty thread token")
154	}
155	pseudoThreadEffectiveToken := windows.GetCurrentThreadEffectiveToken()
156	pseudoThreadEffectiveUser, err := pseudoThreadEffectiveToken.GetTokenUser()
157	if err != nil {
158		t.Fatal(nil)
159	}
160	if !windows.EqualSid(realProcessUser.User.Sid, pseudoThreadEffectiveUser.User.Sid) {
161		t.Fatal("The real process token does not have the same as the pseudo thread effective token, even though we aren't impersonating")
162	}
163
164	err = windows.ImpersonateSelf(windows.SecurityImpersonation)
165	if err != nil {
166		t.Fatal(err)
167	}
168	defer windows.RevertToSelf()
169	pseudoThreadUser, err := pseudoThreadToken.GetTokenUser()
170	if err != nil {
171		t.Fatal(err)
172	}
173	if !windows.EqualSid(realProcessUser.User.Sid, pseudoThreadUser.User.Sid) {
174		t.Fatal("The real process token does not have the same as the pseudo thread token after impersonating self")
175	}
176}
177
178func TestGUID(t *testing.T) {
179	guid, err := windows.GenerateGUID()
180	if err != nil {
181		t.Fatal(err)
182	}
183	if guid.Data1 == 0 && guid.Data2 == 0 && guid.Data3 == 0 && guid.Data4 == [8]byte{} {
184		t.Fatal("Got an all zero GUID, which is overwhelmingly unlikely")
185	}
186	want := fmt.Sprintf("{%08X-%04X-%04X-%04X-%012X}", guid.Data1, guid.Data2, guid.Data3, guid.Data4[:2], guid.Data4[2:])
187	got := guid.String()
188	if got != want {
189		t.Fatalf("String = %q; want %q", got, want)
190	}
191	guid2, err := windows.GUIDFromString(got)
192	if err != nil {
193		t.Fatal(err)
194	}
195	if guid2 != guid {
196		t.Fatalf("Did not parse string back to original GUID = %q; want %q", guid2, guid)
197	}
198	_, err = windows.GUIDFromString("not-a-real-guid")
199	if err != syscall.Errno(windows.CO_E_CLASSSTRING) {
200		t.Fatalf("Bad GUID string error = %v; want CO_E_CLASSSTRING", err)
201	}
202}
203
204func TestKnownFolderPath(t *testing.T) {
205	token, err := windows.OpenCurrentProcessToken()
206	if err != nil {
207		t.Fatal(err)
208	}
209	defer token.Close()
210	profileDir, err := token.GetUserProfileDirectory()
211	if err != nil {
212		t.Fatal(err)
213	}
214	want := filepath.Join(profileDir, "Desktop")
215	got, err := windows.KnownFolderPath(windows.FOLDERID_Desktop, windows.KF_FLAG_DEFAULT)
216	if err != nil {
217		t.Fatal(err)
218	}
219	if want != got {
220		t.Fatalf("Path = %q; want %q", got, want)
221	}
222}
223
224func TestRtlGetVersion(t *testing.T) {
225	version := windows.RtlGetVersion()
226	major, minor, build := windows.RtlGetNtVersionNumbers()
227	// Go is not explictly added to the application compatibility database, so
228	// these two functions should return the same thing.
229	if version.MajorVersion != major || version.MinorVersion != minor || version.BuildNumber != build {
230		t.Fatalf("%d.%d.%d != %d.%d.%d", version.MajorVersion, version.MinorVersion, version.BuildNumber, major, minor, build)
231	}
232}
233
234func TestGetNamedSecurityInfo(t *testing.T) {
235	path, err := windows.GetSystemDirectory()
236	if err != nil {
237		t.Fatal(err)
238	}
239	sd, err := windows.GetNamedSecurityInfo(path, windows.SE_FILE_OBJECT, windows.OWNER_SECURITY_INFORMATION)
240	if err != nil {
241		t.Fatal(err)
242	}
243	if !sd.IsValid() {
244		t.Fatal("Invalid security descriptor")
245	}
246	sdOwner, _, err := sd.Owner()
247	if err != nil {
248		t.Fatal(err)
249	}
250	if !sdOwner.IsValid() {
251		t.Fatal("Invalid security descriptor owner")
252	}
253}
254
255func TestGetSecurityInfo(t *testing.T) {
256	sd, err := windows.GetSecurityInfo(windows.CurrentProcess(), windows.SE_KERNEL_OBJECT, windows.DACL_SECURITY_INFORMATION)
257	if err != nil {
258		t.Fatal(err)
259	}
260	if !sd.IsValid() {
261		t.Fatal("Invalid security descriptor")
262	}
263	sdStr := sd.String()
264	if !strings.HasPrefix(sdStr, "D:(A;") {
265		t.Fatalf("DACL = %q; want D:(A;...", sdStr)
266	}
267}
268
269func TestSddlConversion(t *testing.T) {
270	sd, err := windows.SecurityDescriptorFromString("O:BA")
271	if err != nil {
272		t.Fatal(err)
273	}
274	if !sd.IsValid() {
275		t.Fatal("Invalid security descriptor")
276	}
277	sdOwner, _, err := sd.Owner()
278	if err != nil {
279		t.Fatal(err)
280	}
281	if !sdOwner.IsValid() {
282		t.Fatal("Invalid security descriptor owner")
283	}
284	if !sdOwner.IsWellKnown(windows.WinBuiltinAdministratorsSid) {
285		t.Fatalf("Owner = %q; want S-1-5-32-544", sdOwner)
286	}
287}
288
289func TestBuildSecurityDescriptor(t *testing.T) {
290	const want = "O:SYD:(A;;GA;;;BA)"
291
292	adminSid, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid)
293	if err != nil {
294		t.Fatal(err)
295	}
296	systemSid, err := windows.CreateWellKnownSid(windows.WinLocalSystemSid)
297	if err != nil {
298		t.Fatal(err)
299	}
300
301	access := []windows.EXPLICIT_ACCESS{{
302		AccessPermissions: windows.GENERIC_ALL,
303		AccessMode:        windows.GRANT_ACCESS,
304		Trustee: windows.TRUSTEE{
305			TrusteeForm:  windows.TRUSTEE_IS_SID,
306			TrusteeType:  windows.TRUSTEE_IS_GROUP,
307			TrusteeValue: windows.TrusteeValueFromSID(adminSid),
308		},
309	}}
310	owner := &windows.TRUSTEE{
311		TrusteeForm:  windows.TRUSTEE_IS_SID,
312		TrusteeType:  windows.TRUSTEE_IS_USER,
313		TrusteeValue: windows.TrusteeValueFromSID(systemSid),
314	}
315
316	sd, err := windows.BuildSecurityDescriptor(owner, nil, access, nil, nil)
317	if err != nil {
318		t.Fatal(err)
319	}
320	sd, err = sd.ToAbsolute()
321	if err != nil {
322		t.Fatal(err)
323	}
324	err = sd.SetSACL(nil, false, false)
325	if err != nil {
326		t.Fatal(err)
327	}
328	if got := sd.String(); got != want {
329		t.Fatalf("SD = %q; want %q", got, want)
330	}
331	sd, err = sd.ToSelfRelative()
332	if err != nil {
333		t.Fatal(err)
334	}
335	if got := sd.String(); got != want {
336		t.Fatalf("SD = %q; want %q", got, want)
337	}
338
339	sd, err = windows.NewSecurityDescriptor()
340	if err != nil {
341		t.Fatal(err)
342	}
343	acl, err := windows.ACLFromEntries(access, nil)
344	if err != nil {
345		t.Fatal(err)
346	}
347	err = sd.SetDACL(acl, true, false)
348	if err != nil {
349		t.Fatal(err)
350	}
351	err = sd.SetOwner(systemSid, false)
352	if err != nil {
353		t.Fatal(err)
354	}
355	if got := sd.String(); got != want {
356		t.Fatalf("SD = %q; want %q", got, want)
357	}
358	sd, err = sd.ToSelfRelative()
359	if err != nil {
360		t.Fatal(err)
361	}
362	if got := sd.String(); got != want {
363		t.Fatalf("SD = %q; want %q", got, want)
364	}
365}
366
367func TestGetDiskFreeSpaceEx(t *testing.T) {
368	cwd, err := windows.UTF16PtrFromString(".")
369	if err != nil {
370		t.Fatalf(`failed to call UTF16PtrFromString("."): %v`, err)
371	}
372	var freeBytesAvailableToCaller, totalNumberOfBytes, totalNumberOfFreeBytes uint64
373	if err := windows.GetDiskFreeSpaceEx(cwd, &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes); err != nil {
374		t.Fatalf("failed to call GetDiskFreeSpaceEx: %v", err)
375	}
376
377	if freeBytesAvailableToCaller == 0 {
378		t.Errorf("freeBytesAvailableToCaller: got 0; want > 0")
379	}
380	if totalNumberOfBytes == 0 {
381		t.Errorf("totalNumberOfBytes: got 0; want > 0")
382	}
383	if totalNumberOfFreeBytes == 0 {
384		t.Errorf("totalNumberOfFreeBytes: got 0; want > 0")
385	}
386}
387
388func TestGetPreferredUILanguages(t *testing.T) {
389	tab := map[string]func(flags uint32) ([]string, error){
390		"GetProcessPreferredUILanguages": windows.GetProcessPreferredUILanguages,
391		"GetThreadPreferredUILanguages":  windows.GetThreadPreferredUILanguages,
392		"GetUserPreferredUILanguages":    windows.GetUserPreferredUILanguages,
393		"GetSystemPreferredUILanguages":  windows.GetSystemPreferredUILanguages,
394	}
395	for fName, f := range tab {
396		lang, err := f(windows.MUI_LANGUAGE_ID)
397		if err != nil {
398			t.Errorf(`failed to call %v(MUI_LANGUAGE_ID): %v`, fName, err)
399		}
400		for _, l := range lang {
401			_, err := strconv.ParseUint(l, 16, 16)
402			if err != nil {
403				t.Errorf(`%v(MUI_LANGUAGE_ID) returned unexpected LANGID: %v`, fName, l)
404			}
405		}
406
407		lang, err = f(windows.MUI_LANGUAGE_NAME)
408		if err != nil {
409			t.Errorf(`failed to call %v(MUI_LANGUAGE_NAME): %v`, fName, err)
410		}
411	}
412}
413
414func TestProcessWorkingSetSizeEx(t *testing.T) {
415	// Grab a handle to the current process
416	hProcess := windows.CurrentProcess()
417
418	// Allocate memory to store the result of the query
419	var minimumWorkingSetSize, maximumWorkingSetSize uintptr
420
421	// Make the system-call
422	var flag uint32
423	windows.GetProcessWorkingSetSizeEx(hProcess, &minimumWorkingSetSize, &maximumWorkingSetSize, &flag)
424
425	// Set the new limits to the current ones
426	if err := windows.SetProcessWorkingSetSizeEx(hProcess, minimumWorkingSetSize, maximumWorkingSetSize, flag); err != nil {
427		t.Error(err)
428	}
429}
430
431func TestJobObjectInfo(t *testing.T) {
432	jo, err := windows.CreateJobObject(nil, nil)
433	if err != nil {
434		t.Fatalf("CreateJobObject failed: %v", err)
435	}
436	defer windows.CloseHandle(jo)
437
438	var info windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION
439
440	err = windows.QueryInformationJobObject(jo, windows.JobObjectExtendedLimitInformation,
441		uintptr(unsafe.Pointer(&info)), uint32(unsafe.Sizeof(info)), nil)
442	if err != nil {
443		t.Fatalf("QueryInformationJobObject failed: %v", err)
444	}
445
446	const wantMemLimit = 4 * 1024
447
448	info.BasicLimitInformation.LimitFlags |= windows.JOB_OBJECT_LIMIT_PROCESS_MEMORY
449	info.ProcessMemoryLimit = wantMemLimit
450	_, err = windows.SetInformationJobObject(jo, windows.JobObjectExtendedLimitInformation,
451		uintptr(unsafe.Pointer(&info)), uint32(unsafe.Sizeof(info)))
452	if err != nil {
453		t.Fatalf("SetInformationJobObject failed: %v", err)
454	}
455
456	err = windows.QueryInformationJobObject(jo, windows.JobObjectExtendedLimitInformation,
457		uintptr(unsafe.Pointer(&info)), uint32(unsafe.Sizeof(info)), nil)
458	if err != nil {
459		t.Fatalf("QueryInformationJobObject failed: %v", err)
460	}
461
462	if have := info.ProcessMemoryLimit; wantMemLimit != have {
463		t.Errorf("ProcessMemoryLimit is wrong: want %v have %v", wantMemLimit, have)
464	}
465}
466
467func TestIsWow64Process2(t *testing.T) {
468	var processMachine, nativeMachine uint16
469	err := windows.IsWow64Process2(windows.CurrentProcess(), &processMachine, &nativeMachine)
470	if errors.Is(err, windows.ERROR_PROC_NOT_FOUND) {
471		maj, min, build := windows.RtlGetNtVersionNumbers()
472		if maj < 10 || (maj == 10 && min == 0 && build < 17763) {
473			t.Skip("not available on older versions of Windows")
474			return
475		}
476	}
477	if err != nil {
478		t.Fatalf("IsWow64Process2 failed: %v", err)
479	}
480	if processMachine == pe.IMAGE_FILE_MACHINE_UNKNOWN {
481		processMachine = nativeMachine
482	}
483	switch {
484	case processMachine == pe.IMAGE_FILE_MACHINE_AMD64 && runtime.GOARCH == "amd64":
485	case processMachine == pe.IMAGE_FILE_MACHINE_I386 && runtime.GOARCH == "386":
486	case processMachine == pe.IMAGE_FILE_MACHINE_ARMNT && runtime.GOARCH == "arm":
487	case processMachine == pe.IMAGE_FILE_MACHINE_ARM64 && runtime.GOARCH == "arm64":
488	default:
489		t.Errorf("IsWow64Process2 is wrong: want %v have %v", runtime.GOARCH, processMachine)
490	}
491}
492
493func TestNTStatusString(t *testing.T) {
494	want := "The name limit for the local computer network adapter card was exceeded."
495	got := windows.STATUS_TOO_MANY_NAMES.Error()
496	if want != got {
497		t.Errorf("NTStatus.Error did not return an expected error string - want %q; got %q", want, got)
498	}
499}
500
501func TestNTStatusConversion(t *testing.T) {
502	want := windows.ERROR_TOO_MANY_NAMES
503	got := windows.STATUS_TOO_MANY_NAMES.Errno()
504	if want != got {
505		t.Errorf("NTStatus.Errno = %q (0x%x); want %q (0x%x)", got.Error(), got, want.Error(), want)
506	}
507}
508
509func TestPEBFilePath(t *testing.T) {
510	peb := windows.RtlGetCurrentPeb()
511	if peb == nil || peb.Ldr == nil {
512		t.Error("unable to retrieve PEB with valid Ldr")
513	}
514	var entry *windows.LDR_DATA_TABLE_ENTRY
515	for cur := peb.Ldr.InMemoryOrderModuleList.Flink; cur != &peb.Ldr.InMemoryOrderModuleList; cur = cur.Flink {
516		e := (*windows.LDR_DATA_TABLE_ENTRY)(unsafe.Pointer(uintptr(unsafe.Pointer(cur)) - unsafe.Offsetof(windows.LDR_DATA_TABLE_ENTRY{}.InMemoryOrderLinks)))
517		if e.DllBase == peb.ImageBaseAddress {
518			entry = e
519			break
520		}
521	}
522	if entry == nil {
523		t.Error("unable to find Ldr entry for current process")
524	}
525	osPath, err := os.Executable()
526	if err != nil {
527		t.Errorf("unable to get path to current executable: %v", err)
528	}
529	pebPath := entry.FullDllName.String()
530	if osPath != pebPath {
531		t.Errorf("peb.Ldr.{entry}.FullDllName = %#q; want %#q", pebPath, osPath)
532	}
533	paramPath := peb.ProcessParameters.ImagePathName.String()
534	if osPath != paramPath {
535		t.Errorf("peb.ProcessParameters.ImagePathName.{entry}.ImagePathName = %#q; want %#q", paramPath, osPath)
536	}
537	osCwd, err := os.Getwd()
538	if err != nil {
539		t.Errorf("unable to get working directory: %v", err)
540	}
541	osCwd = filepath.Clean(osCwd)
542	paramCwd := filepath.Clean(peb.ProcessParameters.CurrentDirectory.DosPath.String())
543	if paramCwd != osCwd {
544		t.Errorf("peb.ProcessParameters.CurrentDirectory.DosPath = %#q; want %#q", paramCwd, osCwd)
545	}
546}
547
548func TestResourceExtraction(t *testing.T) {
549	system32, err := windows.GetSystemDirectory()
550	if err != nil {
551		t.Errorf("unable to find system32 directory: %v", err)
552	}
553	cmd, err := windows.LoadLibrary(filepath.Join(system32, "cmd.exe"))
554	if err != nil {
555		t.Errorf("unable to load cmd.exe: %v", err)
556	}
557	defer windows.FreeLibrary(cmd)
558	rsrc, err := windows.FindResource(cmd, windows.CREATEPROCESS_MANIFEST_RESOURCE_ID, windows.RT_MANIFEST)
559	if err != nil {
560		t.Errorf("unable to find cmd.exe manifest resource: %v", err)
561	}
562	manifest, err := windows.LoadResourceData(cmd, rsrc)
563	if err != nil {
564		t.Errorf("unable to load cmd.exe manifest resource data: %v", err)
565	}
566	if !bytes.Contains(manifest, []byte("</assembly>")) {
567		t.Errorf("did not find </assembly> in manifest")
568	}
569}
570
571func TestCommandLineRecomposition(t *testing.T) {
572	const (
573		maxCharsPerArg  = 35
574		maxArgsPerTrial = 80
575		doubleQuoteProb = 4
576		singleQuoteProb = 1
577		backSlashProb   = 3
578		spaceProb       = 1
579		trials          = 1000
580	)
581	randString := func(l int) []rune {
582		s := make([]rune, l)
583		for i := range s {
584			s[i] = rand.Int31()
585		}
586		return s
587	}
588	mungeString := func(s []rune, char rune, timesInTen int) {
589		if timesInTen < rand.Intn(10)+1 || len(s) == 0 {
590			return
591		}
592		s[rand.Intn(len(s))] = char
593	}
594	argStorage := make([]string, maxArgsPerTrial+1)
595	for i := 0; i < trials; i++ {
596		args := argStorage[:rand.Intn(maxArgsPerTrial)+2]
597		args[0] = "valid-filename-for-arg0"
598		for j := 1; j < len(args); j++ {
599			arg := randString(rand.Intn(maxCharsPerArg + 1))
600			mungeString(arg, '"', doubleQuoteProb)
601			mungeString(arg, '\'', singleQuoteProb)
602			mungeString(arg, '\\', backSlashProb)
603			mungeString(arg, ' ', spaceProb)
604			args[j] = string(arg)
605		}
606		commandLine := windows.ComposeCommandLine(args)
607		decomposedArgs, err := windows.DecomposeCommandLine(commandLine)
608		if err != nil {
609			t.Errorf("Unable to decompose %#q made from %v: %v", commandLine, args, err)
610			continue
611		}
612		if len(decomposedArgs) != len(args) {
613			t.Errorf("Incorrect decomposition length from %v to %#q to %v", args, commandLine, decomposedArgs)
614			continue
615		}
616		badMatches := make([]int, 0, len(args))
617		for i := range args {
618			if args[i] != decomposedArgs[i] {
619				badMatches = append(badMatches, i)
620			}
621		}
622		if len(badMatches) != 0 {
623			t.Errorf("Incorrect decomposition at indices %v from %v to %#q to %v", badMatches, args, commandLine, decomposedArgs)
624			continue
625		}
626	}
627}
628
629func TestWinVerifyTrust(t *testing.T) {
630	evsignedfile := `.\testdata\ev-signed-file.exe`
631	evsignedfile16, err := windows.UTF16PtrFromString(evsignedfile)
632	if err != nil {
633		t.Fatalf("unable to get utf16 of %s: %v", evsignedfile, err)
634	}
635	data := &windows.WinTrustData{
636		Size:             uint32(unsafe.Sizeof(windows.WinTrustData{})),
637		UIChoice:         windows.WTD_UI_NONE,
638		RevocationChecks: windows.WTD_REVOKE_NONE, // No revocation checking, in case the tests don't have network connectivity.
639		UnionChoice:      windows.WTD_CHOICE_FILE,
640		StateAction:      windows.WTD_STATEACTION_VERIFY,
641		FileOrCatalogOrBlobOrSgnrOrCert: unsafe.Pointer(&windows.WinTrustFileInfo{
642			Size:     uint32(unsafe.Sizeof(windows.WinTrustFileInfo{})),
643			FilePath: evsignedfile16,
644		}),
645	}
646	verifyErr := windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data)
647	data.StateAction = windows.WTD_STATEACTION_CLOSE
648	closeErr := windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data)
649	if verifyErr != nil {
650		t.Errorf("%s did not verify: %v", evsignedfile, verifyErr)
651	}
652	if closeErr != nil {
653		t.Errorf("unable to free verification resources: %v", closeErr)
654	}
655
656	// Now that we've verified the legitimate file verifies, let's corrupt it and see if it correctly fails.
657
658	dir, err := ioutil.TempDir("", "go-build")
659	if err != nil {
660		t.Fatalf("failed to create temp directory: %v", err)
661	}
662	defer os.RemoveAll(dir)
663	corruptedEvsignedfile := filepath.Join(dir, "corrupted-file")
664	evsignedfileBytes, err := ioutil.ReadFile(evsignedfile)
665	if err != nil {
666		t.Fatalf("unable to read %s bytes: %v", evsignedfile, err)
667	}
668	if len(evsignedfileBytes) > 0 {
669		evsignedfileBytes[len(evsignedfileBytes)/2-1]++
670	}
671	err = ioutil.WriteFile(corruptedEvsignedfile, evsignedfileBytes, 0755)
672	if err != nil {
673		t.Fatalf("unable to write corrupted ntoskrnl.exe bytes: %v", err)
674	}
675	evsignedfile16, err = windows.UTF16PtrFromString(corruptedEvsignedfile)
676	if err != nil {
677		t.Fatalf("unable to get utf16 of ntoskrnl.exe: %v", err)
678	}
679	data = &windows.WinTrustData{
680		Size:             uint32(unsafe.Sizeof(windows.WinTrustData{})),
681		UIChoice:         windows.WTD_UI_NONE,
682		RevocationChecks: windows.WTD_REVOKE_NONE, // No revocation checking, in case the tests don't have network connectivity.
683		UnionChoice:      windows.WTD_CHOICE_FILE,
684		StateAction:      windows.WTD_STATEACTION_VERIFY,
685		FileOrCatalogOrBlobOrSgnrOrCert: unsafe.Pointer(&windows.WinTrustFileInfo{
686			Size:     uint32(unsafe.Sizeof(windows.WinTrustFileInfo{})),
687			FilePath: evsignedfile16,
688		}),
689	}
690	verifyErr = windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data)
691	data.StateAction = windows.WTD_STATEACTION_CLOSE
692	closeErr = windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data)
693	if verifyErr != windows.Errno(windows.TRUST_E_BAD_DIGEST) {
694		t.Errorf("%s did not fail to verify as expected: %v", corruptedEvsignedfile, verifyErr)
695	}
696	if closeErr != nil {
697		t.Errorf("unable to free verification resources: %v", closeErr)
698	}
699
700}
701
702func TestProcessModules(t *testing.T) {
703	process, err := windows.GetCurrentProcess()
704	if err != nil {
705		t.Fatalf("unable to get current process: %v", err)
706	}
707	// NB: Assume that we're always the first module. This technically isn't documented anywhere (that I could find), but seems to always hold.
708	var module windows.Handle
709	var cbNeeded uint32
710	err = windows.EnumProcessModules(process, &module, uint32(unsafe.Sizeof(module)), &cbNeeded)
711	if err != nil {
712		t.Fatalf("EnumProcessModules failed: %v", err)
713	}
714
715	var moduleEx windows.Handle
716	err = windows.EnumProcessModulesEx(process, &moduleEx, uint32(unsafe.Sizeof(moduleEx)), &cbNeeded, windows.LIST_MODULES_DEFAULT)
717	if err != nil {
718		t.Fatalf("EnumProcessModulesEx failed: %v", err)
719	}
720	if module != moduleEx {
721		t.Fatalf("module from EnumProcessModules does not match EnumProcessModulesEx: %v != %v", module, moduleEx)
722	}
723
724	exePath, err := os.Executable()
725	if err != nil {
726		t.Fatalf("unable to get current executable path: %v", err)
727	}
728
729	modulePathUTF16 := make([]uint16, len(exePath)+1)
730	err = windows.GetModuleFileNameEx(process, module, &modulePathUTF16[0], uint32(len(modulePathUTF16)))
731	if err != nil {
732		t.Fatalf("GetModuleFileNameEx failed: %v", err)
733	}
734
735	modulePath := windows.UTF16ToString(modulePathUTF16)
736	if modulePath != exePath {
737		t.Fatalf("module does not match executable for GetModuleFileNameEx: %s != %s", modulePath, exePath)
738	}
739
740	err = windows.GetModuleBaseName(process, module, &modulePathUTF16[0], uint32(len(modulePathUTF16)))
741	if err != nil {
742		t.Fatalf("GetModuleBaseName failed: %v", err)
743	}
744
745	modulePath = windows.UTF16ToString(modulePathUTF16)
746	baseExePath := filepath.Base(exePath)
747	if modulePath != baseExePath {
748		t.Fatalf("module does not match executable for GetModuleBaseName: %s != %s", modulePath, baseExePath)
749	}
750
751	var moduleInfo windows.ModuleInfo
752	err = windows.GetModuleInformation(process, module, &moduleInfo, uint32(unsafe.Sizeof(moduleInfo)))
753	if err != nil {
754		t.Fatalf("GetModuleInformation failed: %v", err)
755	}
756
757	peFile, err := pe.Open(exePath)
758	if err != nil {
759		t.Fatalf("unable to open current executable: %v", err)
760	}
761	defer peFile.Close()
762
763	var peSizeOfImage uint32
764	switch runtime.GOARCH {
765	case "amd64", "arm64":
766		peSizeOfImage = peFile.OptionalHeader.(*pe.OptionalHeader64).SizeOfImage
767	case "386", "arm":
768		peSizeOfImage = peFile.OptionalHeader.(*pe.OptionalHeader32).SizeOfImage
769	default:
770		t.Fatalf("unable to test GetModuleInformation on arch %v", runtime.GOARCH)
771	}
772
773	if moduleInfo.SizeOfImage != peSizeOfImage {
774		t.Fatalf("module size does not match executable: %v != %v", moduleInfo.SizeOfImage, peSizeOfImage)
775	}
776}
777
778func TestReadWriteProcessMemory(t *testing.T) {
779	testBuffer := []byte{0xBA, 0xAD, 0xF0, 0x0D}
780
781	process, err := windows.GetCurrentProcess()
782	if err != nil {
783		t.Fatalf("unable to get current process: %v", err)
784	}
785
786	buffer := make([]byte, len(testBuffer))
787	err = windows.ReadProcessMemory(process, uintptr(unsafe.Pointer(&testBuffer[0])), &buffer[0], uintptr(len(buffer)), nil)
788	if err != nil {
789		t.Errorf("ReadProcessMemory failed: %v", err)
790	}
791	if !bytes.Equal(testBuffer, buffer) {
792		t.Errorf("bytes read does not match buffer: 0x%X != 0x%X", testBuffer, buffer)
793	}
794
795	buffer = []byte{0xDE, 0xAD, 0xBE, 0xEF}
796	err = windows.WriteProcessMemory(process, uintptr(unsafe.Pointer(&testBuffer[0])), &buffer[0], uintptr(len(buffer)), nil)
797	if err != nil {
798		t.Errorf("WriteProcessMemory failed: %v", err)
799	}
800	if !bytes.Equal(testBuffer, buffer) {
801		t.Errorf("bytes written does not match buffer: 0x%X != 0x%X", testBuffer, buffer)
802	}
803}
804
805func TestSystemModuleVersions(t *testing.T) {
806	var modules []windows.RTL_PROCESS_MODULE_INFORMATION
807	for bufferSize := uint32(128 * 1024); ; {
808		moduleBuffer := make([]byte, bufferSize)
809		err := windows.NtQuerySystemInformation(windows.SystemModuleInformation, unsafe.Pointer(&moduleBuffer[0]), bufferSize, &bufferSize)
810		switch err {
811		case windows.STATUS_INFO_LENGTH_MISMATCH:
812			continue
813		case nil:
814			break
815		default:
816			t.Error(err)
817			return
818		}
819		mods := (*windows.RTL_PROCESS_MODULES)(unsafe.Pointer(&moduleBuffer[0]))
820		hdr := (*unsafeheader.Slice)(unsafe.Pointer(&modules))
821		hdr.Data = unsafe.Pointer(&mods.Modules[0])
822		hdr.Len = int(mods.NumberOfModules)
823		hdr.Cap = int(mods.NumberOfModules)
824		break
825	}
826	for i := range modules {
827		moduleName := windows.ByteSliceToString(modules[i].FullPathName[modules[i].OffsetToFileName:])
828		driverPath := `\\?\GLOBALROOT` + windows.ByteSliceToString(modules[i].FullPathName[:])
829		var zero windows.Handle
830		infoSize, err := windows.GetFileVersionInfoSize(driverPath, &zero)
831		if err != nil {
832			if err != windows.ERROR_FILE_NOT_FOUND {
833				t.Error(err)
834			}
835			continue
836		}
837		versionInfo := make([]byte, infoSize)
838		err = windows.GetFileVersionInfo(driverPath, 0, infoSize, unsafe.Pointer(&versionInfo[0]))
839		if err != nil && err != windows.ERROR_FILE_NOT_FOUND {
840			t.Error(err)
841			continue
842		}
843		var fixedInfo *windows.VS_FIXEDFILEINFO
844		fixedInfoLen := uint32(unsafe.Sizeof(*fixedInfo))
845		err = windows.VerQueryValue(unsafe.Pointer(&versionInfo[0]), `\`, (unsafe.Pointer)(&fixedInfo), &fixedInfoLen)
846		if err != nil {
847			t.Error(err)
848			continue
849		}
850		t.Logf("%s: v%d.%d.%d.%d", moduleName,
851			(fixedInfo.FileVersionMS>>16)&0xff,
852			(fixedInfo.FileVersionMS>>0)&0xff,
853			(fixedInfo.FileVersionLS>>16)&0xff,
854			(fixedInfo.FileVersionLS>>0)&0xff)
855	}
856}
857
858type fileRenameInformation struct {
859	ReplaceIfExists uint32
860	RootDirectory   windows.Handle
861	FileNameLength  uint32
862	FileName        [1]uint16
863}
864
865func TestNtCreateFileAndNtSetInformationFile(t *testing.T) {
866	var iosb windows.IO_STATUS_BLOCK
867	var allocSize int64 = 0
868	// Open test directory with NtCreateFile.
869	testDirPath := t.TempDir()
870	objectName, err := windows.NewNTUnicodeString("\\??\\" + testDirPath)
871	if err != nil {
872		t.Fatal(err)
873	}
874	oa := &windows.OBJECT_ATTRIBUTES{
875		ObjectName: objectName,
876	}
877	oa.Length = uint32(unsafe.Sizeof(*oa))
878	var testDirHandle windows.Handle
879	err = windows.NtCreateFile(&testDirHandle, windows.FILE_GENERIC_READ|windows.FILE_GENERIC_WRITE, oa, &iosb,
880		&allocSize, 0, windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE, windows.FILE_OPEN,
881		windows.FILE_DIRECTORY_FILE, 0, 0)
882	if err != nil {
883		t.Fatalf("NtCreateFile(%v) failed: %v", testDirPath, err)
884	}
885	defer windows.CloseHandle(testDirHandle)
886	// Create a file in test directory with NtCreateFile.
887	fileName := "filename"
888	filePath := filepath.Join(testDirPath, fileName)
889	objectName, err = windows.NewNTUnicodeString(fileName)
890	if err != nil {
891		t.Fatal(err)
892	}
893	oa.RootDirectory = testDirHandle
894	oa.ObjectName = objectName
895	var fileHandle windows.Handle
896	err = windows.NtCreateFile(&fileHandle, windows.FILE_GENERIC_READ|windows.FILE_GENERIC_WRITE|windows.DELETE, oa, &iosb,
897		&allocSize, 0, windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE, windows.FILE_CREATE,
898		0, 0, 0)
899	if err != nil {
900		t.Fatalf("NtCreateFile(%v) failed: %v", filePath, err)
901	}
902	defer windows.CloseHandle(fileHandle)
903	_, err = os.Stat(filePath)
904	if err != nil {
905		t.Fatalf("cannot stat file created with NtCreatefile: %v", err)
906	}
907	// Rename file with NtSetInformationFile.
908	newName := "newname"
909	newPath := filepath.Join(testDirPath, newName)
910	newNameUTF16, err := windows.UTF16FromString(newName)
911	if err != nil {
912		t.Fatal(err)
913	}
914	fileNameLen := len(newNameUTF16)*2 - 2
915	var dummyFileRenameInfo fileRenameInformation
916	bufferSize := int(unsafe.Offsetof(dummyFileRenameInfo.FileName)) + fileNameLen
917	buffer := make([]byte, bufferSize)
918	typedBufferPtr := (*fileRenameInformation)(unsafe.Pointer(&buffer[0]))
919	typedBufferPtr.ReplaceIfExists = windows.FILE_RENAME_REPLACE_IF_EXISTS | windows.FILE_RENAME_POSIX_SEMANTICS
920	typedBufferPtr.FileNameLength = uint32(fileNameLen)
921	copy((*[windows.MAX_LONG_PATH]uint16)(unsafe.Pointer(&typedBufferPtr.FileName[0]))[:fileNameLen/2:fileNameLen/2], newNameUTF16)
922	err = windows.NtSetInformationFile(fileHandle, &iosb, &buffer[0], uint32(bufferSize), windows.FileRenameInformation)
923	if err != nil {
924		t.Fatalf("NtSetInformationFile(%v) failed: %v", newPath, err)
925	}
926	_, err = os.Stat(newPath)
927	if err != nil {
928		t.Fatalf("cannot stat rename target %v: %v", newPath, err)
929	}
930}
931
932var deviceClassNetGUID = &windows.GUID{0x4d36e972, 0xe325, 0x11ce, [8]byte{0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}}
933var deviceInterfaceNetGUID = &windows.GUID{0xcac88484, 0x7515, 0x4c03, [8]byte{0x82, 0xe6, 0x71, 0xa8, 0x7a, 0xba, 0xc3, 0x61}}
934
935func TestListLoadedNetworkDevices(t *testing.T) {
936	devInfo, err := windows.SetupDiGetClassDevsEx(deviceClassNetGUID, "", 0, windows.DIGCF_PRESENT, 0, "")
937	if err != nil {
938		t.Fatal(err)
939	}
940	defer devInfo.Close()
941	for i := 0; ; i++ {
942		devInfoData, err := devInfo.EnumDeviceInfo(i)
943		if err != nil {
944			if err == windows.ERROR_NO_MORE_ITEMS {
945				break
946			}
947			continue
948		}
949		friendlyName, err := devInfo.DeviceRegistryProperty(devInfoData, windows.SPDRP_DEVICEDESC)
950		if err != nil {
951			t.Fatal(err)
952		}
953		var status, problemCode uint32
954		err = windows.CM_Get_DevNode_Status(&status, &problemCode, devInfoData.DevInst, 0)
955		if err != nil || (status&windows.DN_DRIVER_LOADED|windows.DN_STARTED) != windows.DN_DRIVER_LOADED|windows.DN_STARTED {
956			continue
957		}
958		instanceId, err := devInfo.DeviceInstanceID(devInfoData)
959		if err != nil {
960			t.Fatal(err)
961		}
962		interfaces, err := windows.CM_Get_Device_Interface_List(instanceId, deviceInterfaceNetGUID, windows.CM_GET_DEVICE_INTERFACE_LIST_PRESENT)
963		if err != nil || len(interfaces) == 0 {
964			continue
965		}
966		t.Logf("%s - %s", friendlyName, interfaces[0])
967	}
968}
969
970func TestListWireGuardDrivers(t *testing.T) {
971	devInfo, err := windows.SetupDiCreateDeviceInfoListEx(deviceClassNetGUID, 0, "")
972	if err != nil {
973		t.Fatal(err)
974	}
975	defer devInfo.Close()
976	devInfoData, err := devInfo.CreateDeviceInfo("WireGuard", deviceClassNetGUID, "", 0, windows.DICD_GENERATE_ID)
977	if err != nil {
978		t.Fatal(err)
979	}
980	err = devInfo.SetDeviceRegistryProperty(devInfoData, windows.SPDRP_HARDWAREID, []byte("W\x00i\x00r\x00e\x00G\x00u\x00a\x00r\x00d\x00\x00\x00\x00\x00"))
981	if err != nil {
982		t.Fatal(err)
983	}
984	err = devInfo.BuildDriverInfoList(devInfoData, windows.SPDIT_COMPATDRIVER)
985	if err != nil {
986		t.Fatal(err)
987	}
988	defer devInfo.DestroyDriverInfoList(devInfoData, windows.SPDIT_COMPATDRIVER)
989	for i := 0; ; i++ {
990		drvInfoData, err := devInfo.EnumDriverInfo(devInfoData, windows.SPDIT_COMPATDRIVER, i)
991		if err != nil {
992			if err == windows.ERROR_NO_MORE_ITEMS {
993				break
994			}
995			continue
996		}
997		drvInfoDetailData, err := devInfo.DriverInfoDetail(devInfoData, drvInfoData)
998		if err != nil {
999			t.Error(err)
1000			continue
1001		}
1002		t.Logf("%s - %s", drvInfoData.Description(), drvInfoDetailData.InfFileName())
1003	}
1004}
1005