github.com/iDigitalFlame/xmt@v0.5.4/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  	// TODO(dij): There is a memory leak here!
   671  	//            Need to call 'localFree' with the ptr to 'i'.
   672  	return i.SID, nil
   673  }
   674  func heapFree(h, m uintptr) error {
   675  	r, _, err := syscallN(funcRtlFreeHeap.address(), h, 0, m)
   676  	if r == 0 {
   677  		return unboxError(err)
   678  	}
   679  	return nil
   680  }
   681  func heapDestroy(h uintptr) error {
   682  	r, _, err := syscallN(funcRtlDestroyHeap.address(), h)
   683  	if r == 0 {
   684  		return unboxError(err)
   685  	}
   686  	return nil
   687  }
   688  
   689  // SetWallpaper uses the 'SystemParametersInfo' API call to set the user's
   690  // wallpaper. Changes take effect immediately.
   691  //
   692  // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfoa
   693  func SetWallpaper(s string) error {
   694  	v, err := UTF16PtrFromString(s)
   695  	if err != nil {
   696  		return err
   697  	}
   698  	// 0x14 - SPI_SETDESKWALLPAPER
   699  	r, _, err1 := syscallN(funcSystemParametersInfo.address(), 0x14, 1, uintptr(unsafe.Pointer(v)), 0x3)
   700  	if r == 0 {
   701  		return unboxError(err1)
   702  	}
   703  	return nil
   704  }
   705  
   706  // SetHighContrast uses the 'SystemParametersInfo' API call to trigger the
   707  // HighContrast theme setting. Set to 'True' to enable it and 'False' to disbale
   708  // it.
   709  //
   710  // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfoa
   711  func SetHighContrast(e bool) error {
   712  	var c highContrast
   713  	if c.Size = uint32(unsafe.Sizeof(c)); e {
   714  		c.Flags = 1
   715  	}
   716  	// 0x43 - SPI_SETHIGHCONTRAST
   717  	r, _, err := syscallN(funcSystemParametersInfo.address(), 0x43, 0, uintptr(unsafe.Pointer(&c)), 0x3)
   718  	if r == 0 {
   719  		return unboxError(err)
   720  	}
   721  	return nil
   722  }
   723  
   724  // InWow64Process is a helper function that just calls'IsWow64Process' with the
   725  // 'CurrentProcess' handle to determine if the current process is a WOW64 process.
   726  func InWow64Process() (bool, error) {
   727  	return IsWow64Process(CurrentProcess)
   728  }
   729  
   730  /*
   731  // IsVirtualizationEnabled return true if the current device processor has the
   732  // PF_VIRT_FIRMWARE_ENABLED flag. This will just indicate if the device has the
   733  // capability to run Virtual Machines. This is commonly not the case of many VMs
   734  // themselves.
   735  //
   736  // Be cautious, as many hypervisors have the ability to still expose this CPU
   737  // flag to guests.
   738  //
   739  // This flag is grabbed from KSHARED_USER_DATA.
   740  func IsVirtualizationEnabled() bool {
   741  	// 0x15 - PF_VIRT_FIRMWARE_ENABLED
   742  	return (*kernelSharedData)(unsafe.Pointer(kernelShared)).ProcessorFeatures[0x15] > 0
   743  }*/
   744  
   745  // SetCommandLine will attempt to read the Process PEB and overrite the
   746  // 'ProcessParameters.CommandLine' property with the supplied string value.
   747  //
   748  // This will NOT change the ImagePath or Binary Name.
   749  //
   750  // This will return any errors that occur during reading the PEB.
   751  //
   752  // DOES NOT WORK ON WOW6432 PEBs!
   753  //   - These are in a separate memory space and seem to only be read once? or the
   754  //     data is copied somewhere else. Even if I call 'NtWow64QueryInformationProcess64'
   755  //     and change it, it does NOT seem to care. *shrug* who TF uses x86 anyway in 2022!?
   756  //
   757  // TODO(dij): Since we have backwards compatibility now. The 32bit PEB can be read
   758  // using NtQueryInformationProcess/ProcessWow64Information which returns
   759  // 32bit pointer to the PEB in 32bit mode.
   760  func SetCommandLine(s string) error {
   761  	c, err := UTF16FromString(s)
   762  	if err != nil {
   763  		return err
   764  	}
   765  	p, err := getProcessPeb()
   766  	if err != nil {
   767  		return err
   768  	}
   769  	p.ProcessParameters.CommandLine.Buffer = &c[0]
   770  	p.ProcessParameters.CommandLine.Length = uint16(len(c)*2) - 1
   771  	p.ProcessParameters.CommandLine.MaximumLength = p.ProcessParameters.CommandLine.Length
   772  	return nil
   773  }
   774  
   775  // SwapMouseButtons uses the 'SystemParametersInfo' API call to trigger the
   776  // swapping of the left and right mouse buttons. Set to 'True' to swap and
   777  // 'False' to disable it.
   778  //
   779  // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfoa
   780  func SwapMouseButtons(e bool) error {
   781  	var v uint32
   782  	if e {
   783  		v = 1
   784  	}
   785  	// 0x21 - SPI_SETMOUSEBUTTONSWAP
   786  	r, _, err := syscallN(funcSystemParametersInfo.address(), 0x21, uintptr(v), 0, 0x3)
   787  	if r == 0 {
   788  		return unboxError(err)
   789  	}
   790  	return nil
   791  }
   792  func formatNtError(e uintptr) error {
   793  	// NOTE(dij): Not loading NTDLL here as we /should/ already have loaded it
   794  	//            as we're calling this function due to an Nt* function error
   795  	//            status. If not, this just acts like a standard 'FormatMessage'
   796  	//            call.
   797  	var (
   798  		o       [300]uint16
   799  		r, _, _ = syscallN(funcFormatMessage.address(), 0x3A00, dllNtdll.addr, e, 0x409, uintptr(unsafe.Pointer(&o)), 0x12C, 0)
   800  		// 0x3A00 - FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_HMODULE |
   801  		//          FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS
   802  		// 0x409  - English LANG and English SUB
   803  	)
   804  	if r == 0 {
   805  		return syscall.Errno(e)
   806  	}
   807  	v := r
   808  	// Remove newline at the end
   809  	for ; r > 0; r-- {
   810  		if o[r] == '\n' || o[r] == '\r' {
   811  			if r > 1 && (o[r-1] == '\n' || o[r-1] == '\r') {
   812  				r--
   813  			}
   814  			break
   815  		}
   816  	}
   817  	// CAan't find it? Just return what we have.
   818  	if r == 0 {
   819  		return errors.New(UTF16ToString(o[:v]))
   820  	}
   821  	// Remove prepended "{TYPE}" string
   822  	if o[0] == '{' {
   823  		for i := uintptr(1); i < r; i++ {
   824  			if o[i] == '\n' || o[i] == '\r' {
   825  				if i+1 < r && (o[i+1] == '\n' || o[i+1] == '\r') {
   826  					i++
   827  				}
   828  				return errors.New(UTF16ToString(o[i+1 : r]))
   829  			}
   830  		}
   831  	}
   832  	return errors.New(UTF16ToString(o[:r]))
   833  }
   834  
   835  // GetLocalUser attempts to return the username associated with the current Thread
   836  // or Process.
   837  //
   838  // This function will first check if the Thread is using a Token (Impersonation)
   839  // and if not it will then pull the Token for the Process instead.
   840  //
   841  // This function will concationate the domain (or local workstation) name if the
   842  // Token provides one.
   843  //
   844  // If any errors occur, an empty string with the error will be returned.
   845  func GetLocalUser() (string, error) {
   846  	var t uintptr
   847  	// 0x20008 - TOKEN_READ | TOKEN_QUERY
   848  	if err := OpenThreadToken(CurrentThread, 0x20008, true, &t); err != nil {
   849  		if err = OpenProcessToken(CurrentProcess, 0x20008, &t); err != nil {
   850  			return "", err
   851  		}
   852  	}
   853  	u, err := UserFromToken(t)
   854  	if CloseHandle(t); err != nil {
   855  		return "", err
   856  	}
   857  	return u, nil
   858  }
   859  
   860  // CheckDebugWithLoad will attempt to check for a debugger by loading a non-loaded
   861  // DLL specified and will check for exclusive access (which is false for debuggers).
   862  //
   863  // If the file can be opened, the library is freed and the file is closed. This
   864  // will return true ONLY if opening for exclusive access fails.
   865  //
   866  // Any errors opening or loading DLLs will silently return false.
   867  func CheckDebugWithLoad(d string) bool {
   868  	var (
   869  		p      = fullPath(d)
   870  		n, err = UTF16PtrFromString(p)
   871  	)
   872  	if err != nil {
   873  		return false
   874  	}
   875  	var (
   876  		h       uintptr
   877  		r, _, _ = syscallN(funcGetModuleHandleEx.address(), 0x2, uintptr(unsafe.Pointer(n)), uintptr(unsafe.Pointer(&h)))
   878  		// 0x2 - GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
   879  	)
   880  	if r > 0 {
   881  		return false
   882  	}
   883  	if h, err = loadLibraryEx(p); err != nil || h == 0 {
   884  		return false
   885  	}
   886  	// 0x80000000 - FILE_FLAG_WRITE_THROUGH
   887  	// 0x0        - EXCLUSIVE
   888  	// 0x3        - OPEN_EXISTING
   889  	f, err := CreateFile(p, 0x80000000, 0, nil, 0x3, 0, 0)
   890  	if syscall.FreeLibrary(syscall.Handle(h)); err != nil {
   891  		return err.(syscall.Errno) != 0x2
   892  	}
   893  	CloseHandle(f)
   894  	return false
   895  }
   896  
   897  // IsUserNetworkToken will return true if the origin of the Token was a LoginUser
   898  // network impersonation API call and NOT a duplicated Token via Token or Thread
   899  // impersonation.
   900  func IsUserNetworkToken(t uintptr) bool {
   901  	if t == 0 {
   902  		return false
   903  	}
   904  	var (
   905  		n   uint32
   906  		b   [16]byte
   907  		err = GetTokenInformation(t, 0x7, &b[0], 16, &n)
   908  		// 0x7 - TokenSource
   909  	)
   910  	if err != nil {
   911  		return false
   912  	}
   913  	// Match [65 100 118 97 112 105 32 32] == "Advapi"
   914  	return b[0] == 65 && b[1] == 100 && b[6] == 32 && b[7] == 32
   915  }
   916  
   917  // EnablePrivileges will attempt to enable the supplied Windows privilege values
   918  // on the current process's Token.
   919  //
   920  // Errors during encoding, lookup or assignment will be returned and not all
   921  // privileges will be assigned, if they occur.
   922  func EnablePrivileges(s ...string) error {
   923  	if len(s) == 0 {
   924  		return nil
   925  	}
   926  	var (
   927  		t   uintptr
   928  		err = OpenProcessToken(CurrentProcess, 0x200E8, &t)
   929  		// 0x200E8 - TOKEN_READ (STANDARD_RIGHTS_READ | TOKEN_QUERY) | TOKEN_WRITE
   930  		//            (TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT)
   931  	)
   932  	if err != nil {
   933  		return xerr.Wrap("OpenProcessToken", err)
   934  	}
   935  	err = EnableTokenPrivileges(t, s...)
   936  	CloseHandle(t)
   937  	return err
   938  }
   939  
   940  // IsSecureBootEnabled returns true if Secure Boot is enabled in the current device.
   941  //
   942  // This function returns true or false and any errors that may occur during checking
   943  // for secure boot.
   944  func IsSecureBootEnabled() (bool, error) {
   945  	var (
   946  		i       uint16
   947  		n       uint32
   948  		r, _, _ = syscallN(funcNtQuerySystemInformation.address(), 0x91, uintptr(unsafe.Pointer(&i)), 2, uintptr(unsafe.Pointer(&n)))
   949  		// 0x91 - SystemSecureBootInformation
   950  	)
   951  	if r > 0 {
   952  		return false, formatNtError(r)
   953  	}
   954  	return (i & 0xFF) == 1, nil
   955  }
   956  
   957  // SetAllThreadsToken sets the Token for all current Golang threads. This is an
   958  // easy way to do thread impersonation across the entire runtime.
   959  //
   960  // Calls 'ForEachThread' -> 'SetThreadToken' under the hood.
   961  func SetAllThreadsToken(h uintptr) error {
   962  	return ForEachThread(func(t uintptr) error { return SetThreadToken(t, h) })
   963  }
   964  func getProcessPeb() (*processPeb, error) {
   965  	/*
   966  		PVOID64 GetPeb64()
   967  		{
   968  			PVOID64 peb64 = NULL;
   969  
   970  			if (API::IsAvailable(API_IDENTIFIER::API_NtWow64QueryInformationProcess64))
   971  			{
   972  				PROCESS_BASIC_INFORMATION_WOW64 pbi64 = {};
   973  
   974  				auto NtWow64QueryInformationProcess64 = static_cast<pNtWow64QueryInformationProcess64>(API::GetAPI(API_IDENTIFIER::API_NtWow64QueryInformationProcess64));
   975  				NTSTATUS status = NtWow64QueryInformationProcess64(GetCurrentProcess(), ProcessBasicInformation, &pbi64, sizeof(pbi64), nullptr);
   976  				if ( NT_SUCCESS ( status ) )
   977  					peb64 = pbi64.PebBaseAddress;
   978  			}
   979  
   980  			return peb64;
   981  		}
   982  	*/
   983  	var (
   984  		p       processBasicInfo
   985  		r, _, _ = syscallN(
   986  			funcNtQueryInformationProcess.address(), CurrentProcess, 0, uintptr(unsafe.Pointer(&p)),
   987  			unsafe.Sizeof(p), 0,
   988  		)
   989  	)
   990  	if r > 0 {
   991  		return nil, formatNtError(r)
   992  	}
   993  	return (*processPeb)(unsafe.Pointer(p.PebBaseAddress)), nil
   994  }
   995  
   996  // ImpersonatePipeToken will attempt to impersonate the Token used by the Named
   997  // Pipe client.
   998  //
   999  // This function is only usable on Windows with a Server Pipe handle.
  1000  //
  1001  // BUG(dij): I'm not sure if this is broken or this is how it's handled. I'm
  1002  //
  1003  //	getting error 5.
  1004  //
  1005  // Pipe insights:
  1006  //
  1007  //	https://papers.vx-underground.org/papers/Windows/System%20Components%20and%20Abuse/Offensive%20Windows%20IPC%20Internals%201%20Named%20Pipes.pdf
  1008  func ImpersonatePipeToken(h uintptr) error {
  1009  	// NOTE(dij): For best results, we FIRST impersonate the token, THEN
  1010  	//            we try to set the token to each user thread with a duplicated
  1011  	//            token set to impersonate. (Similar to an Impersonate call).
  1012  	runtime.LockOSThread()
  1013  	if err := ImpersonateNamedPipeClient(h); err != nil {
  1014  		runtime.UnlockOSThread()
  1015  		return err
  1016  	}
  1017  	var y uintptr
  1018  	// 0xF01FF - TOKEN_ALL_ACCESS
  1019  	if err := OpenThreadToken(CurrentThread, 0xF01FF, true, &y); err != nil {
  1020  		runtime.UnlockOSThread()
  1021  		return err
  1022  	}
  1023  	err := SetAllThreadsToken(y)
  1024  	CloseHandle(y)
  1025  	runtime.UnlockOSThread()
  1026  	return err
  1027  }
  1028  func heapCreate(n uint64) (uintptr, error) {
  1029  	// 0x1002 - MEM_COMMIT? | HEAP_GROWABLE
  1030  	r, _, err := syscallN(funcRtlCreateHeap.address(), 0x1002, 0, 0, uintptr(n), 0, 0)
  1031  	if r == 0 {
  1032  		return 0, unboxError(err)
  1033  	}
  1034  	return r, nil
  1035  }
  1036  func (p *dumpParam) resize(n uint64) error {
  1037  	if n < p.s {
  1038  		return nil
  1039  	}
  1040  	var (
  1041  		v      = (p.s + n) * 2
  1042  		h, err = heapReAlloc(p.b, p.h, v, false)
  1043  	)
  1044  	if err != nil {
  1045  		return err
  1046  	}
  1047  	p.h, p.s = h, v
  1048  	return nil
  1049  }
  1050  
  1051  // PhysicalInfo will query the system using NtQuerySystemInformation to grab the
  1052  // number of CPUs installed and the current memory (in MB) that is avaliable to
  1053  // the system (installed physically).
  1054  func PhysicalInfo() (uint8, uint32, error) {
  1055  	var (
  1056  		n       uint64
  1057  		i       systemBasicInfo
  1058  		r, _, _ = syscallN(funcNtQuerySystemInformation.address(), 0x0, uintptr(unsafe.Pointer(&i)), unsafe.Sizeof(i), uintptr(unsafe.Pointer(&n)))
  1059  		// 0x0 - SystemBasicInformation
  1060  	)
  1061  	if r > 0 {
  1062  		return 0, 0, formatNtError(r)
  1063  	}
  1064  	return i.NumProc, uint32((uint64(i.PageSize)*uint64(i.PhysicalPages))/0x100000) + 1, nil
  1065  }
  1066  func getThreadID(h uintptr) (uint32, error) {
  1067  	var (
  1068  		t       threadBasicInfo
  1069  		r, _, _ = syscallN(funcNtQueryInformationThread.address(), h, 0, uintptr(unsafe.Pointer(&t)), unsafe.Sizeof(t), 0)
  1070  	)
  1071  	if r > 0 {
  1072  		return 0, formatNtError(r)
  1073  	}
  1074  	return uint32(t.ClientID.Thread), nil
  1075  }
  1076  
  1077  // GetCodeIntegrityState returns a bitvalue that returns the Code Integrity status
  1078  // of the current device. If the return value is zero without an error, this means
  1079  // that code integrity is disabled.
  1080  func GetCodeIntegrityState() (uint32, error) {
  1081  	var (
  1082  		n       uint32
  1083  		s       = [2]uint32{8, 0}
  1084  		r, _, _ = syscallN(funcNtQuerySystemInformation.address(), 0x67, uintptr(unsafe.Pointer(&s)), 8, uintptr(unsafe.Pointer(&n)))
  1085  		// 0x67 - SystemCodeIntegrityInformation
  1086  	)
  1087  	if r > 0 {
  1088  		return 0, formatNtError(r)
  1089  	}
  1090  	return s[1], nil
  1091  }
  1092  func (p *dumpParam) write(w io.Writer) error {
  1093  	var (
  1094  		b      = (*[]byte)(unsafe.Pointer(&SliceHeader{Data: unsafe.Pointer(p.h), Len: int(p.w), Cap: int(p.w)}))
  1095  		n, err = w.Write(*b)
  1096  	)
  1097  	if b, *b = nil, nil; err != nil {
  1098  		return err
  1099  	}
  1100  	if n != int(p.w) {
  1101  		return io.ErrShortWrite
  1102  	}
  1103  	return nil
  1104  }
  1105  
  1106  // GetDiskSize returns the size in bytes of the disk by it's NT path or the path
  1107  // to a partition or volume on the disk.
  1108  //
  1109  // Any errors encountered during reading will be returned.
  1110  //
  1111  // The name can be in the format of an NT path such as:
  1112  //
  1113  //   - \\.\C:
  1114  //   - \\.\PhysicalDrive0
  1115  //
  1116  // Both are equal on /most/ systems.
  1117  func GetDiskSize(name string) (uint64, error) {
  1118  	// 0x1 - FILE_SHARE_READ
  1119  	// 0x3 - OPEN_EXISTING
  1120  	h, err := CreateFile(name, 0, 0x1, nil, 0x3, 0, 0)
  1121  	if err != nil {
  1122  		return 0, err
  1123  	}
  1124  	var (
  1125  		g diskGeometryEx
  1126  		s [4 + ptrSize]byte // IO_STATUS_BLOCK
  1127  	)
  1128  	// 0x700A0 - IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
  1129  	r, _, err := syscallN(funcNtDeviceIoControlFile.address(), h, 0, 0, 0, uintptr(unsafe.Pointer(&s)), 0x700A0, 0, 0, uintptr(unsafe.Pointer(&g)), 0x20+ptrSize)
  1130  	if CloseHandle(h); r > 0 {
  1131  		return 0, formatNtError(r)
  1132  	}
  1133  	return g.Size, nil
  1134  }
  1135  
  1136  // UserFromToken will attempt to get the User SID from the supplied Token and
  1137  // return the associated Username and Domain string from the SID.
  1138  func UserFromToken(h uintptr) (string, error) {
  1139  	u, err := GetTokenUser(h)
  1140  	if err != nil {
  1141  		return "", err
  1142  	}
  1143  	return u.User.Sid.UserName()
  1144  }
  1145  
  1146  // ForEachThread is a helper function that allows a function to be executed with
  1147  // the handle of the Thread.
  1148  //
  1149  // This function only returns an error if enumerating the Threads generates an
  1150  // error or the supplied function returns an error.
  1151  //
  1152  // This function ONLY targets Golang threads. To target all Process threads,
  1153  // use 'ForEachProcThread'.
  1154  func ForEachThread(f func(uintptr) error) error {
  1155  	var err error
  1156  	for i := uintptr(allm); ; {
  1157  		if h := *(*uintptr)(unsafe.Pointer(i + ptrThread)); h > 0 {
  1158  			if err = f(h); err != nil {
  1159  				break
  1160  			}
  1161  		}
  1162  		n := (*uintptr)(unsafe.Pointer(i + ptrNext))
  1163  		if n == nil || *n == 0 {
  1164  			break // Reached bottom of linked list
  1165  		}
  1166  		i = *n
  1167  	}
  1168  	return err
  1169  }
  1170  
  1171  // GetTokenUser retrieves access token user account information and SID.
  1172  func GetTokenUser(h uintptr) (*TokenUser, error) {
  1173  	u, err := getTokenInfo(h, 1, 50)
  1174  	if err != nil {
  1175  		return nil, err
  1176  	}
  1177  	return (*TokenUser)(u), nil
  1178  }
  1179  
  1180  // GetVersionNumbers returns the NTDLL internal version numbers as Major, Minor
  1181  // and Build.
  1182  //
  1183  // This function should return the correct values regardless of manifest version.
  1184  func GetVersionNumbers() (uint32, uint32, uint16) {
  1185  	var m, n, b uint32
  1186  	syscallN(funcRtlGetNtVersionNumbers.address(), uintptr(unsafe.Pointer(&m)), uintptr(unsafe.Pointer(&n)), uintptr(unsafe.Pointer(&b)))
  1187  	return m, n, uint16(b)
  1188  }
  1189  func enablePrivileges(h uintptr, s []string) error {
  1190  	var (
  1191  		p   privileges
  1192  		err error
  1193  	)
  1194  	for i := range s {
  1195  		if i > 5 {
  1196  			break
  1197  		}
  1198  		if err = LookupPrivilegeValue("", s[i], &p.Privileges[i].Luid); err != nil {
  1199  			if xerr.ExtendedInfo {
  1200  				return xerr.Wrap(`cannot lookup "`+s[i]+`"`, err)
  1201  			}
  1202  			return xerr.Wrap("cannot lookup Privilege", err)
  1203  		}
  1204  		p.Privileges[i].Attributes = 0x2 // SE_PRIVILEGE_ENABLED
  1205  	}
  1206  	p.PrivilegeCount = uint32(len(s))
  1207  	if err = AdjustTokenPrivileges(h, false, unsafe.Pointer(&p), uint32(unsafe.Sizeof(p)), nil, nil); err != nil {
  1208  		return xerr.Wrap("cannot assign all Privileges", err)
  1209  	}
  1210  	return nil
  1211  }
  1212  
  1213  // GetProcessFileName will attempt to retrieve the basename of the process
  1214  // related to the open Process handle supplied.
  1215  func GetProcessFileName(h uintptr) (string, error) {
  1216  	var (
  1217  		u ntUnicodeString
  1218  		n uint32
  1219  	)
  1220  	r, _, _ := syscallN(
  1221  		funcNtQueryInformationProcess.address(), h, 0x1B, uintptr(unsafe.Pointer(&u)),
  1222  		unsafe.Sizeof(u)+260, uintptr(unsafe.Pointer(&n)),
  1223  	)
  1224  	// 0x1B - ProcessImageFileName
  1225  	if r > 0 {
  1226  		return "", formatNtError(r)
  1227  	}
  1228  	v := UTF16ToString(u.Buffer[4:n])
  1229  	for i := len(v) - 1; i > 0; i-- {
  1230  		if v[i] == '\\' {
  1231  			return v[i+1:], nil
  1232  		}
  1233  	}
  1234  	return v, nil
  1235  }
  1236  
  1237  // ForEachProcThread is a helper function that allows a function to be executed
  1238  // with the handle of the Thread.
  1239  //
  1240  // This function only returns an error if enumerating the Threads generates an
  1241  // error or the supplied function returns an error.
  1242  //
  1243  // This function targets ALL threads (including non-Golang threads). To target
  1244  // all only Golang threads, use 'ForEachThread'.
  1245  func ForEachProcThread(f func(uintptr) error) error {
  1246  	return EnumThreads(GetCurrentProcessID(), func(t ThreadEntry) error {
  1247  		// old (0xE0 - THREAD_QUERY_INFORMATION | THREAD_SET_INFORMATION | THREAD_SET_THREAD_TOKEN)
  1248  		// 0x1FFFFF - THREAD_ALL_ACCESS
  1249  		v, err := t.Handle(0x1FFFFF)
  1250  		if err != nil {
  1251  			return err
  1252  		}
  1253  		err = f(v)
  1254  		CloseHandle(v)
  1255  		return err
  1256  	})
  1257  }
  1258  func getThreadStartAddress(h uintptr) (uintptr, error) {
  1259  	var (
  1260  		i       uintptr
  1261  		r, _, _ = syscallN(funcNtQueryInformationThread.address(), h, 0x9, uintptr(unsafe.Pointer(&i)), ptrSize, 0)
  1262  		// 0x9 - ThreadQuerySetWin32StartAddress
  1263  	)
  1264  	if r > 0 {
  1265  		return 0, formatNtError(r)
  1266  	}
  1267  	return i, nil
  1268  }
  1269  
  1270  // FileSigningIssuerName attempts to read the Authenticate signing certificate
  1271  // issuer name for the specified file path.
  1272  //
  1273  // If the file does not exist or a certificate cannot be found, this returns the
  1274  // error 'syscall.EINVAL'.
  1275  //
  1276  // If the function success, the return result will be the string name of the
  1277  // certificate issuer.
  1278  func FileSigningIssuerName(path string) (string, error) {
  1279  	f, err1 := UTF16PtrFromString(path)
  1280  	if err1 != nil {
  1281  		return "", err1
  1282  	}
  1283  	var (
  1284  		s, h      uintptr
  1285  		r, _, err = syscallN(
  1286  			funcCryptQueryObject.address(), 0x1, uintptr(unsafe.Pointer(f)), 0x400, 0x2,
  1287  			0, 0, 0, 0, uintptr(unsafe.Pointer(&s)), uintptr(unsafe.Pointer(&h)), 0,
  1288  		)
  1289  		// 0x1   - CERT_QUERY_OBJECT_FILE
  1290  		// 0x400 - CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED
  1291  		// 0x2   - CERT_QUERY_FORMAT_FLAG_BINARY
  1292  	)
  1293  	if r == 0 {
  1294  		if err == 0x80092009 { // 0x80092009 - Object not found, file isn't signed.
  1295  			return "", syscall.EINVAL
  1296  		}
  1297  		return "", unboxError(err)
  1298  	}
  1299  	var x uint32
  1300  	// 0x6 - CMSG_SIGNER_INFO_PARAM
  1301  	if r, _, err = syscallN(funcCryptMsgGetParam.address(), h, 0x6, 0, 0, uintptr(unsafe.Pointer(&x))); r == 0 {
  1302  		syscallN(funcCryptMsgClose.address(), h)
  1303  		syscallN(funcCertCloseStore.address(), s)
  1304  		return "", unboxError(err)
  1305  	}
  1306  	b := make([]byte, x)
  1307  	// 0x6 - CMSG_SIGNER_INFO_PARAM
  1308  	r, _, err = syscallN(funcCryptMsgGetParam.address(), h, 0x6, 0, uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(&x)))
  1309  	if syscallN(funcCryptMsgClose.address(), h); r == 0 {
  1310  		syscallN(funcCertCloseStore.address(), s)
  1311  		return "", unboxError(err)
  1312  	}
  1313  	var (
  1314  		v = (*certSigner)(unsafe.Pointer(&b[0]))
  1315  		i = certInfo{Issuer: v.Issuer, Serial: v.Serial}
  1316  	)
  1317  	// 0x10001 - X509_ASN_ENCODING | PKCS_7_ASN_ENCODING
  1318  	// 0xB0000 - CERT_FIND_SUBJECT_CERT
  1319  	r, _, err = syscallN(funcCertFindCertificateInStore.address(), s, 0x10001, 0, 0xB0000, uintptr(unsafe.Pointer(&i)), 0)
  1320  	if syscallN(funcCertCloseStore.address(), s); r == 0 {
  1321  		return "", unboxError(err)
  1322  	}
  1323  	var (
  1324  		n string
  1325  		k uintptr
  1326  	)
  1327  	// 0x4 - CERT_NAME_SIMPLE_DISPLAY_TYPE
  1328  	// 0x0 - CERT_NAME_ISSUER_FLAG
  1329  	if k, _, err = syscallN(funcCertGetNameString.address(), r, 0x4, 0x0, 0, 0, 0); k > 0 {
  1330  		c := make([]uint16, k)
  1331  		if k, _, err = syscallN(funcCertGetNameString.address(), r, 0x4, 0x0, 0, uintptr(unsafe.Pointer(&c[0])), k); k > 0 {
  1332  			n = UTF16ToString(c[:k])
  1333  		}
  1334  	}
  1335  	if syscallN(funcCertFreeCertificateContext.address(), r); k == 0 {
  1336  		return "", unboxError(err)
  1337  	}
  1338  	return n, nil
  1339  }
  1340  
  1341  // StringListToUTF16Block creates a UTF16 encoded block for usage as a Process
  1342  // environment block.
  1343  //
  1344  // This function returns an error if any of the environment strings are not in
  1345  // the 'KEY=VALUE' format or contain a NUL byte.
  1346  func StringListToUTF16Block(s []string) (*uint16, error) {
  1347  	if len(s) == 0 {
  1348  		return nil, nil
  1349  	}
  1350  	var t, i, l int
  1351  	for _, x := range s {
  1352  		for v := range x {
  1353  			if x[v] == 0 {
  1354  				return nil, syscall.EINVAL
  1355  			}
  1356  		}
  1357  		if q := strings.IndexByte(x, '='); q <= 0 {
  1358  			if xerr.ExtendedInfo {
  1359  				return nil, xerr.Sub(`invalid env value "`+x+`"`, 0x17)
  1360  			}
  1361  			return nil, xerr.Sub("invalid env value", 0x17)
  1362  		}
  1363  		t += len(x) + 1
  1364  	}
  1365  	t++
  1366  	b := make([]byte, t)
  1367  	for _, v := range s {
  1368  		l = len(v)
  1369  		copy(b[i:i+l], v)
  1370  		b[i+l] = 0
  1371  		i = i + l + 1
  1372  	}
  1373  	b[i] = 0
  1374  	return &UTF16EncodeStd([]rune(string(b)))[0], nil
  1375  }
  1376  
  1377  // EnableTokenPrivileges will attempt to enable the supplied Windows privilege
  1378  // values on the supplied process Token.
  1379  //
  1380  // Errors during encoding, lookup or assignment will be returned and not all
  1381  // privileges will be assigned, if they occur.
  1382  func EnableTokenPrivileges(h uintptr, s ...string) error {
  1383  	if len(s) == 0 {
  1384  		return nil
  1385  	}
  1386  	if len(s) <= 5 {
  1387  		return enablePrivileges(h, s)
  1388  	}
  1389  	for x, w := 0, 0; x < len(s); {
  1390  		if w = 5; x+w > len(s) {
  1391  			w = len(s) - x
  1392  		}
  1393  		if err := enablePrivileges(h, s[x:x+w]); err != nil {
  1394  			return err
  1395  		}
  1396  		x += w
  1397  	}
  1398  	return nil
  1399  }
  1400  func heapAlloc(h uintptr, s uint64, z bool) (uintptr, error) {
  1401  	var f uint32
  1402  	if z {
  1403  		f |= 0x08
  1404  	}
  1405  	r, _, err := syscallN(funcRtlAllocateHeap.address(), h, uintptr(f), uintptr(s))
  1406  	if r == 0 {
  1407  		return 0, unboxError(err)
  1408  	}
  1409  	return r, nil
  1410  }
  1411  func (p *dumpParam) copy(o uint64, b uintptr, s uint32) error {
  1412  	if err := p.resize(o + uint64(s)); err != nil {
  1413  		return err
  1414  	}
  1415  	copyMemory(p.h+uintptr(o), b, s)
  1416  	p.w += uint64(s)
  1417  	return nil
  1418  }
  1419  func heapReAlloc(h, m uintptr, s uint64, z bool) (uintptr, error) {
  1420  	var f uint32
  1421  	if z {
  1422  		// 0x8 - HEAP_ZERO_MEMORY
  1423  		f |= 0x8
  1424  	}
  1425  	r, _, err := syscallN(funcRtlReAllocateHeap.address(), h, uintptr(f), m, uintptr(s))
  1426  	if r == 0 {
  1427  		return 0, unboxError(err)
  1428  	}
  1429  	return r, nil
  1430  }
  1431  func dumpCallbackFunc(_ uintptr, i uintptr, r *dumpOutput) uintptr {
  1432  	switch *(*uint32)(unsafe.Pointer(i + 4 + ptrSize)) {
  1433  	case 11:
  1434  		r.Status = 1
  1435  	case 12:
  1436  		var (
  1437  			o = *(*uint64)(unsafe.Pointer(i + 8 + (ptrSize * 2)))   // Offset
  1438  			b = *(*uintptr)(unsafe.Pointer(i + 16 + (ptrSize * 2))) // Buffer
  1439  			s = *(*uint32)(unsafe.Pointer(i + 16 + (ptrSize * 3)))  // Size
  1440  		)
  1441  		if err := dumpStack.copy(o, b, s); err != nil {
  1442  			r.Status = 1
  1443  			return 0
  1444  		}
  1445  		r.Status = 0
  1446  	case 13:
  1447  		r.Status = 0
  1448  	case 16, 17:
  1449  		r.Status = 0
  1450  	}
  1451  	return 1
  1452  }
  1453  func getTokenInfo(t uintptr, c uint32, i int) (unsafe.Pointer, error) {
  1454  	for n := uint32(i); ; {
  1455  		var (
  1456  			b   = make([]byte, n)
  1457  			err = GetTokenInformation(t, c, &b[0], uint32(len(b)), &n)
  1458  		)
  1459  		if err == nil {
  1460  			return unsafe.Pointer(&b[0]), nil
  1461  		}
  1462  		if err != syscall.ERROR_INSUFFICIENT_BUFFER {
  1463  			return nil, err
  1464  		}
  1465  		if n <= uint32(len(b)) {
  1466  			return nil, err
  1467  		}
  1468  	}
  1469  }
  1470  
  1471  // MiniDumpWriteDump Windows API Call
  1472  //
  1473  //	Writes user-mode minidump information to the specified file handle.
  1474  //
  1475  // https://docs.microsoft.com/en-us/windows/win32/api/minidumpapiset/nf-minidumpapiset-minidumpwritedump
  1476  //
  1477  // Updated version that will take and use the supplied Writer instead of the file
  1478  // handle is zero.
  1479  //
  1480  // This function may fail if attempting to dump a process that is a different CPU
  1481  // architecture than the host process.
  1482  //
  1483  // Dumping to a Writer instead of a file is not avaliable on systems older than
  1484  // Windows Vista and will return 'syscall.EINVAL' instead.
  1485  func MiniDumpWriteDump(h uintptr, pid uint32, o uintptr, f uint32, w io.Writer) error {
  1486  	if o > 0 {
  1487  		r, _, err := syscallN(funcMiniDumpWriteDump.address(), h, uintptr(pid), o, uintptr(f), 0, 0, 0)
  1488  		if r == 0 {
  1489  			return unboxError(err)
  1490  		}
  1491  		return nil
  1492  	}
  1493  	if !IsWindowsVista() {
  1494  		return syscall.EINVAL
  1495  	}
  1496  	if err := dumpStack.init(); err != nil {
  1497  		return err
  1498  	}
  1499  	var (
  1500  		a          = dumpCallback{Func: dumpCallbackOnce.f}
  1501  		r, _, err1 = syscallN(funcMiniDumpWriteDump.address(), h, uintptr(pid), 0, uintptr(f), 0, 0, uintptr(unsafe.Pointer(&a)))
  1502  	)
  1503  	if r == 0 {
  1504  		dumpStack.close()
  1505  		return unboxError(err1)
  1506  	}
  1507  	err := dumpStack.write(w)
  1508  	dumpStack.close()
  1509  	return err
  1510  }