github.com/iDigitalFlame/xmt@v0.5.4/device/winapi/wts.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 "time" 24 "unsafe" 25 26 "github.com/iDigitalFlame/xmt/data" 27 ) 28 29 const ( 30 epoch = 0x19DB1DED53E8000 31 wtsProcSize = unsafe.Sizeof(wtsProcess{}) 32 wtsSessionSize = unsafe.Sizeof(wtsSession{}) 33 ) 34 35 // Session is a struct that is used to indicate Windows Terminal Services (WTS) 36 // Login/Session data. 37 // 38 // This struct is similar to 'device.Login' but contains more non-generic data. 39 type Session struct { 40 _ [0]func() 41 User string 42 Host string 43 Domain string 44 Login int64 45 LastInput int64 46 ID uint32 47 From [16]byte 48 Remote bool 49 Status uint8 50 } 51 type wtsAddr struct { 52 // DO NOT REORDER 53 Family uint32 54 Address [16]byte 55 _ uint32 56 } 57 type wtsInfo struct { 58 // DO NOT REORDER 59 _ uint32 60 _, _, _ uint64 61 _ [32]uint16 62 Domain [17]uint16 63 User [21]uint16 64 _, _ int64 65 LastInput int64 66 Logon int64 67 Now int64 68 } 69 type wtsSession struct { 70 // DO NOT REORDER 71 SessionID uint32 72 Station *uint16 73 State uint32 74 } 75 type wtsProcess struct { 76 // DO NOT REORDER 77 SessionID uint32 78 PID uint32 79 Name *uint16 80 SID *SID 81 } 82 83 // SessionProcess is a struct that contains information about a Process reterived 84 // via a 'WTSEnumerateProcesses' call. 85 type SessionProcess struct { 86 _ [0]func() 87 Name string 88 User string 89 SessionID uint32 90 PID uint32 91 } 92 93 // WTSCloseServer Windows API Call 94 // 95 // Closes an open handle to a Remote Desktop Session Host (RD Session Host) 96 // server. 97 // 98 // https://learn.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtscloseserver 99 func WTSCloseServer(h uintptr) { 100 syscallN(funcWTSCloseServer.address(), h) 101 } 102 103 // WTSOpenServer Windows API Call 104 // 105 // Opens a handle to the specified Remote Desktop Session Host (RD Session Host) 106 // server. 107 // 108 // https://learn.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtsopenserverw 109 func WTSOpenServer(server string) (uintptr, error) { 110 if len(server) == 0 { 111 return 0, nil 112 } 113 n, err := UTF16PtrFromString(server) 114 if err != nil { 115 return invalid, err 116 } 117 r, _, err1 := syscallN(funcWTSOpenServer.address(), uintptr(unsafe.Pointer(n))) 118 if r == invalid { 119 return invalid, unboxError(err1) 120 } 121 return r, nil 122 } 123 124 // WTSGetSessions will attempt to reterive a detailed list of all Sessions 125 // on the target server handle (use 0 for the current host or use 'WTSOpenServer') 126 // 127 // This function will return a 'Session' struct for each Session found or any 128 // errors that may occur during enumeration. 129 func WTSGetSessions(server uintptr) ([]Session, error) { 130 var ( 131 b uintptr 132 c uint32 133 r, _, err1 = syscallN(funcWTSEnumerateSessions.address(), server, 0, 1, uintptr(unsafe.Pointer(&b)), uintptr(unsafe.Pointer(&c))) 134 ) 135 if r == 0 { 136 return nil, unboxError(err1) 137 } 138 if c == 0 { 139 localFree(b) 140 return nil, nil 141 } 142 var ( 143 o = make([]Session, 0, c) 144 err error 145 ) 146 for i := uint32(0); i < c; i++ { 147 var ( 148 s = *(*wtsSession)(unsafe.Pointer(b + (wtsSessionSize * uintptr(i)))) 149 v = Session{ID: s.SessionID, Status: uint8(s.State), Host: UTF16PtrToString(s.Station)} 150 ) 151 if err = v.getSessionInfo(IsWindows7(), server); err != nil { 152 break 153 } 154 o = append(o, v) 155 } 156 localFree(b) 157 return o, err 158 } 159 160 // WTSGetSessionsHost will attempt to reterive a detailed list of all Sessions 161 // on the target server name (use an empty string for the local host). 162 // 163 // This function will return a 'Session' struct for each Session found or any 164 // errors that may occur during enumeration. 165 // 166 // This function calls 'WTSOpenServer(server)' then enumerates the Sessions and 167 // closes the handle after. If you would like more control, use the 'WTSGetSessions' 168 // function which takes a server handle instead. 169 func WTSGetSessionsHost(server string) ([]Session, error) { 170 if len(server) == 0 { 171 return WTSGetSessions(0) 172 } 173 h, err := WTSOpenServer(server) 174 if err != nil { 175 return nil, err 176 } 177 r, err := WTSGetSessions(h) 178 if h > 0 { 179 WTSCloseServer(h) 180 } 181 return r, err 182 } 183 func (s *Session) getSessionInfo(c bool, h uintptr) error { 184 var x uint32 185 if c { 186 var ( 187 i *wtsInfo 188 r, _, err = syscallN( 189 funcWTSQuerySessionInformation.address(), h, uintptr(s.ID), 0x18, uintptr(unsafe.Pointer(&i)), 190 uintptr(unsafe.Pointer(&x)), 191 ) 192 // 0x18 - WTSSessionInfo 193 ) 194 if r == 0 { 195 return unboxError(err) 196 } 197 if s.User, s.Domain = UTF16ToString(i.User[:]), UTF16ToString(i.Domain[:]); i.Logon > 0 { 198 s.Login = time.Unix(0, (i.Logon-epoch)*100).Unix() 199 } 200 if i.LastInput > 0 { 201 s.LastInput = time.Unix(0, (i.LastInput-epoch)*100).Unix() 202 } else if i.Logon > 0 { 203 s.LastInput = time.Unix(0, (i.Now-epoch)*100).Unix() 204 } 205 localFree(uintptr(unsafe.Pointer(i))) 206 } else { 207 var ( 208 v *uint16 209 r, _, err = syscallN( 210 funcWTSQuerySessionInformation.address(), h, uintptr(s.ID), 0x5, uintptr(unsafe.Pointer(&v)), 211 uintptr(unsafe.Pointer(&x)), 212 ) 213 // 0x5 - WTSUserName 214 ) 215 if r == 0 { 216 return unboxError(err) 217 } 218 s.User = UTF16PtrToString(v) 219 localFree(uintptr(unsafe.Pointer(v))) 220 // 0x7 - WTSDomainName 221 r, _, err = syscallN( 222 funcWTSQuerySessionInformation.address(), h, uintptr(s.ID), 0x7, uintptr(unsafe.Pointer(&v)), 223 uintptr(unsafe.Pointer(&x)), 224 ) 225 if r == 0 { 226 return unboxError(err) 227 } 228 s.Domain = UTF16PtrToString(v) 229 localFree(uintptr(unsafe.Pointer(v))) 230 } 231 var a *wtsAddr 232 r, _, err := syscallN( 233 funcWTSQuerySessionInformation.address(), h, uintptr(s.ID), 0xE, uintptr(unsafe.Pointer(&a)), 234 uintptr(unsafe.Pointer(&x)), 235 ) 236 if s.Remote = false; r == 0 { 237 return unboxError(err) 238 } 239 switch a.Family { 240 case 0x2: 241 copy(s.From[0:], a.Address[2:6]) 242 s.Remote = true 243 case 0x17: 244 copy(s.From[0:], a.Address[0:]) 245 s.Remote = true 246 default: 247 s.Remote = false 248 } 249 localFree(uintptr(unsafe.Pointer(a))) 250 return nil 251 } 252 253 // MarshalStream transforms this struct into a binary format and writes to the 254 // supplied data.Writer. 255 func (p SessionProcess) MarshalStream(w data.Writer) error { 256 if err := w.WriteUint32(p.PID); err != nil { 257 return err 258 } 259 if err := w.WriteUint32(0); err != nil { 260 return err 261 } 262 if err := w.WriteString(p.Name); err != nil { 263 return err 264 } 265 return w.WriteString(p.User) 266 } 267 268 // WTSLogoffSession Windows API Call 269 // 270 // Logs off a specified Remote Desktop Services session. 271 // 272 // https://learn.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtslogoffsession 273 func WTSLogoffSession(server uintptr, sid int32, wait bool) error { 274 var w uint32 275 if wait { 276 w = 1 277 } 278 if r, _, err := syscallN(funcWTSLogoffSession.address(), server, uintptr(sid), uintptr(w)); r == 0 { 279 return unboxError(err) 280 } 281 return nil 282 } 283 284 // WTSDisconnectSession Windows API Call 285 // 286 // Disconnects the logged-on user from the specified Remote Desktop Services 287 // session without closing the session. If the user subsequently logs on to 288 // the same Remote Desktop Session Host (RD Session Host) server, the user is 289 // reconnected to the same session. 290 // 291 // https://learn.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtsdisconnectsession 292 func WTSDisconnectSession(server uintptr, sid int32, wait bool) error { 293 var w uint32 294 if wait { 295 w = 1 296 } 297 if r, _, err := syscallN(funcWTSDisconnectSession.address(), server, uintptr(sid), uintptr(w)); r == 0 { 298 return unboxError(err) 299 } 300 return nil 301 } 302 303 // WTSEnumerateProcesses Windows API Call 304 // 305 // Retrieves information about the active processes on a specified Remote 306 // Desktop Session Host (RD Session Host) server. 307 // 308 // https://learn.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtsenumerateprocessesw 309 func WTSEnumerateProcesses(server uintptr, sid int32) ([]SessionProcess, error) { 310 var ( 311 c uint32 312 b uintptr 313 r, _, err = syscallN(funcWTSEnumerateProcesses.address(), server, 0, 1, uintptr(unsafe.Pointer(&b)), uintptr(unsafe.Pointer(&c))) 314 ) 315 if r == 0 { 316 return nil, unboxError(err) 317 } 318 if c == 0 { 319 localFree(b) 320 return nil, nil 321 } 322 o := make([]SessionProcess, 0, c) 323 for i := uint32(0); i < c; i++ { 324 if s := *(*wtsProcess)(unsafe.Pointer(b + (wtsProcSize * uintptr(i)))); sid < 0 || uint32(sid) == s.SessionID { 325 u, _ := s.SID.UserName() 326 o = append(o, SessionProcess{Name: UTF16PtrToString(s.Name), User: u, SessionID: s.SessionID, PID: s.PID}) 327 } 328 } 329 localFree(b) 330 return o, nil 331 } 332 333 // WTSSendMessage Windows API Call 334 // 335 // Displays a message box on the client desktop of a specified Remote Desktop 336 // Services session. 337 // 338 // https://learn.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtssendmessagew 339 func WTSSendMessage(server uintptr, sid int32, title, text string, f, secs uint32, wait bool) (uint32, error) { 340 t, err := UTF16PtrFromString(title) 341 if err != nil { 342 return 0, err 343 } 344 d, err := UTF16PtrFromString(text) 345 if err != nil { 346 return 0, err 347 } 348 var o, w uint32 349 if wait { 350 w = 1 351 } 352 r, _, err1 := syscallN( 353 funcWTSSendMessage.address(), server, uintptr(sid), uintptr(unsafe.Pointer(t)), uintptr(len(title)*2), uintptr(unsafe.Pointer(d)), 354 uintptr(len(text)*2), uintptr(f), uintptr(secs), uintptr(unsafe.Pointer(&o)), uintptr(w), 355 ) 356 if r == 0 { 357 return 0, unboxError(err1) 358 } 359 return o, nil 360 }