mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-24 12:11:52 +08:00
feat: v2.1.0 核心重构与功能增强
## 架构重构
- 全局变量消除,迁移至 Config/State 对象
- SMB 插件融合(smb/smb2/smbghost/smbinfo)
- 服务探测重构,实现 Nmap 风格 fallback 机制
- 输出系统重构,TXT 实时刷盘 + 双写机制
- i18n 框架升级至 go-i18n
## 性能优化
- 正则表达式预编译
- 内存优化 map[string]struct{}
- 并发指纹匹配
- SOCKS5 连接复用
- 滑动窗口调度 + 自适应线程池
## 新功能
- Web 管理界面
- 多格式 POC 适配(xray/afrog)
- 增强指纹库(3139条)
- Favicon hash 指纹识别
- 插件选择性编译(Build Tags)
- fscan-lab 靶场环境
- 默认端口扩展(62→133)
## 构建系统
- 添加 no_local tag 支持排除本地插件
- 多版本构建:fscan/fscan-nolocal/fscan-web
- CI 添加 snapshot 模式支持仅测试构建
## Bug 修复
- 修复 120+ 个问题,包括 RDP panic、批量扫描漏报、
JSON 输出格式、Redis 检测、Context 超时等
## 测试增强
- 单元测试覆盖率 74-100%
- 并发安全测试
- 集成测试(Web/端口/服务/SSH/ICMP)
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
//go:build (plugin_avdetect || !plugin_selective) && !no_local
|
||||
|
||||
package local
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/shadow1ng/fscan/common"
|
||||
"github.com/shadow1ng/fscan/common/i18n"
|
||||
"github.com/shadow1ng/fscan/plugins"
|
||||
)
|
||||
|
||||
//go:embed auto.json
|
||||
var avDatabase []byte
|
||||
|
||||
// AVProduct AV产品信息结构
|
||||
type AVProduct struct {
|
||||
Processes []string `json:"processes"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// AVDetectPlugin 杀软检测插件
|
||||
// 设计哲学:"做一件事并做好" - 专注AV检测
|
||||
// - 使用JSON数据库加载AV信息
|
||||
// - 删除复杂的结果结构体
|
||||
// - 跨平台支持,运行时适配
|
||||
type AVDetectPlugin struct {
|
||||
plugins.BasePlugin
|
||||
avProducts map[string]AVProduct
|
||||
}
|
||||
|
||||
// NewAVDetectPlugin 创建AV检测插件
|
||||
func NewAVDetectPlugin() *AVDetectPlugin {
|
||||
plugin := &AVDetectPlugin{
|
||||
BasePlugin: plugins.NewBasePlugin("avdetect"),
|
||||
avProducts: make(map[string]AVProduct),
|
||||
}
|
||||
|
||||
// 加载AV数据库
|
||||
if err := json.Unmarshal(avDatabase, &plugin.avProducts); err != nil {
|
||||
common.LogError(i18n.Tr("avdetect_load_failed", err))
|
||||
} else {
|
||||
common.LogInfo(i18n.Tr("avdetect_loaded", len(plugin.avProducts)))
|
||||
}
|
||||
|
||||
return plugin
|
||||
}
|
||||
|
||||
// Scan 执行AV/EDR检测 - 直接、有效
|
||||
func (p *AVDetectPlugin) Scan(ctx context.Context, info *common.HostInfo, config *common.Config, state *common.State) *plugins.Result {
|
||||
var output strings.Builder
|
||||
var detectedAVs []string
|
||||
|
||||
output.WriteString("=== AV/EDR检测 ===\n")
|
||||
|
||||
// 获取运行进程
|
||||
processes := p.getRunningProcesses()
|
||||
if len(processes) == 0 {
|
||||
return &plugins.Result{
|
||||
Success: false,
|
||||
Output: "无法获取进程列表",
|
||||
Error: fmt.Errorf("进程列表获取失败"),
|
||||
}
|
||||
}
|
||||
|
||||
output.WriteString(fmt.Sprintf("扫描进程数: %d\n\n", len(processes)))
|
||||
|
||||
// 检测AV产品 - 使用JSON数据库
|
||||
for avName, avProduct := range p.avProducts {
|
||||
var foundProcesses []string
|
||||
|
||||
for _, avProcess := range avProduct.Processes {
|
||||
for _, runningProcess := range processes {
|
||||
// 提取进程名部分进行匹配(去除PID信息)
|
||||
processName := runningProcess
|
||||
if strings.Contains(runningProcess, " (PID: ") {
|
||||
processName = strings.Split(runningProcess, " (PID: ")[0]
|
||||
}
|
||||
|
||||
// 简单字符串匹配,忽略大小写
|
||||
if strings.Contains(strings.ToLower(processName), strings.ToLower(avProcess)) {
|
||||
foundProcesses = append(foundProcesses, runningProcess)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(foundProcesses) > 0 {
|
||||
detectedAVs = append(detectedAVs, avName)
|
||||
output.WriteString(fmt.Sprintf("✓ 检测到 %s:\n", avName))
|
||||
|
||||
common.LogSuccess(i18n.Tr("avdetect_found", avName, len(foundProcesses)))
|
||||
|
||||
// 输出详细进程信息到控制台
|
||||
for _, proc := range foundProcesses {
|
||||
output.WriteString(fmt.Sprintf(" - %s\n", proc))
|
||||
common.LogInfo(i18n.Tr("avdetect_process", proc))
|
||||
}
|
||||
output.WriteString("\n")
|
||||
}
|
||||
}
|
||||
|
||||
// 统计结果
|
||||
output.WriteString("=== 检测结果 ===\n")
|
||||
output.WriteString(fmt.Sprintf("检测到的AV产品: %d个\n", len(detectedAVs)))
|
||||
|
||||
if len(detectedAVs) > 0 {
|
||||
output.WriteString("检测到的产品: " + strings.Join(detectedAVs, ", ") + "\n")
|
||||
} else {
|
||||
output.WriteString("未检测到已知的AV/EDR产品\n")
|
||||
}
|
||||
|
||||
return &plugins.Result{
|
||||
Success: len(detectedAVs) > 0,
|
||||
Output: output.String(),
|
||||
Error: nil,
|
||||
}
|
||||
}
|
||||
|
||||
// getRunningProcesses 获取运行进程列表 - 跨平台适配
|
||||
func (p *AVDetectPlugin) getRunningProcesses() []string {
|
||||
var processes []string
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
processes = p.getWindowsProcesses()
|
||||
case "linux", "darwin":
|
||||
processes = p.getUnixProcesses()
|
||||
default:
|
||||
// 不支持的平台,返回空列表
|
||||
return processes
|
||||
}
|
||||
|
||||
return processes
|
||||
}
|
||||
|
||||
// getWindowsProcesses 获取Windows进程 - 包含PID和进程名
|
||||
func (p *AVDetectPlugin) getWindowsProcesses() []string {
|
||||
var processes []string
|
||||
|
||||
// 使用tasklist命令
|
||||
cmd := exec.Command("tasklist", "/fo", "csv", "/nh")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return processes
|
||||
}
|
||||
|
||||
lines := strings.Split(string(output), "\n")
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// 解析CSV格式:进程名,PID,会话名,会话号,内存
|
||||
if strings.HasPrefix(line, "\"") {
|
||||
parts := strings.Split(line, "\",\"")
|
||||
if len(parts) >= 2 {
|
||||
processName := strings.Trim(parts[0], "\"")
|
||||
pid := strings.Trim(parts[1], "\"")
|
||||
if processName != "" && pid != "" {
|
||||
// 格式:进程名 (PID: xxxx)
|
||||
processInfo := fmt.Sprintf("%s (PID: %s)", processName, pid)
|
||||
processes = append(processes, processInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return processes
|
||||
}
|
||||
|
||||
// getUnixProcesses 获取Unix进程 - 简化实现
|
||||
func (p *AVDetectPlugin) getUnixProcesses() []string {
|
||||
var processes []string
|
||||
|
||||
// 使用ps命令
|
||||
cmd := exec.Command("ps", "-eo", "comm")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return processes
|
||||
}
|
||||
|
||||
lines := strings.Split(string(output), "\n")
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line != "" && line != "COMMAND" {
|
||||
processes = append(processes, line)
|
||||
}
|
||||
}
|
||||
|
||||
return processes
|
||||
}
|
||||
|
||||
// 注册插件
|
||||
func init() {
|
||||
RegisterLocalPlugin("avdetect", func() Plugin {
|
||||
return NewAVDetectPlugin()
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user