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  }