mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00

将复杂的三文件插件架构(connector/exploiter/plugin)重构为简化的单文件插件架构, 大幅减少代码重复和维护成本,提升插件开发效率。 主要改进: • 将每个服务插件从3个文件简化为1个文件 • 删除过度设计的工厂模式、适配器模式等抽象层 • 消除plugins/services/、plugins/adapters/、plugins/base/复杂目录结构 • 实现直接的插件注册机制,提升系统简洁性 • 保持完全向后兼容,所有扫描功能和输出格式不变 重构统计: • 删除文件:100+个复杂架构文件 • 新增文件:20个简化的单文件插件 • 代码减少:每个插件减少60-80%代码量 • 功能增强:所有插件包含完整扫描和利用功能 已重构插件: MySQL, SSH, Redis, MongoDB, PostgreSQL, MSSQL, Oracle, Neo4j, Memcached, RabbitMQ, ActiveMQ, Cassandra, FTP, Kafka, LDAP, Rsync, SMTP, SNMP, Telnet, VNC 验证通过: 新系统编译运行正常,所有插件功能验证通过
250 lines
6.7 KiB
Markdown
250 lines
6.7 KiB
Markdown
# FScan 插件开发规范
|
||
|
||
## 概述
|
||
|
||
FScan 采用简化的单文件插件架构,每个插件一个 `.go` 文件,消除了过度设计的多文件结构。
|
||
|
||
## 设计原则 (Linus Torvalds "好品味" 原则)
|
||
|
||
1. **简洁至上**:消除所有不必要的抽象层
|
||
2. **直击本质**:专注于解决实际问题,不为架构而架构
|
||
3. **向后兼容**:不破坏用户接口和现有功能
|
||
4. **消除特殊情况**:统一处理逻辑,减少 if/else 分支
|
||
|
||
## 插件架构
|
||
|
||
### 核心接口
|
||
|
||
```go
|
||
// 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
|
||
}
|
||
```
|
||
|
||
### 数据结构
|
||
|
||
```go
|
||
// 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)
|
||
|
||
```go
|
||
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)
|
||
|
||
```go
|
||
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` 作为带利用功能的插件实现
|
||
|
||
---
|
||
|
||
**记住:好的代码不是写出来的,是重构出来的。消除所有不必要的复杂性,直击问题本质。** |