feat(remote): 自 go-hua/remote 并入远程控制服务管理
- RDP/VNC(TightVNC)/Radmin 服务状态查询与启停重置、VNC 安装/配置读写/密码 加解密、GetRemoteInfo 汇总、CAD/SAS 注册表开关 - 与本库 file/shell/process 同属被控端远程运维能力; 新增依赖 kardianos/service、KarpelesLab/vncpasswd - 附 remote_test.go 与包 README(含平台支持矩阵与权限注意)
This commit is contained in:
@@ -0,0 +1,451 @@
|
||||
//go:build windows
|
||||
|
||||
package remote
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
"math/big"
|
||||
"os"
|
||||
)
|
||||
|
||||
// GetRemoteInfo 一次性获取远程控制相关信息(服务状态/端口/密码/主机信息)
|
||||
func GetRemoteInfo() Info {
|
||||
info := Info{}
|
||||
|
||||
status, _ := GetVncServiceStatus()
|
||||
info.VncState = int(status)
|
||||
|
||||
status, _ = GetRdpServiceStatus()
|
||||
info.RdpState = int(status)
|
||||
|
||||
port, _ := GetRdpPort()
|
||||
info.RdpPort = port
|
||||
|
||||
port, _ = GetVncPort()
|
||||
info.VncPort = port
|
||||
|
||||
password, _ := GetVncPassword()
|
||||
info.VncPassword = password
|
||||
|
||||
hostname, _ := os.Hostname()
|
||||
info.Hostname = hostname
|
||||
info.IpAddr = getIPs()
|
||||
info.MacAddr = getMacAddrs()
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
// GetVncPort 从注册表读取 TightVNC 主端口
|
||||
func GetVncPort() (int, error) {
|
||||
var key registry.Key
|
||||
var err error
|
||||
key, err = registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\TightVNC\Server`, registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var val uint64
|
||||
val, _, err = key.GetIntegerValue("RfbPort")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var port int = int(val)
|
||||
return port, nil
|
||||
}
|
||||
|
||||
// GetRdpPort 从注册表读取远程桌面端口
|
||||
func GetRdpPort() (int, error) {
|
||||
var key registry.Key
|
||||
var err error
|
||||
key, err = registry.OpenKey(registry.LOCAL_MACHINE, `SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp`, registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var val uint64
|
||||
val, _, err = key.GetIntegerValue("PortNumber")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var port int = int(val)
|
||||
return port, nil
|
||||
}
|
||||
|
||||
// GetVncPassword 读取并解密 TightVNC 连接密码
|
||||
func GetVncPassword() (string, error) {
|
||||
var key registry.Key
|
||||
var err error
|
||||
key, err = registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\TightVNC\Server`, registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
val, _, err := key.GetBinaryValue("Password")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(val) == 0 {
|
||||
return "", err
|
||||
}
|
||||
password := VncPasswordDecrypt(val)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return password, nil
|
||||
}
|
||||
|
||||
// VncServerDefaultConfig 生成 TightVNC 服务端默认配置(连接 RDP 会话)
|
||||
// randPass 为 true 时生成 8 位随机密码,否则使用固定密码
|
||||
func VncServerDefaultConfig(randPass bool) VncServerConfig {
|
||||
password := "tvncpass"
|
||||
if randPass {
|
||||
password = createRandomString(8)
|
||||
}
|
||||
config := VncServerConfig{
|
||||
ExtraPorts: "",
|
||||
QueryTimeout: 30,
|
||||
QueryAcceptOnTimeout: 0,
|
||||
LocalInputPriorityTimeout: 3,
|
||||
LocalInputPriority: 1,
|
||||
BlockRemoteInput: 0,
|
||||
BlockLocalInput: 0,
|
||||
IpAccessControl: "",
|
||||
RfbPort: 5900,
|
||||
HttpPort: 5800,
|
||||
DisconnectAction: 0,
|
||||
AcceptRfbConnections: 1,
|
||||
UseVncAuthentication: 1,
|
||||
UseControlAuthentication: 0,
|
||||
RepeatControlAuthentication: 0,
|
||||
LoopbackOnly: 0,
|
||||
AcceptHttpConnections: 0,
|
||||
LogLevel: 0,
|
||||
EnableFileTransfers: 1,
|
||||
RemoveWallpaper: 1,
|
||||
UseD3D: 1,
|
||||
UseMirrorDriver: 1,
|
||||
EnableUrlParams: 1,
|
||||
Password: VncPasswordCrypt(password),
|
||||
AlwaysShared: 0,
|
||||
NeverShared: 0,
|
||||
DisconnectClients: 1,
|
||||
PollingInterval: 1000,
|
||||
AllowLoopback: 1,
|
||||
VideoRecognitionInterval: 3000,
|
||||
GrabTransparentWindows: 1,
|
||||
SaveLogToAllUsersPath: 0,
|
||||
RunControlInterface: 0,
|
||||
IdleTimeout: 0,
|
||||
VideoClasses: "",
|
||||
VideoRects: "",
|
||||
ConnectToRdp: 1,
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// VncClientDefaultConfig 生成 TightVNC 客户端默认配置(不连接 RDP 会话)
|
||||
// randPass 为 true 时生成 8 位随机密码,否则使用固定密码
|
||||
func VncClientDefaultConfig(randPass bool) VncServerConfig {
|
||||
password := "tvncpass"
|
||||
if randPass {
|
||||
password = createRandomString(8)
|
||||
}
|
||||
config := VncServerConfig{
|
||||
ExtraPorts: "",
|
||||
QueryTimeout: 30,
|
||||
QueryAcceptOnTimeout: 0,
|
||||
LocalInputPriorityTimeout: 3,
|
||||
LocalInputPriority: 1,
|
||||
BlockRemoteInput: 0,
|
||||
BlockLocalInput: 0,
|
||||
IpAccessControl: "",
|
||||
RfbPort: 5900,
|
||||
HttpPort: 5800,
|
||||
DisconnectAction: 0,
|
||||
AcceptRfbConnections: 1,
|
||||
UseVncAuthentication: 1,
|
||||
UseControlAuthentication: 0,
|
||||
RepeatControlAuthentication: 0,
|
||||
LoopbackOnly: 0,
|
||||
AcceptHttpConnections: 0,
|
||||
LogLevel: 0,
|
||||
EnableFileTransfers: 1,
|
||||
RemoveWallpaper: 0,
|
||||
UseD3D: 1,
|
||||
UseMirrorDriver: 1,
|
||||
EnableUrlParams: 1,
|
||||
Password: VncPasswordCrypt(password),
|
||||
AlwaysShared: 0,
|
||||
NeverShared: 0,
|
||||
DisconnectClients: 1,
|
||||
PollingInterval: 1000,
|
||||
AllowLoopback: 1,
|
||||
VideoRecognitionInterval: 3000,
|
||||
GrabTransparentWindows: 1,
|
||||
SaveLogToAllUsersPath: 0,
|
||||
RunControlInterface: 0,
|
||||
IdleTimeout: 0,
|
||||
VideoClasses: "",
|
||||
VideoRects: "",
|
||||
ConnectToRdp: 0,
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// WriteVncServerConfig 将 TightVNC 服务端配置写入注册表(项不存在时自动创建)
|
||||
func WriteVncServerConfig(config VncServerConfig) error {
|
||||
|
||||
key, err := registry.OpenKey(registry.LOCAL_MACHINE, "SOFTWARE\\TightVNC\\Server", registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
if err == registry.ErrNotExist {
|
||||
// 项不存在 创建它
|
||||
key, _, err = registry.CreateKey(registry.LOCAL_MACHINE, "SOFTWARE\\TightVNC\\Server", registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
defer key.Close()
|
||||
|
||||
err = key.SetStringValue("ExtraPorts", config.ExtraPorts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("QueryTimeout", config.QueryTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("QueryAcceptOnTimeout", config.QueryAcceptOnTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("LocalInputPriorityTimeout", config.LocalInputPriorityTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("LocalInputPriority", config.LocalInputPriority)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("BlockRemoteInput", config.BlockRemoteInput)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("BlockLocalInput", config.BlockLocalInput)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetStringValue("IpAccessControl", config.IpAccessControl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("RfbPort", config.RfbPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("HttpPort", config.HttpPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("DisconnectAction", config.DisconnectAction)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("AcceptRfbConnections", config.AcceptRfbConnections)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("UseVncAuthentication", config.UseVncAuthentication)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("UseControlAuthentication", config.UseControlAuthentication)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("RepeatControlAuthentication", config.RepeatControlAuthentication)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("LoopbackOnly", config.LoopbackOnly)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("AcceptHttpConnections", config.AcceptHttpConnections)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("EnableFileTransfers", config.EnableFileTransfers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("RemoveWallpaper", config.RemoveWallpaper)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("UseD3D", config.UseD3D)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("UseMirrorDriver", config.UseMirrorDriver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("EnableUrlParams", config.EnableUrlParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetBinaryValue("Password", config.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("AlwaysShared", config.AlwaysShared)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("NeverShared", config.NeverShared)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("DisconnectClients", config.DisconnectClients)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("PollingInterval", config.PollingInterval)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("AllowLoopback", config.AllowLoopback)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("VideoRecognitionInterval", config.VideoRecognitionInterval)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("GrabTransparentWindows", config.GrabTransparentWindows)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("SaveLogToAllUsersPath", config.SaveLogToAllUsersPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("RunControlInterface", config.RunControlInterface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("IdleTimeout", config.IdleTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetStringValue("VideoClasses", config.VideoClasses)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetStringValue("VideoRects", config.VideoRects)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = key.SetDWordValue("ConnectToRdp", config.ConnectToRdp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnableSoftwareSASGeneration 启用软件SAS模拟输入
|
||||
func EnableSoftwareSASGeneration() error {
|
||||
// SAS模拟输入
|
||||
key, err := registry.OpenKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
if err == registry.ErrNotExist {
|
||||
// 项不存在 创建它
|
||||
key, _, err = registry.CreateKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
defer key.Close()
|
||||
|
||||
err = key.SetDWordValue("SoftwareSASGeneration", 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnableWinlogonDisableCAD 关闭 Ctrl + Alt + Del 组合键来登录
|
||||
func EnableWinlogonDisableCAD() error {
|
||||
key, err := registry.OpenKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
if err == registry.ErrNotExist {
|
||||
// 项不存在 创建它
|
||||
key, _, err = registry.CreateKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
defer key.Close()
|
||||
|
||||
err = key.SetDWordValue("DisableCAD", 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnableSystemDisableCAD 关闭 Ctrl + Alt + Del 组合键来登录
|
||||
func EnableSystemDisableCAD() error {
|
||||
key, err := registry.OpenKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
if err == registry.ErrNotExist {
|
||||
// 项不存在 创建它
|
||||
key, _, err = registry.CreateKey(registry.LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
defer key.Close()
|
||||
|
||||
err = key.SetDWordValue("DisableCAD", 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createRandomString 生成指定长度的随机字符串
|
||||
func createRandomString(len int) string {
|
||||
var container string
|
||||
var str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
|
||||
b := bytes.NewBufferString(str)
|
||||
length := b.Len()
|
||||
bigInt := big.NewInt(int64(length))
|
||||
for i := 0; i < len; i++ {
|
||||
randomInt, _ := rand.Int(rand.Reader, bigInt)
|
||||
container += string(str[randomInt.Int64()])
|
||||
}
|
||||
return container
|
||||
}
|
||||
Reference in New Issue
Block a user