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 }