diff --git a/client/client.go b/client/client.go index 4ebccbe1..95f794a4 100644 --- a/client/client.go +++ b/client/client.go @@ -1,4 +1,3 @@ -// client.go package client import ( @@ -6,23 +5,16 @@ import ( "os" "github.com/tomatome/grdp/glog" - "github.com/tomatome/grdp/protocol/pdu" - "github.com/tomatome/grdp/protocol/rfb" ) -const ( - CLIP_OFF = 0 - CLIP_IN = 0x1 - CLIP_OUT = 0x2 -) - -const ( - TC_RDP = 0 - TC_VNC = 1 -) +func init() { + logger := log.New(os.Stdout, "", 0) + glog.SetLogger(logger) +} -type Control interface { +type RemoteControl interface { Login(host, user, passwd string, width, height int) error + Connect() error KeyUp(sc int, name string) KeyDown(sc int, name string) MouseMove(x, y int) @@ -30,110 +22,10 @@ type Control interface { MouseUp(button int, x, y int) MouseDown(button int, x, y int) On(event string, msg interface{}) + OnBitmap(handler func([]Bitmap)) Close() } -type Client struct { - host string - user string - passwd string - ctl Control - tc int - setting *Setting -} - -func init() { - logger := log.New(os.Stdout, "", 0) - glog.SetLogger(logger) -} -func NewClient(host, user, passwd string, t int, s *Setting) *Client { - if s == nil { - s = NewSetting() - } - c := &Client{ - host: host, - user: user, - passwd: passwd, - tc: t, - setting: s, - } - - switch t { - case TC_VNC: - c.ctl = newVncClient(s) - default: - c.ctl = newRdpClient(s) - } - - s.SetLogLevel() - return c -} - -func (c *Client) Login() error { - return c.ctl.Login(c.host, c.user, c.passwd, c.setting.Width, c.setting.Height) -} - -func (c *Client) KeyUp(sc int, name string) { - c.ctl.KeyUp(sc, name) -} -func (c *Client) KeyDown(sc int, name string) { - c.ctl.KeyDown(sc, name) -} -func (c *Client) MouseMove(x, y int) { - c.ctl.MouseMove(x, y) -} -func (c *Client) MouseWheel(scroll, x, y int) { - c.ctl.MouseWheel(scroll, x, y) -} -func (c *Client) MouseUp(button, x, y int) { - c.ctl.MouseUp(button, x, y) -} -func (c *Client) MouseDown(button, x, y int) { - c.ctl.MouseDown(button, x, y) -} -func (c *Client) OnError(f func(e error)) { - c.ctl.On("error", f) -} -func (c *Client) OnClose(f func()) { - c.ctl.On("close", f) -} -func (c *Client) OnSuccess(f func()) { - c.ctl.On("success", f) -} -func (c *Client) OnReady(f func()) { - c.ctl.On("ready", f) -} -func (c *Client) OnBitmap(f func([]Bitmap)) { - f1 := func(data interface{}) { - bs := make([]Bitmap, 0, 50) - if c.tc == TC_VNC { - br := data.(*rfb.BitRect) - for _, v := range br.Rects { - b := Bitmap{int(v.Rect.X), int(v.Rect.Y), int(v.Rect.X + v.Rect.Width), int(v.Rect.Y + v.Rect.Height), - int(v.Rect.Width), int(v.Rect.Height), - Bpp(uint16(br.Pf.BitsPerPixel)), false, v.Data} - bs = append(bs, b) - } - } else { - for _, v := range data.([]pdu.BitmapData) { - IsCompress := v.IsCompress() - stream := v.BitmapDataStream - if IsCompress { - stream = bitmapDecompress(&v) - IsCompress = false - } - - b := Bitmap{int(v.DestLeft), int(v.DestTop), int(v.DestRight), int(v.DestBottom), - int(v.Width), int(v.Height), Bpp(v.BitsPerPixel), IsCompress, stream} - bs = append(bs, b) - } - } - f(bs) - } - - c.ctl.On("update", f1) -} - type Bitmap struct { DestLeft int `json:"destLeft"` DestTop int `json:"destTop"` @@ -165,24 +57,3 @@ func Bpp(bp uint16) (pixel int) { } return } - -type Setting struct { - Width int - Height int - Protocol string - LogLevel glog.LEVEL -} - -func NewSetting() *Setting { - return &Setting{ - Width: 1024, - Height: 768, - LogLevel: glog.INFO, - } -} -func (s *Setting) SetLogLevel() { - glog.SetLevel(s.LogLevel) -} - -func (s *Setting) SetRequestedProtocol(p uint32) {} -func (s *Setting) SetClipboard(c int) {} diff --git a/client/client_test.go b/client/client_test.go deleted file mode 100644 index f69f467b..00000000 --- a/client/client_test.go +++ /dev/null @@ -1,20 +0,0 @@ -// client_test.go -package client - -import ( - "fmt" - "testing" - "time" -) - -func TestClient(t *testing.T) { - c := NewClient("192.168.0.132:3389", "administrator", "Jhadmin123", TC_RDP, nil) - err := c.Login() - if err != nil { - fmt.Println("Login:", err) - } - c.OnBitmap(func(b []Bitmap) { - fmt.Println("ready:", b) - }) - time.Sleep(100 * time.Second) -} diff --git a/client/rdp.go b/client/rdp.go index 145b1aaa..89ebdac5 100644 --- a/client/rdp.go +++ b/client/rdp.go @@ -7,6 +7,7 @@ import ( "time" "github.com/tomatome/grdp/core" + "github.com/tomatome/grdp/glog" "github.com/tomatome/grdp/plugin" "github.com/tomatome/grdp/protocol/nla" "github.com/tomatome/grdp/protocol/pdu" @@ -16,22 +17,69 @@ import ( "github.com/tomatome/grdp/protocol/x224" ) +const ( + RdpProtocolRDP = "rdp" + RdpProtocolSSL = "ssl" + RdpProtocolHybrid = "hybrid" + RdpProtocolHybridEx = "hybrid_ex" +) + type RdpClient struct { - tpkt *tpkt.TPKT - x224 *x224.X224 - mcs *t125.MCSClient - sec *sec.Client - pdu *pdu.Client - channels *plugin.Channels + // connection + host string + port string + domain string + username string + password string + + // window + windowHeight uint16 + windowWidth uint16 + + // protocols + tpkt *tpkt.TPKT + x224 *x224.X224 + mcs *t125.MCSClient + sec *sec.Client + pdu *pdu.Client + channels *plugin.Channels + requestedProtocol string +} + +func (c *RdpClient) setRequestedProtocol() { + switch c.requestedProtocol { + case RdpProtocolRDP: + c.x224.SetRequestedProtocol(x224.PROTOCOL_RDP) + case RdpProtocolSSL: + c.x224.SetRequestedProtocol(x224.PROTOCOL_SSL) + case RdpProtocolHybrid: + c.x224.SetRequestedProtocol(x224.PROTOCOL_HYBRID) + case RdpProtocolHybridEx: + c.x224.SetRequestedProtocol(x224.PROTOCOL_HYBRID_EX) + } +} + +func (c *RdpClient) WithWindowSize(height, width uint16) { + c.windowWidth = width + c.windowHeight = height } -func newRdpClient(s *Setting) *RdpClient { - return &RdpClient{} +func (c *RdpClient) WithRequestedProtocol(protocol string) { + c.requestedProtocol = protocol +} + +func NewRdpClient(host, port, domain, username, password string) *RdpClient { + return &RdpClient{ + host: host, port: port, domain: domain, + username: username, password: password, + windowHeight: 600, windowWidth: 800, + } } func bitmapDecompress(bitmap *pdu.BitmapData) []byte { return core.Decompress(bitmap.BitmapDataStream, int(bitmap.Width), int(bitmap.Height), Bpp(bitmap.BitsPerPixel)) } + func split(user string) (domain string, uname string) { if strings.Index(user, "\\") != -1 { t := strings.Split(user, "\\") @@ -46,12 +94,49 @@ func split(user string) (domain string, uname string) { } return } + +func (c *RdpClient) Connect() error { + addr := net.JoinHostPort(c.host, c.port) + conn, err := net.DialTimeout("tcp", addr, 10*time.Second) + if err != nil { + return err + } + + domain := c.domain + username := c.username + if c.domain == "" { + domain, username = split(username) + } + + glog.Infof("Connect to %v", addr) + + c.tpkt = tpkt.New(core.NewSocketLayer(conn), nla.NewNTLMv2(domain, username, c.password)) + c.x224 = x224.New(c.tpkt) + c.mcs = t125.NewMCSClient(c.x224) + c.sec = sec.NewClient(c.mcs) + c.pdu = pdu.NewClient(c.sec) + c.channels = plugin.NewChannels(c.sec) + + c.mcs.SetClientCoreData(c.windowWidth, c.windowHeight) + + c.sec.SetUser(username) + c.sec.SetPwd(c.password) + c.sec.SetDomain(domain) + + c.tpkt.SetFastPathListener(c.sec) + c.sec.SetFastPathListener(c.pdu) + c.sec.SetChannelSender(c.mcs) + c.channels.SetChannelSender(c.sec) + + c.setRequestedProtocol() + return c.x224.Connect() +} + func (c *RdpClient) Login(host, user, pwd string, width, height int) error { conn, err := net.DialTimeout("tcp", host, 3*time.Second) if err != nil { return fmt.Errorf("[dial err] %v", err) } - domain, user := split(user) c.tpkt = tpkt.New(core.NewSocketLayer(conn), nla.NewNTLMv2(domain, user, pwd)) c.x224 = x224.New(c.tpkt) @@ -80,15 +165,18 @@ func (c *RdpClient) Login(host, user, pwd string, width, height int) error { } return nil } + func (c *RdpClient) On(event string, f interface{}) { c.pdu.On(event, f) } + func (c *RdpClient) KeyUp(sc int, name string) { p := &pdu.ScancodeKeyEvent{} p.KeyCode = uint16(sc) p.KeyboardFlags |= pdu.KBDFLAGS_RELEASE c.pdu.SendInputEvents(pdu.INPUT_EVENT_SCANCODE, []pdu.InputEventsInterface{p}) } + func (c *RdpClient) KeyDown(sc int, name string) { p := &pdu.ScancodeKeyEvent{} p.KeyCode = uint16(sc) @@ -129,6 +217,7 @@ func (c *RdpClient) MouseUp(button int, x, y int) { p.YPos = uint16(y) c.pdu.SendInputEvents(pdu.INPUT_EVENT_MOUSE, []pdu.InputEventsInterface{p}) } + func (c *RdpClient) MouseDown(button int, x, y int) { p := &pdu.PointerEvent{} @@ -149,8 +238,29 @@ func (c *RdpClient) MouseDown(button int, x, y int) { p.YPos = uint16(y) c.pdu.SendInputEvents(pdu.INPUT_EVENT_MOUSE, []pdu.InputEventsInterface{p}) } + func (c *RdpClient) Close() { if c != nil && c.tpkt != nil { c.tpkt.Close() } } + +func (c *RdpClient) OnBitmap(handler func([]Bitmap)) { + bitmapsFunc := func(data interface{}) { + bs := make([]Bitmap, 0, 50) + for _, v := range data.([]pdu.BitmapData) { + IsCompress := v.IsCompress() + stream := v.BitmapDataStream + if IsCompress { + stream = bitmapDecompress(&v) + IsCompress = false + } + + b := Bitmap{int(v.DestLeft), int(v.DestTop), int(v.DestRight), int(v.DestBottom), + int(v.Width), int(v.Height), Bpp(v.BitsPerPixel), IsCompress, stream} + bs = append(bs, b) + } + handler(bs) + } + c.On("update", bitmapsFunc) +} diff --git a/client/rdp_test.go b/client/rdp_test.go new file mode 100644 index 00000000..f1a69d15 --- /dev/null +++ b/client/rdp_test.go @@ -0,0 +1,66 @@ +package client + +import ( + "fmt" + "os" + "testing" + "time" +) + +func getHost() string { + return os.Getenv("RDP_HOST") +} + +func getPort() string { + port := os.Getenv("RDP_POST") + if port == "" { + port = "3389" + } + return port +} + +func getDomain() string { + return os.Getenv("RDP_DOMAIN") +} + +func getUsername() string { + username := os.Getenv("RDP_USERNAME") + if username == "" { + username = "administrator" + } + return username +} + +func getPassword() string { + return os.Getenv("RDP_PASSWORD") +} + +func TestClientLogin(t *testing.T) { + c := NewRdpClient(getHost(), getPort(), getDomain(), getUsername(), getPassword()) + err := c.Login(getHost()+":"+getPort(), getUsername(), getPassword(), 800, 600) + if err != nil { + fmt.Println("Login:", err) + } + c.OnBitmap(func(b []Bitmap) { + fmt.Println("ready:", b) + }) + time.Sleep(100 * time.Second) +} + +func TestClientConnect(t *testing.T) { + host := getHost() + port := getPort() + domain := getDomain() + username := getUsername() + password := getPassword() + c := NewRdpClient(host, port, domain, username, password) + err := c.Connect() + if err != nil { + panic(err) + } + c.OnBitmap(func(bitmaps []Bitmap) { + fmt.Printf("Ready %d bitmaps\n", len(bitmaps)) + fmt.Println("Ready: ", bitmaps) + }) + time.Sleep(100 * time.Second) +} diff --git a/client/rfb.go b/client/rfb.go index 48f69be3..78336cff 100644 --- a/client/rfb.go +++ b/client/rfb.go @@ -13,8 +13,8 @@ type VncClient struct { vnc *rfb.RFB } -func newVncClient(s *Setting) *VncClient { - return &VncClient{} +func (c *VncClient) Connect() error { + return nil } func (c *VncClient) Login(host, user, pwd string, width, height int) error { @@ -101,3 +101,18 @@ func (c *VncClient) Close() { c.vnc.Close() } } + +func (c *VncClient) OnBitmap(handler func([]Bitmap)) { + f1 := func(data interface{}) { + bs := make([]Bitmap, 0, 50) + br := data.(*rfb.BitRect) + for _, v := range br.Rects { + b := Bitmap{int(v.Rect.X), int(v.Rect.Y), int(v.Rect.X + v.Rect.Width), int(v.Rect.Y + v.Rect.Height), + int(v.Rect.Width), int(v.Rect.Height), + Bpp(uint16(br.Pf.BitsPerPixel)), false, v.Data} + bs = append(bs, b) + } + handler(bs) + } + c.On("update", f1) +} diff --git a/client/unused.go b/client/unused.go new file mode 100644 index 00000000..dd7074d9 --- /dev/null +++ b/client/unused.go @@ -0,0 +1,9 @@ +// module with old unused code + +package client + +const ( + CLIP_OFF = 0 + CLIP_IN = 0x1 + CLIP_OUT = 0x2 +) diff --git a/go.mod b/go.mod index 874d5a98..314298e6 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 github.com/tomatome/win v0.3.1 - golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d + golang.org/x/crypto v0.2.0 ) require ( diff --git a/go.sum b/go.sum index 129e9361..8556df7b 100644 --- a/go.sum +++ b/go.sum @@ -38,8 +38,11 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/tomatome/win v0.3.0 h1:IezE/bpr7HBb5/gl4IRxputcFOrjGZHHiL3Rn1JswEo= github.com/tomatome/win v0.3.0/go.mod h1:YY2OpEdJ5Z1gOya7w+W7Ziv/lRIiokxlQgj4i7QgbhY= +github.com/tomatome/win v0.3.1/go.mod h1:YY2OpEdJ5Z1gOya7w+W7Ziv/lRIiokxlQgj4i7QgbhY= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE= +golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/image v0.0.0-20220617043117-41969df76e82 h1:KpZB5pUSBvrHltNEdK/tw0xlPeD13M6M6aGP32gKqiw= golang.org/x/image v0.0.0-20220617043117-41969df76e82/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= diff --git a/protocol/nla/cssp_test.go b/protocol/nla/cssp_test.go index fc32ddcb..4b6a91b5 100644 --- a/protocol/nla/cssp_test.go +++ b/protocol/nla/cssp_test.go @@ -9,7 +9,7 @@ import ( func TestEncodeDERTRequest(t *testing.T) { ntlm := nla.NewNTLMv2("", "", "") - result := nla.EncodeDERTRequest([]nla.Message{ntlm.GetNegotiateMessage()}, "", "") + result := nla.EncodeDERTRequest([]nla.Message{ntlm.GetNegotiateMessage()}, []byte(""), []byte("")) if hex.EncodeToString(result) != "302fa003020102a12830263024a02204204e544c4d53535000010000003582086000000000000000000000000000000000" { t.Error("not equal") } diff --git a/protocol/nla/ntlm_test.go b/protocol/nla/ntlm_test.go index 905a6ff3..4e4ca27c 100644 --- a/protocol/nla/ntlm_test.go +++ b/protocol/nla/ntlm_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/lunixbochs/struc" + "github.com/tomatome/grdp/protocol/nla" ) @@ -33,11 +34,11 @@ func TestNTLMv2_ComputeResponse(t *testing.T) { Timestamp, _ := hex.DecodeString("a02f44f01267d501") ServerName, _ := hex.DecodeString("02001e00570049004e002d00460037005200410041004d004100500034004a00430001001e00570049004e002d00460037005200410041004d004100500034004a00430004001e00570049004e002d00460037005200410041004d004100500034004a00430003001e00570049004e002d00460037005200410041004d004100500034004a00430007000800a02f44f01267d50100000000") - NtChallengeResponse, LmChallengeResponse, SessionBaseKey := ntlm.ComputeResponse(ResponseKeyNT, ResponseKeyLM, ServerChallenge, ClienChallenge, Timestamp, ServerName) + NtChallengeResponse, LmChallengeResponse, SessionBaseKey := ntlm.ComputeResponseV2(ResponseKeyNT, ResponseKeyLM, ServerChallenge, ClienChallenge, Timestamp, ServerName) - ntChallRespExpected := "4e7316531937d2fc91e7230853844b890101000000000000a02f44f01267d5011a78bed8e5d5efa70000000002001e00570049004e002d00460037005200410041004d004100500034004a00430001001e00570049004e002d00460037005200410041004d004100500034004a00430004001e00570049004e002d00460037005200410041004d004100500034004a00430003001e00570049004e002d00460037005200410041004d004100500034004a00430007000800a02f44f01267d50100000000" + ntChallRespExpected := "ea942653ab115d382d9206f1fe9d44d60101000000000000a02f44f01267d5011a78bed8e5d5efa70000000002001e00570049004e002d00460037005200410041004d004100500034004a00430001001e00570049004e002d00460037005200410041004d004100500034004a00430004001e00570049004e002d00460037005200410041004d004100500034004a00430003001e00570049004e002d00460037005200410041004d004100500034004a00430007000800a02f44f01267d5010000000000000000" lmChallRespExpected := "d4dc6edc0c37dd70f69b5c4f05a615661a78bed8e5d5efa7" - sessBaseKeyExpected := "034009be89a0507b2bd6d28e966e1dab" + sessBaseKeyExpected := "0f400e7b256b77f28a5c7ff5e40e82b9" if hex.EncodeToString(NtChallengeResponse) != ntChallRespExpected { t.Error("NtChallengeResponse incorrect") @@ -51,12 +52,3 @@ func TestNTLMv2_ComputeResponse(t *testing.T) { t.Error("SessionBaseKey incorrect") } } - -func TestSIGNKEY(t *testing.T) { - exportedSessionKey, _ := hex.DecodeString("be32c3c56ea6683200a35329d67880c3") - result := hex.EncodeToString(nla.SIGNKEY(exportedSessionKey, true)) - expected := "79b4f9a4113230f378a0af99f784adae" - if result != expected { - t.Error(result, "not equal to", expected) - } -} diff --git a/protocol/rfb/rfb.go b/protocol/rfb/rfb.go index 8be378bd..73f93093 100644 --- a/protocol/rfb/rfb.go +++ b/protocol/rfb/rfb.go @@ -405,6 +405,11 @@ func NewRFB(t core.Transport) *RFB { return fb } +func (fb *RFB) Connect() error { + // TODO: implement + return nil +} + func (fb *RFB) recvProtocolVersion(version string) { if version != RFB003003 || version != RFB003007 || version != RFB003008 { version = RFB003008 diff --git a/protocol/t125/gcc/gcc.go b/protocol/t125/gcc/gcc.go index 9ad43a1e..b9e82cde 100644 --- a/protocol/t125/gcc/gcc.go +++ b/protocol/t125/gcc/gcc.go @@ -11,6 +11,7 @@ import ( "github.com/tomatome/grdp/glog" "github.com/lunixbochs/struc" + "github.com/tomatome/grdp/core" "github.com/tomatome/grdp/protocol/t125/per" ) @@ -597,7 +598,7 @@ func ReadConferenceCreateResponse(data []byte) []interface{} { err := d.Unpack(r) if err != nil { glog.Error("Unpack:", err) - return ret + continue } ret = append(ret, d) } diff --git a/unmaintained/README.md b/unmaintained/README.md new file mode 100644 index 00000000..5d1fc73a --- /dev/null +++ b/unmaintained/README.md @@ -0,0 +1 @@ +Some old, may be working code. diff --git a/grdp.go b/unmaintained/grdp.go similarity index 100% rename from grdp.go rename to unmaintained/grdp.go