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 }