diff --git a/Common/Flag.go b/Common/Flag.go index 5cf0093..a772ad0 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -148,6 +148,7 @@ func Flag(Info *HostInfo) { flag.BoolVar(&DisablePing, "np", false, i18n.GetText("flag_disable_ping")) flag.BoolVar(&EnableFingerprint, "fingerprint", false, i18n.GetText("flag_enable_fingerprint")) flag.BoolVar(&LocalMode, "local", false, i18n.GetText("flag_local_mode")) + flag.BoolVar(&AliveOnly, "ao", false, i18n.GetText("flag_alive_only")) // ═════════════════════════════════════════════════ // 认证与凭据参数 diff --git a/Common/globals.go b/Common/globals.go index 7d3e2ab..6a15e13 100644 --- a/Common/globals.go +++ b/Common/globals.go @@ -40,6 +40,7 @@ var ( Timeout int64 // 直接映射到base.Timeout DisablePing bool // 直接映射到base.DisablePing LocalMode bool // 直接映射到base.LocalMode + AliveOnly bool // 仅存活探测模式 ) // ============================================================================= diff --git a/Common/i18n/messages/flag.go b/Common/i18n/messages/flag.go index f075070..b042be5 100644 --- a/Common/i18n/messages/flag.go +++ b/Common/i18n/messages/flag.go @@ -70,6 +70,10 @@ var FlagMessages = map[string]map[string]string{ LangZH: "本地扫描模式", LangEN: "Local scan mode", }, + "flag_alive_only": { + LangZH: "仅进行存活探测", + LangEN: "Alive detection only", + }, "flag_username": { LangZH: "用户名", LangEN: "Username", diff --git a/Common/i18n/messages/parse.go b/Common/i18n/messages/parse.go index 667b194..ed2a5f0 100644 --- a/Common/i18n/messages/parse.go +++ b/Common/i18n/messages/parse.go @@ -280,4 +280,8 @@ var ParseMessages = map[string]map[string]string{ LangZH: "无效的目标地址格式: %s", LangEN: "Invalid target address format: %s", }, + "parse_error_no_hosts": { + LangZH: "解析后没有找到有效的目标主机", + LangEN: "No valid target hosts found after parsing", + }, } \ No newline at end of file diff --git a/Common/i18n/messages/scan.go b/Common/i18n/messages/scan.go index 00830f9..8aea94a 100644 --- a/Common/i18n/messages/scan.go +++ b/Common/i18n/messages/scan.go @@ -14,6 +14,10 @@ var ScanMessages = map[string]map[string]string{ LangZH: "已选择服务扫描模式", LangEN: "Service scan mode selected", }, + "scan_mode_alive_selected": { + LangZH: "已选择存活探测模式", + LangEN: "Alive detection mode selected", + }, "scan_mode_local_selected": { LangZH: "已选择本地扫描模式", LangEN: "Local scan mode selected", @@ -154,6 +158,58 @@ var ScanMessages = map[string]map[string]string{ LangZH: "存活主机数量: %d", LangEN: "Alive hosts count: %d", }, + "scan_strategy_alive_name": { + LangZH: "存活探测", + LangEN: "Alive Detection", + }, + "scan_strategy_alive_desc": { + LangZH: "快速探测主机存活状态", + LangEN: "Fast detection of host alive status", + }, + "scan_alive_start": { + LangZH: "开始存活探测", + LangEN: "Starting alive detection", + }, + "scan_alive_single_target": { + LangZH: "目标主机: %s", + LangEN: "Target host: %s", + }, + "scan_alive_multiple_targets": { + LangZH: "目标主机数量: %d (示例: %s)", + LangEN: "Target hosts count: %d (example: %s)", + }, + "scan_alive_summary_title": { + LangZH: "存活探测结果摘要", + LangEN: "Alive Detection Summary", + }, + "scan_alive_total_hosts": { + LangZH: "总主机数: %d", + LangEN: "Total hosts: %d", + }, + "scan_alive_hosts_found": { + LangZH: "存活主机: %d", + LangEN: "Alive hosts: %d", + }, + "scan_alive_dead_hosts": { + LangZH: "死亡主机: %d", + LangEN: "Dead hosts: %d", + }, + "scan_alive_success_rate": { + LangZH: "存活率: %.2f%%", + LangEN: "Success rate: %.2f%%", + }, + "scan_alive_duration": { + LangZH: "扫描耗时: %v", + LangEN: "Scan duration: %v", + }, + "scan_alive_hosts_list": { + LangZH: "存活主机列表:", + LangEN: "Alive hosts list:", + }, + "target_alive": { + LangZH: "存活主机: %s (%s)", + LangEN: "Alive host: %s (%s)", + }, // ========================= 进度条消息 ========================= "progress_scanning_description": { diff --git a/Core/AliveScanner.go b/Core/AliveScanner.go new file mode 100644 index 0000000..4c206dc --- /dev/null +++ b/Core/AliveScanner.go @@ -0,0 +1,160 @@ +package core + +import ( + "fmt" + "github.com/shadow1ng/fscan/common" + "github.com/shadow1ng/fscan/common/i18n" + "github.com/shadow1ng/fscan/common/parsers" + "strings" + "sync" + "time" +) + +/* +AliveScanner.go - 存活探测扫描器 + +专门用于主机存活探测,仅执行ICMP/Ping检测, +快速识别网络中的存活主机,不进行端口扫描。 +*/ + +// AliveScanStrategy 存活探测扫描策略 +type AliveScanStrategy struct { + *BaseScanStrategy + startTime time.Time + stats AliveStats +} + +// AliveStats 存活探测统计信息 +type AliveStats struct { + TotalHosts int // 总主机数 + AliveHosts int // 存活主机数 + DeadHosts int // 死亡主机数 + ScanDuration time.Duration // 扫描耗时 + SuccessRate float64 // 成功率 +} + +// NewAliveScanStrategy 创建新的存活探测扫描策略 +func NewAliveScanStrategy() *AliveScanStrategy { + return &AliveScanStrategy{ + BaseScanStrategy: NewBaseScanStrategy("存活探测", FilterNone), + startTime: time.Now(), + } +} + +// Name 返回策略名称 +func (s *AliveScanStrategy) Name() string { + return i18n.GetText("scan_strategy_alive_name") +} + +// Description 返回策略描述 +func (s *AliveScanStrategy) Description() string { + return i18n.GetText("scan_strategy_alive_desc") +} + +// Execute 执行存活探测扫描策略 +func (s *AliveScanStrategy) Execute(info common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) { + // 验证扫描目标 + if info.Host == "" { + common.LogError(i18n.GetText("parse_error_target_empty")) + return + } + + // 输出存活探测开始信息 + common.LogBase(i18n.GetText("scan_alive_start")) + + // 执行存活探测 + s.performAliveScan(info) + + // 输出统计信息 + s.outputStats() +} + +// performAliveScan 执行存活探测 +func (s *AliveScanStrategy) performAliveScan(info common.HostInfo) { + // 解析目标主机 + hosts, err := parsers.ParseIP(info.Host, common.HostsFile, common.ExcludeHosts) + if err != nil { + common.LogError(i18n.GetText("parse_error_target_failed", err)) + return + } + + if len(hosts) == 0 { + common.LogError(i18n.GetText("parse_error_no_hosts")) + return + } + + // 初始化统计信息 + s.stats.TotalHosts = len(hosts) + s.stats.AliveHosts = 0 + s.stats.DeadHosts = 0 + + // 显示扫描信息 + if len(hosts) == 1 { + common.LogBase(i18n.GetText("scan_alive_single_target", hosts[0])) + } else { + common.LogBase(i18n.GetText("scan_alive_multiple_targets", len(hosts), hosts[0])) + } + + // 清空之前的存活主机记录 + AliveHosts = nil + for k := range ExistHosts { + delete(ExistHosts, k) + } + + // 执行存活检测 + aliveList := CheckLive(hosts, false) // 使用ICMP探测 + + // 更新统计信息 + s.stats.AliveHosts = len(aliveList) + s.stats.DeadHosts = s.stats.TotalHosts - s.stats.AliveHosts + s.stats.ScanDuration = time.Since(s.startTime) + + if s.stats.TotalHosts > 0 { + s.stats.SuccessRate = float64(s.stats.AliveHosts) / float64(s.stats.TotalHosts) * 100 + } +} + +// outputStats 输出详细统计信息 +func (s *AliveScanStrategy) outputStats() { + // 输出分隔线 + common.LogBase("=" + strings.Repeat("=", 60)) + + // 输出扫描结果摘要 + common.LogBase(i18n.GetText("scan_alive_summary_title")) + + // 基础统计 + common.LogBase(i18n.GetText("scan_alive_total_hosts", s.stats.TotalHosts)) + common.LogBase(i18n.GetText("scan_alive_hosts_found", s.stats.AliveHosts)) + common.LogBase(i18n.GetText("scan_alive_dead_hosts", s.stats.DeadHosts)) + common.LogBase(i18n.GetText("scan_alive_success_rate", s.stats.SuccessRate)) + common.LogBase(i18n.GetText("scan_alive_duration", s.stats.ScanDuration.Round(time.Millisecond))) + + // 如果有存活主机,显示详细列表 + if s.stats.AliveHosts > 0 { + common.LogBase("") + common.LogBase(i18n.GetText("scan_alive_hosts_list")) + + for i, host := range AliveHosts { + common.LogSuccess(fmt.Sprintf(" [%d] %s", i+1, host)) + } + } + + // 输出分隔线 + common.LogBase("=" + strings.Repeat("=", 60)) +} + +// PrepareTargets 存活探测不需要准备扫描目标 +func (s *AliveScanStrategy) PrepareTargets(info common.HostInfo) []common.HostInfo { + // 存活探测不需要返回目标列表,因为它不进行后续扫描 + return nil +} + +// GetPlugins 存活探测不使用插件 +func (s *AliveScanStrategy) GetPlugins() ([]string, bool) { + return []string{}, false +} + +// IsPluginApplicable 存活探测不适用任何插件 +func (s *AliveScanStrategy) IsPluginApplicable(plugin common.ScanPlugin, targetPort int, isCustomMode bool) bool { + return false +} \ No newline at end of file diff --git a/Core/Scanner.go b/Core/Scanner.go index 7ea2ae7..8ced7f9 100644 --- a/Core/Scanner.go +++ b/Core/Scanner.go @@ -24,6 +24,9 @@ type ScanStrategy interface { // selectStrategy 根据扫描配置选择适当的扫描策略 func selectStrategy(info common.HostInfo) ScanStrategy { switch { + case common.AliveOnly: + common.LogBase(i18n.GetText("scan_mode_alive_selected")) + return NewAliveScanStrategy() case common.LocalMode: common.LogBase(i18n.GetText("scan_mode_local_selected")) return NewLocalScanStrategy()