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

     1  //go:build windows
     2  // +build windows
     3  
     4  // Copyright (C) 2020 - 2023 iDigitalFlame
     5  //
     6  // This program is free software: you can redistribute it and/or modify
     7  // it under the terms of the GNU General Public License as published by
     8  // the Free Software Foundation, either version 3 of the License, or
     9  // any later version.
    10  //
    11  // This program is distributed in the hope that it will be useful,
    12  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  // GNU General Public License for more details.
    15  //
    16  // You should have received a copy of the GNU General Public License
    17  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    18  //
    19  
    20  package winapi
    21  
    22  import (
    23  	"syscall"
    24  	"unsafe"
    25  )
    26  
    27  // ThreadEntry is a basic struct passed to the user supplied function during
    28  // a call to 'EnumThreads'. This struct supplies basic Thread information and
    29  // can be used to gain more information about a Thread.
    30  type ThreadEntry struct {
    31  	_   [0]func()
    32  	TID uint32
    33  	PID uint32
    34  	sus uint8
    35  }
    36  
    37  // ProcessEntry is a basic struct passed to the user supplied function during
    38  // a call to 'EnumProcesses'. This struct supplies basic Process information
    39  // and can be used to gain more information about a Process.
    40  type ProcessEntry struct {
    41  	_       [0]func()
    42  	Name    string
    43  	PID     uint32
    44  	PPID    uint32
    45  	Threads uint32
    46  	session int32
    47  }
    48  
    49  // User attempts to reterive a string version of the username that this Process
    50  // is running under.
    51  //
    52  // A string username and any errors during reterival will be returned.
    53  func (p ProcessEntry) User() (string, error) {
    54  	// 0x400 - PROCESS_QUERY_INFORMATION
    55  	h, err := OpenProcess(0x400, false, p.PID)
    56  	if err != nil {
    57  		return "", err
    58  	}
    59  	var t uintptr
    60  	// 0x8 - TOKEN_QUERY
    61  	if err = OpenProcessToken(h, 0x8, &t); err != nil {
    62  		CloseHandle(h)
    63  		return "", err
    64  	}
    65  	u, err := UserFromToken(t)
    66  	CloseHandle(t)
    67  	CloseHandle(h)
    68  	return u, err
    69  }
    70  
    71  // IsSuspended will attempt to determine if the current Thread is suspended. If
    72  // the state information was supplied initially during discovery, it will be
    73  // immediately returned, otherwise a Suspend/Resume cycle will be done to get the
    74  // Thread suspension count.
    75  //
    76  // The return result will be true if the Thread is currently suspended and any
    77  // errors that may have occurred.
    78  func (t ThreadEntry) IsSuspended() (bool, error) {
    79  	if t.sus > 0 {
    80  		return t.sus == 2, nil
    81  	}
    82  	// 0x42 - THREAD_QUERY_INFORMATION | THREAD_SUSPEND_RESUME
    83  	h, err := OpenThread(0x42, false, t.TID)
    84  	if err != nil {
    85  		return false, err
    86  	}
    87  	s, err := t.suspended(h)
    88  	if CloseHandle(h); err != nil {
    89  		return false, err
    90  	}
    91  	return s, nil
    92  }
    93  
    94  // Handle is a convenience function that calls 'OpenThread' on the Thread with
    95  // the supplied access mask and returns a Thread handle that must be closed
    96  // when you are done using it.
    97  //
    98  // This function does NOT make handles inheritable.
    99  //
   100  // Any errors that occur during the operation will be returned.
   101  func (t ThreadEntry) Handle(a uint32) (uintptr, error) {
   102  	return OpenThread(a, false, t.TID)
   103  }
   104  
   105  // Handle is a convenience function that calls 'OpenProcess' on the Process with
   106  // the supplied access mask and returns a Process handle that must be closed
   107  // when you are done using it.
   108  //
   109  // This function does NOT make handles inheritable.
   110  //
   111  // Any errors that occur during the operation will be returned.
   112  func (p ProcessEntry) Handle(a uint32) (uintptr, error) {
   113  	return OpenProcess(a, false, p.PID)
   114  }
   115  func (t ThreadEntry) suspended(h uintptr) (bool, error) {
   116  	if t.sus > 0 {
   117  		return t.sus == 2, nil
   118  	}
   119  	if getCurrentThreadID() == t.TID {
   120  		// Can't do a suspend/resume cycle on ourselves.
   121  		return false, syscall.EINVAL
   122  	}
   123  	if _, err := SuspendThread(h); err != nil {
   124  		return false, err
   125  	}
   126  	c, err := ResumeThread(h)
   127  	if err != nil {
   128  		return false, err
   129  	}
   130  	return c > 1, nil
   131  }
   132  
   133  // Info will attempt to retrieve the Process session and Token elevation status
   134  // and return it as a boolean (true if elevated) and a Session ID.
   135  //
   136  // The access mask can be used to determine the open permissions for the Process
   137  // and this function will automatically add the PROCESS_QUERY_INFORMATION mask.
   138  // If no access testing is desired, a value of zero is accepted.
   139  //
   140  // Boolean values for the elevation and session checks are passed as parameters
   141  // to disable/enable checking of the value. If the value check is disabled (false)
   142  // the return result will be the default value.
   143  //
   144  // Any errors during checking will be returned.
   145  //
   146  // To gain access to the underlying handle instead of opening a new one, use the
   147  // 'InfoEx' function.
   148  func (p ProcessEntry) Info(a uint32, elevated, session bool) (bool, uint32, error) {
   149  	_, e, s, err := p.InfoEx(a, elevated, session, false)
   150  	return e, s, err
   151  }
   152  
   153  // InfoEx will attempt to retrieve the Process handle (optional) session and Token
   154  // elevation status and return it as a boolean (true if elevated) and a Session ID.
   155  //
   156  // The access mask can be used to determine the open permissions for the Process
   157  // and this function will automatically add the PROCESS_QUERY_INFORMATION mask.
   158  // If no access testing is desired, a value of zero is accepted. Unlike the non-Ex
   159  // function 'Info', this function will return the un-closed Process handle if
   160  // the last Boolean value for handle is true.
   161  //
   162  // Boolean values for the elevation and session checks are passed as parameters
   163  // to disable/enable checking of the value. If the value check is disabled (false)
   164  // the return result will be the default value.
   165  //
   166  // Any errors during checking will be returned.
   167  func (p ProcessEntry) InfoEx(a uint32, elevated, session, handle bool) (uintptr, bool, uint32, error) {
   168  	if !handle && !elevated && !session {
   169  		return 0, false, 0, nil
   170  	}
   171  	if !handle && !elevated && session && p.session >= 0 {
   172  		return 0, false, uint32(p.session), nil
   173  	}
   174  	// 0x400 - PROCESS_QUERY_INFORMATION
   175  	//
   176  	// NOTE(dij): The reason we have an access param is so we can only open
   177  	//            the handle once while we're doing this to "check" if we can
   178  	//            access it with the requested access we want.
   179  	//            When Filters call this function, we do a quick 'Handle' check
   180  	//            to make sure we can open it before adding to the eval list.
   181  	h, err := OpenProcess(a|0x400, false, p.PID)
   182  	if err != nil {
   183  		return 0, false, 0, err
   184  	}
   185  	if !elevated && !session {
   186  		if !handle {
   187  			CloseHandle(h)
   188  			return 0, false, 0, nil
   189  		}
   190  		return h, false, 0, nil
   191  	}
   192  	var t uintptr
   193  	// 0x20008 - TOKEN_READ | TOKEN_QUERY
   194  	if err = OpenProcessToken(h, 0x20008|tokenPerms, &t); err != nil {
   195  		CloseHandle(h)
   196  		return 0, false, 0, err
   197  	}
   198  	var v uint32
   199  	if p.session >= 0 {
   200  		v = uint32(p.session)
   201  	} else {
   202  		var s uint32
   203  		// 0xC - TokenSessionId
   204  		if err = GetTokenInformation(t, 0xC, (*byte)(unsafe.Pointer(&v)), 4, &s); err != nil || s != 4 {
   205  			CloseHandle(t)
   206  			CloseHandle(h)
   207  			return 0, false, 0, err
   208  		}
   209  	}
   210  	e := IsTokenElevated(t)
   211  	if e {
   212  		switch u, err := GetTokenUser(t); {
   213  		case err != nil:
   214  			fallthrough
   215  		case u.User.Sid.IsWellKnown(0x17): // 0x17 - WinLocalServiceSid
   216  			fallthrough
   217  		case u.User.Sid.IsWellKnown(0x18): // 0x18 - WinNetworkServiceSid
   218  			e = false
   219  		}
   220  	}
   221  	if CloseHandle(t); !handle {
   222  		CloseHandle(h)
   223  		return 0, e, v, nil
   224  	}
   225  	return h, e, v, nil
   226  }