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

     1  //go:build windows && go1.11
     2  // +build windows,go1.11
     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  	"strings"
    24  	"sync"
    25  	"unsafe"
    26  )
    27  
    28  const (
    29  	tokenPerms   = 0
    30  	fallbackLoad = false
    31  )
    32  
    33  var dllKernelOrAdvapi = dllKernelBase
    34  
    35  var compatOnce struct {
    36  	sync.Once
    37  	v bool
    38  }
    39  
    40  // IsWindows7 returns true if the underlying device runs at least Windows 7
    41  // (>=6.2) and built using <= go1.10.
    42  //
    43  // If built using >= go1.11, this function always returns true.
    44  func IsWindows7() bool {
    45  	return true
    46  }
    47  
    48  // EmptyWorkingSet Windows API Call wrapper
    49  //
    50  //	Removes as many pages as possible from the working set of the specified
    51  //	process.
    52  //
    53  // https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-emptyworkingset
    54  //
    55  // Wraps the 'SetProcessWorkingSetSizeEx' call instead to prevent having to track
    56  // the 'EmptyWorkingSet' function between kernel32.dll and psapi.dll.
    57  //
    58  // This function will fallback to 'SetProcessWorkingSetSize' if the underlying
    59  // device is Windows Xp and built using <= go1.10.
    60  func EmptyWorkingSet() {
    61  	syscallN(funcSetProcessWorkingSetSizeEx.address(), CurrentProcess, invalid, invalid, 0)
    62  }
    63  func checkCompatFunc() {
    64  	if m, _, _ := GetVersionNumbers(); m >= 10 {
    65  		compatOnce.v = true
    66  	}
    67  }
    68  
    69  // IsWindowsXp returns true if the underlying device is Windows Xp and NOT Server
    70  // 2003.
    71  //
    72  // If built using >= go1.11, this function always returns false.
    73  func IsWindowsXp() bool {
    74  	return false
    75  }
    76  
    77  // IsWindows10 returns true if the underlying device runs at least Windows 10
    78  // (>=10).
    79  func IsWindows10() bool {
    80  	compatOnce.Do(checkCompatFunc)
    81  	return compatOnce.v
    82  }
    83  
    84  // IsWindowsVista returns true if the underlying device runs at least Windows Vista
    85  // (>=6) and built using <= go1.10.
    86  //
    87  // If built using >= go1.11, this function always returns true.
    88  func IsWindowsVista() bool {
    89  	return true
    90  }
    91  
    92  // UserInAdminGroup returns true if the current thread or process token user is
    93  // part of the Administrators group. This is only used if the device is older than
    94  // Windows Vista and built using <= go1.10.
    95  //
    96  // If built using >= go1.11, this function always returns false.
    97  func UserInAdminGroup() bool {
    98  	return false
    99  }
   100  
   101  // IsTokenElevated returns true if this token has a High or System privileges.
   102  //
   103  // Always returns false on any systems older than Windows Vista.
   104  func IsTokenElevated(h uintptr) bool {
   105  	var (
   106  		e, n uint32
   107  		err  = GetTokenInformation(h, 0x14, (*byte)(unsafe.Pointer(&e)), 4, &n)
   108  		// 0x14 - TokenElevation
   109  	)
   110  	return err == nil && n == 4 && e != 0
   111  }
   112  
   113  // IsWow64Process Windows API Call
   114  //
   115  //	Determines whether the specified process is running under WOW64 or an
   116  //	Intel64 of x64 processor.
   117  //
   118  // https://docs.microsoft.com/en-us/windows/win32/api/wow64apiset/nf-wow64apiset-iswow64process
   119  func IsWow64Process(h uintptr) (bool, error) {
   120  	if funcRtlWow64GetProcessMachines.find() != nil {
   121  		// Running on "true" x86.
   122  		return false, nil
   123  	}
   124  	var p, n uint16
   125  	if r, _, _ := syscallN(funcRtlWow64GetProcessMachines.address(), h, uintptr(unsafe.Pointer(&p)), uintptr(unsafe.Pointer(&n))); r > 0 {
   126  		return false, formatNtError(r)
   127  	}
   128  	return p > 0, nil
   129  }
   130  
   131  // CancelIoEx Windows API Call
   132  //
   133  //	Marks any outstanding I/O operations for the specified file handle. The
   134  //	function only cancels I/O operations in the current process, regardless of
   135  //	which thread created the I/O operation.
   136  //
   137  // https://docs.microsoft.com/en-us/windows/win32/fileio/cancelioex-func
   138  //
   139  // Re-targeted to use 'NtCancelIoFileEx' instead.
   140  // https://learn.microsoft.com/en-us/windows/win32/devnotes/nt-cancel-io-file-ex
   141  //
   142  // NOTE(dij): ^ THIS IS WRONG! It forgets the IO_STATUS_BLOCK entry at the end.
   143  //
   144  //	NtCancelIoFileEx (HANDLE FileHandle, PIO_STATUS_BLOCK IoRequestToCancel, PIO_STATUS_BLOCK IoStatusBlock)
   145  //
   146  // This function will fallback to 'NtCancelIoFile' if the underlying device is
   147  // older than Windows 7 and built using <= go1.10.
   148  //
   149  // Normally, Windows Vista would work, but this has a weird issue that causes
   150  // it to wait forever.
   151  func CancelIoEx(h uintptr, o *Overlapped) error {
   152  	var s [4 + ptrSize]byte // IO_STATUS_BLOCK
   153  	if r, _, _ := syscallN(funcNtCancelIoFileEx.address(), h, uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(&s))); r > 0 {
   154  		return formatNtError(r)
   155  	}
   156  	return nil
   157  }
   158  func copyMemory(d uintptr, s uintptr, x uint32) {
   159  	syscallN(funcRtlCopyMappedMemory.address(), d, s, uintptr(x))
   160  }
   161  
   162  // RegDeleteTree Windows API Call
   163  //
   164  //	Deletes the subkeys and values of the specified key recursively.
   165  //
   166  // https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regdeletetreew
   167  //
   168  // This function returns 'syscall.EINVAL' if the underlying device is older than
   169  // Windows Vista and built using <= go1.10.
   170  func RegDeleteTree(h uintptr, path string) error {
   171  	p, err := UTF16PtrFromString(path)
   172  	if err != nil {
   173  		return err
   174  	}
   175  	r, _, err1 := syscallN(funcRegDeleteTree.address(), h, uintptr(unsafe.Pointer(p)))
   176  	if r > 0 {
   177  		return unboxError(err1)
   178  	}
   179  	return nil
   180  }
   181  
   182  // EnumDrivers attempts to reterive the list of currently loaded drivers
   183  // and will call the supplied function with the handle of each driver along with
   184  // the base name of the driver file.
   185  //
   186  // The user supplied function can return an error that if non-nil, will stop
   187  // Driver iteration immediately and will be returned by this function.
   188  //
   189  // Callers can return the special 'winapi.ErrNoMoreFiles' error that will stop
   190  // iteration but will cause this function to return nil. This can be used to
   191  // stop iteration without errors if needed.
   192  func EnumDrivers(f func(uintptr, string) error) error {
   193  	var (
   194  		n          uint32
   195  		r, _, err1 = syscallN(funcK32EnumDeviceDrivers.address(), 0, 0, uintptr(unsafe.Pointer(&n)))
   196  	)
   197  	if r == 0 {
   198  		return unboxError(err1)
   199  	}
   200  	e := make([]uintptr, (n/uint32(ptrSize))+32)
   201  	r, _, err1 = syscallN(funcK32EnumDeviceDrivers.address(), uintptr(unsafe.Pointer(&e[0])), uintptr(n+uint32(32*ptrSize)), uintptr(unsafe.Pointer(&n)))
   202  	if r == 0 {
   203  		return unboxError(err1)
   204  	}
   205  	var (
   206  		s   [260]uint16
   207  		err error
   208  		b   = UTF16ToString((*kernelSharedData)(unsafe.Pointer(kernelShared)).NtSystemRoot[:])
   209  	)
   210  	for i := range e {
   211  		if e[i] == 0 {
   212  			continue
   213  		}
   214  		if r, _, err1 = syscallN(funcK32GetDeviceDriverFileName.address(), e[i], uintptr(unsafe.Pointer(&s[0])), 260); r == 0 {
   215  			return unboxError(err1)
   216  		}
   217  		v := strings.Replace(UTF16ToString(s[:r]), sysRoot, b, 1)
   218  		if len(v) > 5 && v[0] == '\\' && v[1] == '?' && v[3] == '\\' {
   219  			v = v[4:]
   220  		}
   221  		if err = f(e[i], v); err != nil {
   222  			break
   223  		}
   224  	}
   225  	if err != nil && err == ErrNoMoreFiles {
   226  		return err
   227  	}
   228  	return nil
   229  }
   230  func getCurrentModuleInfo(h uintptr, i *modInfo) error {
   231  	if r, _, err := syscallN(funcK32GetModuleInformation.address(), CurrentProcess, h, uintptr(unsafe.Pointer(i)), ptrSize*3); r == 0 {
   232  		return err
   233  	}
   234  	return nil
   235  }
   236  
   237  // RegDeleteKeyEx Windows API Call
   238  //
   239  //	Deletes a subkey and its values. Note that key names are not case sensitive.
   240  //	ONLY DELETES EMPTY SUBKEYS. (invalid argument if non-empty)
   241  //
   242  // https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regdeletekeyexw
   243  //
   244  // This function will fallback to 'RegDeleteKey' if the underlying device is
   245  // older than Windows Vista and built using <= go1.10.
   246  func RegDeleteKeyEx(h uintptr, path string, f uint32) error {
   247  	p, err := UTF16PtrFromString(path)
   248  	if err != nil {
   249  		return err
   250  	}
   251  	r, _, err1 := syscallN(funcRegDeleteKeyEx.address(), h, uintptr(unsafe.Pointer(p)), uintptr(f), 0)
   252  	if r > 0 {
   253  		return unboxError(err1)
   254  	}
   255  	return nil
   256  }
   257  
   258  // WinHTTPGetDefaultProxyConfiguration Windows API Call
   259  //
   260  //	The WinHttpGetDefaultProxyConfiguration function retrieves the default WinHTTP
   261  //	proxy configuration from the registry.
   262  //
   263  // https://docs.microsoft.com/en-us/windows/win32/api/winhttp/nf-winhttp-winhttpgetdefaultproxyconfiguration
   264  //
   265  // This function returns 'syscall.EINVAL' if the underlying device is Windows Xp
   266  // and built using <= go1.10.
   267  func WinHTTPGetDefaultProxyConfiguration(i *ProxyInfo) error {
   268  	r, _, err := syscallN(funcWinHTTPGetDefaultProxyConfiguration.address(), uintptr(unsafe.Pointer(&i)))
   269  	if r == 0 {
   270  		return unboxError(err)
   271  	}
   272  	return nil
   273  }
   274  
   275  // NtCreateThreadEx Windows API Call
   276  //
   277  //	Creates a thread that runs in the virtual address space of another process
   278  //	and optionally specifies extended attributes such as processor group affinity.
   279  //
   280  // http://pinvoke.net/default.aspx/ntdll/NtCreateThreadEx.html
   281  //
   282  // This function will fallback to 'CreateRemoteThread' if the underlying device
   283  // is older than Windows Vista and built using <= go1.10.
   284  func NtCreateThreadEx(h, address, args uintptr, suspended bool) (uintptr, error) {
   285  	// TODO(dij): Add additional injection types?
   286  	//            - NtQueueApcThread
   287  	//            - Kernel Table Callback
   288  	f := uint32(0x4)
   289  	// 0x4 - THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER
   290  	if suspended {
   291  		// 0x1 - CREATE_SUSPENDED
   292  		f |= 0x1
   293  	}
   294  	var (
   295  		t       uintptr
   296  		r, _, _ = syscallN(
   297  			funcNtCreateThreadEx.address(), uintptr(unsafe.Pointer(&t)), 0x10000000, 0, h, address, args, uintptr(f),
   298  			0, 0, 0, 0,
   299  		)
   300  		// 0x10000000 - THREAD_ALL_ACCESS
   301  	)
   302  	if r > 0 {
   303  		return 0, formatNtError(r)
   304  	}
   305  	return t, nil
   306  }
   307  
   308  // CreateProcessWithToken Windows API Call
   309  //
   310  //	Creates a new process and its primary thread. The new process runs in the
   311  //	security context of the specified token. It can optionally load the user
   312  //	profile for the specified user.
   313  //
   314  // https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createprocesswithtokenw
   315  //
   316  // This function returns 'syscall.EINVAL' if the underlying device is Windows Xp
   317  // and built using <= go1.10.
   318  func CreateProcessWithToken(t uintptr, loginFlags uint32, name, cmd string, flags uint32, env []string, dir string, y *StartupInfo, x *StartupInfoEx, i *ProcessInformation) error {
   319  	var (
   320  		n, c, d, e *uint16
   321  		err        error
   322  	)
   323  	if len(name) > 0 {
   324  		if n, err = UTF16PtrFromString(name); err != nil {
   325  			return err
   326  		}
   327  	}
   328  	if len(cmd) > 0 {
   329  		if c, err = UTF16PtrFromString(cmd); err != nil {
   330  			return err
   331  		}
   332  	}
   333  	if len(dir) > 0 {
   334  		if d, err = UTF16PtrFromString(dir); err != nil {
   335  			return err
   336  		}
   337  	}
   338  	if len(env) > 0 {
   339  		if e, err = StringListToUTF16Block(env); err != nil {
   340  			return err
   341  		}
   342  		// 0x400 - CREATE_UNICODE_ENVIRONMENT
   343  		flags |= 0x400
   344  	}
   345  	var j unsafe.Pointer
   346  	if y == nil && x != nil {
   347  		// BUG(dij): For some reason adding this flag causes the function
   348  		//           to return "invalid parameter", even this this IS THE ACCEPTED
   349  		//           thing to do???!
   350  		//
   351  		// flags |= 0x80000
   352  		j = unsafe.Pointer(x)
   353  	} else {
   354  		j = unsafe.Pointer(y)
   355  	}
   356  	r, _, err1 := syscallN(
   357  		funcCreateProcessWithToken.address(), t, uintptr(loginFlags), uintptr(unsafe.Pointer(n)), uintptr(unsafe.Pointer(c)),
   358  		uintptr(flags), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(d)), uintptr(j), uintptr(unsafe.Pointer(i)),
   359  	)
   360  	if r == 0 {
   361  		return unboxError(err1)
   362  	}
   363  	return nil
   364  }