github.com/iDigitalFlame/xmt@v0.5.4/device/winapi/calls.go (about)

     1  //go:build windows
     2  // +build windows
     3  
     4  // Copyright (C) 2020 - 2023 iDigitalFlame
     5  //
     6  // This program is free software: you can redistribute it and/or modify
     7  // it under the terms of the GNU General Public License as published by
     8  // the Free Software Foundation, either version 3 of the License, or
     9  // any later version.
    10  //
    11  // This program is distributed in the hope that it will be useful,
    12  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  // GNU General Public License for more details.
    15  //
    16  // You should have received a copy of the GNU General Public License
    17  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    18  //
    19  
    20  package winapi
    21  
    22  import (
    23  	"syscall"
    24  	"unsafe"
    25  
    26  	"github.com/iDigitalFlame/xmt/util/bugtrack"
    27  )
    28  
    29  // RevertToSelf Windows API Call
    30  //
    31  //	The RevertToSelf function terminates the impersonation of a client application.
    32  //
    33  // https://docs.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-revqerttoself
    34  //
    35  // Alias of 'SetAllThreadsToken(0)'
    36  //
    37  // NOTE(dij): This only clears the token on all the Golang Threads. Same as
    38  //
    39  //	'device.RevertToSelf'.
    40  func RevertToSelf() error {
    41  	return SetAllThreadsToken(0)
    42  }
    43  
    44  // BlockInput Windows API Call
    45  //
    46  //	Blocks keyboard and mouse input events from reaching applications.
    47  //
    48  // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-blockinput
    49  func BlockInput(e bool) error {
    50  	var v uint32
    51  	if e {
    52  		v = 1
    53  	}
    54  	r, _, err := syscallN(funcBlockInput.address(), uintptr(v))
    55  	if r == 0 {
    56  		return unboxError(err)
    57  	}
    58  	return nil
    59  }
    60  
    61  // SetEvent Windows API Call
    62  //
    63  //	Sets the specified event object to the signaled state.
    64  //
    65  // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-setevent
    66  //
    67  // Re-targeted to use 'NtSetEvent' instead.
    68  // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-zwsetevent
    69  func SetEvent(h uintptr) error {
    70  	if r, _, _ := syscallN(funcNtSetEvent.address(), h, 0); r > 0 {
    71  		return formatNtError(r)
    72  	}
    73  	return nil
    74  }
    75  
    76  // CloseHandle Windows API Call
    77  //
    78  //	Closes an open object handle.
    79  //
    80  // https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle
    81  //
    82  // Re-targeted to use 'NtClose' instead.
    83  // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntclose
    84  func CloseHandle(h uintptr) error {
    85  	// NOTE(dij): Uncomment to track empty handles
    86  	//	if h == 0 {
    87  	// 		panic("invalid")
    88  	//	}
    89  	r, _, _ := syscallN(funcNtClose.address(), h)
    90  	if bugtrack.Enabled { // Trace Bad Handles
    91  		bugtrack.Track("winapi.CloseHandle() h=0x%X, r=0x%X", h, r)
    92  	}
    93  	if r > 0 {
    94  		return formatNtError(r)
    95  	}
    96  	return nil
    97  }
    98  
    99  // GetCurrentProcessID Windows API Call
   100  //
   101  //	Retrieves the process identifier of the calling process.
   102  //
   103  // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocessid
   104  func GetCurrentProcessID() uint32 {
   105  	r, _, _ := syscallN(funcGetCurrentProcessID.address())
   106  	return uint32(r)
   107  }
   108  
   109  // RegFlushKey Windows API Call
   110  //
   111  //	Writes all the attributes of the specified open registry key into the registry.
   112  //
   113  // https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regflushkey
   114  func RegFlushKey(h uintptr) error {
   115  	r, _, err := syscallN(funcRegFlushKey.address(), h)
   116  	if r > 0 {
   117  		return unboxError(err)
   118  	}
   119  	return nil
   120  }
   121  
   122  // NtResumeProcess Windows API Call
   123  //
   124  //	Resumes a process and all it's threads.
   125  //
   126  // http://www.pinvoke.net/default.aspx/ntdll/NtResumeProcess.html
   127  func NtResumeProcess(h uintptr) error {
   128  	if r, _, _ := syscallN(funcNtResumeProcess.address(), h); r > 0 {
   129  		return formatNtError(r)
   130  	}
   131  	return nil
   132  }
   133  
   134  // NtSuspendProcess Windows API Call
   135  //
   136  //	Suspends a process and all it's threads.
   137  //
   138  // http://www.pinvoke.net/default.aspx/ntdll/NtSuspendProcess.html
   139  func NtSuspendProcess(h uintptr) error {
   140  	if r, _, _ := syscallN(funcNtSuspendProcess.address(), h); r > 0 {
   141  		return formatNtError(r)
   142  	}
   143  	return nil
   144  }
   145  
   146  // GetLogicalDrives Windows API Call
   147  //
   148  //	Retrieves a bitmask representing the currently available disk drives.
   149  //
   150  // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getlogicaldrives
   151  func GetLogicalDrives() (uint32, error) {
   152  	// NOTE(dij): This is a super UNDOCUMENTED call to NtQueryInformationProcess
   153  	//            that somehow returns a bitmask of the current drives connected?
   154  	//            It's something to do with IRQ information?
   155  	//            Also the input blob size is 34b on x64 and 56b on x86? The 56b
   156  	//            works on both as long as we report 36b for both.
   157  	var (
   158  		n       uint32
   159  		b       [14]uint32
   160  		r, _, _ = syscallN(funcNtQueryInformationProcess.address(), CurrentProcess, 0x17, uintptr(unsafe.Pointer(&b[0])), 0x24, uintptr(unsafe.Pointer(&n)))
   161  	)
   162  	if r > 0 {
   163  		return 0, formatNtError(r)
   164  	}
   165  	return b[0], nil
   166  }
   167  
   168  // DisconnectNamedPipe Windows API Call
   169  //
   170  //	Disconnects the server end of a named pipe instance from a client process.
   171  //
   172  // https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-disconnectnamedpipe
   173  func DisconnectNamedPipe(h uintptr) error {
   174  	r, _, err := syscallN(funcDisconnectNamedPipe.address(), h)
   175  	if r == 0 {
   176  		return unboxError(err)
   177  	}
   178  	return nil
   179  }
   180  
   181  // ResumeThread Windows API Call
   182  //
   183  //	Decrements a thread's suspend count. When the suspend count is decremented
   184  //	to zero, the execution of the thread is resumed.
   185  //
   186  // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-resumethread
   187  //
   188  // Re-targeted to use 'NtResumeThread' instead.
   189  // https://docs.rs/ntapi/0.3.1/ntapi/ntpsapi/type.NtResumeThread.html
   190  func ResumeThread(h uintptr) (uint32, error) {
   191  	var c uint32
   192  	if r, _, _ := syscallN(funcNtResumeThread.address(), h, uintptr(unsafe.Pointer(&c))); r > 0 {
   193  		return 0, formatNtError(r)
   194  	}
   195  	return c, nil
   196  }
   197  
   198  // GetProcessID Windows API Call
   199  //
   200  //	Retrieves the process identifier of the specified process.
   201  //
   202  // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getprocessid
   203  //
   204  // Calls 'NtQueryInformationProcess' instead under the hood.
   205  func GetProcessID(h uintptr) (uint32, error) {
   206  	var (
   207  		p       processBasicInfo
   208  		r, _, _ = syscallN(funcNtQueryInformationProcess.address(), h, 0, uintptr(unsafe.Pointer(&p)), unsafe.Sizeof(p), 0)
   209  	)
   210  	if r > 0 {
   211  		return 0, formatNtError(r)
   212  	}
   213  	return uint32(p.UniqueProcessID), nil
   214  }
   215  
   216  // SuspendThread Windows API Call
   217  //
   218  //	Suspends the specified thread.
   219  //
   220  // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-suspendthread
   221  //
   222  // Re-targeted to use 'NtSuspendThread' instead.
   223  // https://docs.rs/ntapi/0.3.1/ntapi/ntpsapi/type.NtSuspendThread.html
   224  func SuspendThread(h uintptr) (uint32, error) {
   225  	var c uint32
   226  	if r, _, _ := syscallN(funcNtSuspendThread.address(), h, uintptr(unsafe.Pointer(&c))); r > 0 {
   227  		return 0, formatNtError(r)
   228  	}
   229  	return c, nil
   230  }
   231  
   232  // TerminateThread Windows API Call
   233  //
   234  //	Terminates a thread.
   235  //
   236  // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminatethread
   237  //
   238  // Re-targeted to use 'NtTerminateThread' instead.
   239  // http://pinvoke.net/default.aspx/ntdll/NtTerminateThread.html
   240  func TerminateThread(h uintptr, e uint32) error {
   241  	if h == 0 {
   242  		// Helper to prevent deadlocks.
   243  		return nil
   244  	}
   245  	if r, _, _ := syscallN(funcNtTerminateThread.address(), h, uintptr(e)); r > 0 {
   246  		return formatNtError(r)
   247  	}
   248  	return nil
   249  }
   250  
   251  // RegDeleteKey Windows API Call
   252  //
   253  //	Deletes a subkey and its values. Note that key names are not case sensitive.
   254  //	ONLY DELETES EMPTY SUBKEYS. (invalid argument if non-empty)
   255  //
   256  // https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regdeletekeyw
   257  func RegDeleteKey(h uintptr, path string) error {
   258  	return RegDeleteKeyEx(h, path, 0)
   259  }
   260  
   261  // SetProcessIsCritical Windows API Call
   262  //
   263  //	Set process system critical status.
   264  //	Returns the last Critical status.
   265  //
   266  // https://www.codeproject.com/articles/43405/protecting-your-process-with-rtlsetprocessiscriti
   267  func SetProcessIsCritical(c bool) (bool, error) {
   268  	var s, o byte
   269  	if c {
   270  		s = 1
   271  	}
   272  	r, _, err := syscallN(funcRtlSetProcessIsCritical.address(), uintptr(s), uintptr(unsafe.Pointer(&o)), 0)
   273  	if r > 0 {
   274  		return false, unboxError(err)
   275  	}
   276  	return o == 1, nil
   277  }
   278  
   279  // SetThreadToken Windows API Call
   280  //
   281  //	The SetThreadToken function assigns an impersonation token to a thread. The
   282  //	function can also cause a thread to stop using an impersonation token.
   283  //
   284  // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadtoken
   285  //
   286  // Calls 'NtSetInformationThread' under the hood.
   287  func SetThreadToken(h uintptr, t uintptr) error {
   288  	// 0x5 - ThreadImpersonationToken
   289  	if r, _, _ := syscallN(funcNtSetInformationThread.address(), h, 0x5, uintptr(unsafe.Pointer(&t)), ptrSize); r > 0 {
   290  		return formatNtError(r)
   291  	}
   292  	return nil
   293  }
   294  
   295  // TerminateProcess Windows API Call
   296  //
   297  //	Terminates the specified process and all of its threads.
   298  //
   299  // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminateprocess
   300  //
   301  // Re-targeted to use 'NtTerminateProcess' instead.
   302  // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-zwterminateprocess
   303  func TerminateProcess(h uintptr, e uint32) error {
   304  	if h == 0 {
   305  		// Helper to prevent deadlocks.
   306  		return nil
   307  	}
   308  	if r, _, _ := syscallN(funcNtTerminateProcess.address(), h, uintptr(e)); r > 0 {
   309  		return formatNtError(r)
   310  	}
   311  	return nil
   312  }
   313  
   314  // ImpersonateNamedPipeClient Windows API Call
   315  //
   316  //	The ImpersonateNamedPipeClient function impersonates a named-pipe client
   317  //	application.
   318  //
   319  // https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-impersonatenamedpipeclient
   320  func ImpersonateNamedPipeClient(h uintptr) error {
   321  	r, _, err := syscallN(funcImpersonateNamedPipeClient.address(), h)
   322  	if r == 0 {
   323  		return unboxError(err)
   324  	}
   325  	return nil
   326  }
   327  
   328  // RegDeleteValue Windows API Call
   329  //
   330  //	Removes a named value from the specified registry key. Note that value names
   331  //	are not case sensitive.
   332  //
   333  // https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regdeletevaluew
   334  func RegDeleteValue(h uintptr, path string) error {
   335  	p, err := UTF16PtrFromString(path)
   336  	if err != nil {
   337  		return err
   338  	}
   339  	r, _, err1 := syscallN(funcRegDeleteValue.address(), h, uintptr(unsafe.Pointer(p)))
   340  	if r > 0 {
   341  		return unboxError(err1)
   342  	}
   343  	return nil
   344  }
   345  
   346  // GetExitCodeThread Windows API Call
   347  //
   348  //	Retrieves the termination status of the specified thread.
   349  //
   350  // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getexitcodethread
   351  //
   352  // Calls 'NtQueryInformationThread' under the hood.
   353  func GetExitCodeThread(h uintptr, e *uint32) error {
   354  	var (
   355  		t       threadBasicInfo
   356  		r, _, _ = syscallN(funcNtQueryInformationThread.address(), h, 0, uintptr(unsafe.Pointer(&t)), unsafe.Sizeof(t), 0)
   357  	)
   358  	if r > 0 {
   359  		return formatNtError(r)
   360  	}
   361  	*e = t.ExitStatus
   362  	return nil
   363  }
   364  
   365  // GetExitCodeProcess Windows API Call
   366  //
   367  //	Retrieves the termination status of the specified process.
   368  //
   369  // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess
   370  //
   371  // Calls 'NtQueryInformationProcess' under the hood.
   372  func GetExitCodeProcess(h uintptr, e *uint32) error {
   373  	var (
   374  		p       processBasicInfo
   375  		r, _, _ = syscallN(funcNtQueryInformationProcess.address(), h, 0, uintptr(unsafe.Pointer(&p)), unsafe.Sizeof(p), 0)
   376  	)
   377  	if r > 0 {
   378  		return formatNtError(r)
   379  	}
   380  	*e = p.ExitStatus
   381  	return nil
   382  }
   383  
   384  // WaitNamedPipe Windows API Call
   385  //
   386  //	Waits until either a time-out interval elapses or an instance of the
   387  //	specified named pipe is available for connection (that is, the pipe's server
   388  //	process has a pending ConnectNamedPipe operation on the pipe).
   389  //
   390  // https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-waitnamedpipea
   391  func WaitNamedPipe(name string, timeout uint32) error {
   392  	n, err := UTF16PtrFromString(name)
   393  	if err != nil {
   394  		return err
   395  	}
   396  	r, _, err1 := syscallN(funcWaitNamedPipe.address(), uintptr(unsafe.Pointer(n)), uintptr(timeout))
   397  	if r == 0 {
   398  		return unboxError(err1)
   399  	}
   400  	return nil
   401  }
   402  
   403  // ConnectNamedPipe Windows API Call
   404  //
   405  //	Enables a named pipe server process to wait for a client process to connect
   406  //	to an instance of a named pipe. A client process connects by calling either
   407  //	the CreateFile or CallNamedPipe function.
   408  //
   409  // https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-connectnamedpipe
   410  func ConnectNamedPipe(h uintptr, o *Overlapped) error {
   411  	r, _, err := syscallN(funcConnectNamedPipe.address(), h, uintptr(unsafe.Pointer(o)))
   412  	if r == 0 {
   413  		return unboxError(err)
   414  	}
   415  	return nil
   416  }
   417  
   418  // NtUnmapViewOfSection Windows API Call
   419  //
   420  //	The NtUnmapViewOfSection routine un-maps a view of a section from the virtual
   421  //	address space of a subject process.
   422  //
   423  // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-zwunmapviewofsection
   424  func NtUnmapViewOfSection(proc, section uintptr) error {
   425  	if r, _, _ := syscallN(funcNtUnmapViewOfSection.address(), proc, section); r > 0 {
   426  		return formatNtError(r)
   427  	}
   428  	return nil
   429  }
   430  
   431  // NtFreeVirtualMemory Windows API Call
   432  //
   433  //	The NtFreeVirtualMemory routine releases, decommits, or both releases and
   434  //	decommits, a region of pages within the virtual address space of a specified
   435  //	process.
   436  //
   437  // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntfreevirtualmemory
   438  func NtFreeVirtualMemory(h, address, size uintptr) error {
   439  	var (
   440  		s       uintptr = size
   441  		r, _, _         = syscallN(
   442  			funcNtFreeVirtualMemory.address(), h, uintptr(unsafe.Pointer(&address)), uintptr(unsafe.Pointer(&s)),
   443  			0x8000,
   444  		)
   445  		// 0x8000 - MEM_RELEASE
   446  	)
   447  	if r > 0 {
   448  		return formatNtError(r)
   449  	}
   450  	return nil
   451  }
   452  
   453  // SetServiceStatus Windows API Call
   454  //
   455  //	Contains status information for a service. The ControlService, EnumDependentServices,
   456  //	EnumServicesStatus, and QueryServiceStatus functions use this structure. A
   457  //	service uses this structure in the SetServiceStatus function to report its
   458  //	current status to the service control manager.
   459  //
   460  // https://docs.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_status
   461  func SetServiceStatus(h uintptr, s *ServiceStatus) error {
   462  	r, _, err := syscallN(funcSetServiceStatus.address(), h, uintptr(unsafe.Pointer(s)))
   463  	if r == 0 {
   464  		return unboxError(err)
   465  	}
   466  	return nil
   467  }
   468  
   469  // CheckRemoteDebuggerPresent Windows API Call
   470  //
   471  //	Determines whether the specified process is being debugged.
   472  //
   473  // https://docs.microsoft.com/en-us/windows/win32/api/debugapi/nf-debugapi-checkremotedebuggerpresent
   474  //
   475  // Calls 'NtQueryInformationProcess' under the hood.
   476  func CheckRemoteDebuggerPresent(h uintptr, b *bool) error {
   477  	var (
   478  		p       uintptr
   479  		r, _, _ = syscallN(funcNtQueryInformationProcess.address(), h, 0x7, uintptr(unsafe.Pointer(&p)), ptrSize, 0)
   480  		// 0x7 - ProcessDebugPort
   481  	)
   482  	if r > 0 {
   483  		return formatNtError(r)
   484  	}
   485  	*b = p > 0
   486  	return nil
   487  }
   488  
   489  // StartServiceCtrlDispatcher Windows API Call
   490  //
   491  //	Connects the main thread of a service process to the service control manager,
   492  //	which causes the thread to be the service control dispatcher thread for the
   493  //	calling process.
   494  //
   495  // https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-startservicectrldispatcherw
   496  func StartServiceCtrlDispatcher(t *ServiceTableEntry) error {
   497  	r, _, err := syscallN(funcStartServiceCtrlDispatcher.address(), uintptr(unsafe.Pointer(t)))
   498  	if r == 0 {
   499  		return unboxError(err)
   500  	}
   501  	return nil
   502  }
   503  
   504  // LoadLibraryEx Windows API Call
   505  //
   506  //	Loads the specified module into the address space of the calling process.
   507  //	The specified module may cause other modules to be loaded.
   508  //
   509  // https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryexw
   510  func LoadLibraryEx(s string, flags uintptr) (uintptr, error) {
   511  	n, err := UTF16PtrFromString(s)
   512  	if err != nil {
   513  		return 0, err
   514  	}
   515  	r, _, e := syscallN(funcLoadLibraryEx.address(), uintptr(unsafe.Pointer(n)), 0, flags)
   516  	if r == 0 {
   517  		return 0, unboxError(e)
   518  	}
   519  	return r, nil
   520  }
   521  
   522  // LookupPrivilegeValue Windows API Call
   523  //
   524  //	The LookupPrivilegeValue function retrieves the locally unique identifier
   525  //	(LUID) used on a specified system to locally represent the specified privilege
   526  //	name.
   527  //
   528  // https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-lookupprivilegevaluew
   529  func LookupPrivilegeValue(system, name string, l *LUID) error {
   530  	var (
   531  		s, n *uint16
   532  		err  error
   533  	)
   534  	if len(system) > 0 {
   535  		if s, err = UTF16PtrFromString(system); err != nil {
   536  			return err
   537  		}
   538  	}
   539  	if n, err = UTF16PtrFromString(name); err != nil {
   540  		return err
   541  	}
   542  	r, _, err1 := syscallN(
   543  		funcLookupPrivilegeValue.address(), uintptr(unsafe.Pointer(s)), uintptr(unsafe.Pointer(n)),
   544  		uintptr(unsafe.Pointer(l)),
   545  	)
   546  	if r == 0 {
   547  		return unboxError(err1)
   548  	}
   549  	return nil
   550  }
   551  
   552  // WaitForSingleObject Windows API Call
   553  //
   554  //	Waits until the specified object is in the signaled state or the time-out
   555  //	interval elapses.
   556  //
   557  // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject
   558  func WaitForSingleObject(h uintptr, timeout int32) (uint32, error) {
   559  	if v := int64(h); v >= -12 && v <= -10 {
   560  		if p, err := getProcessPeb(); err == nil {
   561  			switch v {
   562  			case -10: // STD_INPUT_HANDLE
   563  				h = p.ProcessParameters.StandardInput
   564  			case -11: // STD_OUTPUT_HANDLE
   565  				h = p.ProcessParameters.StandardOutput
   566  			case -12: // STD_ERROR_HANDLE
   567  				h = p.ProcessParameters.StandardError
   568  			}
   569  		}
   570  	}
   571  	var t *uint64
   572  	if timeout != -1 {
   573  		n := uint64(timeout * -10000)
   574  		t = &n
   575  	}
   576  	r, _, _ := syscallN(funcNtWaitForSingleObject.address(), h, 0, uintptr(unsafe.Pointer(t)))
   577  	switch r {
   578  	case 0, 0x000000C0, 0x00000101, 0x00000102:
   579  		return uint32(r), nil
   580  	}
   581  	return 0, formatNtError(r)
   582  }
   583  
   584  // ReadFile Windows API Call
   585  //
   586  //	Reads data from the specified file or input/output (I/O) device. Reads
   587  //	occur at the position specified by the file pointer if supported by the device.
   588  //
   589  // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile
   590  func ReadFile(h uintptr, b []byte, n *uint32, o *Overlapped) error {
   591  	var v *byte
   592  	if len(b) > 0 {
   593  		v = &b[0]
   594  	}
   595  	r, _, err := syscallN(
   596  		funcReadFile.address(), h, uintptr(unsafe.Pointer(v)), uintptr(len(b)), uintptr(unsafe.Pointer(n)),
   597  		uintptr(unsafe.Pointer(o)),
   598  	)
   599  	if r == 0 {
   600  		return unboxError(err)
   601  	}
   602  	return nil
   603  }
   604  
   605  // WriteFile Windows API Call
   606  //
   607  //	Writes data to the specified file or input/output (I/O) device.
   608  //
   609  // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile
   610  func WriteFile(h uintptr, b []byte, n *uint32, o *Overlapped) error {
   611  	var v *byte
   612  	if len(b) > 0 {
   613  		v = &b[0]
   614  	}
   615  	r, _, err := syscallN(
   616  		funcWriteFile.address(), h, uintptr(unsafe.Pointer(v)), uintptr(len(b)), uintptr(unsafe.Pointer(n)),
   617  		uintptr(unsafe.Pointer(o)),
   618  	)
   619  	if r == 0 {
   620  		return unboxError(err)
   621  	}
   622  	return nil
   623  }
   624  
   625  // OpenProcessToken Windows API Call
   626  //
   627  //	The OpenProcessToken function opens the access token associated with a process.
   628  //
   629  // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocesstoken
   630  //
   631  // Re-targeted to use 'NtOpenProcessToken' instead.
   632  // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntopenprocesstoken
   633  func OpenProcessToken(h uintptr, access uint32, res *uintptr) error {
   634  	if r, _, _ := syscallN(funcNtOpenProcessToken.address(), h, uintptr(access), uintptr(unsafe.Pointer(res))); r > 0 {
   635  		return formatNtError(r)
   636  	}
   637  	return nil
   638  }
   639  
   640  // NtWriteVirtualMemory Windows API Call
   641  //
   642  //	This function copies the specified address range from the current process
   643  //	into the specified address range of the specified process.
   644  //
   645  // http://www.codewarrior.cn/ntdoc/winnt/mm/NtWriteVirtualMemory.htm
   646  func NtWriteVirtualMemory(h, address uintptr, b []byte) (uint32, error) {
   647  	var (
   648  		s       uint32
   649  		r, _, _ = syscallN(
   650  			funcNtWriteVirtualMemory.address(), h, address, uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)),
   651  			uintptr(unsafe.Pointer(&s)),
   652  		)
   653  	)
   654  	if r > 0 {
   655  		return 0, formatNtError(r)
   656  	}
   657  	return s, nil
   658  }
   659  
   660  // SecurityDescriptorFromString converts an SDDL string describing a security
   661  // descriptor into a self-relative security descriptor object allocated on the
   662  // Go heap.
   663  func SecurityDescriptorFromString(s string) (*SecurityDescriptor, error) {
   664  	var (
   665  		h   *SecurityDescriptor
   666  		err = securityDescriptorFromString(s, 1, &h, nil)
   667  	)
   668  	if err != nil {
   669  		return nil, err
   670  	}
   671  	c := h.copyRelative()
   672  	localFree(uintptr(unsafe.Pointer(h)))
   673  	return c, nil
   674  }
   675  
   676  // QueryServiceDynamicInformation Windows API Call
   677  //
   678  //	Retrieves dynamic information related to the current service start.
   679  //
   680  // https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-queryservicedynamicinformation
   681  //
   682  // This function is not avaliable to any systems older than Windows 8 (<= Win8).
   683  func QueryServiceDynamicInformation(h uintptr, l uint32) (uint32, error) {
   684  	if funcQueryServiceDynamicInformation.find() != nil {
   685  		return 0, syscall.EINVAL
   686  	}
   687  	var (
   688  		a         *uint32
   689  		r, _, err = syscallN(
   690  			funcQueryServiceDynamicInformation.address(), h, uintptr(l), uintptr(unsafe.Pointer(&a)),
   691  		)
   692  	)
   693  	if r == 0 {
   694  		return 0, unboxError(err)
   695  	}
   696  	v := *a
   697  	localFree(uintptr(unsafe.Pointer(&a)))
   698  	return v, nil
   699  }
   700  
   701  // OpenThread Windows API Call
   702  //
   703  //	Opens an existing thread object.
   704  //
   705  // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openthread
   706  //
   707  // Re-targeted to use 'NtOpenThread' instead.
   708  // https://learn.microsoft.com/en-us/windows/win32/devnotes/ntopenthread
   709  func OpenThread(access uint32, inherit bool, tid uint32) (uintptr, error) {
   710  	var (
   711  		o objAttrs
   712  		h uintptr
   713  		i clientID
   714  	)
   715  	if i.Thread = uintptr(tid); inherit {
   716  		// 0x2 - OBJ_INHERIT
   717  		o.Attributes = 0x2
   718  	}
   719  	o.Length = uint32(unsafe.Sizeof(o))
   720  	r, _, _ := syscallN(
   721  		funcNtOpenThread.address(), uintptr(unsafe.Pointer(&h)), uintptr(access), uintptr(unsafe.Pointer(&o)),
   722  		uintptr(unsafe.Pointer(&i)),
   723  	)
   724  	if r > 0 {
   725  		return 0, formatNtError(r)
   726  	}
   727  	return h, nil
   728  }
   729  
   730  // OpenMutex Windows API Call
   731  //
   732  //	Opens an existing named mutex object.
   733  //
   734  // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-OpenMutexw
   735  func OpenMutex(access uint32, inherit bool, name string) (uintptr, error) {
   736  	n, err := UTF16PtrFromString(name)
   737  	if err != nil {
   738  		return 0, err
   739  	}
   740  	var i uint32
   741  	if inherit {
   742  		i = 1
   743  	}
   744  	r, _, err1 := syscallN(funcOpenMutex.address(), uintptr(access), uintptr(i), uintptr(unsafe.Pointer(n)))
   745  	if r == 0 {
   746  		return 0, unboxError(err1)
   747  	}
   748  	return r, nil
   749  }
   750  
   751  // OpenEvent Windows API Call
   752  //
   753  //	Opens an existing named event object.
   754  //
   755  // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-openeventw
   756  func OpenEvent(access uint32, inherit bool, name string) (uintptr, error) {
   757  	n, err := UTF16PtrFromString(name)
   758  	if err != nil {
   759  		return 0, err
   760  	}
   761  	var i uint32
   762  	if inherit {
   763  		i = 1
   764  	}
   765  	r, _, err1 := syscallN(funcOpenEvent.address(), uintptr(access), uintptr(i), uintptr(unsafe.Pointer(n)))
   766  	if r == 0 {
   767  		return 0, unboxError(err1)
   768  	}
   769  	return r, nil
   770  }
   771  
   772  // OpenProcess Windows API Call
   773  //
   774  //	Opens an existing local process object.
   775  //
   776  // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess
   777  //
   778  // Re-targeted to use 'NtOpenProcess' instead.
   779  // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-ntopenprocess
   780  func OpenProcess(access uint32, inherit bool, pid uint32) (uintptr, error) {
   781  	var (
   782  		o objAttrs
   783  		h uintptr
   784  		i clientID
   785  	)
   786  	if i.Process = uintptr(pid); inherit {
   787  		// 0x2 - OBJ_INHERIT
   788  		o.Attributes = 0x2
   789  	}
   790  	o.Length = uint32(unsafe.Sizeof(o))
   791  	r, _, _ := syscallN(
   792  		funcNtOpenProcess.address(), uintptr(unsafe.Pointer(&h)), uintptr(access), uintptr(unsafe.Pointer(&o)),
   793  		uintptr(unsafe.Pointer(&i)),
   794  	)
   795  	if r > 0 {
   796  		return 0, formatNtError(r)
   797  	}
   798  	return h, nil
   799  }
   800  
   801  // GetOverlappedResult Windows API Call
   802  //
   803  //	Retrieves the results of an overlapped operation on the specified file,
   804  //	named pipe, or communications device. To specify a timeout interval or wait
   805  //	on an alertable thread, use GetOverlappedResultEx.
   806  //
   807  // https://docs.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getoverlappedresult
   808  func GetOverlappedResult(h uintptr, o *Overlapped, n *uint32, w bool) error {
   809  	var z uint32
   810  	if w {
   811  		z = 1
   812  	}
   813  	r, _, err := syscallN(
   814  		funcGetOverlappedResult.address(), h, uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(n)), uintptr(z),
   815  	)
   816  	if r == 0 {
   817  		return unboxError(err)
   818  	}
   819  	return nil
   820  }
   821  
   822  // OpenThreadToken Windows API Call
   823  //
   824  //	The OpenThreadToken function opens the access token associated with a thread.
   825  //
   826  // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openthreadtoken
   827  //
   828  // Re-targeted to use 'NtOpenThreadToken' instead.
   829  // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntopenthreadtoken
   830  func OpenThreadToken(h uintptr, access uint32, self bool, t *uintptr) error {
   831  	var s uint32
   832  	if self {
   833  		s = 1
   834  	}
   835  	r, _, _ := syscallN(funcNtOpenThreadToken.address(), h, uintptr(access), uintptr(s), uintptr(unsafe.Pointer(t)))
   836  	if r > 0 {
   837  		return formatNtError(r)
   838  	}
   839  	return nil
   840  }
   841  
   842  // OpenSemaphore Windows API Call
   843  //
   844  //	Opens an existing named semaphore object.
   845  //
   846  // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-OpenSemaphorew
   847  func OpenSemaphore(access uint32, inherit bool, name string) (uintptr, error) {
   848  	n, err := UTF16PtrFromString(name)
   849  	if err != nil {
   850  		return 0, err
   851  	}
   852  	var i uint32
   853  	if inherit {
   854  		i = 1
   855  	}
   856  	r, _, err1 := syscallN(funcOpenSemaphore.address(), uintptr(access), uintptr(i), uintptr(unsafe.Pointer(n)))
   857  	if r == 0 {
   858  		return 0, unboxError(err1)
   859  	}
   860  	return r, nil
   861  }
   862  
   863  // NtAllocateVirtualMemory Windows API Call
   864  //
   865  //	The NtAllocateVirtualMemory routine reserves, commits, or both, a region of
   866  //	pages within the user-mode virtual address space of a specified process.
   867  //
   868  // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntallocatevirtualmemory
   869  func NtAllocateVirtualMemory(h uintptr, size, access uint32) (uintptr, error) {
   870  	var (
   871  		a       uintptr
   872  		x       = uintptr(size)
   873  		r, _, _ = syscallN(
   874  			funcNtAllocateVirtualMemory.address(), h, uintptr(unsafe.Pointer(&a)), 0, uintptr(unsafe.Pointer(&x)),
   875  			0x3000, uintptr(access),
   876  		)
   877  		// 0x300 - MEM_COMMIT | MEM_RESERVE
   878  	)
   879  	if r > 0 {
   880  		return 0, formatNtError(r)
   881  	}
   882  	return a, nil
   883  }
   884  
   885  // NtImpersonateThread Windows API Call
   886  //
   887  //	This routine is used to cause the server thread to impersonate the client
   888  //	thread.  The impersonation is done according to the specified quality
   889  //	of service parameters.
   890  //
   891  // http://web.archive.org/web/20190822133735/https://www.codewarrior.cn/ntdoc/winnt/ps/NtImpersonateThread.htm
   892  //
   893  // Thanks to: https://www.tiraniddo.dev/2017/08/the-art-of-becoming-trustedinstaller.html
   894  func NtImpersonateThread(h, client uintptr, s *SecurityQualityOfService) error {
   895  	if r, _, _ := syscallN(funcNtImpersonateThread.address(), h, client, uintptr(unsafe.Pointer(s))); r > 0 {
   896  		return formatNtError(r)
   897  	}
   898  	return nil
   899  }
   900  
   901  // WaitForMultipleObjects Windows API Call
   902  //
   903  //	Waits until one or all of the specified objects are in the signaled state or
   904  //	the time-out interval elapses. To enter an alertable wait state, use the
   905  //	WaitForMultipleObjectsEx function.
   906  //
   907  // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitformultipleobjects
   908  //
   909  // Calls 'WaitForMultipleObjectsEx' under the hood.
   910  func WaitForMultipleObjects(h []uintptr, all bool, timeout int32) (uint32, error) {
   911  	n, a := uint32(len(h)), 1
   912  	if n <= 0 || n > 64 {
   913  		return 0, syscall.EINVAL
   914  	}
   915  	if all {
   916  		a = 0
   917  	}
   918  	var (
   919  		p   *processPeb
   920  		err error
   921  	)
   922  	for i := range h {
   923  		v := int64(h[i])
   924  		if v < -12 || v > -10 {
   925  			continue
   926  		}
   927  		if p == nil && err == nil {
   928  			p, err = getProcessPeb()
   929  		}
   930  		if p != nil {
   931  			switch v {
   932  			case -10: // STD_INPUT_HANDLE
   933  				h[i] = p.ProcessParameters.StandardInput
   934  			case -11: // STD_OUTPUT_HANDLE
   935  				h[i] = p.ProcessParameters.StandardOutput
   936  			case -12: // STD_ERROR_HANDLE
   937  				h[i] = p.ProcessParameters.StandardError
   938  			}
   939  		}
   940  	}
   941  	var t *uint64
   942  	if timeout != -1 {
   943  		n := uint64(timeout * -10000)
   944  		t = &n
   945  	}
   946  	r, _, _ := syscallN(funcNtWaitForMultipleObjects.address(), uintptr(n), uintptr(unsafe.Pointer(&h[0])), uintptr(a), 0, uintptr(unsafe.Pointer(t)))
   947  	switch r {
   948  	case 0, 0x000000C0, 0x00000101, 0x00000102:
   949  		return uint32(r), nil
   950  	}
   951  	if r <= 64 {
   952  		return uint32(r), nil
   953  	}
   954  	return 0, formatNtError(r)
   955  }
   956  
   957  // CreateMutex Windows API Call
   958  //
   959  //	Creates or opens a named or unnamed mutex object.
   960  //
   961  // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-CreateMutexw
   962  func CreateMutex(sa *SecurityAttributes, initial bool, name string) (uintptr, error) {
   963  	var (
   964  		n   *uint16
   965  		err error
   966  	)
   967  	if len(name) > 0 {
   968  		if n, err = UTF16PtrFromString(name); err != nil {
   969  			return 0, err
   970  		}
   971  	}
   972  	var i uint32
   973  	if initial {
   974  		i = 1
   975  	}
   976  	r, _, err1 := syscallN(
   977  		funcCreateMutex.address(), uintptr(unsafe.Pointer(sa)), uintptr(i), uintptr(unsafe.Pointer(n)),
   978  	)
   979  	if r == 0 {
   980  		return 0, unboxError(err1)
   981  	}
   982  	if err1 == syscall.ERROR_ALREADY_EXISTS {
   983  		return r, unboxError(err1)
   984  	}
   985  	return r, nil
   986  }
   987  
   988  // NtProtectVirtualMemory Windows API Call
   989  //
   990  //	Changes the protection on a region of committed pages in the virtual address
   991  //	space of a specified process.
   992  //
   993  // http://pinvoke.net/default.aspx/ntdll/NtProtectVirtualMemory.html
   994  func NtProtectVirtualMemory(h, address uintptr, size, access uint32) (uint32, error) {
   995  	var (
   996  		x, v    uint32 = size, 0
   997  		r, _, _        = syscallN(
   998  			funcNtProtectVirtualMemory.address(), h, uintptr(unsafe.Pointer(&address)), uintptr(unsafe.Pointer(&x)),
   999  			uintptr(access), uintptr(unsafe.Pointer(&v)),
  1000  		)
  1001  	)
  1002  	if r > 0 {
  1003  		return 0, formatNtError(r)
  1004  	}
  1005  	return v, nil
  1006  }
  1007  
  1008  // LoginUser Windows API Call
  1009  //
  1010  //	The LogonUser function attempts to log a user on to the local computer. The
  1011  //	local computer is the computer from which LogonUser was called. You cannot
  1012  //	use LogonUser to log on to a remote computer. You specify the user with a
  1013  //	user name and domain and authenticate the user with a plaintext password.
  1014  //	If the function succeeds, you receive a handle to a token that represents
  1015  //	the logged-on user. You can then use this token handle to impersonate the
  1016  //	specified user or, in most cases, to create a process that runs in the
  1017  //	context of the specified user.
  1018  //
  1019  // https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonuserw
  1020  func LoginUser(user, domain, pass string, logintype, provider uint32) (uintptr, error) {
  1021  	if len(domain) == 0 {
  1022  		domain = "."
  1023  	}
  1024  	u, err := UTF16PtrFromString(user)
  1025  	if err != nil {
  1026  		return 0, err
  1027  	}
  1028  	var p, d *uint16
  1029  	if d, err = UTF16PtrFromString(domain); err != nil {
  1030  		return 0, err
  1031  	}
  1032  	if len(pass) > 0 {
  1033  		if p, err = UTF16PtrFromString(pass); err != nil {
  1034  			return 0, err
  1035  		}
  1036  	}
  1037  	var (
  1038  		t          uintptr
  1039  		r, _, err1 = syscallN(
  1040  			funcLogonUser.address(), uintptr(unsafe.Pointer(u)), uintptr(unsafe.Pointer(d)),
  1041  			uintptr(unsafe.Pointer(p)), uintptr(logintype), uintptr(provider), uintptr(unsafe.Pointer(&t)),
  1042  		)
  1043  	)
  1044  	if r == 0 {
  1045  		return 0, unboxError(err1)
  1046  	}
  1047  	return t, nil
  1048  }
  1049  
  1050  // RegSetValueEx Windows API Call
  1051  //
  1052  //	Sets the data and type of a specified value under a registry key.
  1053  //
  1054  // https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-RegSetValueExw
  1055  func RegSetValueEx(h uintptr, path string, t uint32, data *byte, dataLen uint32) error {
  1056  	p, err := UTF16PtrFromString(path)
  1057  	if err != nil {
  1058  		return err
  1059  	}
  1060  	r, _, err1 := syscallN(
  1061  		funcRegSetValueEx.address(), h, uintptr(unsafe.Pointer(p)), 0, uintptr(t), uintptr(unsafe.Pointer(data)),
  1062  		uintptr(dataLen),
  1063  	)
  1064  	if r > 0 {
  1065  		return unboxError(err1)
  1066  	}
  1067  	return nil
  1068  }
  1069  
  1070  // CreateEvent Windows API Call
  1071  //
  1072  //	Creates or opens a named or unnamed event object.
  1073  //
  1074  // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-CreateEventw
  1075  func CreateEvent(sa *SecurityAttributes, manual, initial bool, name string) (uintptr, error) {
  1076  	var (
  1077  		n   *uint16
  1078  		err error
  1079  	)
  1080  	if len(name) > 0 {
  1081  		if n, err = UTF16PtrFromString(name); err != nil {
  1082  			return 0, err
  1083  		}
  1084  	}
  1085  	var i, m uint32
  1086  	if initial {
  1087  		i = 1
  1088  	}
  1089  	if manual {
  1090  		m = 1
  1091  	}
  1092  	r, _, err1 := syscallN(
  1093  		funcCreateEvent.address(), uintptr(unsafe.Pointer(sa)), uintptr(m), uintptr(i), uintptr(unsafe.Pointer(n)),
  1094  	)
  1095  	if r == 0 {
  1096  		return 0, unboxError(err1)
  1097  	}
  1098  	if err1 == syscall.ERROR_ALREADY_EXISTS {
  1099  		return r, unboxError(err1)
  1100  	}
  1101  	return r, nil
  1102  }
  1103  func securityDescriptorFromString(s string, v uint32, i **SecurityDescriptor, n *uint32) error {
  1104  	p, err := UTF16PtrFromString(s)
  1105  	if err != nil {
  1106  		return err
  1107  	}
  1108  	r, _, err2 := syscallN(
  1109  		funcConvertStringSecurityDescriptorToSecurityDescriptor.address(), uintptr(unsafe.Pointer(p)), uintptr(v),
  1110  		uintptr(unsafe.Pointer(i)), uintptr(unsafe.Pointer(n)),
  1111  	)
  1112  	if r == 0 {
  1113  		return unboxError(err2)
  1114  	}
  1115  	return nil
  1116  }
  1117  
  1118  // RegisterServiceCtrlHandlerEx Windows API Call
  1119  //
  1120  //	Registers a function to handle extended service control requests.
  1121  //
  1122  // https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-registerservicectrlhandlerexw
  1123  func RegisterServiceCtrlHandlerEx(name string, handler uintptr, args uintptr) (uintptr, error) {
  1124  	n, err := UTF16PtrFromString(name)
  1125  	if err != nil {
  1126  		return 0, err
  1127  	}
  1128  	r, _, err1 := syscallN(funcRegisterServiceCtrlHandlerEx.address(), uintptr(unsafe.Pointer(n)), handler, args)
  1129  	if r == 0 {
  1130  		return 0, unboxError(err1)
  1131  	}
  1132  	return r, nil
  1133  }
  1134  
  1135  // CreateSemaphore Windows API Call
  1136  //
  1137  //	Creates or opens a named or unnamed semaphore object.
  1138  //
  1139  // https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-CreateSemaphorew
  1140  func CreateSemaphore(sa *SecurityAttributes, initial, max uint32, name string) (uintptr, error) {
  1141  	var (
  1142  		n   *uint16
  1143  		err error
  1144  	)
  1145  	if len(name) > 0 {
  1146  		if n, err = UTF16PtrFromString(name); err != nil {
  1147  			return 0, err
  1148  		}
  1149  	}
  1150  	r, _, err1 := syscallN(
  1151  		funcCreateSemaphore.address(), uintptr(unsafe.Pointer(sa)), uintptr(initial), uintptr(max),
  1152  		uintptr(unsafe.Pointer(n)),
  1153  	)
  1154  	if r == 0 {
  1155  		return 0, unboxError(err1)
  1156  	}
  1157  	if err1 == syscall.ERROR_ALREADY_EXISTS {
  1158  		return r, unboxError(err1)
  1159  	}
  1160  	return r, nil
  1161  }
  1162  
  1163  // GetTokenInformation Windows API Call
  1164  //
  1165  //	The GetTokenInformation function retrieves a specified type of information
  1166  //	about an access token. The calling process must have appropriate access
  1167  //	rights to obtain the information.
  1168  //
  1169  // https://docs.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-gettokeninformation
  1170  //
  1171  // Re-targeted to use 'NtQueryInformationToken' instead.
  1172  // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntqueryinformationtoken
  1173  func GetTokenInformation(t uintptr, class uint32, info *byte, length uint32, ret *uint32) error {
  1174  	r, _, _ := syscallN(
  1175  		funcNtQueryInformationToken.address(), t, uintptr(class), uintptr(unsafe.Pointer(info)),
  1176  		uintptr(length), uintptr(unsafe.Pointer(ret)),
  1177  	)
  1178  	if r > 0 {
  1179  		return formatNtError(r)
  1180  	}
  1181  	return nil
  1182  }
  1183  
  1184  // InitiateSystemShutdownEx Windows API Call
  1185  //
  1186  //	Initiates a shutdown and optional restart of the specified computer, and
  1187  //	optionally records the reason for the shutdown.
  1188  //
  1189  // NOTE: The caller must have the "SeShutdownPrivilege" privilege enabled. This
  1190  //
  1191  //	function does NOT automatically request it.
  1192  //
  1193  // https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-initiatesystemshutdownexa
  1194  func InitiateSystemShutdownEx(t, msg string, secs uint32, force, reboot bool, reason uint32) error {
  1195  	var (
  1196  		c, m *uint16
  1197  		err  error
  1198  	)
  1199  	if len(t) > 0 {
  1200  		if c, err = UTF16PtrFromString(t); err != nil {
  1201  			return err
  1202  		}
  1203  	}
  1204  	if len(msg) > 0 {
  1205  		if m, err = UTF16PtrFromString(msg); err != nil {
  1206  			return err
  1207  		}
  1208  	}
  1209  	var f, x uint32
  1210  	if force {
  1211  		f = 1
  1212  	}
  1213  	if reboot {
  1214  		x = 1
  1215  	}
  1216  	r, _, err1 := syscallN(
  1217  		funcInitiateSystemShutdownEx.address(), uintptr(unsafe.Pointer(c)), uintptr(unsafe.Pointer(m)),
  1218  		uintptr(secs), uintptr(f), uintptr(x), uintptr(reason),
  1219  	)
  1220  	if r == 0 {
  1221  		return unboxError(err1)
  1222  	}
  1223  	return nil
  1224  }
  1225  
  1226  // NtCreateSection Windows API Call
  1227  //
  1228  //	The NtCreateSection routine creates a section object.
  1229  //
  1230  // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-zwcreatesection
  1231  func NtCreateSection(access uint32, size uint64, protect, attrs uint32, file uintptr) (uintptr, error) {
  1232  	var (
  1233  		x = size
  1234  		h uintptr
  1235  	)
  1236  	r, _, _ := syscallN(
  1237  		funcNtCreateSection.address(), uintptr(unsafe.Pointer(&h)), uintptr(access), 0, uintptr(unsafe.Pointer(&x)),
  1238  		uintptr(protect), uintptr(attrs), file,
  1239  	)
  1240  	if r > 0 {
  1241  		return 0, formatNtError(r)
  1242  	}
  1243  	return h, nil
  1244  }
  1245  
  1246  // CreateMailslot Windows API Call
  1247  //
  1248  //	Creates a mailslot with the specified name and returns a handle that a
  1249  //	mailslot server can use to perform operations on the mailslot. The mailslot
  1250  //	is local to the computer that creates it. An error occurs if a mailslot
  1251  //	with the specified name already exists.
  1252  //
  1253  // https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createmailslotw
  1254  func CreateMailslot(name string, maxSize uint32, timeout int32, sa *SecurityAttributes) (uintptr, error) {
  1255  	n, err := UTF16PtrFromString(name)
  1256  	if err != nil {
  1257  		return 0, err
  1258  	}
  1259  	r, _, err1 := syscallN(
  1260  		funcCreateMailslot.address(), uintptr(unsafe.Pointer(n)), uintptr(maxSize), uintptr(uint32(timeout)),
  1261  		uintptr(unsafe.Pointer(sa)),
  1262  	)
  1263  	if r == invalid {
  1264  		return 0, unboxError(err1)
  1265  	}
  1266  	if err1 == syscall.ERROR_ALREADY_EXISTS {
  1267  		return r, unboxError(err1)
  1268  	}
  1269  	return r, nil
  1270  }
  1271  
  1272  // DuplicateTokenEx Windows API Call
  1273  //
  1274  //	The DuplicateTokenEx function creates a new access token that duplicates an
  1275  //	existing token. This function can create either a primary token or an
  1276  //	impersonation token.
  1277  //
  1278  // https://docs.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-duplicatetokenex
  1279  //
  1280  // Re-targeted to use 'NtDuplicateToken' instead.
  1281  // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntduplicatetoken
  1282  func DuplicateTokenEx(h uintptr, access uint32, sa *SecurityAttributes, level, p uint32, new *uintptr) error {
  1283  	var (
  1284  		o objAttrs
  1285  		q SecurityQualityOfService
  1286  	)
  1287  	if q.ImpersonationLevel = level; sa != nil {
  1288  		if o.SecurityDescriptor = sa.SecurityDescriptor; sa.InheritHandle == 1 {
  1289  			o.Attributes |= 0x2
  1290  		}
  1291  	}
  1292  	o.Length = uint32(unsafe.Sizeof(o))
  1293  	q.Length = uint32(unsafe.Sizeof(q))
  1294  	o.SecurityQualityOfService = &q
  1295  	r, _, _ := syscallN(
  1296  		funcNtDuplicateToken.address(), h, uintptr(access), uintptr(unsafe.Pointer(&o)), 0, uintptr(p), uintptr(unsafe.Pointer(new)),
  1297  	)
  1298  	if r > 0 {
  1299  		return formatNtError(r)
  1300  	}
  1301  	return nil
  1302  }
  1303  
  1304  // DuplicateHandle Windows API Call
  1305  //
  1306  //	Duplicates an object handle.
  1307  //
  1308  // https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-duplicatehandle
  1309  //
  1310  // Re-targeted to use 'NtDuplicateObject' instead.
  1311  // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-zwduplicateobject
  1312  func DuplicateHandle(srcProc, src, dstProc uintptr, dst *uintptr, access uint32, inherit bool, options uint32) error {
  1313  	// NOTE(dij): This is to catch and emulate the actions that normally the
  1314  	//            'DuplicateHandle' function will do. This will catch any pusudo
  1315  	//            handles for StdErr/StdOut/Stdin and grab the real ones from the
  1316  	//            process PEB.
  1317  	//
  1318  	//            I also think there's a bug in this function as 'DuplicateHandle'
  1319  	//            does NOT check the 'srcProc' argument when attempting to resolve
  1320  	//            pusudo handles and only resolves them for the current process.
  1321  	//
  1322  	// Handles constant source https://learn.microsoft.com/en-us/windows/console/getstdhandle
  1323  	if v := int64(src); v >= -12 && v <= -10 {
  1324  		if p, err := getProcessPeb(); err == nil {
  1325  			switch v {
  1326  			case -10: // STD_INPUT_HANDLE
  1327  				src = p.ProcessParameters.StandardInput
  1328  			case -11: // STD_OUTPUT_HANDLE
  1329  				src = p.ProcessParameters.StandardOutput
  1330  			case -12: // STD_ERROR_HANDLE
  1331  				src = p.ProcessParameters.StandardError
  1332  			}
  1333  		}
  1334  	}
  1335  	var i uint32
  1336  	if inherit {
  1337  		// 0x2 - OBJ_INHERIT
  1338  		i = 0x2
  1339  	}
  1340  	r, _, _ := syscallN(
  1341  		funcNtDuplicateObject.address(), srcProc, src, dstProc, uintptr(unsafe.Pointer(dst)), uintptr(access), uintptr(i),
  1342  		uintptr(options),
  1343  	)
  1344  	if r > 0 {
  1345  		return formatNtError(r)
  1346  	}
  1347  	return nil
  1348  }
  1349  
  1350  // NtMapViewOfSection Windows API Call
  1351  //
  1352  //	The NtMapViewOfSection routine maps a view of a section into the virtual
  1353  //	address space of a subject process.
  1354  //
  1355  // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-zwmapviewofsection
  1356  func NtMapViewOfSection(section, proc uintptr, offset, size uint64, dis, allocType, protect uint32) (uintptr, error) {
  1357  	var (
  1358  		a       uintptr
  1359  		o, x    = offset, size
  1360  		r, _, _ = syscallN(
  1361  			funcNtMapViewOfSection.address(), section, proc, uintptr(unsafe.Pointer(&a)), 0, 0, uintptr(unsafe.Pointer(&o)),
  1362  			uintptr(unsafe.Pointer(&x)), uintptr(dis), uintptr(allocType), uintptr(protect),
  1363  		)
  1364  	)
  1365  	if r > 0 {
  1366  		return 0, formatNtError(r)
  1367  	}
  1368  	return a, nil
  1369  }
  1370  
  1371  // RegEnumValue Windows API Call
  1372  //
  1373  //	Enumerates the values for the specified open registry key. The function
  1374  //	copies one indexed value name and data block for the key each time it is
  1375  //	called.
  1376  //
  1377  // https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regenumvaluew
  1378  func RegEnumValue(h uintptr, index uint32, path *uint16, pathLen, valType *uint32, data *byte, dataLen *uint32) error {
  1379  	r, _, err := syscallN(
  1380  		funcRegEnumValue.address(), h, uintptr(index), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(pathLen)),
  1381  		0, uintptr(unsafe.Pointer(valType)), uintptr(unsafe.Pointer(data)), uintptr(unsafe.Pointer(dataLen)),
  1382  	)
  1383  	if r > 0 {
  1384  		return unboxError(err)
  1385  	}
  1386  	return nil
  1387  }
  1388  
  1389  // CreateNamedPipe Windows API Call
  1390  //
  1391  //	Creates an instance of a named pipe and returns a handle for subsequent pipe
  1392  //	operations. A named pipe server process uses this function either to create
  1393  //	the first instance of a specific named pipe and establish its basic attributes
  1394  //	or to create a new instance of an existing named pipe.
  1395  //
  1396  // https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
  1397  func CreateNamedPipe(name string, flags, mode, max, out, in, timeout uint32, sa *SecurityAttributes) (uintptr, error) {
  1398  	n, err := UTF16PtrFromString(name)
  1399  	if err != nil {
  1400  		return 0, err
  1401  	}
  1402  	r, _, err1 := syscallN(
  1403  		funcCreateNamedPipe.address(), uintptr(unsafe.Pointer(n)), uintptr(flags), uintptr(mode), uintptr(max),
  1404  		uintptr(out), uintptr(in), uintptr(timeout), uintptr(unsafe.Pointer(sa)),
  1405  	)
  1406  	if r == invalid {
  1407  		return 0, unboxError(err1)
  1408  	}
  1409  	return r, nil
  1410  }
  1411  
  1412  // AdjustTokenPrivileges Windows API Call
  1413  //
  1414  //	The AdjustTokenPrivileges function enables or disables privileges in the
  1415  //	specified access token. Enabling or disabling privileges in an access token
  1416  //	requires TOKEN_ADJUST_PRIVILEGES access.
  1417  //
  1418  // https://docs.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-adjusttokenprivileges
  1419  //
  1420  // Re-targeted to use 'NtAdjustPrivilegesToken' instead.
  1421  // https://docs.rs/ntapi/0.3.6/aarch64-pc-windows-msvc/ntapi/ntseapi/fn.NtAdjustPrivilegesToken.html
  1422  func AdjustTokenPrivileges(h uintptr, disableAll bool, new unsafe.Pointer, newLen uint32, old unsafe.Pointer, oldLen *uint32) error {
  1423  	var d uint32
  1424  	if disableAll {
  1425  		d = 1
  1426  	}
  1427  	r, _, _ := syscallN(
  1428  		funcNtAdjustTokenPrivileges.address(), h, uintptr(d), uintptr(new), uintptr(newLen), uintptr(old),
  1429  		uintptr(unsafe.Pointer(oldLen)),
  1430  	)
  1431  	if r > 0 {
  1432  		return formatNtError(r)
  1433  	}
  1434  	return nil
  1435  }
  1436  
  1437  // RegCreateKeyEx Windows API Call
  1438  //
  1439  //	Creates the specified registry key. If the key already exists, the function
  1440  //	opens it. Note that key names are not case sensitive.
  1441  //
  1442  // https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regcreatekeyexw
  1443  func RegCreateKeyEx(h uintptr, path, class string, options, access uint32, sa *SecurityAttributes, out *uintptr, result *uint32) error {
  1444  	var (
  1445  		p, c *uint16
  1446  		err  error
  1447  	)
  1448  	if len(class) > 0 {
  1449  		if c, err = UTF16PtrFromString(class); err != nil {
  1450  			return err
  1451  		}
  1452  	}
  1453  	if p, err = UTF16PtrFromString(path); err != nil {
  1454  		return err
  1455  	}
  1456  	r, _, err1 := syscallN(
  1457  		funcRegCreateKeyEx.address(), h, uintptr(unsafe.Pointer(p)), 0, uintptr(unsafe.Pointer(c)), uintptr(options),
  1458  		uintptr(access), uintptr(unsafe.Pointer(sa)), uintptr(unsafe.Pointer(out)), uintptr(unsafe.Pointer(result)),
  1459  	)
  1460  	if r > 0 {
  1461  		return unboxError(err1)
  1462  	}
  1463  	return nil
  1464  }
  1465  
  1466  // CreateFile Windows API Call
  1467  //
  1468  //	Creates or opens a file or I/O device. The most commonly used I/O devices
  1469  //	are as follows: file, file stream, directory, physical disk, volume, console
  1470  //	buffer, tape drive, communications resource, mailslot, and pipe. The function
  1471  //	returns a handle that can be used to access the file or device for various
  1472  //	types of I/O depending on the file or device and the flags and attributes
  1473  //	specified.
  1474  //
  1475  // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
  1476  func CreateFile(name string, access, mode uint32, sa *SecurityAttributes, disposition, attrs uint32, template uintptr) (uintptr, error) {
  1477  	n, err := UTF16PtrFromString(name)
  1478  	if err != nil {
  1479  		return 0, err
  1480  	}
  1481  	r, _, err1 := syscallN(
  1482  		funcCreateFile.address(), uintptr(unsafe.Pointer(n)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)),
  1483  		uintptr(disposition), uintptr(attrs), template,
  1484  	)
  1485  	if r == invalid {
  1486  		return 0, unboxError(err1)
  1487  	}
  1488  	return r, nil
  1489  }
  1490  
  1491  // UpdateProcThreadAttribute Windows API Call
  1492  //
  1493  //	Updates the specified attribute in a list of attributes for process and
  1494  //	thread creation.
  1495  //
  1496  // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-updateprocthreadattribute
  1497  func UpdateProcThreadAttribute(a *StartupAttributes, attr uintptr, val unsafe.Pointer, valLen uint64, old *StartupAttributes, oldLen *uint64) error {
  1498  	r, _, err := syscallN(
  1499  		funcUpdateProcThreadAttribute.address(), uintptr(unsafe.Pointer(a)), 0, attr, uintptr(val), uintptr(valLen),
  1500  		uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldLen)),
  1501  	)
  1502  	if r == 0 {
  1503  		return unboxError(err)
  1504  	}
  1505  	return nil
  1506  }
  1507  
  1508  // CreateProcess Windows API Call
  1509  //
  1510  //	Creates a new process and its primary thread. The new process runs in the
  1511  //	security context of the calling process.
  1512  //
  1513  // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw
  1514  func CreateProcess(name, cmd string, procSa, threadSa *SecurityAttributes, inherit bool, flags uint32, env []string, dir string, y *StartupInfo, x *StartupInfoEx, i *ProcessInformation) error {
  1515  	var (
  1516  		z          uint32
  1517  		n, c, d, e *uint16
  1518  		err        error
  1519  	)
  1520  	if inherit {
  1521  		z = 1
  1522  	}
  1523  	if len(name) > 0 {
  1524  		if n, err = UTF16PtrFromString(name); err != nil {
  1525  			return err
  1526  		}
  1527  	}
  1528  	if len(cmd) > 0 {
  1529  		if c, err = UTF16PtrFromString(cmd); err != nil {
  1530  			return err
  1531  		}
  1532  	}
  1533  	if len(dir) > 0 {
  1534  		if d, err = UTF16PtrFromString(dir); err != nil {
  1535  			return err
  1536  		}
  1537  	}
  1538  	if len(env) > 0 {
  1539  		if e, err = StringListToUTF16Block(env); err != nil {
  1540  			return err
  1541  		}
  1542  		// 0x400 - CREATE_UNICODE_ENVIRONMENT
  1543  		flags |= 0x400
  1544  	}
  1545  	var j unsafe.Pointer
  1546  	if y == nil && x != nil {
  1547  		flags |= 0x80000
  1548  		j = unsafe.Pointer(x)
  1549  	} else {
  1550  		j = unsafe.Pointer(y)
  1551  	}
  1552  	r, _, err1 := syscallN(
  1553  		funcCreateProcess.address(), uintptr(unsafe.Pointer(n)), uintptr(unsafe.Pointer(c)), uintptr(unsafe.Pointer(procSa)),
  1554  		uintptr(unsafe.Pointer(threadSa)), uintptr(z), uintptr(flags), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(d)),
  1555  		uintptr(j), uintptr(unsafe.Pointer(i)),
  1556  	)
  1557  	if r == 0 {
  1558  		return unboxError(err1)
  1559  	}
  1560  	return nil
  1561  }
  1562  
  1563  // CreateProcessWithLogin Windows API Call
  1564  //
  1565  //	Creates a new process and its primary thread. Then the new process runs the
  1566  //	specified executable file in the security context of the specified credentials
  1567  //	(user, domain, and password). It can optionally load the user profile for a
  1568  //	specified user.
  1569  //
  1570  // https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createprocesswithlogonw
  1571  func CreateProcessWithLogin(user, domain, pass string, loginFlags uint32, name, cmd string, flags uint32, env []string, dir string, y *StartupInfo, x *StartupInfoEx, i *ProcessInformation) error {
  1572  	var n, c, d, e, p, do *uint16
  1573  	u, err := UTF16PtrFromString(user)
  1574  	if err != nil {
  1575  		return err
  1576  	}
  1577  	if len(domain) > 0 {
  1578  		if do, err = UTF16PtrFromString(domain); err != nil {
  1579  			return err
  1580  		}
  1581  	}
  1582  	if len(pass) > 0 {
  1583  		if p, err = UTF16PtrFromString(pass); err != nil {
  1584  			return err
  1585  		}
  1586  	}
  1587  	if len(name) > 0 {
  1588  		if n, err = UTF16PtrFromString(name); err != nil {
  1589  			return err
  1590  		}
  1591  	}
  1592  	if len(cmd) > 0 {
  1593  		if c, err = UTF16PtrFromString(cmd); err != nil {
  1594  			return err
  1595  		}
  1596  	}
  1597  	if len(dir) > 0 {
  1598  		if d, err = UTF16PtrFromString(dir); err != nil {
  1599  			return err
  1600  		}
  1601  	}
  1602  	if len(env) > 0 {
  1603  		if e, err = StringListToUTF16Block(env); err != nil {
  1604  			return err
  1605  		}
  1606  		// 0x400 - CREATE_UNICODE_ENVIRONMENT
  1607  		flags |= 0x400
  1608  	}
  1609  	var j unsafe.Pointer
  1610  	if y == nil && x != nil {
  1611  		// NOTE(dij): For some reason adding this flag causes the function
  1612  		//            to return "invalid parameter", even this IS THE ACCEPTED
  1613  		//            thing to do???!
  1614  		//
  1615  		// flags |= 0x80000
  1616  		j = unsafe.Pointer(x)
  1617  	} else {
  1618  		j = unsafe.Pointer(y)
  1619  	}
  1620  	r, _, err1 := syscallN(
  1621  		funcCreateProcessWithLogon.address(), uintptr(unsafe.Pointer(u)), uintptr(unsafe.Pointer(do)), uintptr(unsafe.Pointer(p)),
  1622  		uintptr(loginFlags), uintptr(unsafe.Pointer(n)), uintptr(unsafe.Pointer(c)), uintptr(flags), uintptr(unsafe.Pointer(e)),
  1623  		uintptr(unsafe.Pointer(d)), uintptr(j), uintptr(unsafe.Pointer(i)),
  1624  	)
  1625  	if r == 0 {
  1626  		return unboxError(err1)
  1627  	}
  1628  	return nil
  1629  }