fix: 解决端口扫描与服务识别的显示时序问题

问题:
- 端口发现日志在25.2s显示
- 服务识别结果在30.2s-40.2s分批显示
- 用户希望合并为统一显示

解决方案:
- 延迟端口开放日志输出直到服务识别完成
- 将端口状态和服务信息合并为一行显示
- 格式: "端口开放 IP:PORT [服务](类型) 版本/详情"
- Web服务标注"(Web服务)"便于识别

效果:
- 消除了分批显示的时间差
- 信息更加简洁统一
- 保持了-fp参数的详细信息展示
This commit is contained in:
ZacharyZcR 2025-09-02 06:08:35 +00:00
parent af2c92a591
commit 5133010ed2

View File

@ -84,57 +84,71 @@ func EnhancedPortScan(hosts []string, ports string, timeout int64) []string {
}
defer conn.Close()
// 记录开放端口
// 记录开放端口(先记录到内存,延迟输出直到服务识别完成)
atomic.AddInt64(&count, 1)
aliveMap.Store(addr, struct{}{})
common.LogInfo("端口开放 " + addr)
common.SaveResult(&output.ScanResult{
Time: time.Now(), Type: output.TypePort, Target: host,
Status: "open", Details: map[string]interface{}{"port": port},
})
// 服务识别
if common.EnableFingerprint {
if info, err := NewPortInfoScanner(host, port, conn, to).Identify(); err == nil {
// 构建结果详情
details := map[string]interface{}{"port": port, "service": info.Name}
if info.Version != "" {
details["version"] = info.Version
}
// 统一服务识别(默认启用,替代原有的-fp选项逻辑
serviceInfo, err := NewPortInfoScanner(host, port, conn, to).Identify()
if err == nil {
// 构建结果详情
details := map[string]interface{}{"port": port, "service": serviceInfo.Name}
if serviceInfo.Version != "" {
details["version"] = serviceInfo.Version
}
// 处理额外信息
for k, v := range info.Extras {
if v == "" {
continue
}
switch k {
case "vendor_product":
details["product"] = v
case "os", "info":
details[k] = v
}
// 处理额外信息
for k, v := range serviceInfo.Extras {
if v == "" {
continue
}
if len(info.Banner) > 0 {
details["banner"] = strings.TrimSpace(info.Banner)
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)
}
// 保存服务结果
common.SaveResult(&output.ScanResult{
Time: time.Now(), Type: output.TypeService, Target: host,
Status: "identified", Details: details,
})
// 智能判断是否为Web服务
isWeb := IsWebServiceByFingerprint(serviceInfo)
if isWeb {
details["is_web"] = true
// 标记该端口为Web服务后续会自动启用WebTitle和WebPOC
MarkAsWebService(host, port, serviceInfo)
}
// 记录服务信息
var sb strings.Builder
sb.WriteString("服务识别 " + addr + " => ")
if info.Name != "unknown" {
sb.WriteString("[" + info.Name + "]")
}
if info.Version != "" {
sb.WriteString(" 版本:" + info.Version)
}
// 保存服务结果
common.SaveResult(&output.ScanResult{
Time: time.Now(), Type: output.TypeService, Target: host,
Status: "identified", Details: details,
})
for k, v := range info.Extras {
// 构建统一的服务信息日志
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)
}
// 添加详细信息(在-fp启用时
if common.EnableFingerprint {
for k, v := range serviceInfo.Extras {
if v == "" {
continue
}
@ -148,12 +162,17 @@ func EnhancedPortScan(hosts []string, ports string, timeout int64) []string {
}
}
if len(info.Banner) > 0 && len(info.Banner) < 100 {
sb.WriteString(" Banner:[" + strings.TrimSpace(info.Banner) + "]")
if len(serviceInfo.Banner) > 0 && len(serviceInfo.Banner) < 100 {
sb.WriteString(" Banner:[" + strings.TrimSpace(serviceInfo.Banner) + "]")
}
common.LogInfo(sb.String())
}
// 统一输出端口和服务信息
common.LogInfo(sb.String())
} else {
// 服务识别失败时,只输出端口开放信息
common.LogInfo("端口开放 " + addr)
common.LogDebug(fmt.Sprintf("服务识别失败 %s: %v回退到Web检测", addr, err))
}
return nil