fix: 修复6个运行时问题

1. 抑制 gmtls 库的 handshake error stdout 噪声
   gmtls/conn.go:1304 硬编码了 fmt.Println,在调用时临时重定向 os.Stdout

2. MySQL 3306 服务名误识别为 genetec-5400
   nmap 指纹库将 MySQL 握手包的随机 salt 误匹配,通过 banner 特征校正

3. 管道输出时自动禁用 ANSI 控制码
   检测 stdout 是否为终端,非终端时自动启用 NoColor

4. 进度条完成消息措辞精确化
   去掉冗余冒号,保持信息简洁一致

5. URL 模式跳过不必要的 TLS 探测
   用户已通过 -u 显式指定 http:// 协议时直接使用,不再做 TLS 握手

6. 无网络探测数据时降低默认重试次数
   -np 跳过存活探测后,将默认重试从 3 降到 2,加速不可达主机的超时
This commit is contained in:
ZacharyZcR
2026-06-14 22:23:49 +08:00
parent 46a6d812a4
commit 4f6bb28138
10 changed files with 100 additions and 23 deletions
+12 -5
View File
@@ -97,11 +97,18 @@ func (ep *EnvironmentProfile) TuneConfig(config *common.Config, session *common.
// 含义: 重试 N 次后仍然全部丢包的概率 < 1%
// 例: 丢包率 5% → N=2, 丢包率 20% → N=3, 丢包率 50% → N=7
// 下限 1(零丢包也至少试一次),上限 6(避免对不可达目标死磕)
if !isExplicit(config, "retry") && net.Samples > 0 {
computed := computeRetries(net.LossRate, net.Env)
old := config.MaxRetries
config.MaxRetries = computed
session.LogDebug(fmt.Sprintf("MaxRetries: %d -> %d (loss_rate=%.2f%%)", old, computed, net.LossRate*100))
if !isExplicit(config, "retry") {
if net.Samples > 0 {
computed := computeRetries(net.LossRate, net.Env)
old := config.MaxRetries
config.MaxRetries = computed
session.LogDebug(fmt.Sprintf("MaxRetries: %d -> %d (loss_rate=%.2f%%)", old, computed, net.LossRate*100))
} else if config.MaxRetries > 2 {
// 无网络探测数据(-np 跳过存活探测),降低默认重试避免对不可达主机死磕
old := config.MaxRetries
config.MaxRetries = 2
session.LogDebug(fmt.Sprintf("MaxRetries: %d -> %d (no network probe data)", old, config.MaxRetries))
}
}
// ---------- ICMPRate ----------
+3 -2
View File
@@ -344,8 +344,9 @@ func TestTuneConfig_NoSamples(t *testing.T) {
if config.Timeout != origTimeout {
t.Errorf("零样本不应改 Timeout: %v -> %v", origTimeout, config.Timeout)
}
if config.MaxRetries != origRetry {
t.Errorf("零样本不应改 MaxRetries: %d -> %d", origRetry, config.MaxRetries)
// 零样本时,默认重试降到 2(避免对不可达主机死磕)
if origRetry > 2 && config.MaxRetries != 2 {
t.Errorf("零样本应降 MaxRetries 至 2: %d -> %d", origRetry, config.MaxRetries)
}
if config.Network.ICMPRate != origICMP {
t.Errorf("零样本不应改 ICMPRate: %.2f -> %.2f", origICMP, config.Network.ICMPRate)
+15
View File
@@ -700,6 +700,18 @@ func saveOpenPort(session *common.ScanSession, host string, port int) {
})
}
// correctServiceByBanner 根据 banner 特征校正被 nmap 指纹误匹配的服务名
func correctServiceByBanner(info *ServiceInfo) {
if info == nil || info.Banner == "" {
return
}
banner := strings.ToLower(info.Banner)
// MySQL 握手包包含认证插件名,nmap 随机 salt 可能导致误匹配
if strings.Contains(banner, "mysql_native_password") || strings.Contains(banner, "caching_sha2_password") {
info.Name = "mysql"
}
}
// processServiceResult 处理服务识别结果
func processServiceResult(ctx context.Context, host string, port int, addr string, serviceInfo *ServiceInfo, config *common.Config, session *common.ScanSession) {
if serviceInfo == nil {
@@ -710,6 +722,9 @@ func processServiceResult(ctx context.Context, host string, port int, addr strin
return
}
// Banner 校正:nmap 指纹库可能将 MySQL 握手包的随机 salt 误匹配为其他服务
correctServiceByBanner(serviceInfo)
// 缓存指纹识别结果,供插件按服务类型匹配(解决非标准端口问题)
CacheServiceInfo(host, port, serviceInfo)
+32 -8
View File
@@ -7,6 +7,7 @@ import (
"net"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"sync"
@@ -57,14 +58,17 @@ func DetectHTTPSchemeContext(ctx context.Context, host string, port int, config
}
// 第二步:尝试国密TLS握手(GM TLS fallback
gmConn, gmErr := gmtls.DialWithDialer(
tlsDialer,
"tcp", addr,
&gmtls.Config{
GMSupport: gmtls.NewGMSupport(),
InsecureSkipVerify: true,
},
)
// 抑制 gmtls 库的 fmt.Println("handshake error") 噪声输出
gmConn, gmErr := suppressGMTLSStdout(func() (net.Conn, error) {
return gmtls.DialWithDialer(
tlsDialer,
"tcp", addr,
&gmtls.Config{
GMSupport: gmtls.NewGMSupport(),
InsecureSkipVerify: true,
},
)
})
if gmErr == nil {
_ = gmConn.Close()
@@ -498,3 +502,23 @@ func hasMalformedURLPort(host string) bool {
}
return strings.Contains(host, ":")
}
// suppressGMTLSStdout 抑制 gmtls 库硬编码的 fmt.Println("handshake error") 输出
// gmtls/conn.go:1304 在握手失败时直接 Println 到 os.Stdout,无法通过 API 关闭
var gmtlsStdoutMu sync.Mutex
func suppressGMTLSStdout(fn func() (net.Conn, error)) (net.Conn, error) {
gmtlsStdoutMu.Lock()
orig := os.Stdout
devNull, err := os.OpenFile(os.DevNull, os.O_WRONLY, 0)
if err == nil {
os.Stdout = devNull
}
conn, dialErr := fn()
os.Stdout = orig
if devNull != nil {
_ = devNull.Close()
}
gmtlsStdoutMu.Unlock()
return conn, dialErr
}