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,60 @@
|
||||
# remote
|
||||
|
||||
远程控制服务管理(被控端能力):RDP / VNC(TightVNC) / Radmin 的服务状态查询、
|
||||
启停与重置,VNC 服务端安装/配置读写,VNC 密码加解密,远程信息汇总。
|
||||
|
||||
> 自 go-hua/remote 平移并入(包名保持 `remote`),与本库 file/shell/process
|
||||
> 同属被控端远程运维能力。
|
||||
|
||||
## 用法
|
||||
|
||||
```go
|
||||
import "git.zeroonesoft.cn/golib/remote-core/remote"
|
||||
|
||||
// 汇总信息(主机名/IP/MAC/远程端口/VNC 密码等)
|
||||
info := remote.GetRemoteInfo()
|
||||
|
||||
// 远程服务状态与控制
|
||||
_ = remote.GetRdpServiceStatus() // kardianos/service Status
|
||||
_ = remote.GetVncServiceStatus() // 同上
|
||||
_ = remote.GetRadminServiceStatus() // (int, error):1 运行 / 0 停止 / -1 未安装
|
||||
_ = remote.GetServiceStatus("SessionEnv") // 通用按服务名查询
|
||||
err := remote.ServiceStart(name) // 启动 / ServiceStop 停止
|
||||
_ = remote.GetServiceReset(name) // 重置类服务状态
|
||||
|
||||
// RDP / VNC 端口与 VNC 密码读取
|
||||
port, err := remote.GetRdpPort()
|
||||
vncPort, err := remote.GetVncPort()
|
||||
pass, err := remote.GetVncPassword()
|
||||
|
||||
// VNC(TightVNC) 服务端管理(Windows)
|
||||
cfg := remote.VncServerDefaultConfig(true /* 随机密码 */) // VncServerConfig
|
||||
err = remote.WriteVncServerConfig(cfg)
|
||||
cfg, err = remote.ReadVncServerConfig()
|
||||
_, msg, err := remote.InstallVncServer() // 安装服务(需管理员权限)
|
||||
err = remote.VncInit() // 初始化/修复配置
|
||||
err = remote.VncStart() // VncStop / VncRestart
|
||||
err = remote.VncInstall() // VncUninstall
|
||||
err = remote.RunVncService() // 以服务方式运行
|
||||
|
||||
// VNC 密码加解密(TightVNC 注册表存储格式)
|
||||
enc := remote.VncPasswordCrypt("plainpass") // []byte
|
||||
plain := remote.VncPasswordDecrypt(enc) // string
|
||||
|
||||
// CAD/SAS 相关注册表开关(Windows)
|
||||
err = remote.EnableSoftwareSASGeneration()
|
||||
```
|
||||
|
||||
## 平台支持
|
||||
|
||||
- `remote.go` / `type.go`:跨平台(依赖 `kardianos/service`)。
|
||||
- `windows.go` / `vnc.go`:`//go:build windows`(注册表、TightVNC 配置)。
|
||||
- `unix.go`:`//go:build linux`(精简实现)。
|
||||
|
||||
## 注意
|
||||
|
||||
- `GetServiceStatus` 系列对未安装服务返回 -1 而非错误;`GetRdpServiceStatus` /
|
||||
`GetVncServiceStatus` 走 `kardianos/service`,未安装返回 error。
|
||||
- `InstallVncServer` / `VncInit` / CAD 开关需要管理员权限,失败信息见返回 msg 或 error。
|
||||
- VNC 密码格式与 TightVNC 兼容(`KarpelesLab/vncpasswd` 实现),加解密为确定性
|
||||
变换,注意密文落盘位置的访问控制。
|
||||
@@ -0,0 +1,191 @@
|
||||
package remote
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kardianos/service"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Info 远程控制信息汇总(主机、IP、MAC 与 RDP/VNC 状态端口)
|
||||
type Info struct {
|
||||
Hostname string `json:"hostname"`
|
||||
IpAddr []string `json:"ipAddr"`
|
||||
MacAddr []string `json:"macAddr"`
|
||||
RdpState int `json:"rdpState"`
|
||||
RdpPort int `json:"rdpPort"`
|
||||
VncState int `json:"vncState"`
|
||||
VncPort int `json:"vncPort"`
|
||||
VncPassword string `json:"vncPassword"`
|
||||
}
|
||||
|
||||
// GetRadminServiceStatus 获取服务状态 0.无效 1.运行 2.停止
|
||||
func GetRadminServiceStatus() (int, error) {
|
||||
svcConfig := &service.Config{
|
||||
Name: "RServer3",
|
||||
}
|
||||
s, err := service.New(nil, svcConfig)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var status service.Status
|
||||
status, err = s.Status()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(status), nil
|
||||
}
|
||||
|
||||
// GetVncServiceStatus 获取服务状态 0.无效 1.运行 2.停止
|
||||
func GetVncServiceStatus() (service.Status, error) {
|
||||
svcConfig := &service.Config{
|
||||
Name: "tvnserver",
|
||||
}
|
||||
s, err := service.New(nil, svcConfig)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var status service.Status
|
||||
status, err = s.Status()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return status, nil
|
||||
}
|
||||
|
||||
// GetRdpServiceStatus 获取服务状态 0.无效 1.运行 2.停止
|
||||
func GetRdpServiceStatus() (service.Status, error) {
|
||||
svcConfig := &service.Config{
|
||||
Name: "TermService",
|
||||
}
|
||||
s, err := service.New(nil, svcConfig)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var status service.Status
|
||||
status, err = s.Status()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return status, nil
|
||||
}
|
||||
|
||||
// getMacAddrs 获取本机所有非空 MAC 地址
|
||||
func getMacAddrs() (macAddrs []string) {
|
||||
netInterfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
fmt.Printf("fail to get net interfaces: %v", err)
|
||||
return macAddrs
|
||||
}
|
||||
|
||||
for _, netInterface := range netInterfaces {
|
||||
macAddr := netInterface.HardwareAddr.String()
|
||||
if len(macAddr) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
macAddrs = append(macAddrs, macAddr)
|
||||
}
|
||||
return macAddrs
|
||||
}
|
||||
|
||||
// getIPs 获取本机所有 IPv4 地址(不含回环)
|
||||
func getIPs() (ips []string) {
|
||||
|
||||
interfaceAddr, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
fmt.Printf("fail to get net interface addrs: %v", err)
|
||||
return ips
|
||||
}
|
||||
|
||||
for _, address := range interfaceAddr {
|
||||
ipNet, isValidIpNet := address.(*net.IPNet)
|
||||
if isValidIpNet && !ipNet.IP.IsLoopback() {
|
||||
if ipNet.IP.To4() != nil {
|
||||
ips = append(ips, ipNet.IP.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
return ips
|
||||
}
|
||||
|
||||
// GetServiceStatus 获取服务状态 0.无效 1.运行 2.停止
|
||||
func GetServiceStatus(name string) (int, error) {
|
||||
svcConfig := &service.Config{
|
||||
Name: name,
|
||||
}
|
||||
s, err := service.New(nil, svcConfig)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var status service.Status
|
||||
status, err = s.Status()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(status), nil
|
||||
}
|
||||
|
||||
// ServiceStart 启动服务
|
||||
func ServiceStart(name string) error {
|
||||
svcConfig := &service.Config{
|
||||
Name: name,
|
||||
}
|
||||
s, err := service.New(nil, svcConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ServiceStop 停止服务
|
||||
func ServiceStop(name string) (i error) {
|
||||
svcConfig := &service.Config{
|
||||
Name: name,
|
||||
}
|
||||
s, err := service.New(nil, svcConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.Stop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetServiceReset 重启服务
|
||||
func GetServiceReset(name string) (int, error) {
|
||||
svcConfig := &service.Config{
|
||||
Name: name,
|
||||
}
|
||||
s, err := service.New(nil, svcConfig)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var status service.Status
|
||||
status, err = s.Status()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(status), nil
|
||||
}
|
||||
|
||||
// RunVncService 启动 TightVNC 服务
|
||||
func RunVncService() error {
|
||||
svcConfig := &service.Config{
|
||||
Name: "tvnserver",
|
||||
}
|
||||
s, err := service.New(nil, svcConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//go:build windows
|
||||
|
||||
package remote
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGetServiceStatusNonExist(t *testing.T) {
|
||||
// 不存在的服务应返回错误(Radmin 服务普通机器上不会安装)
|
||||
_, err := GetRadminServiceStatus()
|
||||
if err == nil {
|
||||
t.Log("本机安装了 Radmin 服务,跳过该断言")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRemoteInfo(t *testing.T) {
|
||||
info := GetRemoteInfo()
|
||||
if info.Hostname == "" {
|
||||
t.Error("Hostname 不应为空")
|
||||
}
|
||||
if len(info.IpAddr) == 0 {
|
||||
t.Log("本机无 IPv4 地址(CI 环境可能出现)")
|
||||
}
|
||||
// 只读查询,不应 panic
|
||||
_ = info.RdpState
|
||||
_ = info.VncState
|
||||
}
|
||||
|
||||
func TestVncPasswordCryptDecrypt(t *testing.T) {
|
||||
password := "tvncpass"
|
||||
encrypted := VncPasswordCrypt(password)
|
||||
if len(encrypted) == 0 {
|
||||
t.Fatal("加密结果不应为空")
|
||||
}
|
||||
if string(encrypted) == password {
|
||||
t.Error("加密结果不应等于明文")
|
||||
}
|
||||
decrypted := VncPasswordDecrypt(encrypted)
|
||||
if decrypted != password {
|
||||
t.Errorf("解密 = %q, want %q", decrypted, password)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVncServerDefaultConfig(t *testing.T) {
|
||||
cfg := VncServerDefaultConfig(false)
|
||||
if cfg.RfbPort == 0 {
|
||||
t.Error("RfbPort 不应为 0")
|
||||
}
|
||||
if cfg.HttpPort == 0 {
|
||||
t.Error("HttpPort 不应为 0")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Package remote 提供 RDP/VNC 等远程控制的服务状态查询、配置读写与启停控制。
|
||||
package remote
|
||||
|
||||
// VncServerConfig TightVNC 服务端配置(对应注册表 SOFTWARE\TightVNC\Server 下的键值)
|
||||
type VncServerConfig struct {
|
||||
ExtraPorts string `json:"extraPorts"` // 额外端口
|
||||
QueryTimeout uint32 `json:"queryTimeout"` // 查询超时
|
||||
QueryAcceptOnTimeout uint32 `json:"queryAcceptOnTimeout"` // 查询接受超时
|
||||
LocalInputPriorityTimeout uint32 `json:"localInputPriorityTimeout"` // 本地输入优先级超时
|
||||
LocalInputPriority uint32 `json:"localInputPriority"` // 本地输入优先
|
||||
BlockRemoteInput uint32 `json:"blockRemoteInput"` // 阻止远程输入
|
||||
BlockLocalInput uint32 `json:"blockLocalInput"` // 阻止本地输入
|
||||
IpAccessControl string `json:"ipAccessControl"` // IP访问控制
|
||||
RfbPort uint32 `json:"rfbPort"` // 主端口
|
||||
HttpPort uint32 `json:"httpPort"` // WEB端口
|
||||
DisconnectAction uint32 `json:"disconnectAction"` // 断开动作
|
||||
AcceptRfbConnections uint32 `json:"acceptRfbConnections"` // 接受RFB连接
|
||||
UseVncAuthentication uint32 `json:"useVncAuthentication"` // 使用VNC认证
|
||||
UseControlAuthentication uint32 `json:"useControlAuthentication"` // 使用控制认证
|
||||
RepeatControlAuthentication uint32 `json:"repeatControlAuthentication"` // 重复控制认证
|
||||
LoopbackOnly uint32 `json:"loopbackOnly"` // 只允许环回
|
||||
AcceptHttpConnections uint32 `json:"acceptHttpConnections"` // HTTP请求连接
|
||||
LogLevel uint32 `json:"logLevel"` // 日志等级
|
||||
EnableFileTransfers uint32 `json:"enableFileTransfers"` // 使文件传输
|
||||
RemoveWallpaper uint32 `json:"removeWallpaper"` // 移除墙纸
|
||||
UseD3D uint32 `json:"useD3D"` // 使用D3D驱动
|
||||
UseMirrorDriver uint32 `json:"useMirrorDriver"` // 使用镜像驱动
|
||||
EnableUrlParams uint32 `json:"enableUrlParams"` // 用URL参数
|
||||
Password []byte `json:"password"` // 密码(需要加密)
|
||||
AlwaysShared uint32 `json:"alwaysShared"` // 总是共享
|
||||
NeverShared uint32 `json:"neverShared"` // 从不共享
|
||||
DisconnectClients uint32 `json:"disconnectClients"` // 断开客户端后操作
|
||||
PollingInterval uint32 `json:"pollingInterval"` // 轮询间隔
|
||||
AllowLoopback uint32 `json:"allowLoopback"` // 允许环回
|
||||
VideoRecognitionInterval uint32 `json:"videoRecognitionInterval"` // 视频轮询间隔
|
||||
GrabTransparentWindows uint32 `json:"grabTransparentWindows"` // 抓取透明窗口
|
||||
SaveLogToAllUsersPath uint32 `json:"saveLogToAllUsersPath"` // 保存日志所有用户路径
|
||||
RunControlInterface uint32 `json:"runControlInterface"` // 运行控制台接口
|
||||
IdleTimeout uint32 `json:"idleTimeout"` // 空闲超时
|
||||
VideoClasses string `json:"videoClasses"` // 视频窗口类名
|
||||
VideoRects string `json:"videoRects"` // 视频窗口区域
|
||||
ConnectToRdp uint32 `json:"connectToRdp"` // 连接到RDP会话
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
//go:build linux
|
||||
|
||||
package remote
|
||||
|
||||
import "os"
|
||||
|
||||
// GetRemoteInfo 获取远程控制相关信息(Linux 平台仅主机名/IP/MAC)
|
||||
func GetRemoteInfo() Info {
|
||||
info := Info{}
|
||||
|
||||
/*var status int
|
||||
status, _ = GetRadminServiceStatus()
|
||||
info.RadminState = status
|
||||
|
||||
status, _ = GetVncServiceStatus()
|
||||
info.VncState = status
|
||||
|
||||
status, _ = GetRdpServiceStatus()
|
||||
info.RdpState = status*/
|
||||
|
||||
/* var port int
|
||||
port, _ = GetRadminPort()
|
||||
info.RadminPort = port
|
||||
|
||||
port, _ = GetRdpPort()
|
||||
info.RdpPort = port
|
||||
|
||||
port, _ = GetVncPort()
|
||||
info.VncPort = port*/
|
||||
|
||||
hostname, _ := os.Hostname()
|
||||
info.Hostname = hostname
|
||||
info.IpAddr = getIPs()
|
||||
info.MacAddr = getMacAddrs()
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
// VncServerDefaultConfig 生成 TightVNC 服务端默认配置(Linux 未实现,返回空配置)
|
||||
func VncServerDefaultConfig(randPass bool) VncServerConfig {
|
||||
config := VncServerConfig{}
|
||||
return config
|
||||
}
|
||||
|
||||
// VncClientDefaultConfig 生成 TightVNC 客户端默认配置(Linux 未实现,返回空配置)
|
||||
func VncClientDefaultConfig(randPass bool) VncServerConfig {
|
||||
config := VncServerConfig{}
|
||||
return config
|
||||
}
|
||||
|
||||
// WriteVncServerConfig 写入 TightVNC 服务端配置(Linux 未实现,直接返回 nil)
|
||||
func WriteVncServerConfig(config VncServerConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnableSoftwareSASGeneration 启用软件SAS模拟输入
|
||||
func EnableSoftwareSASGeneration() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnableWinlogonDisableCAD 关闭 Ctrl + Alt + Del 组合键来登录
|
||||
func EnableWinlogonDisableCAD() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnableSystemDisableCAD 关闭 Ctrl + Alt + Del 组合键来登录
|
||||
func EnableSystemDisableCAD() error {
|
||||
return nil
|
||||
}
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
//go:build windows
|
||||
|
||||
package remote
|
||||
|
||||
import (
|
||||
"github.com/KarpelesLab/vncpasswd"
|
||||
"github.com/kardianos/service"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
var VncService service.Service
|
||||
|
||||
// ReadVncServerConfig 从注册表读取 TightVNC 服务端配置
|
||||
func ReadVncServerConfig() (VncServerConfig, error) {
|
||||
config := VncServerConfig{
|
||||
ExtraPorts: "",
|
||||
QueryTimeout: 0,
|
||||
QueryAcceptOnTimeout: 0,
|
||||
LocalInputPriorityTimeout: 0,
|
||||
LocalInputPriority: 0,
|
||||
BlockRemoteInput: 0,
|
||||
BlockLocalInput: 0,
|
||||
IpAccessControl: "",
|
||||
RfbPort: 0,
|
||||
HttpPort: 0,
|
||||
DisconnectAction: 0,
|
||||
AcceptRfbConnections: 0,
|
||||
UseVncAuthentication: 0,
|
||||
UseControlAuthentication: 0,
|
||||
RepeatControlAuthentication: 0,
|
||||
LoopbackOnly: 0,
|
||||
AcceptHttpConnections: 0,
|
||||
LogLevel: 0,
|
||||
EnableFileTransfers: 0,
|
||||
RemoveWallpaper: 0,
|
||||
UseD3D: 0,
|
||||
UseMirrorDriver: 0,
|
||||
EnableUrlParams: 0,
|
||||
Password: []byte{},
|
||||
AlwaysShared: 0,
|
||||
NeverShared: 0,
|
||||
DisconnectClients: 0,
|
||||
PollingInterval: 0,
|
||||
AllowLoopback: 0,
|
||||
VideoRecognitionInterval: 0,
|
||||
GrabTransparentWindows: 0,
|
||||
SaveLogToAllUsersPath: 0,
|
||||
RunControlInterface: 0,
|
||||
IdleTimeout: 0,
|
||||
VideoClasses: "",
|
||||
VideoRects: "",
|
||||
ConnectToRdp: 0,
|
||||
}
|
||||
|
||||
key, err := registry.OpenKey(registry.LOCAL_MACHINE, "SOFTWARE\\TightVNC\\Server", registry.ALL_ACCESS)
|
||||
if err != nil {
|
||||
return config, err
|
||||
}
|
||||
defer key.Close()
|
||||
|
||||
config.ExtraPorts, _, _ = key.GetStringValue("ExtraPorts")
|
||||
config.QueryTimeout, _, _ = GetDwordValue(key, "QueryTimeout")
|
||||
config.QueryAcceptOnTimeout, _, _ = GetDwordValue(key, "QueryAcceptOnTimeout")
|
||||
config.LocalInputPriorityTimeout, _, _ = GetDwordValue(key, "LocalInputPriorityTimeout")
|
||||
config.LocalInputPriority, _, _ = GetDwordValue(key, "LocalInputPriority")
|
||||
config.BlockRemoteInput, _, _ = GetDwordValue(key, "BlockRemoteInput")
|
||||
config.BlockLocalInput, _, _ = GetDwordValue(key, "BlockLocalInput")
|
||||
config.IpAccessControl, _, _ = key.GetStringValue("IpAccessControl")
|
||||
config.RfbPort, _, _ = GetDwordValue(key, "RfbPort")
|
||||
config.HttpPort, _, _ = GetDwordValue(key, "HttpPort")
|
||||
config.DisconnectAction, _, _ = GetDwordValue(key, "DisconnectAction")
|
||||
config.AcceptRfbConnections, _, _ = GetDwordValue(key, "AcceptRfbConnections")
|
||||
config.UseVncAuthentication, _, _ = GetDwordValue(key, "UseVncAuthentication")
|
||||
config.UseControlAuthentication, _, _ = GetDwordValue(key, "UseControlAuthentication")
|
||||
config.RepeatControlAuthentication, _, _ = GetDwordValue(key, "RepeatControlAuthentication")
|
||||
config.LoopbackOnly, _, _ = GetDwordValue(key, "LoopbackOnly")
|
||||
config.AcceptHttpConnections, _, _ = GetDwordValue(key, "AcceptHttpConnections")
|
||||
config.EnableFileTransfers, _, _ = GetDwordValue(key, "EnableFileTransfers")
|
||||
config.RemoveWallpaper, _, _ = GetDwordValue(key, "RemoveWallpaper")
|
||||
config.UseD3D, _, _ = GetDwordValue(key, "UseD3D")
|
||||
config.UseMirrorDriver, _, _ = GetDwordValue(key, "UseMirrorDriver")
|
||||
config.EnableUrlParams, _, _ = GetDwordValue(key, "EnableUrlParams")
|
||||
config.Password, _, _ = key.GetBinaryValue("Password")
|
||||
config.AlwaysShared, _, _ = GetDwordValue(key, "AlwaysShared")
|
||||
config.NeverShared, _, _ = GetDwordValue(key, "NeverShared")
|
||||
config.DisconnectClients, _, _ = GetDwordValue(key, "DisconnectClients")
|
||||
config.PollingInterval, _, _ = GetDwordValue(key, "PollingInterval")
|
||||
config.AllowLoopback, _, _ = GetDwordValue(key, "AllowLoopback")
|
||||
config.VideoRecognitionInterval, _, _ = GetDwordValue(key, "VideoRecognitionInterval")
|
||||
config.GrabTransparentWindows, _, _ = GetDwordValue(key, "GrabTransparentWindows")
|
||||
config.SaveLogToAllUsersPath, _, _ = GetDwordValue(key, "SaveLogToAllUsersPath")
|
||||
config.RunControlInterface, _, _ = GetDwordValue(key, "RunControlInterface")
|
||||
config.IdleTimeout, _, _ = GetDwordValue(key, "IdleTimeout")
|
||||
config.VideoClasses, _, _ = key.GetStringValue("VideoClasses")
|
||||
config.VideoRects, _, _ = key.GetStringValue("VideoRects")
|
||||
config.ConnectToRdp, _, _ = GetDwordValue(key, "ExtraPorts")
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// GetDwordValue 读取 REG_DWORD 类型注册表值
|
||||
func GetDwordValue(k registry.Key, name string) (val uint32, valtype uint32, err error) {
|
||||
val64 := uint64(0)
|
||||
val64, valtype, err = k.GetIntegerValue(name)
|
||||
val = uint32(val64)
|
||||
return
|
||||
}
|
||||
|
||||
var vncSvcConfig = &service.Config{
|
||||
Name: "tvnserver",
|
||||
DisplayName: "TightVNC Server",
|
||||
Description: "",
|
||||
Arguments: []string{"-service"},
|
||||
}
|
||||
|
||||
// InstallVncServer 安装VNC服务端 返回:结果,密码,错误
|
||||
func InstallVncServer() (bool, string, error) {
|
||||
//安装服务
|
||||
//导入注册表
|
||||
//生成随机密码
|
||||
return true, "12345678", nil
|
||||
}
|
||||
|
||||
// VncInit 初始化 VNC 服务对象,VncInstall/VncStart 等操作前必须调用
|
||||
func VncInit() error {
|
||||
var err error
|
||||
VncService, err = service.New(nil, vncSvcConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VncInstall 安装 TightVNC 服务
|
||||
func VncInstall() error {
|
||||
err := VncService.Install()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VncUninstall 停止并卸载 TightVNC 服务
|
||||
func VncUninstall() error {
|
||||
_ = VncService.Stop()
|
||||
err := VncService.Uninstall()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VncStart 启动 TightVNC 服务
|
||||
func VncStart() error {
|
||||
err := VncService.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VncStop 停止 TightVNC 服务
|
||||
func VncStop() error {
|
||||
err := VncService.Stop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VncRestart 重启 TightVNC 服务
|
||||
func VncRestart() error {
|
||||
err := VncService.Stop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = VncService.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VncPasswordCrypt 加密 VNC 密码(TightVNC 注册表存储格式)
|
||||
func VncPasswordCrypt(password string) []byte {
|
||||
return vncpasswd.Crypt(password)
|
||||
}
|
||||
|
||||
// VncPasswordDecrypt 解密 VNC 密码
|
||||
func VncPasswordDecrypt(data []byte) string {
|
||||
return vncpasswd.Decrypt(data)
|
||||
}
|
||||
@@ -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