mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 05:56:46 +08:00
refactor: 简化插件利用架构,移除不必要的exploit控制参数
- 移除-ne (DisableExploit)参数及其相关控制逻辑 - 统一服务插件exploit实现为空函数,保持接口一致性 - MySQL/FTP/Cassandra/ActiveMQ插件exploit方法改为空实现 - Redis插件移除-ne控制,利用功能由专用参数控制(-rwp/-rf/-rs) - VNC插件exploit功能改为空实现,专注于服务检测和弱密码扫描 - 更新插件描述,准确反映实际功能范围 所有exploit功能现在由明确的用户参数控制,无需通用禁用开关
This commit is contained in:
parent
1cb1e8b2aa
commit
074aebfeec
@ -57,7 +57,6 @@ var (
|
||||
RedisWriteFile string
|
||||
|
||||
DisableBrute bool
|
||||
DisableExploit bool
|
||||
MaxRetries int
|
||||
|
||||
DisableSave bool
|
||||
@ -227,7 +226,6 @@ func Flag(Info *HostInfo) {
|
||||
// 暴力破解控制参数
|
||||
// ═════════════════════════════════════════════════
|
||||
flag.BoolVar(&DisableBrute, "nobr", false, i18n.GetText("flag_disable_brute"))
|
||||
flag.BoolVar(&DisableExploit, "ne", false, i18n.GetText("flag_disable_exploit"))
|
||||
flag.IntVar(&MaxRetries, "retry", 3, i18n.GetText("flag_max_retries"))
|
||||
|
||||
// ═════════════════════════════════════════════════
|
||||
|
@ -190,10 +190,6 @@ var FlagMessages = map[string]map[string]string{
|
||||
LangZH: "禁用暴力破解",
|
||||
LangEN: "Disable brute force",
|
||||
},
|
||||
"flag_disable_exploit": {
|
||||
LangZH: "禁用利用攻击",
|
||||
LangEN: "Disable exploit attacks",
|
||||
},
|
||||
"flag_max_retries": {
|
||||
LangZH: "最大重试次数",
|
||||
LangEN: "Maximum retries",
|
||||
|
@ -83,45 +83,12 @@ func (p *ActiveMQPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base
|
||||
// 专注于STOMP协议的成功消息
|
||||
common.LogSuccess(i18n.GetText("activemq_stomp_scan_success", target, cred.Username, cred.Password))
|
||||
|
||||
// 自动利用功能(可通过-ne参数禁用)
|
||||
if result.Success && len(result.Credentials) > 0 && !common.DisableExploit {
|
||||
// 同步执行利用攻击,确保显示结果
|
||||
p.autoExploit(context.Background(), info, result.Credentials[0])
|
||||
}
|
||||
// ActiveMQ插件不提供利用功能,仅进行弱密码扫描
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// autoExploit 自动利用
|
||||
func (p *ActiveMQPlugin) autoExploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) {
|
||||
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
|
||||
common.LogDebug(i18n.GetText("plugin_exploit_start", "ActiveMQ", target))
|
||||
|
||||
// 执行利用
|
||||
result, err := p.exploiter.Exploit(ctx, info, creds)
|
||||
if err != nil {
|
||||
common.LogDebug(i18n.GetText("plugin_exploit_failed", "ActiveMQ", err))
|
||||
return
|
||||
}
|
||||
|
||||
if result != nil && result.Success {
|
||||
// 使用利用结果中的Method字段作为方法名称
|
||||
methodName := result.Method
|
||||
if methodName == "" {
|
||||
methodName = p.getExploitMethodName(result.Type)
|
||||
}
|
||||
|
||||
// 只显示一次完整的利用结果
|
||||
if result.Output != "" {
|
||||
common.LogSuccess(fmt.Sprintf("ActiveMQ %s %s 利用成功 输出: %s", target, methodName, result.Output))
|
||||
} else {
|
||||
common.LogSuccess(fmt.Sprintf("ActiveMQ %s %s 利用成功", target, methodName))
|
||||
}
|
||||
|
||||
// 保存利用结果(不显示额外日志)
|
||||
// base.SaveExploitResult(info, result, "ActiveMQ")
|
||||
}
|
||||
}
|
||||
// autoExploit方法已移除 - ActiveMQ插件不提供利用功能
|
||||
|
||||
// getExploitMethodName 获取利用方法的中文名称
|
||||
func (p *ActiveMQPlugin) getExploitMethodName(method base.ExploitType) string {
|
||||
|
@ -77,34 +77,14 @@ func (p *CassandraPlugin) Scan(ctx context.Context, info *common.HostInfo) (*bas
|
||||
common.LogSuccess(i18n.GetText("plugin_login_success", "Cassandra", target, cred.Username, cred.Password))
|
||||
}
|
||||
|
||||
// 自动利用功能(可通过-ne参数禁用)
|
||||
if result.Success && len(result.Credentials) > 0 && !common.DisableExploit {
|
||||
p.autoExploit(context.Background(), info, result.Credentials[0])
|
||||
}
|
||||
// Cassandra插件不提供利用功能,仅进行弱密码扫描
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 已移除未使用的 generateCredentials 方法
|
||||
|
||||
// autoExploit 自动利用功能
|
||||
func (p *CassandraPlugin) autoExploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) {
|
||||
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
|
||||
common.LogDebug(i18n.GetText("plugin_exploit_start", "Cassandra", target))
|
||||
|
||||
// 执行利用操作
|
||||
result, err := p.exploiter.Exploit(ctx, info, creds)
|
||||
if err != nil {
|
||||
common.LogError(i18n.GetText("plugin_exploit_failed", "Cassandra", err))
|
||||
return
|
||||
}
|
||||
|
||||
// 处理利用结果
|
||||
if result != nil && result.Success {
|
||||
// SaveExploitResult会自动使用LogSuccess显示红色利用成功消息
|
||||
base.SaveExploitResult(info, result, "Cassandra")
|
||||
}
|
||||
}
|
||||
// autoExploit方法已移除 - Cassandra插件不提供利用功能
|
||||
|
||||
// Exploit 使用exploiter执行利用
|
||||
func (p *CassandraPlugin) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) {
|
||||
|
@ -72,10 +72,7 @@ func (p *FTPPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.Scan
|
||||
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
|
||||
common.LogSuccess(i18n.GetText("ftp_anonymous_success", target))
|
||||
|
||||
// 自动利用匿名访问
|
||||
if !common.DisableExploit {
|
||||
p.autoExploit(context.Background(), info, anonymousCred)
|
||||
}
|
||||
// FTP插件不提供利用功能,仅记录匿名访问
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@ -91,34 +88,14 @@ func (p *FTPPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.Scan
|
||||
cred := result.Credentials[0]
|
||||
common.LogSuccess(i18n.GetText("ftp_weak_pwd_success", target, cred.Username, cred.Password))
|
||||
|
||||
// 自动利用功能(可通过-ne参数禁用)
|
||||
if result.Success && len(result.Credentials) > 0 && !common.DisableExploit {
|
||||
p.autoExploit(context.Background(), info, result.Credentials[0])
|
||||
}
|
||||
// FTP插件不提供利用功能,仅进行弱密码扫描
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 已移除未使用的 generateCredentials 方法
|
||||
|
||||
// autoExploit 自动利用功能
|
||||
func (p *FTPPlugin) autoExploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) {
|
||||
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
|
||||
common.LogDebug(i18n.GetText("plugin_exploit_start", "FTP", target))
|
||||
|
||||
// 执行利用操作
|
||||
result, err := p.exploiter.Exploit(ctx, info, creds)
|
||||
if err != nil {
|
||||
common.LogError(i18n.GetText("plugin_exploit_failed", "FTP", err))
|
||||
return
|
||||
}
|
||||
|
||||
// 处理利用结果
|
||||
if result != nil && result.Success {
|
||||
// SaveExploitResult会自动使用LogSuccess显示红色利用成功消息
|
||||
base.SaveExploitResult(info, result, "FTP")
|
||||
}
|
||||
}
|
||||
// autoExploit方法已移除 - FTP插件不提供利用功能
|
||||
|
||||
// Exploit 使用exploiter执行利用
|
||||
func (p *FTPPlugin) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) {
|
||||
|
@ -82,32 +82,12 @@ func (p *MySQLPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.Sc
|
||||
cred := result.Credentials[0]
|
||||
common.LogSuccess(i18n.GetText("mysql_scan_success", target, cred.Username, cred.Password))
|
||||
|
||||
// 自动利用功能(可通过-ne参数禁用)
|
||||
if result.Success && len(result.Credentials) > 0 && !common.DisableExploit {
|
||||
// 异步执行利用攻击,避免阻塞扫描进程
|
||||
go p.autoExploit(context.Background(), info, result.Credentials[0])
|
||||
}
|
||||
// MySQL插件不提供利用功能,仅进行弱密码扫描
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// autoExploit 自动利用
|
||||
func (p *MySQLPlugin) autoExploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) {
|
||||
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
|
||||
common.LogDebug(i18n.GetText("plugin_exploit_start", "MySQL", target))
|
||||
|
||||
// 执行利用
|
||||
result, err := p.exploiter.Exploit(ctx, info, creds)
|
||||
if err != nil {
|
||||
common.LogError(i18n.GetText("plugin_exploit_failed", "MySQL", err))
|
||||
return
|
||||
}
|
||||
|
||||
if result != nil && result.Success {
|
||||
common.LogSuccess(i18n.GetText("plugin_exploit_success", "MySQL", i18n.GetExploitMethodName(result.Method)))
|
||||
base.SaveExploitResult(info, result, "MySQL")
|
||||
}
|
||||
}
|
||||
// autoExploit方法已移除 - MySQL插件不提供利用功能
|
||||
|
||||
// Exploit 手动利用接口
|
||||
func (p *MySQLPlugin) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) {
|
||||
|
@ -69,10 +69,8 @@ func (p *RedisPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.Sc
|
||||
if unauthorizedResult != nil && unauthorizedResult.Success {
|
||||
common.LogSuccess(i18n.GetText("redis_unauth_success", target))
|
||||
|
||||
// 如果启用了利用功能,执行自动利用
|
||||
if !common.DisableExploit { // 使用DisableExploit控制利用功能
|
||||
go p.autoExploit(context.Background(), info, nil) // 未授权访问不需要凭据
|
||||
}
|
||||
// Redis利用功能由明确的用户参数控制(-rwp, -rf, -rs),无需-ne参数控制
|
||||
go p.autoExploit(context.Background(), info, nil) // 未授权访问不需要凭据
|
||||
|
||||
return unauthorizedResult, nil
|
||||
}
|
||||
@ -91,8 +89,8 @@ func (p *RedisPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.Sc
|
||||
common.LogSuccess(i18n.GetText("redis_weak_pwd_success",
|
||||
target, result.Credentials[0].Password))
|
||||
|
||||
// 如果扫描成功并且启用了利用功能,执行自动利用
|
||||
if result.Success && len(result.Credentials) > 0 && !common.DisableExploit {
|
||||
// Redis利用功能由明确的用户参数控制(-rwp, -rf, -rs),无需-ne参数控制
|
||||
if result.Success && len(result.Credentials) > 0 {
|
||||
go p.autoExploit(context.Background(), info, result.Credentials[0])
|
||||
}
|
||||
|
||||
|
@ -2,154 +2,35 @@ package vnc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/shadow1ng/fscan/common"
|
||||
"github.com/shadow1ng/fscan/common/i18n"
|
||||
"github.com/shadow1ng/fscan/plugins/base"
|
||||
)
|
||||
|
||||
// VNCExploiter VNC利用器
|
||||
// VNCExploiter VNC利用器实现 - 最小化版本,不提供利用功能
|
||||
type VNCExploiter struct {
|
||||
connector *VNCConnector
|
||||
*base.BaseExploiter
|
||||
}
|
||||
|
||||
// NewVNCExploiter 创建VNC利用器
|
||||
func NewVNCExploiter() *VNCExploiter {
|
||||
return &VNCExploiter{
|
||||
connector: NewVNCConnector(),
|
||||
exploiter := &VNCExploiter{
|
||||
BaseExploiter: base.NewBaseExploiter("vnc"),
|
||||
}
|
||||
|
||||
// VNC插件不提供利用功能
|
||||
exploiter.setupExploitMethods()
|
||||
|
||||
return exploiter
|
||||
}
|
||||
|
||||
// Exploit 执行VNC利用
|
||||
// setupExploitMethods 设置利用方法
|
||||
func (e *VNCExploiter) setupExploitMethods() {
|
||||
// VNC插件不提供利用功能,仅进行弱密码扫描和服务识别
|
||||
}
|
||||
|
||||
// Exploit 利用接口实现 - 空实现
|
||||
func (e *VNCExploiter) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) {
|
||||
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
|
||||
|
||||
// 尝试连接VNC服务
|
||||
conn, err := e.connector.Connect(ctx, info)
|
||||
if err != nil {
|
||||
return &base.ExploitResult{
|
||||
Success: false,
|
||||
Error: fmt.Errorf("VNC连接失败: %v", err),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 尝试认证
|
||||
authErr := e.connector.Authenticate(ctx, conn, creds)
|
||||
if authErr != nil {
|
||||
return &base.ExploitResult{
|
||||
Success: false,
|
||||
Error: authErr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 认证成功,收集信息
|
||||
connectionInfo := conn.(map[string]interface{})
|
||||
version := connectionInfo["version"].(string)
|
||||
|
||||
exploitData := map[string]interface{}{
|
||||
"service": "VNC",
|
||||
"target": target,
|
||||
"version": version,
|
||||
"credentials": map[string]string{
|
||||
"username": creds.Username,
|
||||
"password": creds.Password,
|
||||
},
|
||||
"access_type": e.getAccessType(creds),
|
||||
"description": "VNC远程桌面访问",
|
||||
}
|
||||
|
||||
// 尝试获取更多信息
|
||||
e.gatherVNCInfo(ctx, info, exploitData)
|
||||
|
||||
common.LogSuccess(i18n.GetText("exploit_success", "VNC", target))
|
||||
|
||||
return &base.ExploitResult{
|
||||
Success: true,
|
||||
Output: fmt.Sprintf("VNC利用成功 - %s", target),
|
||||
Data: exploitData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// IsExploitSupported 检查是否支持指定的利用类型
|
||||
func (e *VNCExploiter) IsExploitSupported(exploitType base.ExploitType) bool {
|
||||
switch exploitType {
|
||||
case base.ExploitDataExtraction:
|
||||
return true
|
||||
case base.ExploitUnauthorized:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// getAccessType 获取访问类型描述
|
||||
func (e *VNCExploiter) getAccessType(creds *base.Credential) string {
|
||||
if creds.Password == "" {
|
||||
return "无认证访问"
|
||||
}
|
||||
return "密码认证访问"
|
||||
}
|
||||
|
||||
// gatherVNCInfo 收集VNC相关信息
|
||||
func (e *VNCExploiter) gatherVNCInfo(ctx context.Context, info *common.HostInfo, data map[string]interface{}) {
|
||||
// 添加端口信息
|
||||
if portNum := info.Ports; portNum != "" {
|
||||
data["port"] = portNum
|
||||
|
||||
// VNC端口通常对应显示器编号
|
||||
if len(portNum) >= 4 && portNum[:2] == "59" {
|
||||
if displayNum := portNum[2:]; len(displayNum) >= 2 {
|
||||
data["display_number"] = displayNum
|
||||
data["display_info"] = fmt.Sprintf("VNC显示器 :%s", displayNum)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加安全信息
|
||||
data["security_info"] = map[string]interface{}{
|
||||
"encryption_support": "取决于VNC版本",
|
||||
"authentication_types": []string{"None", "VNC Authentication", "RA2", "RA2ne", "Tight", "ARD"},
|
||||
"common_vulnerabilities": []string{
|
||||
"弱密码",
|
||||
"无认证访问",
|
||||
"未加密传输",
|
||||
"DES加密漏洞",
|
||||
},
|
||||
}
|
||||
|
||||
// 添加建议的后续操作
|
||||
data["next_steps"] = []string{
|
||||
"尝试连接VNC客户端进行远程控制",
|
||||
"检查VNC服务配置",
|
||||
"查看可用的安全设置",
|
||||
"评估网络流量加密状态",
|
||||
}
|
||||
|
||||
// 添加风险评估
|
||||
risk := "中等"
|
||||
if data["access_type"] == "无认证访问" {
|
||||
risk = "高"
|
||||
}
|
||||
data["risk_level"] = risk
|
||||
}
|
||||
|
||||
// GetSupportedExploits 获取支持的利用类型
|
||||
func (e *VNCExploiter) GetSupportedExploits() []base.ExploitType {
|
||||
return []base.ExploitType{
|
||||
base.ExploitDataExtraction,
|
||||
base.ExploitUnauthorized,
|
||||
}
|
||||
}
|
||||
|
||||
// GetExploitDescription 获取利用描述
|
||||
func (e *VNCExploiter) GetExploitDescription(exploitType base.ExploitType) string {
|
||||
switch exploitType {
|
||||
case base.ExploitDataExtraction:
|
||||
return "收集VNC服务信息,包括版本、认证类型等"
|
||||
case base.ExploitUnauthorized:
|
||||
return "尝试无认证访问或弱密码攻击VNC服务"
|
||||
default:
|
||||
return "未知的利用类型"
|
||||
}
|
||||
// VNC插件不提供利用功能
|
||||
return nil, nil
|
||||
}
|
@ -22,7 +22,7 @@ func NewVNCPlugin() *VNCPlugin {
|
||||
Name: "vnc",
|
||||
Version: "1.0.0",
|
||||
Author: "fscan-team",
|
||||
Description: "VNC远程桌面协议检测和利用插件",
|
||||
Description: "VNC远程桌面协议检测插件",
|
||||
Category: "service",
|
||||
Tags: []string{"vnc", "remote-desktop", "service", "unauth"},
|
||||
Protocols: []string{"vnc"},
|
||||
@ -103,8 +103,12 @@ func (p *VNCPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.Scan
|
||||
}
|
||||
result.Extra["auth_result"] = authResults[0]
|
||||
|
||||
// 执行自动利用
|
||||
p.autoExploit(ctx, info, successCreds)
|
||||
// VNC插件不提供利用功能,仅进行服务识别和弱密码检测
|
||||
if successCreds.Password == "" {
|
||||
common.LogSuccess(i18n.GetText("vnc_unauth_success", fmt.Sprintf("%s:%s", info.Host, info.Ports)))
|
||||
} else {
|
||||
common.LogSuccess(i18n.GetText("vnc_weak_pwd_success", fmt.Sprintf("%s:%s", info.Host, info.Ports), successCreds.Password))
|
||||
}
|
||||
} else {
|
||||
result.Extra["vulnerable"] = false
|
||||
result.Extra["auth_result"] = "认证失败"
|
||||
@ -114,44 +118,19 @@ func (p *VNCPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.Scan
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// autoExploit 自动利用功能
|
||||
func (p *VNCPlugin) autoExploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) {
|
||||
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
|
||||
common.LogDebug(i18n.GetText("plugin_exploit_start", "VNC", target))
|
||||
|
||||
// 执行利用操作
|
||||
result, err := p.exploiter.Exploit(ctx, info, creds)
|
||||
if err != nil {
|
||||
common.LogError(i18n.GetText("plugin_exploit_failed", "VNC", err))
|
||||
return
|
||||
}
|
||||
|
||||
if result.Success {
|
||||
common.LogSuccess(i18n.GetText("plugin_exploit_success", "VNC", target))
|
||||
|
||||
// 输出利用结果
|
||||
if result.Data != nil {
|
||||
if accessType, exists := result.Data["access_type"]; exists {
|
||||
common.LogInfo(fmt.Sprintf("VNC访问类型: %s", accessType))
|
||||
}
|
||||
if version, exists := result.Data["version"]; exists {
|
||||
common.LogInfo(fmt.Sprintf("VNC版本: %s", version))
|
||||
}
|
||||
if riskLevel, exists := result.Data["risk_level"]; exists {
|
||||
common.LogInfo(fmt.Sprintf("风险等级: %s", riskLevel))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
common.LogError(fmt.Sprintf("VNC利用失败: %v", result.Error))
|
||||
}
|
||||
}
|
||||
// autoExploit方法已移除 - VNC插件不提供利用功能
|
||||
|
||||
// Exploit 使用exploiter执行利用
|
||||
func (p *VNCPlugin) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) {
|
||||
return p.exploiter.Exploit(ctx, info, creds)
|
||||
}
|
||||
|
||||
// IsExploitSupported 检查是否支持指定的利用方法
|
||||
// GetExploitMethods 获取利用方法
|
||||
func (p *VNCPlugin) GetExploitMethods() []base.ExploitMethod {
|
||||
return p.exploiter.GetExploitMethods()
|
||||
}
|
||||
|
||||
// IsExploitSupported 检查利用支持
|
||||
func (p *VNCPlugin) IsExploitSupported(method base.ExploitType) bool {
|
||||
return p.exploiter.IsExploitSupported(method)
|
||||
}
|
||||
@ -165,7 +144,7 @@ func RegisterVNCPlugin() {
|
||||
Name: "vnc",
|
||||
Version: "1.0.0",
|
||||
Author: "fscan-team",
|
||||
Description: "VNC远程桌面协议检测和利用插件",
|
||||
Description: "VNC远程桌面协议检测插件",
|
||||
Category: "service",
|
||||
Tags: []string{"vnc", "remote-desktop", "service", "unauth"},
|
||||
Protocols: []string{"vnc"},
|
||||
@ -179,15 +158,6 @@ func RegisterVNCPlugin() {
|
||||
base.GlobalPluginRegistry.Register("vnc", factory)
|
||||
}
|
||||
|
||||
// GetInfo 获取插件信息
|
||||
func (p *VNCPlugin) GetInfo() string {
|
||||
return fmt.Sprintf(`VNC远程桌面协议插件
|
||||
支持端口: %v
|
||||
功能: 服务识别、无认证检测、弱密码检测
|
||||
作者: fscan-team
|
||||
版本: 1.0.0`, p.GetMetadata().Ports)
|
||||
}
|
||||
|
||||
// 插件注册函数
|
||||
func init() {
|
||||
RegisterVNCPlugin()
|
||||
|
Loading…
Reference in New Issue
Block a user