mirror of
https://github.com/yv1ing/ShotRDP.git
synced 2025-09-16 15:10:57 +08:00
185 lines
4.5 KiB
Go
185 lines
4.5 KiB
Go
package main
|
||
|
||
import (
|
||
"context"
|
||
"log"
|
||
"net"
|
||
"os"
|
||
"strings"
|
||
"sync"
|
||
"time"
|
||
|
||
"ShotRDP/common"
|
||
"ShotRDP/grdp/core"
|
||
"ShotRDP/grdp/glog"
|
||
"ShotRDP/grdp/plugin"
|
||
"ShotRDP/grdp/protocol/nla"
|
||
"ShotRDP/grdp/protocol/pdu"
|
||
"ShotRDP/grdp/protocol/sec"
|
||
"ShotRDP/grdp/protocol/t125"
|
||
"ShotRDP/grdp/protocol/tpkt"
|
||
"ShotRDP/grdp/protocol/x224"
|
||
)
|
||
|
||
type Client struct {
|
||
Host string // 服务地址(ip:port)
|
||
tpkt *tpkt.TPKT // TPKT协议层
|
||
x224 *x224.X224 // X224协议层
|
||
mcs *t125.MCSClient // MCS协议层
|
||
sec *sec.Client // 安全层
|
||
pdu *pdu.Client // PDU协议层
|
||
|
||
channels *plugin.Channels
|
||
}
|
||
|
||
type Position struct {
|
||
Top int
|
||
Left int
|
||
Right int
|
||
Bottom int
|
||
}
|
||
|
||
var (
|
||
modifyMux = sync.Mutex{}
|
||
positionMap = make(map[Position]*common.Bitmap)
|
||
)
|
||
|
||
func NewClient(host string, logLevel glog.LEVEL) *Client {
|
||
_logger := log.New(os.Stdout, "", 0)
|
||
|
||
glog.SetLogger(_logger)
|
||
glog.SetLevel(logLevel)
|
||
|
||
return &Client{Host: host}
|
||
}
|
||
|
||
func (client *Client) Login(domain, username, password string) error {
|
||
conn, err := net.Dial("tcp", client.Host)
|
||
if err != nil {
|
||
glog.Errorf("TCP连接失败:%v", err)
|
||
return err
|
||
}
|
||
|
||
// 初始化协议栈
|
||
client.initProtocolStack(conn, domain, username, password)
|
||
|
||
// 建立X224连接
|
||
err = client.x224.Connect()
|
||
if err != nil {
|
||
glog.Errorf("建立X224连接失败:%v", err)
|
||
return err
|
||
}
|
||
|
||
wg := &sync.WaitGroup{}
|
||
wg.Add(1)
|
||
|
||
// 设置事件处理
|
||
client.setEventHandler(wg)
|
||
|
||
wg.Wait()
|
||
return nil
|
||
}
|
||
|
||
// initProtocolStack 初始化RDP协议栈
|
||
func (client *Client) initProtocolStack(conn net.Conn, domain, username, password string) {
|
||
// 创建协议层实例
|
||
client.tpkt = tpkt.New(core.NewSocketLayer(conn), nla.NewNTLMv2(domain, username, password))
|
||
client.x224 = x224.New(client.tpkt)
|
||
client.mcs = t125.NewMCSClient(client.x224)
|
||
client.sec = sec.NewClient(client.mcs)
|
||
client.pdu = pdu.NewClient(client.sec)
|
||
|
||
// 配置桌面信息
|
||
client.channels = plugin.NewChannels(client.sec)
|
||
client.mcs.SetClientDesktop(uint16(1920), uint16(1080))
|
||
|
||
// 设置认证信息
|
||
client.sec.SetDomain(domain)
|
||
client.sec.SetUsername(username)
|
||
client.sec.SetPassword(password)
|
||
|
||
// 配置协议层关联
|
||
client.tpkt.SetFastPathListener(client.sec)
|
||
client.sec.SetFastPathListener(client.pdu)
|
||
|
||
client.sec.SetChannelSender(client.mcs)
|
||
}
|
||
|
||
// setEventHandler 设置PDU事件处理器
|
||
func (client *Client) setEventHandler(wg *sync.WaitGroup) {
|
||
client.pdu.On("ready", func() {
|
||
glog.Info("PDU连接就绪")
|
||
|
||
// Test
|
||
//glog.Infof("服务端核心信息: %+v", *client.sec.ServerCoreData())
|
||
//glog.Infof("服务端安全信息: %+v", *client.sec.ServerSecurityData())
|
||
})
|
||
|
||
client.pdu.On("success", func() {
|
||
glog.Info("PDU连接成功")
|
||
})
|
||
|
||
client.pdu.On("close", func() {
|
||
glog.Info("PDU连接关闭")
|
||
})
|
||
|
||
client.pdu.On("done", func() {
|
||
glog.Info("PDU处理完成")
|
||
})
|
||
|
||
client.pdu.On("error", func(err error) {
|
||
glog.Errorf("PDU错误事件:%v", err)
|
||
client.pdu.Emit("done")
|
||
})
|
||
|
||
client.pdu.On("bitmap", func(rectangles []pdu.BitmapData) {
|
||
modifyMux.Lock()
|
||
for _, rectangle := range rectangles {
|
||
pos := Position{Left: int(rectangle.DestLeft / rectangle.Width), Top: int(rectangle.DestTop / rectangle.Height), Right: int(rectangle.DestRight / rectangle.Width), Bottom: int(rectangle.DestBottom / rectangle.Height)}
|
||
isCompress := rectangle.IsCompress()
|
||
data := rectangle.BitmapDataStream
|
||
if isCompress {
|
||
data = common.BitmapDecompress(&rectangle)
|
||
isCompress = false
|
||
}
|
||
|
||
bitmap := &common.Bitmap{
|
||
DestLeft: int(rectangle.DestLeft),
|
||
DestTop: int(rectangle.DestTop),
|
||
DestRight: int(rectangle.DestRight),
|
||
DestBottom: int(rectangle.DestBottom),
|
||
Width: int(rectangle.Width),
|
||
Height: int(rectangle.Height),
|
||
BitsPerPixel: common.Bpp(rectangle.BitsPerPixel),
|
||
IsCompress: isCompress,
|
||
Data: data,
|
||
}
|
||
positionMap[pos] = bitmap
|
||
}
|
||
modifyMux.Unlock()
|
||
})
|
||
|
||
// 合并登录界面位图
|
||
surfaceflingerCtx, _ := context.WithCancel(context.Background())
|
||
go func(ctx context.Context, c *Client) {
|
||
outputName := strings.ReplaceAll(client.Host, ":", "_")
|
||
|
||
for common.Opened(ctx) {
|
||
// TODO: 寻找合适的截图时机
|
||
time.Sleep(5 * time.Second)
|
||
modifyMux.Lock()
|
||
|
||
var bitmapList = make([]*common.Bitmap, 0)
|
||
for _, bitmap := range positionMap {
|
||
//common.DrawOneBitmap(bitmap, fmt.Sprintf("%d-%d-%d-%d.png", pos.Left, pos.Top, pos.Right, pos.Bottom))
|
||
bitmapList = append(bitmapList, bitmap)
|
||
}
|
||
|
||
common.DrawFullImage(outputName, bitmapList)
|
||
modifyMux.Unlock()
|
||
|
||
wg.Done()
|
||
}
|
||
}(surfaceflingerCtx, client)
|
||
}
|