github.com/iDigitalFlame/xmt@v0.5.1/device/winapi/helpers.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  	"errors"
    24  	"io"
    25  	"runtime"
    26  	"runtime/debug"
    27  	"strings"
    28  	"sync"
    29  	"syscall"
    30  	"time"
    31  	"unsafe"
    32  
    33  	"github.com/iDigitalFlame/xmt/util/bugtrack"
    34  	"github.com/iDigitalFlame/xmt/util/xerr"
    35  )
    36  
    37  const (
    38  	ptrSize      = unsafe.Sizeof(uintptr(0))
    39  	kernelShared = uintptr(0x7FFE0000)
    40  )
    41  
    42  var caught struct{}
    43  
    44  //go:linkname allm runtime.allm
    45  var allm unsafe.Pointer
    46  
    47  // We have this to be used to prevent crashing the stack of the program
    48  // when we call minidump as we need to track extra parameters.
    49  // The lock will stay enabled until it's done, so it's "thread safe".
    50  var dumpStack dumpParam
    51  
    52  //go:linkname setConsoleCtrlHandler runtime._SetConsoleCtrlHandler
    53  var setConsoleCtrlHandler uintptr
    54  
    55  var dumpCallbackOnce struct {
    56  	_ [0]func()
    57  	sync.Once
    58  	f uintptr
    59  }
    60  
    61  type dumpCallback struct {
    62  	// DO NOT REORDER
    63  	Func uintptr
    64  	Args uintptr
    65  }
    66  type kernelSharedData struct {
    67  	// DO NOT REORDER
    68  	_          [20]byte
    69  	SystemTime struct {
    70  		LowPart  uint32
    71  		HighPart int32
    72  		_        int32
    73  	}
    74  	_                     [16]byte
    75  	NtSystemRoot          [260]uint16
    76  	MaxStackTraceDepth    uint32
    77  	_                     uint32
    78  	_                     [32]byte
    79  	NtBuildNumber         uint32
    80  	_                     [8]byte
    81  	NtMajorVersion        uint32
    82  	NtMinorVersion        uint32
    83  	ProcessorFeatures     [64]byte
    84  	_                     [20]byte
    85  	SystemExpirationDate  uint64
    86  	_                     [4]byte
    87  	KdDebuggerEnabled     uint8
    88  	MitigationPolicies    uint8
    89  	_                     [2]byte
    90  	ActiveConsoleID       uint32
    91  	_                     [12]byte
    92  	NumberOfPhysicalPages uint32
    93  	SafeBootMode          uint8
    94  	VirtualizationFlags   uint8
    95  	_                     [2]byte
    96  	SharedDataFlags       uint32
    97  }
    98  
    99  // KillRuntime attempts to walk through the process threads and will forcefully
   100  // kill all Golang based OS-Threads based on their starting address (which
   101  // should be the same when starting from CGo).
   102  //
   103  // This will attempt to determine the base thread and any children that may be
   104  // running and take action on what type of host we're in to best end the
   105  // runtime without crashing.
   106  //
   107  // This function can be used on binaries, shared libraries or Zombified processes.
   108  //
   109  // DO NOT EXPECT ANYTHING (INCLUDING DEFERS) TO HAPPEN AFTER THIS FUNCTION.
   110  func KillRuntime() {
   111  	runtime.GC()
   112  	debug.FreeOSMemory()
   113  	runtime.LockOSThread()
   114  	killRuntime()
   115  	// Below shouldn't run.
   116  	runtime.UnlockOSThread()
   117  }
   118  func killRuntime() {
   119  	//// Workflow for killRuntime()
   120  	//
   121  	// 1 - Find the module that's us (this thread)
   122  	// 2 - Find the base of the module we are in
   123  	// 3 - Enumerate the runtime's M to find all open threads (finally)
   124  	// 4 - Look through process threads to see if any other threads exist
   125  	// 5 - Collect threads that exist in the base address space
   126  	// > 6 - If we are in the base, it's a binary - syscall.Exit(0)
   127  	// 7 - Check suspend cout of each thread in base address to see if we're a Zombie
   128  	// > 8 - If only one thread in base address is suspended, we're a Zombie - syscall.Exit(0)
   129  	// 9 - Iterate through all our threads and terminate them
   130  	// > 0 - Terminate self thread
   131  	//
   132  	var q uintptr
   133  	// 0x2 - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
   134  	if r, _, err := syscallN(funcGetModuleHandleEx.address(), 0x2, 0, uintptr(unsafe.Pointer(&q))); r == 0 {
   135  		if bugtrack.Enabled {
   136  			bugtrack.Track("winapi.killRuntime(): GetModuleHandleEx failed err=%s", err.Error())
   137  		}
   138  		return
   139  	}
   140  	var k modInfo
   141  	if err := getCurrentModuleInfo(q, &k); err != nil {
   142  		if bugtrack.Enabled {
   143  			bugtrack.Track("winapi.killRuntime(): GetModuleInformation failed err=%s", err.Error())
   144  		}
   145  		return
   146  	}
   147  	a, e := k.Base, k.Base+uintptr(k.Size)
   148  	if bugtrack.Enabled {
   149  		bugtrack.Track("winapi.killRuntime(): Module range a=%d, e=%d", a, e)
   150  	}
   151  	runtime.GC()
   152  	debug.FreeOSMemory()
   153  	var (
   154  		x = make(map[uint32]struct{}, 8)
   155  		g = make([]uintptr, 0, 8)
   156  	)
   157  	for i := uintptr(allm); ; {
   158  		if h := *(*uintptr)(unsafe.Pointer(i + ptrThread)); h > 0 {
   159  			if z, err := getThreadID(h); err == nil {
   160  				if x[z] = caught; bugtrack.Enabled {
   161  					bugtrack.Track("winapi.killRuntime(): Found runtime thread ID z=%d, h=%d", z, h)
   162  				}
   163  			}
   164  			g = append(g, h)
   165  		}
   166  		n := (*uintptr)(unsafe.Pointer(i + ptrNext))
   167  		if n == nil || *n == 0 {
   168  			break // Reached bottom of linked list
   169  		}
   170  		i = *n
   171  	}
   172  	var (
   173  		y = getCurrentThreadID()
   174  		m = make([]uintptr, 0, len(x))
   175  		b bool
   176  		z uint8
   177  	)
   178  	err := EnumThreads(GetCurrentProcessID(), func(t ThreadEntry) error {
   179  		// 0x43 - THREAD_QUERY_INFORMATION  | THREAD_SUSPEND_RESUME | THREAD_TERMINATE
   180  		h, err1 := t.Handle(0x43)
   181  		if err1 != nil {
   182  			if bugtrack.Enabled {
   183  				bugtrack.Track("winapi.killRuntime(): Thread failed to have it's handle opened t.TID=%d, err1=%s!", t.TID, err1)
   184  			}
   185  			// NOTE(dij): Workaround on attribute weirdness where we can see our
   186  			//            threads, but we can't get a handle to them. If we are
   187  			//            using QSI, we can see if it's suspended and we can add
   188  			//            it to the Zombie flag as 9/10 it's a Zombie.
   189  			//
   190  			//            We also check to see if it's part of the runtime's threads
   191  			//            as it shouldn't. This makes false-positives less likely.
   192  			var (
   193  				q, _  = t.IsSuspended()
   194  				_, ok = x[t.TID]
   195  			)
   196  			if q && !ok {
   197  				if z++; bugtrack.Enabled {
   198  					bugtrack.Track("winapi.killRuntime(): Failed thread seems to be a Zombie thread t.TID=%d!", t.TID)
   199  				}
   200  			}
   201  			return nil // Continue on handle errors instead of bailing.
   202  		}
   203  		s, err1 := getThreadStartAddress(h)
   204  		if err1 != nil {
   205  			return err1
   206  		}
   207  		if t.TID == y { // Skip the current thread
   208  			if b = s >= a && s < e; b {
   209  				return ErrNoMoreFiles
   210  			}
   211  			if bugtrack.Enabled {
   212  				bugtrack.Track("winapi.killRuntime(): Found our thread t.TID=%d y=%d, s=%d, b=%t", t.TID, y, s, b)
   213  			}
   214  			return nil
   215  		}
   216  		k, err1 := t.suspended(h)
   217  		if err1 != nil {
   218  			return err1
   219  		}
   220  		if (s > a && s < e) && k && z < 0xFF { // Prevent overflow here
   221  			z++
   222  		}
   223  		if _, ok := x[t.TID]; !ok {
   224  			CloseHandle(h)
   225  			return nil
   226  		}
   227  		m = append(m, h)
   228  		return nil
   229  	})
   230  	if err != nil {
   231  		if bugtrack.Enabled {
   232  			bugtrack.Track("winapi.killRuntime(): EnumThreads failed err=%s", err.Error())
   233  		}
   234  		return
   235  	}
   236  	// Unmap all function mappings (if any)
   237  	if FuncUnmapAll(); b || len(m) == 0 {
   238  		for i := range m {
   239  			CloseHandle(m[i])
   240  		}
   241  		if g, m = nil, nil; b {
   242  			if bugtrack.Enabled {
   243  				bugtrack.Track("winapi.killRuntime(): We're in the base thread, we can exit normally.")
   244  			}
   245  			// Base thread (us), is in the base module address
   246  			// This is a binary, it's safe to exit cleanly.
   247  			syscall.Exit(0)
   248  			return
   249  		}
   250  		if bugtrack.Enabled {
   251  			bugtrack.Track("winapi.killRuntime(): Failed to find base threads!")
   252  		}
   253  		return
   254  	}
   255  	if z == 1 {
   256  		if bugtrack.Enabled {
   257  			bugtrack.Track("winapi.killRuntime(): Zombie check passed z=%d", z)
   258  		}
   259  		for i := range m {
   260  			CloseHandle(m[i])
   261  		}
   262  		if g, m = nil, nil; bugtrack.Enabled {
   263  			bugtrack.Track("winapi.killRuntime(): We're a Zombie, we can exit normally.")
   264  		}
   265  		// Out of all the base threads, only one exists and is suspended,
   266  		// 99% chance this is a Zombified process, it's ok to exit cleanly.
   267  		syscall.Exit(0)
   268  		return
   269  	}
   270  	if bugtrack.Enabled {
   271  		bugtrack.Track("winapi.killRuntime(): Zombie check failed z=%d", z)
   272  	}
   273  	freeChunkHeap()
   274  	// NOTE(dij): Potential footgun? Free all loaded libaries since we're leaving
   275  	//            but not /exiting/. FreeLibrary shouldn't cause an issue as it
   276  	//            /should/ only clean unused libraries after we are done.
   277  	//            ntdll.dll will NOT be unloaded.
   278  	freeLoadedLibaries()
   279  	// Stop all running Goroutines
   280  	stopTheWorld("exit")
   281  	// Disable the CTRL console handler that Go sets.
   282  	removeCtrlHandler()
   283  	// What's left is that we're probally injected into memory somewhere, and
   284  	// we just need to nuke the runtime without affecting the host.
   285  	for i := range g {
   286  		CloseHandle(g[i])
   287  	}
   288  	g = nil
   289  	for i := range m {
   290  		if err = TerminateThread(m[i], 0); err != nil {
   291  			break
   292  		}
   293  	}
   294  	// Close all timers and open handles
   295  	// Even if the world is stopped, we still run into this occasionally. So it's
   296  	// down here instead.
   297  	destoryAllM()
   298  	for i := range m {
   299  		CloseHandle(m[i])
   300  	}
   301  	if m = nil; err != nil {
   302  		if bugtrack.Enabled {
   303  			bugtrack.Track("winapi.killRuntime(): Terminate error err=%s", err.Error())
   304  		}
   305  		return
   306  	}
   307  	if bugtrack.Enabled {
   308  		bugtrack.Track("winapi.killRuntime(): Bye bye!")
   309  	}
   310  	EmptyWorkingSet()
   311  	freeRuntimeMemory() // Buck Stops here.
   312  }
   313  
   314  // Getppid returns the Parent Process ID of this Process by reading the PEB.
   315  // If this fails, this returns zero.
   316  func Getppid() uint32 {
   317  	var (
   318  		p       processBasicInfo
   319  		r, _, _ = syscallN(
   320  			funcNtQueryInformationProcess.address(), CurrentProcess, 0, uintptr(unsafe.Pointer(&p)),
   321  			unsafe.Sizeof(p), 0,
   322  		)
   323  	)
   324  	if r > 0 {
   325  		return 0
   326  	}
   327  	return uint32(p.InheritedFromUniqueProcessID)
   328  }
   329  func createDumpFunc() {
   330  	dumpCallbackOnce.f = syscall.NewCallback(dumpCallbackFunc)
   331  }
   332  
   333  // InSafeMode returns true if the current device was booted into Safe Mode, false
   334  // otherwise.
   335  func InSafeMode() bool {
   336  	return (*kernelSharedData)(unsafe.Pointer(kernelShared)).SafeBootMode > 0
   337  }
   338  
   339  // IsDebugged attempts to check multiple system calls in order to determine
   340  // REAL debugging status.
   341  //
   342  // NOTE: Systems that are "Debug" / "Checked" versions of Windows will always
   343  // return false!
   344  //
   345  // This function checks in this order:
   346  //
   347  //   - KSHARED.KdDebuggerEnabled
   348  //   - KSHARED.SharedDataFlags.DbgErrorPortPresent
   349  //   - NtQuerySystemInformation/SystemKernelDebuggerInformation
   350  //   - IsDebuggerPresent (from PEB)
   351  //   - NtGlobalFlag (from PEB)
   352  //   - OutputDebugStringA
   353  //   - CheckRemoteDebuggerPresent
   354  //
   355  // Errors make the function return false only if they are the last call.
   356  func IsDebugged() bool {
   357  	switch s := (*kernelSharedData)(unsafe.Pointer(kernelShared)); {
   358  	case s.KdDebuggerEnabled > 1:
   359  		return true
   360  	case s.SharedDataFlags&0x1 != 0: // 0x1 - DbgErrorPortPresent
   361  		// NOTE(dij): This returns true when on a Debug/Checked version on Windows.
   362  		//            Not sure if we want to ignore this or not, but I doubt that
   363  		//            actual systems are using "Multiprocessor Debug/Checked" unless
   364  		//            the system is a driver test or builder.
   365  		return true
   366  	}
   367  	var (
   368  		d uint16
   369  		x uint32
   370  	)
   371  	// 0x23 - SystemKernelDebuggerInformation
   372  	syscallN(funcNtQuerySystemInformation.address(), 0x23, uintptr(unsafe.Pointer(&d)), 2, uintptr(unsafe.Pointer(&x)))
   373  	// The SYSTEM_KERNEL_DEBUGGER_INFORMATION short offset 1 (last 8 bits) is not
   374  	// filled out by systems older than Vista, so we ignore them.
   375  	if x == 2 && ((d&0xFF) > 1 || ((d>>8) == 0 && IsWindowsVista())) {
   376  		return true
   377  	}
   378  	switch p, err := getProcessPeb(); {
   379  	case err != nil:
   380  	case p.BeingDebugged > 0:
   381  		return true
   382  	case p.NtGlobalFlag&(0x70) != 0: // 0x70 - FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_VALIDATE_PARAMETERS
   383  		return true
   384  	}
   385  	o := [2]byte{'_', 0}
   386  	// Take advantage of a "bug" in OutputDebugStringA where the "r2" return value
   387  	// will NOT be zero when a debugger is present to receive the debug string.
   388  	if _, r, _ := syscallN(funcOutputDebugString.address(), uintptr(unsafe.Pointer(&o[0]))); r > 0 {
   389  		return true
   390  	}
   391  	// 0x400 - PROCESS_QUERY_INFORMATION
   392  	h, err := OpenProcess(0x400, false, GetCurrentProcessID())
   393  	if err != nil {
   394  		return false
   395  	}
   396  	var v bool
   397  	err = CheckRemoteDebuggerPresent(h, &v)
   398  	CloseHandle(h)
   399  	return err == nil && v
   400  }
   401  
   402  //go:linkname stopTheWorld runtime.stopTheWorld
   403  func stopTheWorld(string)
   404  
   405  // IsSystemEval returns true if the KSHARED_USER_DATA.SystemExpirationDate value
   406  // is greater than zero.
   407  //
   408  // SystemExpirationDate is the time that remains in any evaluation copies of
   409  // Windows. This can be used to find systems that may be used for testing and
   410  // are not production machines.
   411  func IsSystemEval() bool {
   412  	return (*kernelSharedData)(unsafe.Pointer(kernelShared)).SystemExpirationDate > 0
   413  }
   414  
   415  // IsUACEnabled returns true if UAC (User Account Control) is enabled, false
   416  // otherwise.
   417  func IsUACEnabled() bool {
   418  	// 0x2 - DbgElevationEnabled
   419  	return (*kernelSharedData)(unsafe.Pointer(kernelShared)).SharedDataFlags&0x2 != 0
   420  }
   421  func freeLoadedLibaries() {
   422  	dllAmsi.free()
   423  	dllGdi32.free()
   424  	dllUser32.free()
   425  	dllWinhttp.free()
   426  	dllDbgHelp.free()
   427  	dllAdvapi32.free()
   428  	dllWtsapi32.free()
   429  	dllKernel32.free()
   430  	dllKernelBase.free()
   431  }
   432  
   433  // ErasePEHeader erases the first page of the mapped PE memory data. This is
   434  // recommended to ONLY use when using a shipped binary.
   435  //
   436  // Any errors found during zeroing will returned.
   437  //
   438  // Retrieved from: https://github.com/LordNoteworthy/al-khaser/blob/master/al-khaser/AntiDump/ErasePEHeaderFromMemory.cpp
   439  func ErasePEHeader() error {
   440  	var (
   441  		h          uintptr
   442  		r, _, err1 = syscallN(funcGetModuleHandleEx.address(), 0x2, 0, uintptr(unsafe.Pointer(&h)))
   443  		// 0x2 - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
   444  	)
   445  	if r == 0 {
   446  		return unboxError(err1)
   447  	}
   448  	var (
   449  		n      = uint32(syscall.Getpagesize())
   450  		o, err = NtProtectVirtualMemory(CurrentProcess, h, n, 0x40)
   451  		// 0x40 - PAGE_EXECUTE_READWRITE
   452  	)
   453  	if err != nil {
   454  		return err
   455  	}
   456  	for i := uint32(0); i < n; i++ {
   457  		(*(*[1]byte)(unsafe.Pointer(h + uintptr(i))))[0] = 0
   458  	}
   459  	_, err = NtProtectVirtualMemory(CurrentProcess, h, n, o)
   460  	return err
   461  }
   462  func (p *dumpParam) close() {
   463  	heapFree(p.b, p.h)
   464  	heapDestroy(p.b)
   465  	p.Unlock()
   466  }
   467  
   468  // Untrust will attempt to revoke all Token permissions and change the Token
   469  // integrity level to "Untrusted".
   470  //
   471  // This effectively revokes all permissions for the application with the supplied
   472  // PID to run.
   473  //
   474  // Ensure a call to 'GetDebugPrivilege' is made first before starting.
   475  //
   476  // Thanks for the find by @zha0gongz1 in their article:
   477  //
   478  //	https://golangexample.com/without-closing-windows-defender-to-make-defender-useless-by-removing-its-token-privileges-and-lowering-the-token-integrity/
   479  func Untrust(p uint32) error {
   480  	// 0x400 - PROCESS_QUERY_INFORMATION
   481  	h, err := OpenProcess(0x400, false, p)
   482  	if err != nil {
   483  		return err
   484  	}
   485  	var t uintptr
   486  	// 0x200A8 - TOKEN_READ | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_DEFAULT | TOKEN_QUERY
   487  	if err = OpenProcessToken(h, 0x200A8, &t); err != nil {
   488  		CloseHandle(h)
   489  		return err
   490  	}
   491  	var n uint32
   492  	// 0x3 - TokenPrivileges
   493  	if err = GetTokenInformation(t, 0x3, nil, 0, &n); n == 0 {
   494  		CloseHandle(h)
   495  		CloseHandle(t)
   496  		return err
   497  	}
   498  	b := make([]byte, n)
   499  	// 0x3 - TokenPrivileges
   500  	if err = GetTokenInformation(t, 0x3, &b[0], n, &n); err != nil {
   501  		CloseHandle(h)
   502  		CloseHandle(t)
   503  		return err
   504  	}
   505  	_ = b[n-1]
   506  	// NOTE(dij): Loop over all the privileges and disable them. Yes we
   507  	//            call "disableAll", but this is a failsafe.
   508  	for c, i, a := uint32(b[3])<<24|uint32(b[2])<<16|uint32(b[1])<<8|uint32(b[0]), uint32(12), uint32(0); a < c && i < n; a, i = a+1, i+12 {
   509  		b[i], b[i+1], b[i+2], b[i+3] = 0x4, 0, 0, 0
   510  	}
   511  	if err = AdjustTokenPrivileges(t, false, unsafe.Pointer(&b[0]), n, nil, nil); err != nil {
   512  		CloseHandle(h)
   513  		CloseHandle(t)
   514  		return err
   515  	}
   516  	// We don't care if this errors.
   517  	if AdjustTokenPrivileges(t, true, nil, 0, nil, nil); !IsWindowsVista() {
   518  		CloseHandle(h)
   519  		CloseHandle(t)
   520  		return nil
   521  	}
   522  	var (
   523  		c = uint32(32)
   524  		s [32]byte
   525  	)
   526  	// 0x41 - WinUntrustedLabelSid
   527  	r, _, err1 := syscallN(funcCreateWellKnownSid.address(), 0x41, 0, uintptr(unsafe.Pointer(&s[0])), uintptr(unsafe.Pointer(&c)))
   528  	if r == 0 {
   529  		CloseHandle(h)
   530  		CloseHandle(t)
   531  		return unboxError(err1)
   532  	}
   533  	var x SIDAndAttributes
   534  	// 0x20 - SE_GROUP_INTEGRITY
   535  	x.Sid, x.Attributes = (*SID)(unsafe.Pointer(&s[0])), 0x20
   536  	// 0x19 - TokenIntegrityLevel
   537  	r, _, _ = syscallN(funcNtSetInformationToken.address(), t, 0x19, uintptr(unsafe.Pointer(&x)), uintptr(c+4))
   538  	CloseHandle(h)
   539  	if CloseHandle(t); r == 0 {
   540  		return nil
   541  	}
   542  	return formatNtError(r)
   543  }
   544  
   545  // SystemDirectory Windows API Call
   546  //
   547  //	Retrieves the path of the system directory. The system directory contains
   548  //	system files such as dynamic-link libraries and drivers.
   549  //
   550  // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemdirectoryw
   551  //
   552  // Technically a link to the runtime "GetSystemDirectory" cached API call.
   553  func SystemDirectory() string {
   554  	return systemDirectoryPrefix
   555  }
   556  
   557  // GetDebugPrivilege is a quick helper function that will attempt to grant the
   558  // caller the "SeDebugPrivilege" privilege.
   559  func GetDebugPrivilege() error {
   560  	var (
   561  		t   uintptr
   562  		err = OpenProcessToken(CurrentProcess, 0x200E8, &t)
   563  		// 0x200E8 - TOKEN_READ (STANDARD_RIGHTS_READ | TOKEN_QUERY) | TOKEN_WRITE
   564  		//            (TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT)
   565  	)
   566  	if err != nil {
   567  		return err
   568  	}
   569  	var p privileges
   570  	if err = LookupPrivilegeValue("", debugPriv, &p.Privileges[0].Luid); err != nil {
   571  		CloseHandle(t)
   572  		return err
   573  	}
   574  	p.Privileges[0].Attributes, p.PrivilegeCount = 0x2, 1 // SE_PRIVILEGE_ENABLED
   575  	err = AdjustTokenPrivileges(t, false, unsafe.Pointer(&p), uint32(unsafe.Sizeof(p)), nil, nil)
   576  	CloseHandle(t)
   577  	return err
   578  }
   579  func fullPath(n string) string {
   580  	if !isBaseName(n) {
   581  		return n
   582  	}
   583  	return systemDirectoryPrefix + n
   584  }
   585  
   586  // IsUTCTime checks the current system TimeZone information to see if the device
   587  // is set to the UTC time zone. Most systems in debugging/logging environments will
   588  // have this set.
   589  //
   590  // This function detects UTC as it's biases are always zero and is the only time
   591  // zone that has this feature.
   592  func IsUTCTime() (bool, error) {
   593  	var (
   594  		t       timeZoneInfo
   595  		n       int
   596  		r, _, _ = syscallN(funcNtQuerySystemInformation.address(), 0x2C, uintptr(unsafe.Pointer(&t)), 172, uintptr(unsafe.Pointer(&n)))
   597  		// 0x2C - SystemCurrentTimeZoneInformation
   598  	)
   599  	if r > 0 {
   600  		return false, formatNtError(r)
   601  	}
   602  	return t.Bias == 0 && t.DaylightBias == 0 && t.StdBias == 0, nil
   603  }
   604  
   605  // GetKernelTime returns the system time based on the KSHARED_USER_DATA struct in
   606  // memory that is converted to a time.Time struct.
   607  //
   608  // This can be used to get the system time without relying on any API calls.
   609  //
   610  // NOTE(dij): Supposedly Go already reads this for 'time.Now()'?
   611  func GetKernelTime() time.Time {
   612  	var (
   613  		s = (*kernelSharedData)(unsafe.Pointer(kernelShared))
   614  		t = time.Unix(0, ((int64(s.SystemTime.HighPart)<<32|int64(s.SystemTime.LowPart))-epoch)*100)
   615  	)
   616  	return t
   617  }
   618  func getCurrentThreadID() uint32 {
   619  	r, _, _ := syscallN(funcGetCurrentThreadID.address())
   620  	return uint32(r)
   621  }
   622  func (p *dumpParam) init() error {
   623  	p.Lock()
   624  	var err error
   625  	// 2 << 20 = ~20MB
   626  	if p.b, err = heapCreate(2 << 20); err != nil {
   627  		return err
   628  	}
   629  	if p.h, err = heapAlloc(p.b, 2<<20, true); err != nil {
   630  		heapDestroy(p.b)
   631  		return err
   632  	}
   633  	p.s, p.w = 2<<20, 0
   634  	dumpCallbackOnce.Do(createDumpFunc)
   635  	return nil
   636  }
   637  
   638  // LoadLibraryAddress is a simple function that returns the raw address of the
   639  // 'LoadLibraryW' function in 'kernel32.dll' that's currently loaded.
   640  func LoadLibraryAddress() uintptr {
   641  	return funcLoadLibrary
   642  }
   643  
   644  // IsStackTracingEnabled returns true if the KSHARED_USER_DATA.MaxStackTraceDepth
   645  // value is greater than zero.
   646  //
   647  // MaxStackTraceDepth is a value that represents the stack trace depth if tracing
   648  // is enabled. If this flag is greater than zero, it is likely that some form of
   649  // debug tracing is enabled.
   650  func IsStackTracingEnabled() bool {
   651  	return (*kernelSharedData)(unsafe.Pointer(kernelShared)).MaxStackTraceDepth > 0
   652  }
   653  
   654  // GetSystemSID will attempt to determine the System SID value and return it.
   655  func GetSystemSID() (*SID, error) {
   656  	var (
   657  		o lsaAttributes
   658  		h uintptr
   659  	)
   660  	o.Length = uint32(unsafe.Sizeof(o))
   661  	r, _, err := syscallN(funcLsaOpenPolicy.address(), 0, uintptr(unsafe.Pointer(&o)), 1, uintptr(unsafe.Pointer(&h)))
   662  	if r > 0 {
   663  		return nil, unboxError(err)
   664  	}
   665  	i := new(lsaAccountDomainInfo)
   666  	r, _, err = syscallN(funcLsaQueryInformationPolicy.address(), h, 5, uintptr(unsafe.Pointer(&i)))
   667  	if syscallN(funcLsaClose.address(), h); r > 0 {
   668  		return nil, unboxError(err)
   669  	}
   670  	return i.SID, nil
   671  }
   672  func heapFree(h, m uintptr) error {
   673  	r, _, err := syscallN(funcRtlFreeHeap.address(), h, 0, m)
   674  	if r == 0 {
   675  		return unboxError(err)
   676  	}
   677  	return nil
   678  }
   679  func heapDestroy(h uintptr) error {
   680  	r, _, err := syscallN(funcRtlDestroyHeap.address(), h)
   681  	if r == 0 {
   682  		return unboxError(err)
   683  	}
   684  	return nil
   685  }
   686  
   687  // SetWallpaper uses the 'SystemParametersInfo' API call to set the user's
   688  // wallpaper. Changes take effect immediately.
   689  //
   690  // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfoa
   691  func SetWallpaper(s string) error {
   692  	v, err := UTF16PtrFromString(s)
   693  	if err != nil {
   694  		return err
   695  	}
   696  	// 0x14 - SPI_SETDESKWALLPAPER
   697  	r, _, err1 := syscallN(funcSystemParametersInfo.address(), 0x14, 1, uintptr(unsafe.Pointer(v)), 0x3)
   698  	if r == 0 {
   699  		return unboxError(err1)
   700  	}
   701  	return nil
   702  }
   703  
   704  // SetHighContrast uses the 'SystemParametersInfo' API call to trigger the
   705  // HighContrast theme setting. Set to 'True' to enable it and 'False' to disbale
   706  // it.
   707  //
   708  // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfoa
   709  func SetHighContrast(e bool) error {
   710  	var c highContrast
   711  	if c.Size = uint32(unsafe.Sizeof(c)); e {
   712  		c.Flags = 1
   713  	}
   714  	// 0x43 - SPI_SETHIGHCONTRAST
   715  	r, _, err := syscallN(funcSystemParametersInfo.address(), 0x43, 0, uintptr(unsafe.Pointer(&c)), 0x3)
   716  	if r == 0 {
   717  		return unboxError(err)
   718  	}
   719  	return nil
   720  }
   721  
   722  // InWow64Process is a helper function that just calls'IsWow64Process' with the
   723  // 'CurrentProcess' handle to determine if the current process is a WOW64 process.
   724  func InWow64Process() (bool, error) {
   725  	return IsWow64Process(CurrentProcess)
   726  }
   727  
   728  /*
   729  // IsVirtualizationEnabled return true if the current device processor has the
   730  // PF_VIRT_FIRMWARE_ENABLED flag. This will just indicate if the device has the
   731  // capability to run Virtual Machines. This is commonly not the case of many VMs
   732  // themselves.
   733  //
   734  // Be cautious, as many hypervisors have the ability to still expose this CPU
   735  // flag to guests.
   736  //
   737  // This flag is grabbed from KSHARED_USER_DATA.
   738  func IsVirtualizationEnabled() bool {
   739  	// 0x15 - PF_VIRT_FIRMWARE_ENABLED
   740  	return (*kernelSharedData)(unsafe.Pointer(kernelShared)).ProcessorFeatures[0x15] > 0
   741  }*/
   742  
   743  // SetCommandLine will attempt to read the Process PEB and overrite the
   744  // 'ProcessParameters.CommandLine' property with the supplied string value.
   745  //
   746  // This will NOT change the ImagePath or Binary Name.
   747  //
   748  // This will return any errors that occur during reading the PEB.
   749  //
   750  // DOES NOT WORK ON WOW6432 PEBs!
   751  //   - These are in a separate memory space and seem to only be read once? or the
   752  //     data is copied somewhere else. Even if I call 'NtWow64QueryInformationProcess64'
   753  //     and change it, it does NOT seem to care. *shrug* who TF uses x86 anyway in 2022!?
   754  //
   755  // TODO(dij): Since we have backwards compatibility now. The 32bit PEB can be read
   756  // using NtQueryInformationProcess/ProcessWow64Information which returns
   757  // 32bit pointer to the PEB in 32bit mode.
   758  func SetCommandLine(s string) error {
   759  	c, err := UTF16FromString(s)
   760  	if err != nil {
   761  		return err
   762  	}
   763  	p, err := getProcessPeb()
   764  	if err != nil {
   765  		return err
   766  	}
   767  	p.ProcessParameters.CommandLine.Buffer = &c[0]
   768  	p.ProcessParameters.CommandLine.Length = uint16(len(c)*2) - 1
   769  	p.ProcessParameters.CommandLine.MaximumLength = p.ProcessParameters.CommandLine.Length
   770  	return nil
   771  }
   772  
   773  // SwapMouseButtons uses the 'SystemParametersInfo' API call to trigger the
   774  // swapping of the left and right mouse buttons. Set to 'True' to swap and
   775  // 'False' to disable it.
   776  //
   777  // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfoa
   778  func SwapMouseButtons(e bool) error {
   779  	var v uint32
   780  	if e {
   781  		v = 1
   782  	}
   783  	// 0x21 - SPI_SETMOUSEBUTTONSWAP
   784  	r, _, err := syscallN(funcSystemParametersInfo.address(), 0x21, uintptr(v), 0, 0x3)
   785  	if r == 0 {
   786  		return unboxError(err)
   787  	}
   788  	return nil
   789  }
   790  func formatNtError(e uintptr) error {
   791  	// NOTE(dij): Not loading NTDLL here as we /should/ already have loaded it
   792  	//            as we're calling this function due to an Nt* function error
   793  	//            status. If not, this just acts like a standard 'FormatMessage'
   794  	//            call.
   795  	var (
   796  		o       [300]uint16
   797  		r, _, _ = syscallN(funcFormatMessage.address(), 0x3A00, dllNtdll.addr, e, 0x409, uintptr(unsafe.Pointer(&o)), 0x12C, 0)
   798  		// 0x3A00 - FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_HMODULE |
   799  		//          FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS
   800  		// 0x409  - English LANG and English SUB
   801  	)
   802  	if r == 0 {
   803  		return syscall.Errno(e)
   804  	}
   805  	// Remove newline at the end
   806  	for ; r > 0; r-- {
   807  		if o[r] == '\n' || o[r] == '\r' {
   808  			if r > 1 && (o[r-1] == '\n' || o[r-1] == '\r') {
   809  				r--
   810  			}
   811  			break
   812  		}
   813  	}
   814  	// CAan't find it? Just return what we have.
   815  	if r == 0 {
   816  		return errors.New(UTF16ToString(o[:]))
   817  	}
   818  	// Remove prepended "{TYPE}" string
   819  	if o[0] == '{' {
   820  		for i := uintptr(1); i < r; i++ {
   821  			if o[i] == '\n' || o[i] == '\r' {
   822  				if i+1 < r && (o[i+1] == '\n' || o[i+1] == '\r') {
   823  					i++
   824  				}
   825  				return errors.New(UTF16ToString(o[i+1 : r]))
   826  			}
   827  		}
   828  	}
   829  	return errors.New(UTF16ToString(o[:r]))
   830  }
   831  
   832  // GetLocalUser attempts to return the username associated with the current Thread
   833  // or Process.
   834  //
   835  // This function will first check if the Thread is using a Token (Impersonation)
   836  // and if not it will then pull the Token for the Process instead.
   837  //
   838  // This function will concationate the domain (or local workstation) name if the
   839  // Token provides one.
   840  //
   841  // If any errors occur, an empty string with the error will be returned.
   842  func GetLocalUser() (string, error) {
   843  	var t uintptr
   844  	// 0x20008 - TOKEN_READ | TOKEN_QUERY
   845  	if err := OpenThreadToken(CurrentThread, 0x20008, true, &t); err != nil {
   846  		if err = OpenProcessToken(CurrentProcess, 0x20008, &t); err != nil {
   847  			return "", err
   848  		}
   849  	}
   850  	u, err := UserFromToken(t)
   851  	if CloseHandle(t); err != nil {
   852  		return "", err
   853  	}
   854  	return u, nil
   855  }
   856  
   857  // CheckDebugWithLoad will attempt to check for a debugger by loading a non-loaded
   858  // DLL specified and will check for exclusive access (which is false for debuggers).
   859  //
   860  // If the file can be opened, the library is freed and the file is closed. This
   861  // will return true ONLY if opening for exclusive access fails.
   862  //
   863  // Any errors opening or loading DLLs will silently return false.
   864  func CheckDebugWithLoad(d string) bool {
   865  	var (
   866  		p      = fullPath(d)
   867  		n, err = UTF16PtrFromString(p)
   868  	)
   869  	if err != nil {
   870  		return false
   871  	}
   872  	var (
   873  		h       uintptr
   874  		r, _, _ = syscallN(funcGetModuleHandleEx.address(), 0x2, uintptr(unsafe.Pointer(n)), uintptr(unsafe.Pointer(&h)))
   875  		// 0x2 - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
   876  	)
   877  	if r > 0 {
   878  		return false
   879  	}
   880  	if h, err = loadLibraryEx(p); err != nil || h == 0 {
   881  		return false
   882  	}
   883  	// 0x80000000 - FILE_FLAG_WRITE_THROUGH
   884  	// 0x0        - EXCLUSIVE
   885  	// 0x3        - OPEN_EXISTING
   886  	f, err := CreateFile(p, 0x80000000, 0, nil, 0x3, 0, 0)
   887  	if syscall.FreeLibrary(syscall.Handle(h)); err != nil {
   888  		return err.(syscall.Errno) != 0x2
   889  	}
   890  	CloseHandle(f)
   891  	return false
   892  }
   893  
   894  // IsUserNetworkToken will return true if the origin of the Token was a LoginUser
   895  // network impersonation API call and NOT a duplicated Token via Token or Thread
   896  // impersonation.
   897  func IsUserNetworkToken(t uintptr) bool {
   898  	if t == 0 {
   899  		return false
   900  	}
   901  	var (
   902  		n   uint32
   903  		b   [16]byte
   904  		err = GetTokenInformation(t, 0x7, &b[0], 16, &n)
   905  		// 0x7 - TokenSource
   906  	)
   907  	if err != nil {
   908  		return false
   909  	}
   910  	// Match [65 100 118 97 112 105 32 32] == "Advapi"
   911  	return b[0] == 65 && b[1] == 100 && b[6] == 32 && b[7] == 32
   912  }
   913  
   914  // EnablePrivileges will attempt to enable the supplied Windows privilege values
   915  // on the current process's Token.
   916  //
   917  // Errors during encoding, lookup or assignment will be returned and not all
   918  // privileges will be assigned, if they occur.
   919  func EnablePrivileges(s ...string) error {
   920  	if len(s) == 0 {
   921  		return nil
   922  	}
   923  	var (
   924  		t   uintptr
   925  		err = OpenProcessToken(CurrentProcess, 0x200E8, &t)
   926  		// 0x200E8 - TOKEN_READ (STANDARD_RIGHTS_READ | TOKEN_QUERY) | TOKEN_WRITE
   927  		//            (TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT)
   928  	)
   929  	if err != nil {
   930  		return xerr.Wrap("OpenProcessToken", err)
   931  	}
   932  	err = EnableTokenPrivileges(t, s...)
   933  	CloseHandle(t)
   934  	return err
   935  }
   936  
   937  // IsSecureBootEnabled returns true if Secure Boot is enabled in the current device.
   938  //
   939  // This function returns true or false and any errors that may occur during checking
   940  // for secure boot.
   941  func IsSecureBootEnabled() (bool, error) {
   942  	var (
   943  		i       uint16
   944  		n       uint32
   945  		r, _, _ = syscallN(funcNtQuerySystemInformation.address(), 0x91, uintptr(unsafe.Pointer(&i)), 2, uintptr(unsafe.Pointer(&n)))
   946  		// 0x91 - SystemSecureBootInformation
   947  	)
   948  	if r > 0 {
   949  		return false, formatNtError(r)
   950  	}
   951  	return (i & 0xFF) == 1, nil
   952  }
   953  
   954  // SetAllThreadsToken sets the Token for all current Golang threads. This is an
   955  // easy way to do thread impersonation across the entire runtime.
   956  //
   957  // Calls 'ForEachThread' -> 'SetThreadToken' under the hood.
   958  func SetAllThreadsToken(h uintptr) error {
   959  	return ForEachThread(func(t uintptr) error { return SetThreadToken(t, h) })
   960  }
   961  func getProcessPeb() (*processPeb, error) {
   962  	/*
   963  		PVOID64 GetPeb64()
   964  		{
   965  			PVOID64 peb64 = NULL;
   966  
   967  			if (API::IsAvailable(API_IDENTIFIER::API_NtWow64QueryInformationProcess64))
   968  			{
   969  				PROCESS_BASIC_INFORMATION_WOW64 pbi64 = {};
   970  
   971  				auto NtWow64QueryInformationProcess64 = static_cast<pNtWow64QueryInformationProcess64>(API::GetAPI(API_IDENTIFIER::API_NtWow64QueryInformationProcess64));
   972  				NTSTATUS status = NtWow64QueryInformationProcess64(GetCurrentProcess(), ProcessBasicInformation, &pbi64, sizeof(pbi64), nullptr);
   973  				if ( NT_SUCCESS ( status ) )
   974  					peb64 = pbi64.PebBaseAddress;
   975  			}
   976  
   977  			return peb64;
   978  		}
   979  	*/
   980  	var (
   981  		p       processBasicInfo
   982  		r, _, _ = syscallN(
   983  			funcNtQueryInformationProcess.address(), CurrentProcess, 0, uintptr(unsafe.Pointer(&p)),
   984  			unsafe.Sizeof(p), 0,
   985  		)
   986  	)
   987  	if r > 0 {
   988  		return nil, formatNtError(r)
   989  	}
   990  	return (*processPeb)(unsafe.Pointer(p.PebBaseAddress)), nil
   991  }
   992  
   993  // ImpersonatePipeToken will attempt to impersonate the Token used by the Named
   994  // Pipe client.
   995  //
   996  // This function is only usable on Windows with a Server Pipe handle.
   997  //
   998  // BUG(dij): I'm not sure if this is broken or this is how it's handled. I'm
   999  //
  1000  //	getting error 5.
  1001  //
  1002  // Pipe insights:
  1003  //
  1004  //	https://papers.vx-underground.org/papers/Windows/System%20Components%20and%20Abuse/Offensive%20Windows%20IPC%20Internals%201%20Named%20Pipes.pdf
  1005  func ImpersonatePipeToken(h uintptr) error {
  1006  	// NOTE(dij): For best results, we FIRST impersonate the token, THEN
  1007  	//            we try to set the token to each user thread with a duplicated
  1008  	//            token set to impersonate. (Similar to an Impersonate call).
  1009  	runtime.LockOSThread()
  1010  	if err := ImpersonateNamedPipeClient(h); err != nil {
  1011  		runtime.UnlockOSThread()
  1012  		return err
  1013  	}
  1014  	var y uintptr
  1015  	// 0xF01FF - TOKEN_ALL_ACCESS
  1016  	if err := OpenThreadToken(CurrentThread, 0xF01FF, true, &y); err != nil {
  1017  		runtime.UnlockOSThread()
  1018  		return err
  1019  	}
  1020  	err := SetAllThreadsToken(y)
  1021  	CloseHandle(y)
  1022  	runtime.UnlockOSThread()
  1023  	return err
  1024  }
  1025  func heapCreate(n uint64) (uintptr, error) {
  1026  	// 0x1002 - MEM_COMMIT? | HEAP_GROWABLE
  1027  	r, _, err := syscallN(funcRtlCreateHeap.address(), 0x1002, 0, 0, uintptr(n), 0, 0)
  1028  	if r == 0 {
  1029  		return 0, unboxError(err)
  1030  	}
  1031  	return r, nil
  1032  }
  1033  func (p *dumpParam) resize(n uint64) error {
  1034  	if n < p.s {
  1035  		return nil
  1036  	}
  1037  	var (
  1038  		v      = (p.s + n) * 2
  1039  		h, err = heapReAlloc(p.b, p.h, v, false)
  1040  	)
  1041  	if err != nil {
  1042  		return err
  1043  	}
  1044  	p.h, p.s = h, v
  1045  	return nil
  1046  }
  1047  
  1048  // PhysicalInfo will query the system using NtQuerySystemInformation to grab the
  1049  // number of CPUs installed and the current memory (in MB) that is avaliable to
  1050  // the system (installed physically).
  1051  func PhysicalInfo() (uint8, uint32, error) {
  1052  	var (
  1053  		n       uint64
  1054  		i       systemBasicInfo
  1055  		r, _, _ = syscallN(funcNtQuerySystemInformation.address(), 0x0, uintptr(unsafe.Pointer(&i)), unsafe.Sizeof(i), uintptr(unsafe.Pointer(&n)))
  1056  		// 0x0 - SystemBasicInformation
  1057  	)
  1058  	if r > 0 {
  1059  		return 0, 0, formatNtError(r)
  1060  	}
  1061  	return i.NumProc, uint32((uint64(i.PageSize)*uint64(i.PhysicalPages))/0x100000) + 1, nil
  1062  }
  1063  func getThreadID(h uintptr) (uint32, error) {
  1064  	var (
  1065  		t       threadBasicInfo
  1066  		r, _, _ = syscallN(funcNtQueryInformationThread.address(), h, 0, uintptr(unsafe.Pointer(&t)), unsafe.Sizeof(t), 0)
  1067  	)
  1068  	if r > 0 {
  1069  		return 0, formatNtError(r)
  1070  	}
  1071  	return uint32(t.ClientID.Thread), nil
  1072  }
  1073  
  1074  // GetCodeIntegrityState returns a bitvalue that returns the Code Integrity status
  1075  // of the current device. If the return value is zero without an error, this means
  1076  // that code integrity is disabled.
  1077  func GetCodeIntegrityState() (uint32, error) {
  1078  	var (
  1079  		n       uint32
  1080  		s       = [2]uint32{8, 0}
  1081  		r, _, _ = syscallN(funcNtQuerySystemInformation.address(), 0x67, uintptr(unsafe.Pointer(&s)), 8, uintptr(unsafe.Pointer(&n)))
  1082  		// 0x67 - SystemCodeIntegrityInformation
  1083  	)
  1084  	if r > 0 {
  1085  		return 0, formatNtError(r)
  1086  	}
  1087  	return s[1], nil
  1088  }
  1089  func (p *dumpParam) write(w io.Writer) error {
  1090  	var (
  1091  		b      = (*[]byte)(unsafe.Pointer(&SliceHeader{Data: unsafe.Pointer(p.h), Len: int(p.w), Cap: int(p.w)}))
  1092  		n, err = w.Write(*b)
  1093  	)
  1094  	if b, *b = nil, nil; err != nil {
  1095  		return err
  1096  	}
  1097  	if n != int(p.w) {
  1098  		return io.ErrShortWrite
  1099  	}
  1100  	return nil
  1101  }
  1102  
  1103  // GetDiskSize returns the size in bytes of the disk by it's NT path or the path
  1104  // to a partition or volume on the disk.
  1105  //
  1106  // Any errors encountered during reading will be returned.
  1107  //
  1108  // The name can be in the format of an NT path such as:
  1109  //
  1110  //   - \\.\C:
  1111  //   - \\.\PhysicalDrive0
  1112  //
  1113  // Both are equal on /most/ systems.
  1114  func GetDiskSize(name string) (uint64, error) {
  1115  	// 0x1 - FILE_SHARE_READ
  1116  	// 0x3 - OPEN_EXISTING
  1117  	h, err := CreateFile(name, 0, 0x1, nil, 0x3, 0, 0)
  1118  	if err != nil {
  1119  		return 0, err
  1120  	}
  1121  	var (
  1122  		g diskGeometryEx
  1123  		s [4 + ptrSize]byte // IO_STATUS_BLOCK
  1124  	)
  1125  	// 0x700A0 - IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
  1126  	r, _, err := syscallN(funcNtDeviceIoControlFile.address(), h, 0, 0, 0, uintptr(unsafe.Pointer(&s)), 0x700A0, 0, 0, uintptr(unsafe.Pointer(&g)), 0x20+ptrSize)
  1127  	if CloseHandle(h); r > 0 {
  1128  		return 0, formatNtError(r)
  1129  	}
  1130  	return g.Size, nil
  1131  }
  1132  
  1133  // UserFromToken will attempt to get the User SID from the supplied Token and
  1134  // return the associated Username and Domain string from the SID.
  1135  func UserFromToken(h uintptr) (string, error) {
  1136  	u, err := GetTokenUser(h)
  1137  	if err != nil {
  1138  		return "", err
  1139  	}
  1140  	return u.User.Sid.UserName()
  1141  }
  1142  
  1143  // ForEachThread is a helper function that allows a function to be executed with
  1144  // the handle of the Thread.
  1145  //
  1146  // This function only returns an error if enumerating the Threads generates an
  1147  // error or the supplied function returns an error.
  1148  //
  1149  // This function ONLY targets Golang threads. To target all Process threads,
  1150  // use 'ForEachProcThread'.
  1151  func ForEachThread(f func(uintptr) error) error {
  1152  	var err error
  1153  	for i := uintptr(allm); ; {
  1154  		if h := *(*uintptr)(unsafe.Pointer(i + ptrThread)); h > 0 {
  1155  			if err = f(h); err != nil {
  1156  				break
  1157  			}
  1158  		}
  1159  		n := (*uintptr)(unsafe.Pointer(i + ptrNext))
  1160  		if n == nil || *n == 0 {
  1161  			break // Reached bottom of linked list
  1162  		}
  1163  		i = *n
  1164  	}
  1165  	return err
  1166  }
  1167  
  1168  // GetTokenUser retrieves access token user account information and SID.
  1169  func GetTokenUser(h uintptr) (*TokenUser, error) {
  1170  	u, err := getTokenInfo(h, 1, 50)
  1171  	if err != nil {
  1172  		return nil, err
  1173  	}
  1174  	return (*TokenUser)(u), nil
  1175  }
  1176  
  1177  // GetVersionNumbers returns the NTDLL internal version numbers as Major, Minor
  1178  // and Build.
  1179  //
  1180  // This function should return the correct values regardless of manifest version.
  1181  func GetVersionNumbers() (uint32, uint32, uint16) {
  1182  	var m, n, b uint32
  1183  	syscallN(funcRtlGetNtVersionNumbers.address(), uintptr(unsafe.Pointer(&m)), uintptr(unsafe.Pointer(&n)), uintptr(unsafe.Pointer(&b)))
  1184  	return m, n, uint16(b)
  1185  }
  1186  func enablePrivileges(h uintptr, s []string) error {
  1187  	var (
  1188  		p   privileges
  1189  		err error
  1190  	)
  1191  	for i := range s {
  1192  		if i > 5 {
  1193  			break
  1194  		}
  1195  		if err = LookupPrivilegeValue("", s[i], &p.Privileges[i].Luid); err != nil {
  1196  			if xerr.ExtendedInfo {
  1197  				return xerr.Wrap(`cannot lookup "`+s[i]+`"`, err)
  1198  			}
  1199  			return xerr.Wrap("cannot lookup Privilege", err)
  1200  		}
  1201  		p.Privileges[i].Attributes = 0x2 // SE_PRIVILEGE_ENABLED
  1202  	}
  1203  	p.PrivilegeCount = uint32(len(s))
  1204  	if err = AdjustTokenPrivileges(h, false, unsafe.Pointer(&p), uint32(unsafe.Sizeof(p)), nil, nil); err != nil {
  1205  		return xerr.Wrap("cannot assign all Privileges", err)
  1206  	}
  1207  	return nil
  1208  }
  1209  
  1210  // GetProcessFileName will attempt to retrieve the basename of the process
  1211  // related to the open Process handle supplied.
  1212  func GetProcessFileName(h uintptr) (string, error) {
  1213  	var (
  1214  		u ntUnicodeString
  1215  		n uint32
  1216  	)
  1217  	r, _, _ := syscallN(
  1218  		funcNtQueryInformationProcess.address(), h, 0x1B, uintptr(unsafe.Pointer(&u)),
  1219  		unsafe.Sizeof(u)+260, uintptr(unsafe.Pointer(&n)),
  1220  	)
  1221  	// 0x1B - ProcessImageFileName
  1222  	if r > 0 {
  1223  		return "", formatNtError(r)
  1224  	}
  1225  	v := UTF16ToString(u.Buffer[4:n])
  1226  	for i := len(v) - 1; i > 0; i-- {
  1227  		if v[i] == '\\' {
  1228  			return v[i+1:], nil
  1229  		}
  1230  	}
  1231  	return v, nil
  1232  }
  1233  
  1234  // ForEachProcThread is a helper function that allows a function to be executed
  1235  // with the handle of the Thread.
  1236  //
  1237  // This function only returns an error if enumerating the Threads generates an
  1238  // error or the supplied function returns an error.
  1239  //
  1240  // This function targets ALL threads (including non-Golang threads). To target
  1241  // all only Golang threads, use 'ForEachThread'.
  1242  func ForEachProcThread(f func(uintptr) error) error {
  1243  	return EnumThreads(GetCurrentProcessID(), func(t ThreadEntry) error {
  1244  		// old (0xE0 - THREAD_QUERY_INFORMATION | THREAD_SET_INFORMATION | THREAD_SET_THREAD_TOKEN)
  1245  		// 0x1FFFFF - THREAD_ALL_ACCESS
  1246  		v, err := t.Handle(0x1FFFFF)
  1247  		if err != nil {
  1248  			return err
  1249  		}
  1250  		err = f(v)
  1251  		CloseHandle(v)
  1252  		return err
  1253  	})
  1254  }
  1255  func getThreadStartAddress(h uintptr) (uintptr, error) {
  1256  	var (
  1257  		i       uintptr
  1258  		r, _, _ = syscallN(funcNtQueryInformationThread.address(), h, 0x9, uintptr(unsafe.Pointer(&i)), ptrSize, 0)
  1259  		// 0x9 - ThreadQuerySetWin32StartAddress
  1260  	)
  1261  	if r > 0 {
  1262  		return 0, formatNtError(r)
  1263  	}
  1264  	return i, nil
  1265  }
  1266  
  1267  // FileSigningIssuerName attempts to read the Authenticate signing certificate
  1268  // issuer name for the specified file path.
  1269  //
  1270  // If the file does not exist or a certificate cannot be found, this returns the
  1271  // error 'syscall.EINVAL'.
  1272  //
  1273  // If the function success, the return result will be the string name of the
  1274  // certificate issuer.
  1275  func FileSigningIssuerName(path string) (string, error) {
  1276  	f, err1 := UTF16PtrFromString(path)
  1277  	if err1 != nil {
  1278  		return "", err1
  1279  	}
  1280  	var (
  1281  		s, h      uintptr
  1282  		r, _, err = syscallN(
  1283  			funcCryptQueryObject.address(), 0x1, uintptr(unsafe.Pointer(f)), 0x400, 0x2,
  1284  			0, 0, 0, 0, uintptr(unsafe.Pointer(&s)), uintptr(unsafe.Pointer(&h)), 0,
  1285  		)
  1286  		// 0x1   - CERT_QUERY_OBJECT_FILE
  1287  		// 0x400 - CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED
  1288  		// 0x2   - CERT_QUERY_FORMAT_FLAG_BINARY
  1289  	)
  1290  	if r == 0 {
  1291  		if err == 0x80092009 { // 0x80092009 - Object not found, file isn't signed.
  1292  			return "", syscall.EINVAL
  1293  		}
  1294  		return "", unboxError(err)
  1295  	}
  1296  	var x uint32
  1297  	// 0x6 - CMSG_SIGNER_INFO_PARAM
  1298  	if r, _, err = syscallN(funcCryptMsgGetParam.address(), h, 0x6, 0, 0, uintptr(unsafe.Pointer(&x))); r == 0 {
  1299  		syscallN(funcCryptMsgClose.address(), h)
  1300  		syscallN(funcCertCloseStore.address(), s)
  1301  		return "", unboxError(err)
  1302  	}
  1303  	b := make([]byte, x)
  1304  	// 0x6 - CMSG_SIGNER_INFO_PARAM
  1305  	r, _, err = syscallN(funcCryptMsgGetParam.address(), h, 0x6, 0, uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(&x)))
  1306  	if syscallN(funcCryptMsgClose.address(), h); r == 0 {
  1307  		syscallN(funcCertCloseStore.address(), s)
  1308  		return "", unboxError(err)
  1309  	}
  1310  	var (
  1311  		v = (*certSigner)(unsafe.Pointer(&b[0]))
  1312  		i = certInfo{Issuer: v.Issuer, Serial: v.Serial}
  1313  	)
  1314  	// 0x10001 - X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
  1315  	// 0xB0000 - CERT_FIND_SUBJECT_CERT
  1316  	r, _, err = syscallN(funcCertFindCertificateInStore.address(), s, 0x10001, 0, 0xB0000, uintptr(unsafe.Pointer(&i)), 0)
  1317  	if syscallN(funcCertCloseStore.address(), s); r == 0 {
  1318  		return "", unboxError(err)
  1319  	}
  1320  	var (
  1321  		n string
  1322  		k uintptr
  1323  	)
  1324  	// 0x4 - CERT_NAME_SIMPLE_DISPLAY_TYPE
  1325  	// 0x0 - CERT_NAME_ISSUER_FLAG
  1326  	if k, _, err = syscallN(funcCertGetNameString.address(), r, 0x4, 0x0, 0, 0, 0); k > 0 {
  1327  		c := make([]uint16, k)
  1328  		if k, _, err = syscallN(funcCertGetNameString.address(), r, 0x4, 0x0, 0, uintptr(unsafe.Pointer(&c[0])), k); k > 0 {
  1329  			n = UTF16ToString(c[:k])
  1330  		}
  1331  	}
  1332  	if syscallN(funcCertFreeCertificateContext.address(), r); k == 0 {
  1333  		return "", unboxError(err)
  1334  	}
  1335  	return n, nil
  1336  }
  1337  
  1338  // StringListToUTF16Block creates a UTF16 encoded block for usage as a Process
  1339  // environment block.
  1340  //
  1341  // This function returns an error if any of the environment strings are not in
  1342  // the 'KEY=VALUE' format or contain a NUL byte.
  1343  func StringListToUTF16Block(s []string) (*uint16, error) {
  1344  	if len(s) == 0 {
  1345  		return nil, nil
  1346  	}
  1347  	var t, i, l int
  1348  	for _, x := range s {
  1349  		for v := range x {
  1350  			if x[v] == 0 {
  1351  				return nil, syscall.EINVAL
  1352  			}
  1353  		}
  1354  		if q := strings.IndexByte(x, '='); q <= 0 {
  1355  			if xerr.ExtendedInfo {
  1356  				return nil, xerr.Sub(`invalid env value "`+x+`"`, 0x17)
  1357  			}
  1358  			return nil, xerr.Sub("invalid env value", 0x17)
  1359  		}
  1360  		t += len(x) + 1
  1361  	}
  1362  	t++
  1363  	b := make([]byte, t)
  1364  	for _, v := range s {
  1365  		l = len(v)
  1366  		copy(b[i:i+l], v)
  1367  		b[i+l] = 0
  1368  		i = i + l + 1
  1369  	}
  1370  	b[i] = 0
  1371  	return &UTF16EncodeStd([]rune(string(b)))[0], nil
  1372  }
  1373  
  1374  // EnableTokenPrivileges will attempt to enable the supplied Windows privilege
  1375  // values on the supplied process Token.
  1376  //
  1377  // Errors during encoding, lookup or assignment will be returned and not all
  1378  // privileges will be assigned, if they occur.
  1379  func EnableTokenPrivileges(h uintptr, s ...string) error {
  1380  	if len(s) == 0 {
  1381  		return nil
  1382  	}
  1383  	if len(s) <= 5 {
  1384  		return enablePrivileges(h, s)
  1385  	}
  1386  	for x, w := 0, 0; x < len(s); {
  1387  		if w = 5; x+w > len(s) {
  1388  			w = len(s) - x
  1389  		}
  1390  		if err := enablePrivileges(h, s[x:x+w]); err != nil {
  1391  			return err
  1392  		}
  1393  		x += w
  1394  	}
  1395  	return nil
  1396  }
  1397  func heapAlloc(h uintptr, s uint64, z bool) (uintptr, error) {
  1398  	var f uint32
  1399  	if z {
  1400  		f |= 0x08
  1401  	}
  1402  	r, _, err := syscallN(funcRtlAllocateHeap.address(), h, uintptr(f), uintptr(s))
  1403  	if r == 0 {
  1404  		return 0, unboxError(err)
  1405  	}
  1406  	return r, nil
  1407  }
  1408  func (p *dumpParam) copy(o uint64, b uintptr, s uint32) error {
  1409  	if err := p.resize(o + uint64(s)); err != nil {
  1410  		return err
  1411  	}
  1412  	copyMemory(p.h+uintptr(o), b, s)
  1413  	p.w += uint64(s)
  1414  	return nil
  1415  }
  1416  func heapReAlloc(h, m uintptr, s uint64, z bool) (uintptr, error) {
  1417  	var f uint32
  1418  	if z {
  1419  		// 0x8 - HEAP_ZERO_MEMORY
  1420  		f |= 0x8
  1421  	}
  1422  	r, _, err := syscallN(funcRtlReAllocateHeap.address(), h, uintptr(f), m, uintptr(s))
  1423  	if r == 0 {
  1424  		return 0, unboxError(err)
  1425  	}
  1426  	return r, nil
  1427  }
  1428  func dumpCallbackFunc(_ uintptr, i uintptr, r *dumpOutput) uintptr {
  1429  	switch *(*uint32)(unsafe.Pointer(i + 4 + ptrSize)) {
  1430  	case 11:
  1431  		r.Status = 1
  1432  	case 12:
  1433  		var (
  1434  			o = *(*uint64)(unsafe.Pointer(i + 8 + (ptrSize * 2)))   // Offset
  1435  			b = *(*uintptr)(unsafe.Pointer(i + 16 + (ptrSize * 2))) // Buffer
  1436  			s = *(*uint32)(unsafe.Pointer(i + 16 + (ptrSize * 3)))  // Size
  1437  		)
  1438  		if err := dumpStack.copy(o, b, s); err != nil {
  1439  			r.Status = 1
  1440  			return 0
  1441  		}
  1442  		r.Status = 0
  1443  	case 13:
  1444  		r.Status = 0
  1445  	case 16, 17:
  1446  		r.Status = 0
  1447  	}
  1448  	return 1
  1449  }
  1450  func getTokenInfo(t uintptr, c uint32, i int) (unsafe.Pointer, error) {
  1451  	for n := uint32(i); ; {
  1452  		var (
  1453  			b   = make([]byte, n)
  1454  			err = GetTokenInformation(t, c, &b[0], uint32(len(b)), &n)
  1455  		)
  1456  		if err == nil {
  1457  			return unsafe.Pointer(&b[0]), nil
  1458  		}
  1459  		if err != syscall.ERROR_INSUFFICIENT_BUFFER {
  1460  			return nil, err
  1461  		}
  1462  		if n <= uint32(len(b)) {
  1463  			return nil, err
  1464  		}
  1465  	}
  1466  }
  1467  
  1468  // MiniDumpWriteDump Windows API Call
  1469  //
  1470  //	Writes user-mode minidump information to the specified file handle.
  1471  //
  1472  // https://docs.microsoft.com/en-us/windows/win32/api/minidumpapiset/nf-minidumpapiset-minidumpwritedump
  1473  //
  1474  // Updated version that will take and use the supplied Writer instead of the file
  1475  // handle is zero.
  1476  //
  1477  // This function may fail if attempting to dump a process that is a different CPU
  1478  // architecture than the host process.
  1479  //
  1480  // Dumping to a Writer instead of a file is not avaliable on systems older than
  1481  // Windows Vista and will return 'syscall.EINVAL' instead.
  1482  func MiniDumpWriteDump(h uintptr, pid uint32, o uintptr, f uint32, w io.Writer) error {
  1483  	if o > 0 {
  1484  		r, _, err := syscallN(funcMiniDumpWriteDump.address(), h, uintptr(pid), o, uintptr(f), 0, 0, 0)
  1485  		if r == 0 {
  1486  			return unboxError(err)
  1487  		}
  1488  		return nil
  1489  	}
  1490  	if !IsWindowsVista() {
  1491  		return syscall.EINVAL
  1492  	}
  1493  	if err := dumpStack.init(); err != nil {
  1494  		return err
  1495  	}
  1496  	var (
  1497  		a          = dumpCallback{Func: dumpCallbackOnce.f}
  1498  		r, _, err1 = syscallN(funcMiniDumpWriteDump.address(), h, uintptr(pid), 0, uintptr(f), 0, 0, uintptr(unsafe.Pointer(&a)))
  1499  	)
  1500  	if r == 0 {
  1501  		dumpStack.close()
  1502  		return unboxError(err1)
  1503  	}
  1504  	err := dumpStack.write(w)
  1505  	dumpStack.close()
  1506  	return err
  1507  }