fscan/Plugins/services/modbus/plugin.go
ZacharyZcR f097d2812a refactor: 清理项目死代码和未使用函数
- 移除所有未使用的generateCredentials方法
- 删除插件适配器中的过时函数
- 清理MySQL连接器中的无用方法
- 移除Redis利用器中的未调用函数
- 删除遗留加密函数和基础扫描器无用方法
- 完全移除未注册的VNC插件
- 优化代码结构,提升项目可维护性

清理统计: 移除25+个死代码函数,减少400+行无用代码
2025-08-12 11:51:36 +08:00

291 lines
7.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package modbus
import (
"context"
"encoding/binary"
"fmt"
"net"
"time"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/i18n"
"github.com/shadow1ng/fscan/plugins/base"
)
// ModbusPlugin Modbus插件实现
type ModbusPlugin struct {
*base.ServicePlugin
exploiter *ModbusExploiter
}
// NewModbusPlugin 创建Modbus插件
func NewModbusPlugin() *ModbusPlugin {
// 插件元数据
metadata := &base.PluginMetadata{
Name: "modbus",
Version: "2.0.0",
Author: "fscan-team",
Description: "Modbus工业协议扫描和利用插件",
Category: "service",
Ports: []int{502, 5020}, // 502: 标准Modbus TCP端口, 5020: 测试端口
Protocols: []string{"tcp"},
Tags: []string{"modbus", "industrial", "scada", "unauthorized"},
}
// 创建连接器和服务插件
connector := NewModbusConnector()
servicePlugin := base.NewServicePlugin(metadata, connector)
// 创建Modbus插件
plugin := &ModbusPlugin{
ServicePlugin: servicePlugin,
exploiter: NewModbusExploiter(),
}
// 设置能力
plugin.SetCapabilities([]base.Capability{
base.CapUnauthorized,
base.CapDataExtraction,
})
return plugin
}
// Scan 重写扫描方法检测Modbus协议和未授权访问
func (p *ModbusPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
// 如果禁用了暴力破解,只进行服务识别
if common.DisableBrute {
return p.performServiceIdentification(ctx, info)
}
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
// Modbus协议通常无认证直接检查协议响应
unauthCred := &base.Credential{Username: "", Password: ""}
result, err := p.ScanCredential(ctx, info, unauthCred)
if err == nil && result.Success {
// 获取设备信息
deviceInfo := p.getDeviceInfo(ctx, info)
// 未授权访问成功
common.LogSuccess(i18n.GetText("modbus_unauth_access", target))
if deviceInfo != "" {
common.LogSuccess(i18n.GetText("modbus_device_info", deviceInfo))
}
return &base.ScanResult{
Success: true,
Service: "Modbus",
Credentials: []*base.Credential{unauthCred},
Extra: map[string]interface{}{
"service": "Modbus",
"port": info.Ports,
"unauthorized": true,
"access_type": "no_authentication",
"device_info": deviceInfo,
},
}, nil
}
// 如果Modbus协议检测失败返回失败结果
return &base.ScanResult{
Success: false,
Error: fmt.Errorf("非Modbus服务或连接失败"),
}, nil
}
// 已移除未使用的 generateCredentials 方法
// getDeviceInfo 获取Modbus设备信息
func (p *ModbusPlugin) getDeviceInfo(ctx context.Context, info *common.HostInfo) string {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
timeout := time.Duration(common.Timeout) * time.Second
// 尝试建立连接
conn, err := net.DialTimeout("tcp", target, timeout)
if err != nil {
return ""
}
defer conn.Close()
// 设置超时
conn.SetDeadline(time.Now().Add(timeout))
// 发送读取设备标识请求 (功能码0x11)
request := buildModbusDeviceIdentifyRequest()
if _, err := conn.Write(request); err != nil {
return ""
}
// 读取响应
response := make([]byte, 256)
n, err := conn.Read(response)
if err != nil {
return ""
}
return parseDeviceInfo(response[:n])
}
// buildModbusDeviceIdentifyRequest 构建Modbus设备标识请求
func buildModbusDeviceIdentifyRequest() []byte {
request := make([]byte, 8)
// Modbus TCP头部
binary.BigEndian.PutUint16(request[0:], 0x0002) // 事务标识符
binary.BigEndian.PutUint16(request[2:], 0x0000) // 协议标识符
binary.BigEndian.PutUint16(request[4:], 0x0002) // 长度
request[6] = 0x01 // 单元标识符
request[7] = 0x11 // 功能码: Report Server ID
return request
}
// parseDeviceInfo 解析设备信息
func parseDeviceInfo(response []byte) string {
if len(response) < 8 {
return "Unknown Device"
}
// 检查是否为有效响应
if !isValidModbusResponse(response) {
return "Unknown Device"
}
unitID := response[6]
funcCode := response[7]
info := fmt.Sprintf("Unit ID: %d, Function: 0x%02X", unitID, funcCode)
// 如果是设备标识响应,尝试解析设备信息
if funcCode == 0x11 && len(response) > 9 {
byteCount := response[8]
if byteCount > 0 && len(response) >= 9+int(byteCount) {
// 提取设备ID信息
deviceData := response[9 : 9+int(byteCount)]
if len(deviceData) > 0 {
info += fmt.Sprintf(", Device Data: %x", deviceData)
}
}
} else if funcCode == 0x01 && len(response) >= 10 {
// 读取线圈响应,显示线圈状态
byteCount := response[8]
if byteCount > 0 && len(response) >= 9+int(byteCount) {
coilValue := response[9] & 0x01
info += fmt.Sprintf(", Coil Status: %d", coilValue)
}
}
return info
}
// Exploit 使用exploiter执行利用
func (p *ModbusPlugin) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) {
return p.exploiter.Exploit(ctx, info, creds)
}
// GetExploitMethods 获取利用方法
func (p *ModbusPlugin) GetExploitMethods() []base.ExploitMethod {
return p.exploiter.GetExploitMethods()
}
// IsExploitSupported 检查利用支持
func (p *ModbusPlugin) IsExploitSupported(method base.ExploitType) bool {
return p.exploiter.IsExploitSupported(method)
}
// performServiceIdentification 执行Modbus服务识别-nobr模式
func (p *ModbusPlugin) performServiceIdentification(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
// 尝试识别Modbus服务
modbusInfo, isModbus := p.identifyModbusService(ctx, info)
if isModbus {
// 记录服务识别成功
common.LogSuccess(i18n.GetText("modbus_service_identified", target, modbusInfo))
return &base.ScanResult{
Success: true,
Service: "Modbus",
Banner: modbusInfo,
Extra: map[string]interface{}{
"service": "Modbus",
"port": info.Ports,
"info": modbusInfo,
},
}, nil
}
// 如果无法识别为Modbus返回失败
return &base.ScanResult{
Success: false,
Error: fmt.Errorf("无法识别为Modbus服务"),
}, nil
}
// identifyModbusService 通过协议识别Modbus服务
func (p *ModbusPlugin) identifyModbusService(ctx context.Context, info *common.HostInfo) (string, bool) {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
timeout := time.Duration(common.Timeout) * time.Second
// 尝试建立TCP连接
conn, err := net.DialTimeout("tcp", target, timeout)
if err != nil {
return "", false
}
defer conn.Close()
// 设置操作超时
conn.SetDeadline(time.Now().Add(timeout))
// 发送Modbus读取线圈请求
request := buildModbusReadCoilsRequest()
if _, err := conn.Write(request); err != nil {
return "", false
}
// 读取响应
response := make([]byte, 256)
n, err := conn.Read(response)
if err != nil {
return "", false
}
// 检查是否为有效Modbus响应
if isValidModbusResponse(response[:n]) {
deviceInfo := parseDeviceInfo(response[:n])
return fmt.Sprintf("Modbus TCP服务: %s", deviceInfo), true
}
return "", false
}
// =============================================================================
// 插件注册
// =============================================================================
// RegisterModbusPlugin 注册Modbus插件
func RegisterModbusPlugin() {
factory := base.NewSimplePluginFactory(
&base.PluginMetadata{
Name: "modbus",
Version: "2.0.0",
Author: "fscan-team",
Description: "Modbus工业协议扫描和利用插件",
Category: "service",
Ports: []int{502, 5020}, // 502: 标准Modbus TCP端口, 5020: 测试端口
Protocols: []string{"tcp"},
Tags: []string{"modbus", "industrial", "scada", "unauthorized"},
},
func() base.Plugin {
return NewModbusPlugin()
},
)
base.GlobalPluginRegistry.Register("modbus", factory)
}
// 自动注册
func init() {
RegisterModbusPlugin()
}