fscan/plugins/README.md
ZacharyZcR be1188d82b refactor: v2.1.0 核心重构和功能增强
**核心架构重构**
- 重构目录结构:Common/ -> common/, Core/ -> core/, Plugins/ -> plugins/, WebScan/ -> webscan/
- 模块化设计:拆分common/为parsers/, logging/, i18n/, output/, proxy/等子模块
- 统一插件系统:消除Plugin/Scanner/LocalPlugin等重复接口
- 配置解析器重写:新增parsers包处理所有配置解析逻辑
- 国际化支持:完整的中英文i18n系统

**服务插件重构** (59个插件)
- 所有服务插件采用统一接口设计
- 新增服务插件:ActiveMQ, Cassandra, Kafka, Neo4j, RabbitMQ, Rsync等
- 增强现有插件:SSH, MySQL, Redis, SMB, FTP, PostgreSQL等
- 本地功能插件:AVDetect, Cleaner, CronTask, KeyLogger, SystemInfo等26个
- 智能错误分类和重试机制
- 并发暴力破解优化

**核心扫描引擎**
- 端口扫描重构:支持SYN/Connect/ICMP多种模式
- 服务识别增强:集成nmap-service-probes指纹库
- Web扫描优化:title抓取、POC检测、指纹识别
- 进度条系统:实时显示扫描进度、发包统计、内存使用
- 发包频率控制:支持-rate和-maxpkts限制

**新增功能**
- user:pass格式字典支持 (-upf参数) #179
- fscan-lite C语言版本:轻量级无依赖版本
- 本地扫描模式:26个本地安全检测插件
- API模式:支持远程调用
- Docker测试环境:13个服务的docker-compose配置

**Bug修复**
- 修复并发输出混乱问题
- 修复包计数双重统计
- 修复进度条显示不准确
- 修复-no参数被忽略
- 修复SMB/SSH认证问题
- 修复Oracle TNS服务识别错误
- 修复SafeDialTimeout缺失TCP计数

**性能优化**
- 删除大量死代码和冗余函数
- 简化过度工程设计
- 优化端口扫描性能
- 减少内存占用
- 清理重复代码

**测试和CI/CD**
- 新增核心模块单元测试 (2000+ loc)
- GitHub Actions优化:统一构建发布流程
- 支持fscan和fscan-lite统一发布
- 中文化工作流配置

**文档**
- 更新README文档
- 新增插件开发文档
- 完善国际化文档

**依赖更新**
- 更新go.mod依赖
- 清理无用依赖
- 升级核心库版本

统计:
- 删除文件:61个旧架构文件
- 新增文件:500+个模块化文件
- 代码行数:~50000 lines
- Commits压缩:289 -> 1
2025-10-02 00:50:10 +08:00

6.7 KiB
Raw Blame History

FScan 插件开发规范

概述

FScan 采用简化的单文件插件架构,每个插件一个 .go 文件,消除了过度设计的多文件结构。

设计原则 (Linus Torvalds "好品味" 原则)

  1. 简洁至上:消除所有不必要的抽象层
  2. 直击本质:专注于解决实际问题,不为架构而架构
  3. 向后兼容:不破坏用户接口和现有功能
  4. 消除特殊情况:统一处理逻辑,减少 if/else 分支

插件架构

核心接口

// Plugin 插件接口 - 只保留必要的方法
type Plugin interface {
    GetName() string                                        // 插件名称
    GetPorts() []int                                       // 支持的端口
    Scan(ctx context.Context, info *common.HostInfo) *ScanResult  // 扫描功能
}

// 可选接口:如果插件支持利用功能
type Exploiter interface {
    Exploit(ctx context.Context, info *common.HostInfo, creds Credential) *ExploitResult
}

数据结构

// ScanResult 扫描结果 - 删除所有冗余字段
type ScanResult struct {
    Success  bool   // 扫描是否成功
    Service  string // 服务类型
    Username string // 发现的用户名(弱密码)
    Password string // 发现的密码(弱密码)  
    Banner   string // 服务版本信息
    Error    error  // 错误信息(如果失败)
}

// ExploitResult 利用结果(仅有利用功能的插件需要)
type ExploitResult struct {
    Success bool   // 利用是否成功
    Output  string // 命令执行输出
    Error   error  // 错误信息
}

// Credential 凭据结构
type Credential struct {
    Username string
    Password string
    KeyData  []byte // SSH私钥等
}

插件开发模板

1. 纯扫描插件如MySQL

package plugins

import (
    "context"
    "fmt"
    // 其他必要导入
)

// PluginName服务扫描插件
type PluginNamePlugin struct {
    name  string
    ports []int
}

// 构造函数
func NewPluginNamePlugin() *PluginNamePlugin {
    return &PluginNamePlugin{
        name:  "plugin_name",
        ports: []int{default_port},
    }
}

// 实现Plugin接口
func (p *PluginNamePlugin) GetName() string { return p.name }
func (p *PluginNamePlugin) GetPorts() []int { return p.ports }

func (p *PluginNamePlugin) Scan(ctx context.Context, info *common.HostInfo) *ScanResult {
    // 如果禁用暴力破解,只做服务识别
    if common.DisableBrute {
        return p.identifyService(info)
    }

    // 生成测试凭据
    credentials := GenerateCredentials("plugin_name")
    
    // 逐个测试凭据
    for _, cred := range credentials {
        select {
        case <-ctx.Done():
            return &ScanResult{Success: false, Error: ctx.Err()}
        default:
        }

        if p.testCredential(ctx, info, cred) {
            return &ScanResult{
                Success:  true,
                Service:  "plugin_name",
                Username: cred.Username,
                Password: cred.Password,
            }
        }
    }

    return &ScanResult{Success: false, Service: "plugin_name"}
}

// 核心认证逻辑
func (p *PluginNamePlugin) testCredential(ctx context.Context, info *common.HostInfo, cred Credential) bool {
    // 实现具体的认证测试逻辑
    return false
}

// 服务识别(-nobr模式
func (p *PluginNamePlugin) identifyService(info *common.HostInfo) *ScanResult {
    // 实现服务识别逻辑
    return &ScanResult{Success: false, Service: "plugin_name"}
}

// 自动注册
func init() {
    RegisterPlugin("plugin_name", func() Plugin {
        return NewPluginNamePlugin()
    })
}

2. 带利用功能的插件如SSH

package plugins

// SSH插件结构
type SSHPlugin struct {
    name  string
    ports []int
}

// 同时实现Plugin和Exploiter接口
func (p *SSHPlugin) Scan(ctx context.Context, info *common.HostInfo) *ScanResult {
    // 扫描逻辑(同上)
}

func (p *SSHPlugin) Exploit(ctx context.Context, info *common.HostInfo, creds Credential) *ExploitResult {
    // 建立SSH连接
    client, err := p.connectSSH(info, creds)
    if err != nil {
        return &ExploitResult{Success: false, Error: err}
    }
    defer client.Close()

    // 执行命令或其他利用操作
    output, err := p.executeCommand(client, "whoami")
    return &ExploitResult{
        Success: err == nil,
        Output:  output,
        Error:   err,
    }
}

// 辅助方法
func (p *SSHPlugin) connectSSH(info *common.HostInfo, creds Credential) (*ssh.Client, error) {
    // SSH连接实现
}

func (p *SSHPlugin) executeCommand(client *ssh.Client, cmd string) (string, error) {
    // 命令执行实现
}

开发规范

文件组织

plugins/
├── base.go           # 核心接口和注册系统
├── mysql.go          # MySQL插件
├── ssh.go            # SSH插件  
├── redis.go          # Redis插件
└── README.md         # 开发文档(本文件)

命名规范

  • 插件文件{service_name}.go
  • 插件结构体{ServiceName}Plugin
  • 构造函数New{ServiceName}Plugin()
  • 插件名称:小写,与文件名一致

代码规范

  1. 错误处理始终使用Context进行超时控制
  2. 日志输出:成功时使用 common.LogSuccess,调试用 common.LogDebug
  3. 凭据生成:使用 GenerateCredentials(service_name) 生成测试凭据
  4. 资源管理:及时关闭连接,使用 defer 确保清理

测试要求

每个插件必须支持:

  1. 暴力破解模式common.DisableBrute = false
  2. 服务识别模式common.DisableBrute = true
  3. Context超时处理:正确响应 ctx.Done()
  4. 代理支持:如果 common.Socks5Proxy 不为空

迁移指南

从三文件架构迁移

  1. 提取核心逻辑:从 connector.go 提取认证逻辑
  2. 合并实现:将 plugin.go 中的组装逻辑内联
  3. 删除垃圾:删除空的 exploiter.go
  4. 简化数据结构:只保留必要的字段

从Legacy插件迁移

  1. 保留核心逻辑:复制扫描和认证的核心算法
  2. 标准化接口实现统一的Plugin接口
  3. 移除全局依赖:通过返回值而不是全局变量传递结果
  4. 统一日志:使用统一的日志接口

性能优化

  1. 连接复用:在同一次扫描中复用连接
  2. 内存管理:及时释放不需要的资源
  3. 并发控制通过Context控制并发度
  4. 超时设置:合理设置各阶段超时时间

示例

参考 mysql.go 作为标准的纯扫描插件实现 参考 ssh.go 作为带利用功能的插件实现


记住:好的代码不是写出来的,是重构出来的。消除所有不必要的复杂性,直击问题本质。