refactor(grdp): 精简RDP库,删除认证检测不需要的代码

- 删除 VNC 协议支持 (protocol/rfb, client/rfb.go)
- 删除完整客户端框架 (client/)
- 删除 RemoteApp 等插件 (plugin/)
- 删除 RLE 图形解压 (core/rle.go)
- 删除绘图指令处理 (pdu/orders.go, pdu/gdi.go)
- 精简 screen.go,移除截图和完整会话功能
- 移除未使用的 RGB 转换函数

grdp 代码从 13,044 行精简至 7,581 行,削减 42%
This commit is contained in:
ZacharyZcR
2026-01-21 20:54:28 +08:00
parent 849c28ede2
commit 26cf7dbb37
16 changed files with 5 additions and 5468 deletions
+3 -438
View File
@@ -1,8 +1,6 @@
package login
import (
"bufio"
"bytes"
"errors"
"fmt"
"github.com/shadow1ng/fscan/mylib/grdp/core"
@@ -15,12 +13,7 @@ import (
"github.com/shadow1ng/fscan/mylib/grdp/protocol/x224"
"golang.org/x/net/context"
"golang.org/x/net/proxy"
"image"
"image/color"
"image/draw"
"image/jpeg"
"log"
"math/rand"
"net"
"net/url"
"os"
@@ -34,39 +27,6 @@ var (
OutputDir string
)
func init() {
}
func RdpConn(host, domain, user, password string, timeout int64, rdpProtocol uint32) (bool, error) {
g := NewClient(host, LogLever)
status, err, reconnectProtocol := g.ScreenShot(domain, user, password, timeout, rdpProtocol)
if status == true {
return true, err
} else {
if reconnectProtocol != rdpProtocol {
glog.Info("reconnect with protocol:", reconnectProtocol)
return RdpConn(host, domain, user, password, timeout, reconnectProtocol)
} else {
return status, err
}
}
}
func RdpCrack(host, domain, user, password string, timeout int64, rdpProtocol uint32) (bool, error) {
g := NewClient(host, LogLever)
status, err, reconnectProtocol := g.Crack(domain, user, password, timeout, rdpProtocol)
if status == true {
return true, err
} else {
if reconnectProtocol != rdpProtocol {
glog.Info("reconnect with protocol:", reconnectProtocol)
return RdpCrack(host, domain, user, password, timeout, reconnectProtocol)
} else {
return status, err
}
}
}
// NlaAuth 仅进行NLA认证验证,不建立RDP会话,不会挤掉已登录用户
// 返回: (认证成功, 错误信息)
func NlaAuth(host, domain, user, password string, timeout int64) (bool, error) {
@@ -99,14 +59,13 @@ func WrapperTcpWithTimeout(network, address string, timeout time.Duration) (net.
net_ip = net.ParseIP("0.0.0.0")
}
local_addr := &net.TCPAddr{
IP: net_ip, // 替换为你想要使用的本地IP地址
IP: net_ip,
}
d := &net.Dialer{Timeout: timeout, LocalAddr: local_addr}
return WrapperTCP(network, address, d)
}
func WrapperTCP(network, address string, forward *net.Dialer) (net.Conn, error) {
//get conn
var conn net.Conn
if Socks5Proxy == "" {
var err error
@@ -121,10 +80,8 @@ func WrapperTCP(network, address string, forward *net.Dialer) (net.Conn, error)
}
conn, err = dailer.Dial(network, address)
if err != nil {
// fmt.Println(err)
return nil, err
}
}
timeout := forward.Timeout
@@ -136,7 +93,6 @@ func WrapperTCP(network, address string, forward *net.Dialer) (net.Conn, error)
}
return conn, nil
}
func Socks5Dailer(forward *net.Dialer) (proxy.Dialer, error) {
@@ -166,60 +122,6 @@ func Socks5Dailer(forward *net.Dialer) (proxy.Dialer, error) {
return dailer, nil
}
type Bitmap struct {
DestLeft int `json:"destLeft"`
DestTop int `json:"destTop"`
DestRight int `json:"destRight"`
DestBottom int `json:"destBottom"`
Width int `json:"width"`
Height int `json:"height"`
BitsPerPixel int `json:"bitsPerPixel"`
IsCompress bool `json:"isCompress"`
Data []byte `json:"data"`
}
func Bpp(BitsPerPixel uint16) (pixel int) {
switch BitsPerPixel {
case 15:
pixel = 1
case 16:
pixel = 2
case 24:
pixel = 3
case 32:
pixel = 4
default:
glog.Error("-------------------------------------Bpp func. invalid bitmap data format")
}
return
}
func BitmapDecompress(bitmap *pdu.BitmapData) []byte {
return core.Decompress(bitmap.BitmapDataStream, int(bitmap.Width), int(bitmap.Height), Bpp(bitmap.BitsPerPixel))
}
func ToRGBA(pixel int, i int, data []byte) (r, g, b, a uint8) {
a = 255
switch pixel {
case 1:
rgb555 := core.Uint16BE(data[i], data[i+1])
r, g, b = core.RGB555ToRGB(rgb555)
case 2:
rgb565 := core.Uint16BE(data[i], data[i+1])
r, g, b = core.RGB565ToRGB(rgb565)
case 3, 4:
fallthrough
default:
r, g, b = data[i+2], data[i+1], data[i]
}
return
}
// NlaAuthOnly 仅进行NLA认证验证凭据,不建立RDP会话
// 这样不会挤掉已登录的用户
func (g *Client) NlaAuthOnly(domain, user, pwd string, timeout int64) (bool, error) {
@@ -307,7 +209,7 @@ func (g *Client) ProbeOSInfo(host, domain, user, pwd string, timeout int64, rdpP
g.pdu.Emit("done")
})
g.x224.SetRequestedProtocol(rdpProtocol) //x224.PROTOCOL_SSL , x224.PROTOCOL_RDP , x224.PROTOCOL_HYBRID , x224.PROTOCOL_HYBRID_EX
g.x224.SetRequestedProtocol(rdpProtocol)
g.x224.On("reconnect", func(protocol uint32) {
info["reconn"] = protocol
g.pdu.Emit("close")
@@ -353,7 +255,7 @@ func (g *Client) ProbeOSInfo(host, domain, user, pwd string, timeout int64, rdpP
loop:
for {
select {
case <-time.After(time.Second * time.Duration(timeout)): //
case <-time.After(time.Second * time.Duration(timeout)):
break loop
case <-exitFlag:
break loop
@@ -365,340 +267,3 @@ loop:
glog.Debug("循环结束,总时间过去了:", time.Since(start))
return info
}
func (g *Client) ScreenShot(domain, user, pwd string, timeout int64, rdpProtocol uint32) (status bool, err error, reconnProtocol uint32) {
//glog.SetLevel(glog.ERROR)
reconnProtocol = rdpProtocol
pic_length := 1280 //1280
pic_width := 800 //800
needReconnect := false
isScreenOK := false
refresh := make(chan bool)
exitFlag := make(chan bool)
start := time.Now()
now := start
screenImage := image.NewRGBA(image.Rect(0, 0, pic_length, pic_width))
targetSlice := strings.Split(g.Host, ":")
ip := targetSlice[0]
port := targetSlice[1]
status = false
conn, err := WrapperTcpWithTimeout("tcp", g.Host, time.Duration(timeout)*time.Second)
if err != nil {
return status, fmt.Errorf("[dial err] %v", err), reconnProtocol
}
defer conn.Close()
glog.Info(conn.LocalAddr().String())
g.tpkt = tpkt.New(core.NewSocketLayer(conn), nla.NewNTLMv2(domain, user, pwd))
g.x224 = x224.New(g.tpkt)
g.mcs = t125.NewMCSClient(g.x224)
g.sec = sec.NewClient(g.mcs)
g.pdu = pdu.NewClient(g.sec)
g.sec.SetUser(user)
g.sec.SetPwd(pwd)
g.sec.SetDomain(domain)
//g.sec.SetClientAutoReconnect()
g.tpkt.SetFastPathListener(g.sec)
g.sec.SetFastPathListener(g.pdu)
g.pdu.SetFastPathSender(g.tpkt)
g.sec.SetChannelSender(g.mcs)
g.tpkt.On("os_info", func(info map[string]any) {
glog.Debug("[+] callback, get os info ........................")
for k, v := range info {
glog.Debugf("%s: %s\n", k, v)
}
})
g.x224.SetRequestedProtocol(rdpProtocol) //x224.PROTOCOL_SSL , x224.PROTOCOL_RDP , x224.PROTOCOL_HYBRID , x224.PROTOCOL_HYBRID_EX
g.x224.On("reconnect", func(protocol uint32) {
needReconnect = true
reconnProtocol = protocol
glog.Info("need reconnect with protocol:", protocol)
g.pdu.Emit("close")
exitFlag <- true
})
g.x224.On("more_timeout", func() {
timeout += 18 //如果是PROTOCOL_RDP协议,可以适当延长超时时间
})
err = g.x224.Connect()
if err != nil {
return status, fmt.Errorf("[x224 connect err] %v", err), reconnProtocol
}
glog.Info("wait connect ok")
g.pdu.On("error", func(e error) {
err = e
glog.Error("error", e)
g.pdu.Emit("done")
})
g.pdu.On("close", func() {
err = errors.New("close")
glog.Info("on close")
g.pdu.Emit("done")
})
g.pdu.On("success", func() {
glog.Debugf("===============login success %s===============", ip)
status = true
err = nil
g.pdu.Emit("done")
})
g.pdu.On("ready", func() {
err = nil
glog.Debug("on ready")
//g.pdu.Emit("done")
})
g.pdu.On("bitmap", func(rectangles []pdu.BitmapData) {
now = time.Now()
// 发送一个鼠标事件,作用是与服务器保持联系不要断开
rand.New(rand.NewSource(time.Now().UnixNano()))
randomNumber := rand.Intn(1000) + 10 // 10到1000之间的随机数
mouseX := 60 + randomNumber
mouseY := 10 + randomNumber
pevent := &pdu.PointerEvent{}
pevent.PointerFlags |= pdu.PTRFLAGS_MOVE
pevent.XPos = uint16(mouseX)
pevent.YPos = uint16(mouseY)
g.pdu.SendInputEvents(pdu.INPUT_EVENT_MOUSE, []pdu.InputEventsInterface{pevent})
glog.Debug("on update bitmap:", len(rectangles))
bs := make([]Bitmap, 0)
for _, v := range rectangles {
IsCompress := v.IsCompress()
data := v.BitmapDataStream
if IsCompress {
data = 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, data}
bs = append(bs, b)
}
var (
pixel int
i int
r, g, b, a uint8
)
for _, bm := range bs {
i = 0
pixel = bm.BitsPerPixel
m := image.NewRGBA(image.Rect(0, 0, bm.Width, bm.Height))
for y := 0; y < bm.Height; y++ {
for x := 0; x < bm.Width; x++ {
r, g, b, a = ToRGBA(pixel, i, bm.Data)
c := color.RGBA{R: r, G: g, B: b, A: a}
i += pixel
m.Set(x, y, c)
}
}
draw.Draw(screenImage, screenImage.Bounds().Add(image.Pt(bm.DestLeft, bm.DestTop)), m, m.Bounds().Min, draw.Src)
}
// Encode to jpeg.
//var imageBuf bytes.Buffer
//err = jpeg.Encode(&imageBuf, screenImage, nil)
//
//if err != nil {
// glog.Info("trans bitmap to jpeg err:", err)
//}
// Write to file.
//fo, err := os.Create(fmt.Sprintf("img/%s-%d.jpg", ip, index))
//if err != nil {
// panic(err)
//}
//index += 1
//fw := bufio.NewWriter(fo)
//fw.Write(imageBuf.Bytes())
isScreenOK = true
refresh <- true
})
g.pdu.On("done", func() {
glog.Debug("done信号触发")
exitFlag <- true
})
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout*6)*time.Second)
defer cancel()
loop:
for {
select {
case <-time.After(time.Second * time.Duration(timeout*3)): //
glog.Debug("本次获取bitmap超时了, 距离上次获取到图像过去了:", time.Since(now))
break loop
case <-refresh:
continue loop
case <-exitFlag:
break loop
case <-ctx.Done():
glog.Debug("总超时已达到,退出")
break loop
}
}
glog.Debug("循环结束,总时间过去了:", time.Since(start))
// 认证结果由 success 事件回调设置,不在此处覆盖
if needReconnect {
return status, err, reconnProtocol
} else if isScreenOK {
glog.Info("get screen ok")
// Encode to jpeg.
var imageBuf bytes.Buffer
encodeErr := jpeg.Encode(&imageBuf, screenImage, nil)
if encodeErr != nil {
glog.Error("Failed to encode screenshot:", encodeErr)
return status, err, reconnProtocol
}
// Write to file.
saveDate := time.Now().Format("2006_01_02_15_04_05")
fo, writeErr := os.Create(fmt.Sprintf("%s/%s_%s_%s.jpg", OutputDir, ip, port, saveDate))
if writeErr != nil {
glog.Error("Can not create rdp screenshot file:", writeErr)
} else {
defer fo.Close()
fw := bufio.NewWriter(fo)
_, writeErr := fw.Write(imageBuf.Bytes())
if writeErr != nil {
glog.Error("Can not write rdp screenshot file:", writeErr)
} else {
fw.Flush()
}
}
}
return status, err, reconnProtocol
}
func (g *Client) Crack(domain, user, pwd string, timeout int64, rdpProtocol uint32) (status bool, err error, reconnProtocol uint32) {
//glog.SetLevel(glog.ERROR)
reconnProtocol = rdpProtocol
refresh := make(chan bool)
exitFlag := make(chan bool)
start := time.Now()
now := start
targetSlice := strings.Split(g.Host, ":")
ip := targetSlice[0]
status = false
conn, err := WrapperTcpWithTimeout("tcp", g.Host, time.Duration(timeout)*time.Second)
if err != nil {
return status, fmt.Errorf("[dial err] %v", err), reconnProtocol
}
defer conn.Close()
glog.Info(conn.LocalAddr().String())
g.tpkt = tpkt.New(core.NewSocketLayer(conn), nla.NewNTLMv2(domain, user, pwd))
g.x224 = x224.New(g.tpkt)
g.mcs = t125.NewMCSClient(g.x224)
g.sec = sec.NewClient(g.mcs)
g.pdu = pdu.NewClient(g.sec)
g.sec.SetUser(user)
g.sec.SetPwd(pwd)
g.sec.SetDomain(domain)
//g.sec.SetClientAutoReconnect()
g.tpkt.SetFastPathListener(g.sec)
g.sec.SetFastPathListener(g.pdu)
g.pdu.SetFastPathSender(g.tpkt)
g.sec.SetChannelSender(g.mcs)
g.tpkt.On("os_info", func(info map[string]any) {
glog.Debug("[+] callback, get os info ........................")
for k, v := range info {
glog.Debugf("%s: %s\n", k, v)
}
})
g.x224.SetRequestedProtocol(rdpProtocol) //x224.PROTOCOL_SSL , x224.PROTOCOL_RDP , x224.PROTOCOL_HYBRID , x224.PROTOCOL_HYBRID_EX
g.x224.On("reconnect", func(protocol uint32) {
reconnProtocol = protocol
glog.Info("need reconnect with protocol:", protocol)
g.pdu.Emit("close")
exitFlag <- true
})
g.x224.On("more_timeout", func() {
timeout += 18 //如果是PROTOCOL_RDP协议,可以适当延长超时时间
})
err = g.x224.Connect()
if err != nil {
return status, fmt.Errorf("[x224 connect err] %v", err), reconnProtocol
}
glog.Info("wait connect ok")
g.pdu.On("error", func(e error) {
err = e
glog.Error("error", e)
g.pdu.Emit("done")
})
g.pdu.On("close", func() {
err = errors.New("close")
glog.Info("on close")
g.pdu.Emit("done")
})
g.pdu.On("success", func() {
glog.Debugf("===============login success %s===============", ip)
status = true
err = nil
g.pdu.Emit("done")
})
g.pdu.On("ready", func() {
err = nil
glog.Debug("on ready")
//g.pdu.Emit("done")
})
g.pdu.On("bitmap", func(rectangles []pdu.BitmapData) {
now = time.Now()
// 发送一个鼠标事件,作用是与服务器保持联系不要断开
rand.New(rand.NewSource(time.Now().UnixNano()))
randomNumber := rand.Intn(1000) + 10 // 10到1000之间的随机数
mouseX := 60 + randomNumber
mouseY := 10 + randomNumber
pevent := &pdu.PointerEvent{}
pevent.PointerFlags |= pdu.PTRFLAGS_MOVE
pevent.XPos = uint16(mouseX)
pevent.YPos = uint16(mouseY)
g.pdu.SendInputEvents(pdu.INPUT_EVENT_MOUSE, []pdu.InputEventsInterface{pevent})
glog.Debug("on update bitmap:", len(rectangles))
refresh <- true
})
g.pdu.On("done", func() {
glog.Debug("done信号触发")
exitFlag <- true
})
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout*6)*time.Second)
defer cancel()
loop:
for {
select {
case <-time.After(time.Second * time.Duration(timeout*3)): //
glog.Debug("本次获取bitmap超时了, 距离上次获取到图像过去了:", time.Since(now))
break loop
case <-refresh:
continue loop
case <-exitFlag:
break loop
case <-ctx.Done():
glog.Debug("总超时已达到,退出")
break loop
}
}
glog.Debug("循环结束,总时间过去了:", time.Since(start))
// 认证结果由 success 事件回调设置,不在此处覆盖
return status, err, reconnProtocol
}