mirror of
				https://github.com/yv1ing/ShotRDP.git
				synced 2025-09-16 15:10:57 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			899 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			899 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package sec
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"crypto/md5"
 | |
| 	"crypto/rand"
 | |
| 	"crypto/rc4"
 | |
| 	"crypto/rsa"
 | |
| 	"crypto/sha1"
 | |
| 	"encoding/hex"
 | |
| 	"errors"
 | |
| 	"io"
 | |
| 	"unicode/utf16"
 | |
| 
 | |
| 	"github.com/lunixbochs/struc"
 | |
| 
 | |
| 	"ShotRDP/grdp/core"
 | |
| 	"ShotRDP/grdp/emission"
 | |
| 	"ShotRDP/grdp/glog"
 | |
| 	"ShotRDP/grdp/protocol/lic"
 | |
| 	"ShotRDP/grdp/protocol/nla"
 | |
| 	"ShotRDP/grdp/protocol/t125"
 | |
| 	"ShotRDP/grdp/protocol/t125/gcc"
 | |
| )
 | |
| 
 | |
| /**
 | |
|  * SecurityFlag
 | |
|  * @see http://msdn.microsoft.com/en-us/library/cc240579.aspx
 | |
|  */
 | |
| const (
 | |
| 	EXCHANGE_PKT       uint16 = 0x0001
 | |
| 	TRANSPORT_REQ             = 0x0002
 | |
| 	TRANSPORT_RSP             = 0x0004
 | |
| 	ENCRYPT                   = 0x0008
 | |
| 	RESET_SEQNO               = 0x0010
 | |
| 	IGNORE_SEQNO              = 0x0020
 | |
| 	INFO_PKT                  = 0x0040
 | |
| 	LICENSE_PKT               = 0x0080
 | |
| 	LICENSE_ENCRYPT_CS        = 0x0200
 | |
| 	LICENSE_ENCRYPT_SC        = 0x0200
 | |
| 	REDIRECTION_PKT           = 0x0400
 | |
| 	SECURE_CHECKSUM           = 0x0800
 | |
| 	AUTODETECT_REQ            = 0x1000
 | |
| 	AUTODETECT_RSP            = 0x2000
 | |
| 	HEARTBEAT                 = 0x4000
 | |
| 	FLAGSHI_VALID             = 0x8000
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	INFO_MOUSE                  uint32 = 0x00000001
 | |
| 	INFO_DISABLECTRLALTDEL             = 0x00000002
 | |
| 	INFO_AUTOLOGON                     = 0x00000008
 | |
| 	INFO_UNICODE                       = 0x00000010
 | |
| 	INFO_MAXIMIZESHELL                 = 0x00000020
 | |
| 	INFO_LOGONNOTIFY                   = 0x00000040
 | |
| 	INFO_COMPRESSION                   = 0x00000080
 | |
| 	INFO_ENABLEWINDOWSKEY              = 0x00000100
 | |
| 	INFO_REMOTECONSOLEAUDIO            = 0x00002000
 | |
| 	INFO_FORCE_ENCRYPTED_CS_PDU        = 0x00004000
 | |
| 	INFO_RAIL                          = 0x00008000
 | |
| 	INFO_LOGONERRORS                   = 0x00010000
 | |
| 	INFO_MOUSE_HAS_WHEEL               = 0x00020000
 | |
| 	INFO_PASSWORD_IS_SC_PIN            = 0x00040000
 | |
| 	INFO_NOAUDIOPLAYBACK               = 0x00080000
 | |
| 	INFO_USING_SAVED_CREDS             = 0x00100000
 | |
| 	INFO_AUDIOCAPTURE                  = 0x00200000
 | |
| 	INFO_VIDEO_DISABLE                 = 0x00400000
 | |
| 	INFO_CompressionTypeMask           = 0x00001E00
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	AF_INET  uint16 = 0x00002
 | |
| 	AF_INET6        = 0x0017
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	PERF_DISABLE_WALLPAPER          uint32 = 0x00000001
 | |
| 	PERF_DISABLE_FULLWINDOWDRAG            = 0x00000002
 | |
| 	PERF_DISABLE_MENUANIMATIONS            = 0x00000004
 | |
| 	PERF_DISABLE_THEMING                   = 0x00000008
 | |
| 	PERF_DISABLE_CURSOR_SHADOW             = 0x00000020
 | |
| 	PERF_DISABLE_CURSORSETTINGS            = 0x00000040
 | |
| 	PERF_ENABLE_FONT_SMOOTHING             = 0x00000080
 | |
| 	PERF_ENABLE_DESKTOP_COMPOSITION        = 0x00000100
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	FASTPATH_OUTPUT_SECURE_CHECKSUM = 0x1
 | |
| 	FASTPATH_OUTPUT_ENCRYPTED       = 0x2
 | |
| )
 | |
| 
 | |
| type ClientAutoReconnect struct {
 | |
| 	CbAutoReconnectLen uint16
 | |
| 	CbLen              uint32
 | |
| 	Version            uint32
 | |
| 	LogonId            uint32
 | |
| 	SecVerifier        []byte
 | |
| }
 | |
| 
 | |
| func NewClientAutoReconnect(id uint32, random []byte) *ClientAutoReconnect {
 | |
| 	return &ClientAutoReconnect{
 | |
| 		CbAutoReconnectLen: 28,
 | |
| 		CbLen:              28,
 | |
| 		Version:            1,
 | |
| 		LogonId:            id,
 | |
| 		SecVerifier:        nla.HMAC_MD5(random, random),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type RDPExtendedInfo struct {
 | |
| 	ClientAddressFamily uint16 `struc:"little"`
 | |
| 	CbClientAddress     uint16 `struc:"little,sizeof=ClientAddress"`
 | |
| 	ClientAddress       []byte `struc:"[]byte"`
 | |
| 	CbClientDir         uint16 `struc:"little,sizeof=ClientDir"`
 | |
| 	ClientDir           []byte `struc:"[]byte"`
 | |
| 	ClientTimeZone      []byte `struc:"[172]byte"`
 | |
| 	ClientSessionId     uint32 `struc:"litttle"`
 | |
| 	PerformanceFlags    uint32 `struc:"little"`
 | |
| 	AutoReconnect       *ClientAutoReconnect
 | |
| }
 | |
| 
 | |
| func NewExtendedInfo(auto *ClientAutoReconnect) *RDPExtendedInfo {
 | |
| 	return &RDPExtendedInfo{
 | |
| 		ClientAddressFamily: AF_INET,
 | |
| 		ClientAddress:       []byte{0, 0},
 | |
| 		ClientDir:           []byte{0, 0},
 | |
| 		ClientTimeZone:      make([]byte, 172),
 | |
| 		ClientSessionId:     0,
 | |
| 		AutoReconnect:       auto,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (o *RDPExtendedInfo) Serialize() []byte {
 | |
| 	buff := &bytes.Buffer{}
 | |
| 	core.WriteUInt16LE(o.ClientAddressFamily, buff)
 | |
| 	core.WriteUInt16LE(uint16(len(o.ClientAddress)), buff)
 | |
| 	core.WriteBytes(o.ClientAddress, buff)
 | |
| 	core.WriteUInt16LE(uint16(len(o.ClientDir)), buff)
 | |
| 	core.WriteBytes(o.ClientDir, buff)
 | |
| 	core.WriteBytes(o.ClientTimeZone, buff)
 | |
| 	core.WriteUInt32LE(o.ClientSessionId, buff)
 | |
| 	core.WriteUInt32LE(o.PerformanceFlags, buff)
 | |
| 
 | |
| 	if o.AutoReconnect != nil {
 | |
| 		core.WriteUInt16LE(o.AutoReconnect.CbAutoReconnectLen, buff)
 | |
| 		core.WriteUInt32LE(o.AutoReconnect.CbLen, buff)
 | |
| 		core.WriteUInt32LE(o.AutoReconnect.Version, buff)
 | |
| 		core.WriteUInt32LE(o.AutoReconnect.LogonId, buff)
 | |
| 		core.WriteBytes(o.AutoReconnect.SecVerifier, buff)
 | |
| 	}
 | |
| 
 | |
| 	return buff.Bytes()
 | |
| }
 | |
| 
 | |
| type RDPInfo struct {
 | |
| 	CodePage         uint32
 | |
| 	Flag             uint32
 | |
| 	CbDomain         uint16
 | |
| 	CbUserName       uint16
 | |
| 	CbPassword       uint16
 | |
| 	CbAlternateShell uint16
 | |
| 	CbWorkingDir     uint16
 | |
| 	Domain           []byte
 | |
| 	UserName         []byte
 | |
| 	Password         []byte
 | |
| 	AlternateShell   []byte
 | |
| 	WorkingDir       []byte
 | |
| 	ExtendedInfo     *RDPExtendedInfo
 | |
| }
 | |
| 
 | |
| func NewRDPInfo() *RDPInfo {
 | |
| 	info := &RDPInfo{
 | |
| 		Flag: INFO_MOUSE | INFO_UNICODE | INFO_MAXIMIZESHELL |
 | |
| 			INFO_ENABLEWINDOWSKEY | INFO_DISABLECTRLALTDEL | INFO_MOUSE_HAS_WHEEL |
 | |
| 			INFO_FORCE_ENCRYPTED_CS_PDU | INFO_AUTOLOGON,
 | |
| 		Domain:         []byte{0, 0},
 | |
| 		UserName:       []byte{0, 0},
 | |
| 		Password:       []byte{0, 0},
 | |
| 		AlternateShell: []byte{0, 0},
 | |
| 		WorkingDir:     []byte{0, 0},
 | |
| 		ExtendedInfo:   NewExtendedInfo(nil),
 | |
| 	}
 | |
| 	return info
 | |
| }
 | |
| 
 | |
| func (o *RDPInfo) SetClientAutoReconnect(auto *ClientAutoReconnect) {
 | |
| 	o.ExtendedInfo.AutoReconnect = auto
 | |
| }
 | |
| 
 | |
| func (o *RDPInfo) SetClientInfo() {
 | |
| 	o.Flag |= INFO_LOGONNOTIFY | INFO_LOGONERRORS
 | |
| }
 | |
| 
 | |
| func (o *RDPInfo) Serialize(hasExtended bool) []byte {
 | |
| 	buff := &bytes.Buffer{}
 | |
| 	core.WriteUInt32LE(o.CodePage, buff)                      // 0000000
 | |
| 	core.WriteUInt32LE(o.Flag, buff)                          // 0530101
 | |
| 	core.WriteUInt16LE(uint16(len(o.Domain)-2), buff)         // 001c
 | |
| 	core.WriteUInt16LE(uint16(len(o.UserName)-2), buff)       // 0008
 | |
| 	core.WriteUInt16LE(uint16(len(o.Password)-2), buff)       //000c
 | |
| 	core.WriteUInt16LE(uint16(len(o.AlternateShell)-2), buff) //0000
 | |
| 	core.WriteUInt16LE(uint16(len(o.WorkingDir)-2), buff)     //0000
 | |
| 	core.WriteBytes(o.Domain, buff)
 | |
| 	core.WriteBytes(o.UserName, buff)
 | |
| 	core.WriteBytes(o.Password, buff)
 | |
| 	core.WriteBytes(o.AlternateShell, buff)
 | |
| 	core.WriteBytes(o.WorkingDir, buff)
 | |
| 	if hasExtended {
 | |
| 		core.WriteBytes(o.ExtendedInfo.Serialize(), buff)
 | |
| 	}
 | |
| 	return buff.Bytes()
 | |
| }
 | |
| 
 | |
| type SecurityHeader struct {
 | |
| 	securityFlag   uint16
 | |
| 	securityFlagHi uint16
 | |
| }
 | |
| 
 | |
| func readSecurityHeader(r io.Reader) *SecurityHeader {
 | |
| 	s := &SecurityHeader{}
 | |
| 	s.securityFlag, _ = core.ReadUint16LE(r)
 | |
| 	s.securityFlagHi, _ = core.ReadUint16LE(r)
 | |
| 	return s
 | |
| }
 | |
| 
 | |
| type SEC struct {
 | |
| 	emission.Emitter
 | |
| 	transport   core.Transport
 | |
| 	info        *RDPInfo
 | |
| 	machineName string
 | |
| 	clientData  []interface{}
 | |
| 	serverData  []interface{}
 | |
| 
 | |
| 	enableEncryption bool
 | |
| 	//Enable Secure Mac generation
 | |
| 	enableSecureCheckSum bool
 | |
| 	//counter before update
 | |
| 	nbEncryptedPacket int
 | |
| 	nbDecryptedPacket int
 | |
| 
 | |
| 	currentDecrytKey  []byte
 | |
| 	currentEncryptKey []byte
 | |
| 
 | |
| 	//current rc4 tab
 | |
| 	decryptRc4 *rc4.Cipher
 | |
| 	encryptRc4 *rc4.Cipher
 | |
| 
 | |
| 	macKey []byte
 | |
| }
 | |
| 
 | |
| func NewSEC(t core.Transport) *SEC {
 | |
| 	sec := &SEC{
 | |
| 		*emission.NewEmitter(),
 | |
| 		t,
 | |
| 		NewRDPInfo(),
 | |
| 		"",
 | |
| 		nil,
 | |
| 		nil,
 | |
| 		false,
 | |
| 		false,
 | |
| 		0,
 | |
| 		0,
 | |
| 		nil,
 | |
| 		nil,
 | |
| 		nil,
 | |
| 		nil,
 | |
| 		nil,
 | |
| 	}
 | |
| 
 | |
| 	t.On("close", func() {
 | |
| 		sec.Emit("close")
 | |
| 	}).On("error", func(err error) {
 | |
| 		sec.Emit("error", err)
 | |
| 	})
 | |
| 	return sec
 | |
| }
 | |
| 
 | |
| func (s *SEC) Read(data []byte) (n int, err error) {
 | |
| 	return s.transport.Read(data)
 | |
| }
 | |
| 
 | |
| func (s *SEC) Write(b []byte) (n int, err error) {
 | |
| 	if !s.enableEncryption {
 | |
| 		return s.transport.Write(b)
 | |
| 	}
 | |
| 	data := s.encrytData(b)
 | |
| 	return s.transport.Write(data)
 | |
| }
 | |
| 
 | |
| func (s *SEC) Close() error {
 | |
| 	return s.transport.Close()
 | |
| }
 | |
| 
 | |
| func (s *SEC) sendFlagged(flag uint16, data []byte) (n int, err error) {
 | |
| 	glog.Trace("sendFlagged:", hex.EncodeToString(data))
 | |
| 	b := s.encryt(flag, data)
 | |
| 	return s.transport.Write(b)
 | |
| }
 | |
| 
 | |
| /*
 | |
| @see: http://msdn.microsoft.com/en-us/library/cc241995.aspx
 | |
| @param macSaltKey: {str} mac key
 | |
| @param data: {str} data to sign
 | |
| @return: {str} signature
 | |
| */
 | |
| func macData(macSaltKey, data []byte) []byte {
 | |
| 	sha1Digest := sha1.New()
 | |
| 	md5Digest := md5.New()
 | |
| 
 | |
| 	b := &bytes.Buffer{}
 | |
| 	core.WriteUInt32LE(uint32(len(data)), b)
 | |
| 
 | |
| 	sha1Digest.Write(macSaltKey)
 | |
| 	for i := 0; i < 40; i++ {
 | |
| 		sha1Digest.Write([]byte("\x36"))
 | |
| 	}
 | |
| 
 | |
| 	sha1Digest.Write(b.Bytes())
 | |
| 	sha1Digest.Write(data)
 | |
| 
 | |
| 	sha1Sig := sha1Digest.Sum(nil)
 | |
| 
 | |
| 	md5Digest.Write(macSaltKey)
 | |
| 	for i := 0; i < 48; i++ {
 | |
| 		md5Digest.Write([]byte("\x5c"))
 | |
| 	}
 | |
| 
 | |
| 	md5Digest.Write(sha1Sig)
 | |
| 
 | |
| 	return md5Digest.Sum(nil)
 | |
| }
 | |
| func (s *SEC) readEncryptedPayload(data []byte, checkSum bool) []byte {
 | |
| 	r := bytes.NewReader(data)
 | |
| 	sign, _ := core.ReadBytes(8, r)
 | |
| 	glog.Debug("read sign:", sign)
 | |
| 	encryptedPayload, _ := core.ReadBytes(r.Len(), r)
 | |
| 	if s.decryptRc4 == nil {
 | |
| 		s.decryptRc4, _ = rc4.NewCipher(s.currentDecrytKey)
 | |
| 	}
 | |
| 	s.nbDecryptedPacket++
 | |
| 	glog.Debug("nbDecryptedPacket:", s.nbDecryptedPacket)
 | |
| 	plaintext := make([]byte, len(encryptedPayload))
 | |
| 	s.decryptRc4.XORKeyStream(plaintext, encryptedPayload)
 | |
| 
 | |
| 	return plaintext
 | |
| 
 | |
| }
 | |
| func (s *SEC) writeEncryptedPayload(data []byte, checkSum bool) []byte {
 | |
| 	if s.nbEncryptedPacket == 4096 {
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	if checkSum {
 | |
| 		glog.Debug("need checkSum")
 | |
| 		return []byte{}
 | |
| 	}
 | |
| 
 | |
| 	s.nbEncryptedPacket++
 | |
| 	glog.Debug("nbEncryptedPacket:", s.nbEncryptedPacket)
 | |
| 	b := &bytes.Buffer{}
 | |
| 
 | |
| 	sign := macData(s.macKey, data)[:8]
 | |
| 	if s.encryptRc4 == nil {
 | |
| 		s.encryptRc4, _ = rc4.NewCipher(s.currentEncryptKey)
 | |
| 	}
 | |
| 
 | |
| 	plaintext := make([]byte, len(data))
 | |
| 	s.encryptRc4.XORKeyStream(plaintext, data)
 | |
| 	b.Write(sign)
 | |
| 	b.Write(plaintext)
 | |
| 	glog.Debug("sign:", hex.EncodeToString(sign), "plaintext:", hex.EncodeToString(plaintext))
 | |
| 	return b.Bytes()
 | |
| }
 | |
| 
 | |
| func (s *SEC) encryt(flag uint16, b []byte) []byte {
 | |
| 	data := b
 | |
| 	if flag&ENCRYPT != 0 {
 | |
| 		data = s.writeEncryptedPayload(b, flag&SECURE_CHECKSUM != 0)
 | |
| 	}
 | |
| 	buff := &bytes.Buffer{}
 | |
| 	core.WriteUInt16LE(flag, buff)
 | |
| 	core.WriteUInt16LE(0, buff)
 | |
| 	core.WriteBytes(data, buff)
 | |
| 
 | |
| 	return buff.Bytes()
 | |
| }
 | |
| func (s *SEC) encrytData(b []byte) []byte {
 | |
| 	if !s.enableEncryption {
 | |
| 		return b
 | |
| 	}
 | |
| 
 | |
| 	var flag uint16 = ENCRYPT
 | |
| 	if s.enableSecureCheckSum {
 | |
| 		flag |= SECURE_CHECKSUM
 | |
| 	}
 | |
| 	return s.encryt(flag, b)
 | |
| }
 | |
| 
 | |
| func (s *SEC) decrytData(b []byte) []byte {
 | |
| 	if !s.enableEncryption {
 | |
| 		return b
 | |
| 	}
 | |
| 
 | |
| 	r := bytes.NewReader(b)
 | |
| 	securityFlag, _ := core.ReadUint16LE(r)
 | |
| 	_, _ = core.ReadUint16LE(r) //securityFlagHi
 | |
| 	data, _ := core.ReadBytes(r.Len(), r)
 | |
| 	if securityFlag&ENCRYPT != 0 {
 | |
| 		data = s.readEncryptedPayload(data, securityFlag&SECURE_CHECKSUM != 0)
 | |
| 	}
 | |
| 	return data
 | |
| }
 | |
| 
 | |
| type Client struct {
 | |
| 	*SEC
 | |
| 	userId    uint16
 | |
| 	channelId uint16
 | |
| 	//initialise decrypt and encrypt keys
 | |
| 	initialDecrytKey  []byte
 | |
| 	initialEncryptKey []byte
 | |
| 
 | |
| 	fastPathListener core.FastPathListener
 | |
| 	channelSender    core.ChannelSender
 | |
| }
 | |
| 
 | |
| func NewClient(t core.Transport) *Client {
 | |
| 	c := &Client{
 | |
| 		SEC: NewSEC(t),
 | |
| 	}
 | |
| 	t.On("connect", c.connect)
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func (c *Client) SetClientAutoReconnect(id uint32, random []byte) {
 | |
| 	auto := NewClientAutoReconnect(id, random)
 | |
| 	c.info.SetClientAutoReconnect(auto)
 | |
| }
 | |
| 
 | |
| func (c *Client) SetAlternateShell(shell string) {
 | |
| 	buff := &bytes.Buffer{}
 | |
| 	for _, ch := range utf16.Encode([]rune(shell)) {
 | |
| 		core.WriteUInt16LE(ch, buff)
 | |
| 	}
 | |
| 	core.WriteUInt16LE(0, buff)
 | |
| 	c.info.AlternateShell = buff.Bytes()
 | |
| 	c.info.Flag |= INFO_RAIL
 | |
| }
 | |
| 
 | |
| func (c *Client) SetUsername(user string) {
 | |
| 	buff := &bytes.Buffer{}
 | |
| 	for _, ch := range utf16.Encode([]rune(user)) {
 | |
| 		core.WriteUInt16LE(ch, buff)
 | |
| 	}
 | |
| 	core.WriteUInt16LE(0, buff)
 | |
| 	c.info.UserName = buff.Bytes()
 | |
| }
 | |
| 
 | |
| func (c *Client) SetPassword(pwd string) {
 | |
| 	buff := &bytes.Buffer{}
 | |
| 	for _, ch := range utf16.Encode([]rune(pwd)) {
 | |
| 		core.WriteUInt16LE(ch, buff)
 | |
| 	}
 | |
| 	core.WriteUInt16LE(0, buff)
 | |
| 	c.info.Password = buff.Bytes()
 | |
| }
 | |
| 
 | |
| func (c *Client) SetDomain(domain string) {
 | |
| 	buff := &bytes.Buffer{}
 | |
| 	for _, ch := range utf16.Encode([]rune(domain)) {
 | |
| 		core.WriteUInt16LE(ch, buff)
 | |
| 	}
 | |
| 	core.WriteUInt16LE(0, buff)
 | |
| 	c.info.Domain = buff.Bytes()
 | |
| }
 | |
| 
 | |
| func (c *Client) connect(clientData []interface{}, serverData []interface{}, userId uint16, channels []t125.MCSChannelInfo) {
 | |
| 	glog.Debug("sec on connect:", clientData)
 | |
| 	glog.Debug("sec on connect:", serverData)
 | |
| 	glog.Debug("sec on connect:", userId)
 | |
| 	glog.Debug("sec on connect:", channels)
 | |
| 	c.clientData = clientData
 | |
| 	c.serverData = serverData
 | |
| 	c.userId = userId
 | |
| 	for _, channel := range channels {
 | |
| 		glog.Infof("channel: %s <%d>:", channel.Name, channel.ID)
 | |
| 		if channel.Name == t125.GLOBAL_CHANNEL_NAME {
 | |
| 			c.channelId = channel.ID
 | |
| 			//break
 | |
| 		}
 | |
| 	}
 | |
| 	c.enableEncryption = c.ClientCoreData().ServerSelectedProtocol == 0
 | |
| 
 | |
| 	if c.enableEncryption {
 | |
| 		c.sendClientRandom()
 | |
| 	}
 | |
| 
 | |
| 	c.sendInfoPkt()
 | |
| 	c.transport.Once("sec", c.recvLicenceInfo)
 | |
| }
 | |
| 
 | |
| func (c *Client) ClientCoreData() *gcc.ClientCoreData {
 | |
| 	return c.clientData[0].(*gcc.ClientCoreData)
 | |
| }
 | |
| func (c *Client) ClientSecurityData() *gcc.ClientSecurityData {
 | |
| 	return c.clientData[1].(*gcc.ClientSecurityData)
 | |
| }
 | |
| func (c *Client) ClientNetworkData() *gcc.ClientNetworkData {
 | |
| 	return c.clientData[2].(*gcc.ClientNetworkData)
 | |
| }
 | |
| 
 | |
| func (c *Client) serverCoreData() *gcc.ServerCoreData {
 | |
| 	return c.serverData[0].(*gcc.ServerCoreData)
 | |
| }
 | |
| func (c *Client) ServerSecurityData() *gcc.ServerSecurityData {
 | |
| 	return c.serverData[1].(*gcc.ServerSecurityData)
 | |
| }
 | |
| 
 | |
| /*
 | |
| @summary: generate 40 bits data from 128 bits data
 | |
| @param data: {str} 128 bits data
 | |
| @return: {str} 40 bits data
 | |
| @see: http://msdn.microsoft.com/en-us/library/cc240785.aspx
 | |
| */
 | |
| func gen40bits(data []byte) []byte {
 | |
| 	return append([]byte("\xd1\x26\x9e"), data[3:8]...)
 | |
| }
 | |
| 
 | |
| /*
 | |
| @summary: generate 56 bits data from 128 bits data
 | |
| @param data: {str} 128 bits data
 | |
| @return: {str} 56 bits data
 | |
| @see: http://msdn.microsoft.com/en-us/library/cc240785.aspx
 | |
| */
 | |
| func gen56bits(data []byte) []byte {
 | |
| 	return append([]byte("\xd1"), data[1:8]...)
 | |
| }
 | |
| 
 | |
| /*
 | |
| @summary: Generate particular signature from combination of sha1 and md5
 | |
| @see: http://msdn.microsoft.com/en-us/library/cc241992.aspx
 | |
| @param inputData: strange input (see doc)
 | |
| @param salt: salt for context call
 | |
| @param salt1: another salt (ex : client random)
 | |
| @param salt2: another another salt (ex: server random)
 | |
| @return : MD5(Salt + SHA1(Input + Salt + Salt1 + Salt2))
 | |
| */
 | |
| func saltedHash(inputData, salt, salt1, salt2 []byte) []byte {
 | |
| 	sha1Digest := sha1.New()
 | |
| 	md5Digest := md5.New()
 | |
| 
 | |
| 	sha1Digest.Write(inputData)
 | |
| 	sha1Digest.Write(salt[:48])
 | |
| 	sha1Digest.Write(salt1)
 | |
| 	sha1Digest.Write(salt2)
 | |
| 	sha1Sig := sha1Digest.Sum(nil)
 | |
| 
 | |
| 	md5Digest.Write(salt[:48])
 | |
| 	md5Digest.Write(sha1Sig)
 | |
| 
 | |
| 	return md5Digest.Sum(nil)[:16]
 | |
| }
 | |
| 
 | |
| /*
 | |
| @summary: MD5(in0[:16] + in1[:32] + in2[:32])
 | |
| @param key: in 16
 | |
| @param random1: in 32
 | |
| @param random2: in 32
 | |
| @return MD5(in0[:16] + in1[:32] + in2[:32])
 | |
| */
 | |
| func finalHash(key, random1, random2 []byte) []byte {
 | |
| 	md5Digest := md5.New()
 | |
| 	md5Digest.Write(key)
 | |
| 	md5Digest.Write(random1)
 | |
| 	md5Digest.Write(random2)
 | |
| 	return md5Digest.Sum(nil)
 | |
| }
 | |
| 
 | |
| /*
 | |
| @summary: Generate master secret
 | |
| @param secret: {str} secret
 | |
| @param clientRandom : {str} client random
 | |
| @param serverRandom : {str} server random
 | |
| @see: http://msdn.microsoft.com/en-us/library/cc241992.aspx
 | |
| */
 | |
| func masterSecret(secret, random1, random2 []byte) []byte {
 | |
| 	sh1 := saltedHash([]byte("A"), secret, random1, random2)
 | |
| 	sh2 := saltedHash([]byte("BB"), secret, random1, random2)
 | |
| 	sh3 := saltedHash([]byte("CCC"), secret, random1, random2)
 | |
| 	ms := bytes.NewBuffer(nil)
 | |
| 	ms.Write(sh1)
 | |
| 	ms.Write(sh2)
 | |
| 	ms.Write(sh3)
 | |
| 	return ms.Bytes()
 | |
| }
 | |
| 
 | |
| /*
 | |
| @summary: Generate master secret
 | |
| @param secret: secret
 | |
| @param clientRandom : client random
 | |
| @param serverRandom : server random
 | |
| */
 | |
| func sessionKeyBlob(secret, random1, random2 []byte) []byte {
 | |
| 	sh1 := saltedHash([]byte("X"), secret, random1, random2)
 | |
| 	sh2 := saltedHash([]byte("YY"), secret, random1, random2)
 | |
| 	sh3 := saltedHash([]byte("ZZZ"), secret, random1, random2)
 | |
| 	ms := bytes.NewBuffer(nil)
 | |
| 	ms.Write(sh1)
 | |
| 	ms.Write(sh2)
 | |
| 	ms.Write(sh3)
 | |
| 	return ms.Bytes()
 | |
| 
 | |
| }
 | |
| func generateKeys(clientRandom, serverRandom []byte, method uint32) ([]byte, []byte, []byte) {
 | |
| 	b := &bytes.Buffer{}
 | |
| 	b.Write(clientRandom[:24])
 | |
| 	b.Write(serverRandom[:24])
 | |
| 	preMasterHash := b.Bytes()
 | |
| 	glog.Debug("preMasterHash:", hex.EncodeToString(preMasterHash))
 | |
| 
 | |
| 	masterHash := masterSecret(preMasterHash, clientRandom, serverRandom)
 | |
| 	glog.Debug("masterHash:", hex.EncodeToString(masterHash))
 | |
| 
 | |
| 	sessionKey := sessionKeyBlob(masterHash, clientRandom, serverRandom)
 | |
| 	glog.Debug("sessionKey:", hex.EncodeToString(sessionKey))
 | |
| 
 | |
| 	macKey128 := sessionKey[:16]
 | |
| 	initialFirstKey128 := finalHash(sessionKey[16:32], clientRandom, serverRandom)
 | |
| 	initialSecondKey128 := finalHash(sessionKey[32:48], clientRandom, serverRandom)
 | |
| 
 | |
| 	glog.Debug("macKey128:", hex.EncodeToString(macKey128))
 | |
| 	glog.Debug("FirstKey128:", hex.EncodeToString(initialFirstKey128))
 | |
| 	glog.Debug("SecondKey128:", hex.EncodeToString(initialSecondKey128))
 | |
| 	//generate valid key
 | |
| 	if method == gcc.ENCRYPTION_FLAG_40BIT {
 | |
| 		return gen40bits(macKey128), gen40bits(initialFirstKey128), gen40bits(initialSecondKey128)
 | |
| 	} else if method == gcc.ENCRYPTION_FLAG_56BIT {
 | |
| 		return gen56bits(macKey128), gen56bits(initialFirstKey128), gen56bits(initialSecondKey128)
 | |
| 	}
 | |
| 	// method == gcc.ENCRYPTION_FLAG_128BIT
 | |
| 	return macKey128, initialFirstKey128, initialSecondKey128
 | |
| 
 | |
| }
 | |
| 
 | |
| type ClientSecurityExchangePDU struct {
 | |
| 	Length                uint32 `struc:"little"`
 | |
| 	EncryptedClientRandom []byte `struc:"little"`
 | |
| 	Padding               []byte `struc:"[8]byte"`
 | |
| }
 | |
| 
 | |
| func (e *ClientSecurityExchangePDU) serialize() []byte {
 | |
| 	buff := &bytes.Buffer{}
 | |
| 	core.WriteUInt32LE(e.Length, buff)
 | |
| 	core.WriteBytes(e.EncryptedClientRandom, buff)
 | |
| 	core.WriteBytes(e.Padding, buff)
 | |
| 
 | |
| 	return buff.Bytes()
 | |
| }
 | |
| func (c *Client) sendClientRandom() {
 | |
| 	glog.Debug("send Client Random")
 | |
| 
 | |
| 	clientRandom := core.Random(32)
 | |
| 	glog.Debug("clientRandom:", hex.EncodeToString(clientRandom))
 | |
| 
 | |
| 	serverRandom := c.ServerSecurityData().ServerRandom
 | |
| 	glog.Debug("ServerRandom:", hex.EncodeToString(serverRandom))
 | |
| 
 | |
| 	c.macKey, c.initialDecrytKey, c.initialEncryptKey = generateKeys(clientRandom,
 | |
| 		serverRandom, c.ServerSecurityData().EncryptionMethod)
 | |
| 
 | |
| 	//initialize keys
 | |
| 	c.currentDecrytKey = c.initialDecrytKey
 | |
| 	c.currentEncryptKey = c.initialEncryptKey
 | |
| 
 | |
| 	//verify certificate
 | |
| 	if !c.ServerSecurityData().ServerCertificate.CertData.Verify() {
 | |
| 		glog.Warn("Cannot verify server identity")
 | |
| 	}
 | |
| 
 | |
| 	serverPubKey, _ := c.ServerSecurityData().ServerCertificate.CertData.GetPublicKey()
 | |
| 	ret, err := rsa.EncryptPKCS1v15(rand.Reader, serverPubKey, core.Reverse(clientRandom))
 | |
| 	if err != nil {
 | |
| 		glog.Error("err:", err)
 | |
| 	}
 | |
| 	message := ClientSecurityExchangePDU{}
 | |
| 	message.EncryptedClientRandom = core.Reverse(ret)
 | |
| 	message.Length = uint32(len(message.EncryptedClientRandom) + 8)
 | |
| 	message.Padding = make([]byte, 8)
 | |
| 
 | |
| 	glog.Debug("message:", message)
 | |
| 
 | |
| 	c.sendFlagged(EXCHANGE_PKT, message.serialize())
 | |
| }
 | |
| func (c *Client) sendInfoPkt() {
 | |
| 	var secFlag uint16 = INFO_PKT
 | |
| 	if c.enableEncryption {
 | |
| 		secFlag |= ENCRYPT
 | |
| 	}
 | |
| 
 | |
| 	glog.Debug("RdpVersion:", c.ClientCoreData().RdpVersion, ":", gcc.RDP_VERSION_5_PLUS)
 | |
| 	c.sendFlagged(secFlag, c.info.Serialize(c.ClientCoreData().RdpVersion == gcc.RDP_VERSION_5_PLUS))
 | |
| }
 | |
| 
 | |
| func (c *Client) recvLicenceInfo(channel string, s []byte) {
 | |
| 	glog.Debug("sec recvLicenceInfo", hex.EncodeToString(s))
 | |
| 	r := bytes.NewReader(s)
 | |
| 	h := readSecurityHeader(r)
 | |
| 	if (h.securityFlag & LICENSE_PKT) == 0 {
 | |
| 		c.Emit("error", errors.New("NODE_RDP_PROTOCOL_PDU_SEC_BAD_LICENSE_HEADER"))
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	p := lic.ReadLicensePacket(r)
 | |
| 	switch p.BMsgtype {
 | |
| 	case lic.NEW_LICENSE:
 | |
| 		glog.Info("sec NEW_LICENSE")
 | |
| 		c.Emit("success")
 | |
| 		goto connect
 | |
| 	case lic.ERROR_ALERT:
 | |
| 		message := p.LicensingMessage.(*lic.ErrorMessage)
 | |
| 		glog.Info("sec ERROR_ALERT and ErrorCode:", message.DwErrorCode)
 | |
| 		if message.DwErrorCode == lic.STATUS_VALID_CLIENT && message.DwStateTransaction == lic.ST_NO_TRANSITION {
 | |
| 			goto connect
 | |
| 		}
 | |
| 		goto retry
 | |
| 	case lic.LICENSE_REQUEST:
 | |
| 		glog.Info("sec LICENSE_REQUEST")
 | |
| 		c.sendClientNewLicenseRequest(p.LicensingMessage.([]byte))
 | |
| 		goto retry
 | |
| 	case lic.PLATFORM_CHALLENGE:
 | |
| 		glog.Info("sec PLATFORM_CHALLENGE")
 | |
| 		c.sendClientChallengeResponse(p.LicensingMessage.([]byte))
 | |
| 		goto retry
 | |
| 	default:
 | |
| 		glog.Error("Not a valid license packet")
 | |
| 		c.Emit("error", errors.New("Not a valid license packet"))
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| connect:
 | |
| 	c.transport.On("sec", c.recvData)
 | |
| 	c.Emit("connect", c.clientData[0].(*gcc.ClientCoreData), c.userId, c.channelId)
 | |
| 	return
 | |
| 
 | |
| retry:
 | |
| 	c.transport.Once("sec", c.recvLicenceInfo)
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func (c *Client) sendClientNewLicenseRequest(data []byte) {
 | |
| 	var req lic.ServerLicenseRequest
 | |
| 	struc.Unpack(bytes.NewReader(data), &req)
 | |
| 
 | |
| 	var sc gcc.ServerCertificate
 | |
| 	if c.ServerSecurityData().ServerCertificate.DwVersion != 0 {
 | |
| 		sc = c.ServerSecurityData().ServerCertificate
 | |
| 	} else {
 | |
| 		rd := bytes.NewReader(req.ServerCertificate.BlobData)
 | |
| 		err := sc.Unpack(rd)
 | |
| 		if err != nil {
 | |
| 			glog.Error("read serverCertificate err:", err)
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	serverRandom := req.ServerRandom
 | |
| 	clientRandom := core.Random(32)
 | |
| 	preMasterSecret := core.Random(48)
 | |
| 	masSecret := masterSecret(preMasterSecret, clientRandom, serverRandom)
 | |
| 	sessionKeyBlob := masterSecret(masSecret, serverRandom, clientRandom)
 | |
| 	c.macKey = sessionKeyBlob[:16]
 | |
| 	c.initialDecrytKey = finalHash(sessionKeyBlob[16:32], clientRandom, serverRandom)
 | |
| 
 | |
| 	//format message
 | |
| 	message := &lic.ClientNewLicenseRequest{}
 | |
| 	message.PreferredKeyExchangeAlg = 0x00000001
 | |
| 	message.PlatformId = 0x04000000 | 0x00010000
 | |
| 	message.ClientRandom = clientRandom
 | |
| 
 | |
| 	buff := &bytes.Buffer{}
 | |
| 
 | |
| 	serverPubKey, _ := sc.CertData.GetPublicKey()
 | |
| 	ret, err := rsa.EncryptPKCS1v15(rand.Reader, serverPubKey, core.Reverse(preMasterSecret))
 | |
| 	if err != nil {
 | |
| 		glog.Error("err:", err)
 | |
| 	}
 | |
| 
 | |
| 	buff.Write(core.Reverse(ret))
 | |
| 	buff.Write([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
 | |
| 	message.EncryptedPreMasterSecret.BlobData = buff.Bytes()
 | |
| 	message.EncryptedPreMasterSecret.WBlobLen = uint16(buff.Len())
 | |
| 	message.EncryptedPreMasterSecret.WBlobType = lic.BB_RANDOM_BLOB
 | |
| 
 | |
| 	buff.Reset()
 | |
| 	buff.Write(c.info.UserName)
 | |
| 	buff.Write([]byte{0x00})
 | |
| 	message.ClientUserName.BlobData = buff.Bytes()
 | |
| 	message.ClientUserName.WBlobLen = uint16(buff.Len())
 | |
| 	message.ClientUserName.WBlobType = lic.BB_CLIENT_USER_NAME_BLOB
 | |
| 
 | |
| 	buff.Reset()
 | |
| 	buff.Write(c.ClientCoreData().ClientName[:])
 | |
| 	buff.Write([]byte{0x00})
 | |
| 	message.ClientMachineName.BlobData = buff.Bytes()
 | |
| 	message.ClientMachineName.WBlobLen = uint16(buff.Len())
 | |
| 	message.ClientMachineName.WBlobType = lic.BB_CLIENT_MACHINE_NAME_BLOB
 | |
| 
 | |
| 	buff.Reset()
 | |
| 	err = struc.Pack(buff, message)
 | |
| 	if err != nil {
 | |
| 		glog.Error("err:", err)
 | |
| 	}
 | |
| 
 | |
| 	c.sendFlagged(LICENSE_PKT, buff.Bytes())
 | |
| }
 | |
| 
 | |
| func (c *Client) sendClientChallengeResponse(data []byte) {
 | |
| 	var pc lic.ServerPlatformChallenge
 | |
| 	struc.Unpack(bytes.NewReader(data), &pc)
 | |
| 
 | |
| 	serverEncryptedChallenge := pc.EncryptedPlatformChallenge.BlobData
 | |
| 	//decrypt server challenge
 | |
| 	//it should be TEST word in unicode format
 | |
| 	rc, _ := rc4.NewCipher(c.initialDecrytKey)
 | |
| 	serverChallenge := make([]byte, 20)
 | |
| 	rc.XORKeyStream(serverChallenge, serverEncryptedChallenge)
 | |
| 	//if serverChallenge != "T\x00E\x00S\x00T\x00\x00\x00":
 | |
| 	//raise InvalidExpectedDataException("bad license server challenge")
 | |
| 
 | |
| 	//generate hwid
 | |
| 	b := &bytes.Buffer{}
 | |
| 	b.Write(c.ClientCoreData().ClientName[:])
 | |
| 	b.Write(c.info.UserName)
 | |
| 	for i := 0; i < 2; i++ {
 | |
| 		b.Write([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
 | |
| 	}
 | |
| 	hwid := b.Bytes()[:20]
 | |
| 
 | |
| 	encryptedHWID := make([]byte, 20)
 | |
| 	rc.XORKeyStream(encryptedHWID, hwid)
 | |
| 
 | |
| 	b.Reset()
 | |
| 	b.Write(serverChallenge)
 | |
| 	b.Write(hwid)
 | |
| 
 | |
| 	message := &lic.ClientPLatformChallengeResponse{}
 | |
| 	message.EncryptedPlatformChallengeResponse.BlobData = serverEncryptedChallenge
 | |
| 	message.EncryptedHWID.BlobData = encryptedHWID
 | |
| 	message.MACData = macData(c.macKey, b.Bytes())[:16]
 | |
| 
 | |
| 	b.Reset()
 | |
| 	struc.Pack(b, message)
 | |
| 	c.sendFlagged(LICENSE_PKT, b.Bytes())
 | |
| }
 | |
| 
 | |
| func (c *Client) recvData(channel string, s []byte) {
 | |
| 	glog.Trace("sec recvData", hex.EncodeToString(s))
 | |
| 	glog.Debugf("channel<%s> data len: %d", channel, len(s))
 | |
| 	data := c.decrytData(s)
 | |
| 	if channel != t125.GLOBAL_CHANNEL_NAME {
 | |
| 		c.Emit("channel", channel, data)
 | |
| 		return
 | |
| 	}
 | |
| 	c.Emit("data", data)
 | |
| }
 | |
| func (c *Client) SetFastPathListener(f core.FastPathListener) {
 | |
| 	c.fastPathListener = f
 | |
| }
 | |
| 
 | |
| func (c *Client) RecvFastPath(secFlag byte, s []byte) {
 | |
| 	data := s
 | |
| 	if c.enableEncryption && secFlag&FASTPATH_OUTPUT_ENCRYPTED != 0 {
 | |
| 		data = c.readEncryptedPayload(s, secFlag&FASTPATH_OUTPUT_SECURE_CHECKSUM != 0)
 | |
| 	}
 | |
| 	c.fastPathListener.RecvFastPath(secFlag, data)
 | |
| }
 | |
| 
 | |
| func (c *Client) SetChannelSender(f core.ChannelSender) {
 | |
| 	c.channelSender = f
 | |
| }
 | |
| 
 | |
| func (c *Client) SendToChannel(channel string, b []byte) (int, error) {
 | |
| 	if !c.enableEncryption {
 | |
| 		glog.Debug("Sec Client write", hex.EncodeToString(b))
 | |
| 		return c.channelSender.SendToChannel(channel, b)
 | |
| 	}
 | |
| 	var flag uint16 = ENCRYPT
 | |
| 	if c.enableSecureCheckSum {
 | |
| 		flag |= SECURE_CHECKSUM
 | |
| 	}
 | |
| 	data := c.writeEncryptedPayload(b, c.enableSecureCheckSum)
 | |
| 
 | |
| 	buff := &bytes.Buffer{}
 | |
| 	core.WriteUInt16LE(flag, buff)
 | |
| 	core.WriteUInt16LE(0, buff)
 | |
| 	core.WriteBytes(data, buff)
 | |
| 	glog.Debug("Sec Client write", channel, hex.EncodeToString(buff.Bytes()))
 | |
| 	return c.channelSender.SendToChannel(channel, buff.Bytes())
 | |
| }
 |