fscan/core/PortScan.go
ZacharyZcR 80644cd6f1 refactor: 删除多余的-fp参数,服务指纹识别默认启用
理由:
- SmartPortInfoScanner已默认进行服务指纹识别
- -fp参数造成用户困惑,存在两种显示模式
- 详细服务信息对安全扫描很有价值,应该默认显示

删除内容:
- 移除-fp命令行参数定义
- 删除EnableFingerprint变量和相关逻辑
- 清理国际化文件中的相关文本
- 移除配置结构体中的指纹识别字段

新行为:
- 服务识别信息默认显示完整详情
- 包含版本、系统、产品、协议信息和Banner
- 简化用户界面,消除参数选择的困惑

效果验证:
- 原来: ./fscan -h IP -fp 显示详细信息
- 现在: ./fscan -h IP 默认显示详细信息
- 用户体验更简洁一致
2025-09-02 07:51:28 +00:00

199 lines
5.1 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 core
import (
"context"
"fmt"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/i18n"
"github.com/shadow1ng/fscan/common/output"
"github.com/shadow1ng/fscan/common/parsers"
"golang.org/x/sync/errgroup"
"golang.org/x/sync/semaphore"
"strings"
"sync"
"sync/atomic"
"time"
)
// EnhancedPortScan 高性能端口扫描函数
func EnhancedPortScan(hosts []string, ports string, timeout int64) []string {
// 解析端口和排除端口
portList := parsers.ParsePort(ports)
if len(portList) == 0 {
common.LogError("无效端口: " + ports)
return nil
}
// 预估排除端口数量通常不会超过100个
excludePorts := parsers.ParsePort(common.ExcludePorts)
exclude := make(map[int]struct{}, len(excludePorts))
for _, p := range excludePorts {
exclude[p] = struct{}{}
}
// 计算总扫描数量
totalTasks := 0
for range hosts {
for _, port := range portList {
if _, excluded := exclude[port]; !excluded {
totalTasks++
}
}
}
// 初始化端口扫描进度条
if totalTasks > 0 && common.ShowProgress {
description := i18n.GetText("progress_port_scanning_with_threads", common.ThreadNum)
common.InitProgressBar(int64(totalTasks), description)
}
// 初始化并发控制
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
to := time.Duration(timeout) * time.Second
sem := semaphore.NewWeighted(int64(common.ThreadNum))
var count int64
var aliveMap sync.Map
g, ctx := errgroup.WithContext(ctx)
// 并发扫描所有目标
for _, host := range hosts {
for _, port := range portList {
if _, excluded := exclude[port]; excluded {
continue
}
host, port := host, port // 捕获循环变量
addr := fmt.Sprintf("%s:%d", host, port)
if err := sem.Acquire(ctx, 1); err != nil {
break
}
g.Go(func() error {
defer func() {
sem.Release(1)
// 更新端口扫描进度
common.UpdateProgressBar(1)
}()
// 连接测试 - 支持SOCKS5代理
conn, err := common.WrapperTcpWithTimeout("tcp", addr, to)
if err != nil {
return nil
}
defer conn.Close()
// 记录开放端口(先记录到内存,延迟输出直到服务识别完成)
atomic.AddInt64(&count, 1)
aliveMap.Store(addr, struct{}{})
common.SaveResult(&output.ScanResult{
Time: time.Now(), Type: output.TypePort, Target: host,
Status: "open", Details: map[string]interface{}{"port": port},
})
// 智能服务识别:保持准确性,优化网络交互
serviceInfo, err := NewSmartPortInfoScanner(host, port, conn, to).SmartIdentify()
if err == nil {
// 构建结果详情
details := map[string]interface{}{"port": port, "service": serviceInfo.Name}
if serviceInfo.Version != "" {
details["version"] = serviceInfo.Version
}
// 处理额外信息
for k, v := range serviceInfo.Extras {
if v == "" {
continue
}
switch k {
case "vendor_product":
details["product"] = v
case "os", "info":
details[k] = v
}
}
if len(serviceInfo.Banner) > 0 {
details["banner"] = strings.TrimSpace(serviceInfo.Banner)
}
// 智能判断是否为Web服务
isWeb := IsWebServiceByFingerprint(serviceInfo)
if isWeb {
details["is_web"] = true
// 标记该端口为Web服务后续会自动启用WebTitle和WebPOC
MarkAsWebService(host, port, serviceInfo)
}
// 保存服务结果
common.SaveResult(&output.ScanResult{
Time: time.Now(), Type: output.TypeService, Target: host,
Status: "identified", Details: details,
})
// 构建统一的服务信息日志
var sb strings.Builder
sb.WriteString("端口开放 " + addr)
if serviceInfo.Name != "unknown" {
sb.WriteString(" [" + serviceInfo.Name + "]")
if isWeb {
sb.WriteString("(Web服务)")
}
}
if serviceInfo.Version != "" {
sb.WriteString(" 版本:" + serviceInfo.Version)
}
// 添加详细服务信息(默认启用)
for k, v := range serviceInfo.Extras {
if v == "" {
continue
}
switch k {
case "vendor_product":
sb.WriteString(" 产品:" + v)
case "os":
sb.WriteString(" 系统:" + v)
case "info":
sb.WriteString(" 信息:" + v)
}
}
if len(serviceInfo.Banner) > 0 && len(serviceInfo.Banner) < 100 {
sb.WriteString(" Banner:[" + strings.TrimSpace(serviceInfo.Banner) + "]")
}
// 统一输出端口和服务信息
common.LogInfo(sb.String())
} else {
// 服务识别失败时,只输出端口开放信息
common.LogInfo("端口开放 " + addr)
common.LogDebug(fmt.Sprintf("服务识别失败 %s: %v回退到Web检测", addr, err))
}
return nil
})
}
}
_ = g.Wait()
// 收集结果
var aliveAddrs []string
aliveMap.Range(func(key, _ interface{}) bool {
aliveAddrs = append(aliveAddrs, key.(string))
return true
})
// 完成端口扫描进度条
if common.IsProgressActive() {
common.FinishProgressBar()
}
common.LogBase(i18n.GetText("scan_complete_ports_found", count))
return aliveAddrs
}