diff --git a/common/logging/logger.go b/common/logging/logger.go index 376daaa..c303d71 100644 --- a/common/logging/logger.go +++ b/common/logging/logger.go @@ -114,10 +114,9 @@ func (l *Logger) log(level LogLevel, content string) { return } - // 格式化消息 - timeStr := l.formatElapsedTime(time.Since(l.startTime)) + // 格式化消息:保留前缀,去掉时间戳 prefix := l.getLevelPrefix(level) - logMsg := fmt.Sprintf("[%s] %s %s", timeStr, prefix, content) + logMsg := fmt.Sprintf("%s %s", prefix, content) // 输出消息 l.outputMessage(level, logMsg) diff --git a/core/port_scan.go b/core/port_scan.go index 9b78318..6cb3bdc 100644 --- a/core/port_scan.go +++ b/core/port_scan.go @@ -311,23 +311,37 @@ func isResourceExhaustedError(err error) bool { } // buildServiceLogMessage 构建服务识别的日志信息 -// 格式: addr service banner (简洁单行,方便复制) +// 格式: addr service [Product:xxx ||Version:xxx] Banner:(xxx) func buildServiceLogMessage(addr string, serviceInfo *ServiceInfo, isWeb bool) string { - var parts []string - parts = append(parts, addr) + var msg strings.Builder + msg.WriteString(fmt.Sprintf("%-21s", addr)) if serviceInfo.Name != "unknown" { - parts = append(parts, serviceInfo.Name) + msg.WriteString(fmt.Sprintf(" %-8s", serviceInfo.Name)) } - // Banner 优先,其次是版本信息 - if len(serviceInfo.Banner) > 0 && len(serviceInfo.Banner) < 100 { - parts = append(parts, strings.TrimSpace(serviceInfo.Banner)) - } else if serviceInfo.Version != "" { - parts = append(parts, serviceInfo.Version) + // 构建 [Product:xxx ||Version:xxx] 格式 + var info []string + if product, ok := serviceInfo.Extras["product"]; ok && product != "" { + info = append(info, fmt.Sprintf("Product:%s", product)) + } + if serviceInfo.Version != "" { + info = append(info, fmt.Sprintf("Version:%s", serviceInfo.Version)) + } + if len(info) > 0 { + msg.WriteString(fmt.Sprintf(" [%s]", strings.Join(info, " ||"))) } - return strings.Join(parts, " ") + // Banner 信息 + if len(serviceInfo.Banner) > 0 { + banner := strings.TrimSpace(serviceInfo.Banner) + if len(banner) > 80 { + banner = banner[:80] + "..." + } + msg.WriteString(fmt.Sprintf(" Banner:(%s)", banner)) + } + + return msg.String() } // scanSinglePort 扫描单个端口并进行服务识别(重构后的简洁版本) diff --git a/plugins/web/webtitle.go b/plugins/web/webtitle.go index 1a29839..fd51c6f 100644 --- a/plugins/web/webtitle.go +++ b/plugins/web/webtitle.go @@ -40,7 +40,7 @@ func NewWebTitlePlugin() *WebTitlePlugin { // Scan 执行WebTitle扫描 func (p *WebTitlePlugin) Scan(ctx context.Context, info *common.HostInfo, config *common.Config, state *common.State) *WebScanResult { - title, status, server, fingerprints, url, err := p.getWebTitle(ctx, info, config) + title, status, length, server, fingerprints, url, err := p.getWebTitle(ctx, info, config) if err != nil { return &WebScanResult{ Success: false, @@ -48,21 +48,24 @@ func (p *WebTitlePlugin) Scan(ctx context.Context, info *common.HostInfo, config } } - msg := fmt.Sprintf("WebTitle %s", url) - if title != "" { - msg += fmt.Sprintf(" [%s]", title) - } - if status != 0 { - msg += fmt.Sprintf(" %d", status) + // 构建输出:URL code:状态码 len:长度 title:标题 server:服务器 [指纹] + titleDisplay := title + if titleDisplay == "" { + titleDisplay = "None" } + msg := fmt.Sprintf("%-30s code:%-3d len:%-5d title:%-20s", url, status, length, titleDisplay) if server != "" { - msg += fmt.Sprintf(" %s", server) + msg += fmt.Sprintf(" server:%s", server) } - // 基础信息用白色输出 - common.LogInfo(msg) - // 指纹信息单独用绿色输出 if len(fingerprints) > 0 { - common.LogSuccess(fmt.Sprintf("WebFinger %s %v", url, fingerprints)) + msg += fmt.Sprintf(" %v", fingerprints) + } + + // 有指纹用绿色,无指纹用白色 + if len(fingerprints) > 0 { + common.LogSuccess(msg) + } else { + common.LogInfo(msg) } return &WebScanResult{ @@ -75,7 +78,7 @@ func (p *WebTitlePlugin) Scan(ctx context.Context, info *common.HostInfo, config } } -func (p *WebTitlePlugin) getWebTitle(ctx context.Context, info *common.HostInfo, config *common.Config) (string, int, string, []string, string, error) { +func (p *WebTitlePlugin) getWebTitle(ctx context.Context, info *common.HostInfo, config *common.Config) (string, int, int, string, []string, string, error) { // 智能协议检测 protocol := p.detectProtocol(info, config) baseURL := fmt.Sprintf("%s://%s:%d", protocol, info.Host, info.Port) @@ -90,7 +93,7 @@ func (p *WebTitlePlugin) getWebTitle(ctx context.Context, info *common.HostInfo, req, err := http.NewRequestWithContext(ctx, "GET", baseURL, nil) if err != nil { - return "", 0, "", nil, displayURL, err + return "", 0, 0, "", nil, displayURL, err } req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") @@ -98,13 +101,14 @@ func (p *WebTitlePlugin) getWebTitle(ctx context.Context, info *common.HostInfo, // 先使用不跟随重定向的Client获取原始响应 resp, err := lib.ClientNoRedirect.Do(req) if err != nil { - return "", 0, "", nil, displayURL, err + return "", 0, 0, "", nil, displayURL, err } body, err := io.ReadAll(resp.Body) _ = resp.Body.Close() - if len(body) <= 0 && err != nil { - return "", resp.StatusCode, resp.Header.Get("Server"), nil, displayURL, err + contentLen := len(body) + if contentLen <= 0 && err != nil { + return "", resp.StatusCode, 0, resp.Header.Get("Server"), nil, displayURL, err } // 收集用于指纹识别的响应数据 @@ -157,7 +161,7 @@ func (p *WebTitlePlugin) getWebTitle(ctx context.Context, info *common.HostInfo, // 执行指纹识别(合并原始响应和跳转后响应的指纹) fingerprints := p.identifyFingerprintsMulti(info, baseURL, checkDataList, config) - return title, statusCode, server, fingerprints, displayURL, nil + return title, statusCode, contentLen, server, fingerprints, displayURL, nil } // resolveRedirectURL 解析重定向URL,处理相对路径