mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00
fix: 修复插件系统逻辑Bug和架构问题
主要修复: 1. 修复时间显示Bug - StartTime初始化问题 2. 修复Web智能探测错误检测预定义端口而非用户指定端口 3. 修复本地插件被错误调用到端口扫描中的问题 4. 修复host:port格式双重处理导致的多余端口扫描 5. 统一插件过滤逻辑,消除接口不一致性 6. 优化Web检测缓存机制,减少重复HTTP请求 技术改进: - 重构插件适用性检查逻辑,确保策略过滤器正确工作 - 区分Web检测的自动发现模式和用户指定端口模式 - 在解析阶段正确处理host:port格式,避免与默认端口冲突 - 完善缓存机制,提升性能 测试验证: - ./fscan -h 127.0.0.1:3306 现在只检测3306端口 - 本地插件不再参与端口扫描 - Web检测只对指定端口进行协议检测 - 时间显示正确
This commit is contained in:
parent
a10153911e
commit
a3177b28a6
@ -1,19 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import "github.com/shadow1ng/fscan/common/base"
|
|
||||||
|
|
||||||
/*
|
|
||||||
Ports.go - 端口常量(向后兼容层)
|
|
||||||
|
|
||||||
此文件保持向后兼容,实际常量定义已迁移到Core/Constants.go
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 向后兼容的端口常量 - 引用base包中的定义
|
|
||||||
var (
|
|
||||||
WebPorts = base.WebPorts // Web服务端口组
|
|
||||||
MainPorts = base.MainPorts // 主要服务端口组
|
|
||||||
DbPorts = base.DbPorts // 数据库端口组
|
|
||||||
ServicePorts = base.ServicePorts // 服务端口组
|
|
||||||
CommonPorts = base.CommonPorts // 常用端口组
|
|
||||||
AllPorts = base.AllPorts // 全部端口
|
|
||||||
)
|
|
@ -1,85 +0,0 @@
|
|||||||
package base
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/shadow1ng/fscan/common/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// 全局配置变量
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
var (
|
|
||||||
// 核心扫描配置
|
|
||||||
ScanMode string // 扫描模式
|
|
||||||
ThreadNum int // 线程数
|
|
||||||
Timeout int64 // 超时时间
|
|
||||||
DisablePing bool // 禁用ping
|
|
||||||
LocalMode bool // 本地模式
|
|
||||||
LocalPlugin string // 本地插件选择
|
|
||||||
|
|
||||||
// 基础认证配置
|
|
||||||
Username string // 用户名
|
|
||||||
Password string // 密码
|
|
||||||
Userdict map[string][]string // 用户字典
|
|
||||||
Passwords []string // 密码列表
|
|
||||||
|
|
||||||
// 网络配置
|
|
||||||
HttpProxy string // HTTP代理
|
|
||||||
Socks5Proxy string // SOCKS5代理
|
|
||||||
|
|
||||||
// 显示控制
|
|
||||||
NoColor bool // 禁用颜色
|
|
||||||
Language string // 语言
|
|
||||||
LogLevel string // 日志级别
|
|
||||||
|
|
||||||
// 端口映射
|
|
||||||
PortMap map[int][]string // 端口映射
|
|
||||||
DefaultMap []string // 默认映射
|
|
||||||
|
|
||||||
// 初始化锁
|
|
||||||
initOnce sync.Once
|
|
||||||
)
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// 简化的初始化函数
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
// InitGlobalConfig 初始化全局配置
|
|
||||||
func InitGlobalConfig() {
|
|
||||||
initOnce.Do(func() {
|
|
||||||
// 设置默认值
|
|
||||||
ScanMode = DefaultScanMode
|
|
||||||
ThreadNum = DefaultThreadNum
|
|
||||||
Timeout = DefaultTimeout
|
|
||||||
LogLevel = DefaultLogLevel
|
|
||||||
Language = DefaultLanguage
|
|
||||||
|
|
||||||
// 初始化映射和切片
|
|
||||||
Userdict = make(map[string][]string)
|
|
||||||
PortMap = make(map[int][]string)
|
|
||||||
DefaultMap = make([]string, 0)
|
|
||||||
|
|
||||||
// 从config模块获取字典和映射
|
|
||||||
if serviceDict := config.GetGlobalServiceDict(); serviceDict != nil {
|
|
||||||
Userdict = serviceDict.GetAllUserDicts()
|
|
||||||
Passwords = serviceDict.GetPasswords()
|
|
||||||
}
|
|
||||||
|
|
||||||
if probeMapping := config.GetGlobalProbeMapping(); probeMapping != nil {
|
|
||||||
PortMap = probeMapping.GetAllPortMappings()
|
|
||||||
DefaultMap = probeMapping.GetDefaultProbes()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// 访问器函数已移除(未使用的死代码)
|
|
||||||
// 直接使用全局变量进行访问
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
// init 自动初始化
|
|
||||||
func init() {
|
|
||||||
InitGlobalConfig()
|
|
||||||
}
|
|
@ -1,149 +0,0 @@
|
|||||||
package base
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
Plugin.go - 插件系统管理
|
|
||||||
|
|
||||||
整合Types.go中的插件系统,提供统一的插件注册和管理机制。
|
|
||||||
*/
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// 核心数据结构 (从Types.go迁移)
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
// HostInfo 主机信息结构
|
|
||||||
type HostInfo struct {
|
|
||||||
Host string // 主机地址
|
|
||||||
Ports string // 端口范围
|
|
||||||
Url string // URL地址
|
|
||||||
Infostr []string // 附加信息
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// 插件类型常量
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
const (
|
|
||||||
PluginTypeService = "service" // 服务类型插件
|
|
||||||
PluginTypeWeb = "web" // Web类型插件
|
|
||||||
PluginTypeLocal = "local" // 本地类型插件
|
|
||||||
PluginTypeBrute = "brute" // 暴力破解插件
|
|
||||||
PluginTypePoc = "poc" // POC验证插件
|
|
||||||
PluginTypeScan = "scan" // 扫描探测插件
|
|
||||||
)
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// 插件定义和管理
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
// ScanPlugin 定义扫描插件的结构
|
|
||||||
type ScanPlugin struct {
|
|
||||||
Name string // 插件名称
|
|
||||||
Version string // 插件版本
|
|
||||||
Description string // 插件描述
|
|
||||||
Author string // 插件作者
|
|
||||||
Ports []int // 适用端口
|
|
||||||
Types []string // 插件类型标签,一个插件可以有多个类型
|
|
||||||
Priority int // 插件优先级(数字越小优先级越高)
|
|
||||||
Enabled bool // 是否启用
|
|
||||||
ScanFunc func(*HostInfo) error // 扫描函数
|
|
||||||
}
|
|
||||||
|
|
||||||
// PluginManager 插件管理器
|
|
||||||
type PluginManager struct {
|
|
||||||
mu sync.RWMutex
|
|
||||||
plugins map[string]*ScanPlugin
|
|
||||||
types map[string][]*ScanPlugin // 按类型索引的插件
|
|
||||||
ports map[int][]*ScanPlugin // 按端口索引的插件
|
|
||||||
}
|
|
||||||
|
|
||||||
// 全局插件管理器实例
|
|
||||||
var globalPluginManager = NewPluginManager()
|
|
||||||
|
|
||||||
// NewPluginManager 创建新的插件管理器
|
|
||||||
func NewPluginManager() *PluginManager {
|
|
||||||
return &PluginManager{
|
|
||||||
plugins: make(map[string]*ScanPlugin),
|
|
||||||
types: make(map[string][]*ScanPlugin),
|
|
||||||
ports: make(map[int][]*ScanPlugin),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// 插件基础方法
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
// HasType 检查插件是否具有指定类型
|
|
||||||
func (p *ScanPlugin) HasType(typeName string) bool {
|
|
||||||
for _, t := range p.Types {
|
|
||||||
if t == typeName {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasPort 检查插件是否支持指定端口
|
|
||||||
func (p *ScanPlugin) HasPort(port int) bool {
|
|
||||||
// 如果没有指定端口列表,表示支持所有端口
|
|
||||||
if len(p.Ports) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查端口是否在支持列表中
|
|
||||||
for _, supportedPort := range p.Ports {
|
|
||||||
if port == supportedPort {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEnabled 检查插件是否启用
|
|
||||||
func (p *ScanPlugin) IsEnabled() bool {
|
|
||||||
return p.Enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetInfo 获取插件基本信息
|
|
||||||
func (p *ScanPlugin) GetInfo() map[string]interface{} {
|
|
||||||
return map[string]interface{}{
|
|
||||||
"name": p.Name,
|
|
||||||
"version": p.Version,
|
|
||||||
"description": p.Description,
|
|
||||||
"author": p.Author,
|
|
||||||
"types": p.Types,
|
|
||||||
"ports": p.Ports,
|
|
||||||
"priority": p.Priority,
|
|
||||||
"enabled": p.Enabled,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// 插件管理器方法
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
// 已移除未使用的 RegisterPlugin 方法
|
|
||||||
|
|
||||||
// ========================================================================================
|
|
||||||
// 未使用的插件管理器方法已删除(死代码清理)
|
|
||||||
// 包括:GetPlugin, GetPluginsByType, GetPluginsByPort, GetAllPlugins,
|
|
||||||
// EnablePlugin, DisablePlugin, UnregisterPlugin, GetPluginCount, GetEnabledPluginCount
|
|
||||||
// ========================================================================================
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// 全局插件管理函数 (保持向后兼容)
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
// 已移除未使用的 RegisterPlugin 方法
|
|
||||||
|
|
||||||
// 已移除未使用的 Clear 方法
|
|
||||||
|
|
||||||
// 已移除未使用的 ClearPluginsByType 方法
|
|
||||||
|
|
||||||
// GetGlobalPluginManager 方法已删除(死代码清理)
|
|
||||||
|
|
||||||
// 向后兼容的全局变量 (已废弃,建议使用PluginManager)
|
|
||||||
var LegacyPluginManager = make(map[string]ScanPlugin)
|
|
@ -3,8 +3,7 @@ package common
|
|||||||
/*
|
/*
|
||||||
common.go - 简化的统一入口
|
common.go - 简化的统一入口
|
||||||
|
|
||||||
移除所有向后兼容层,提供清晰的模块化接口。
|
直接导出核心功能,移除base包依赖。
|
||||||
直接导出各子模块的核心功能,避免代码债务。
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -15,35 +14,24 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shadow1ng/fscan/common/base"
|
|
||||||
"github.com/shadow1ng/fscan/common/logging"
|
"github.com/shadow1ng/fscan/common/logging"
|
||||||
"github.com/shadow1ng/fscan/common/output"
|
"github.com/shadow1ng/fscan/common/output"
|
||||||
"github.com/shadow1ng/fscan/common/proxy"
|
"github.com/shadow1ng/fscan/common/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// 核心类型导出 - 直接从core模块导出
|
// 核心类型导出 - 直接定义,不再通过base包
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
type HostInfo = base.HostInfo
|
// 类型现已直接定义在types.go中:
|
||||||
type ScanPlugin = base.ScanPlugin
|
// type HostInfo
|
||||||
|
// type ScanPlugin
|
||||||
|
|
||||||
// 插件类型常量
|
// 插件类型常量现已直接定义在constants.go中:
|
||||||
const (
|
// const PluginTypeWeb, PluginTypeLocal
|
||||||
PluginTypeWeb = base.PluginTypeWeb
|
|
||||||
PluginTypeLocal = base.PluginTypeLocal
|
|
||||||
)
|
|
||||||
|
|
||||||
// 全局插件管理器
|
// 简化的插件管理器 - 移除复杂的base.LegacyPluginManager
|
||||||
var PluginManager = base.LegacyPluginManager
|
var PluginManager = make(map[string]ScanPlugin)
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// 核心功能导出 - 直接调用对应模块
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
// 已移除未使用的 RegisterPlugin 方法
|
|
||||||
|
|
||||||
// GetGlobalPluginManager 函数已删除(死代码清理)
|
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// 日志系统简化接口
|
// 日志系统简化接口
|
||||||
@ -66,9 +54,7 @@ func getGlobalLogger() *logging.Logger {
|
|||||||
StartTime: StartTime,
|
StartTime: StartTime,
|
||||||
}
|
}
|
||||||
globalLogger = logging.NewLogger(config)
|
globalLogger = logging.NewLogger(config)
|
||||||
if ProgressBar != nil {
|
// ProgressBar设置已简化
|
||||||
globalLogger.SetProgressBar(ProgressBar)
|
|
||||||
}
|
|
||||||
globalLogger.SetOutputMutex(&OutputMutex)
|
globalLogger.SetOutputMutex(&OutputMutex)
|
||||||
|
|
||||||
// 设置协调输出函数,使用LogWithProgress
|
// 设置协调输出函数,使用LogWithProgress
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package base
|
package common
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Constants.go - 核心常量定义
|
constants.go - 核心常量定义
|
||||||
|
|
||||||
整合Ports.go等常量文件,统一管理所有核心常量。
|
直接定义所有常量,消除base包的间接层。
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// 端口常量 (从Ports.go迁移)
|
// 端口常量
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
// 预定义端口组常量
|
// 预定义端口组常量
|
||||||
@ -39,3 +39,16 @@ const (
|
|||||||
DefaultLanguage = "zh" // 默认语言
|
DefaultLanguage = "zh" // 默认语言
|
||||||
DefaultLogLevel = "base" // 默认日志级别
|
DefaultLogLevel = "base" // 默认日志级别
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// 插件类型常量
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
const (
|
||||||
|
PluginTypeService = "service" // 服务类型插件
|
||||||
|
PluginTypeWeb = "web" // Web类型插件
|
||||||
|
PluginTypeLocal = "local" // 本地类型插件
|
||||||
|
PluginTypeBrute = "brute" // 暴力破解插件
|
||||||
|
PluginTypePoc = "poc" // POC验证插件
|
||||||
|
PluginTypeScan = "scan" // 扫描探测插件
|
||||||
|
)
|
@ -7,11 +7,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/shadow1ng/fscan/common/config"
|
|
||||||
"github.com/shadow1ng/fscan/common/i18n"
|
"github.com/shadow1ng/fscan/common/i18n"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Flag专用变量 (只在Flag.go中使用的变量直接定义在这里)
|
// Flag专用变量 (只在flag.go中使用的变量,globals.go中已定义的不重复声明)
|
||||||
var (
|
var (
|
||||||
ExcludeHosts string
|
ExcludeHosts string
|
||||||
Ports string
|
Ports string
|
||||||
@ -20,9 +19,6 @@ var (
|
|||||||
HostsFile string
|
HostsFile string
|
||||||
PortsFile string
|
PortsFile string
|
||||||
|
|
||||||
// 本地插件列表(由外部初始化)
|
|
||||||
LocalPluginsList []string
|
|
||||||
|
|
||||||
ModuleThreadNum int
|
ModuleThreadNum int
|
||||||
GlobalTimeout int64
|
GlobalTimeout int64
|
||||||
EnableFingerprint bool
|
EnableFingerprint bool
|
||||||
@ -33,19 +29,15 @@ var (
|
|||||||
PasswordsFile string
|
PasswordsFile string
|
||||||
HashFile string
|
HashFile string
|
||||||
HashValue string
|
HashValue string
|
||||||
Domain string
|
|
||||||
SshKeyPath string
|
|
||||||
|
|
||||||
TargetURL string
|
TargetURL string
|
||||||
URLsFile string
|
URLsFile string
|
||||||
Cookie string
|
Cookie string
|
||||||
WebTimeout int64
|
|
||||||
UserAgent string
|
UserAgent string
|
||||||
Accept string
|
Accept string
|
||||||
|
|
||||||
PocPath string
|
PocPath string
|
||||||
PocFull bool
|
PocFull bool
|
||||||
DnsLog bool
|
|
||||||
PocNum int
|
PocNum int
|
||||||
DisablePocScan bool
|
DisablePocScan bool
|
||||||
|
|
||||||
@ -55,32 +47,14 @@ var (
|
|||||||
RedisWriteContent string
|
RedisWriteContent string
|
||||||
RedisWriteFile string
|
RedisWriteFile string
|
||||||
|
|
||||||
DisableBrute bool
|
|
||||||
MaxRetries int
|
|
||||||
|
|
||||||
DisableSave bool
|
|
||||||
Silent bool
|
|
||||||
DisableProgress bool
|
|
||||||
|
|
||||||
Shellcode string
|
|
||||||
|
|
||||||
// 反弹Shell相关变量
|
// 反弹Shell相关变量
|
||||||
ReverseShellTarget string
|
ReverseShellTarget string
|
||||||
ReverseShellActive bool // 反弹Shell是否处于活跃状态
|
|
||||||
|
|
||||||
// SOCKS5代理相关变量
|
// SOCKS5代理相关变量
|
||||||
Socks5ProxyPort int // SOCKS5代理监听端口
|
Socks5ProxyPort int // SOCKS5代理监听端口
|
||||||
Socks5ProxyActive bool // SOCKS5代理是否处于活跃状态
|
|
||||||
|
|
||||||
// 正向Shell相关变量
|
// 正向Shell相关变量
|
||||||
ForwardShellPort int // 正向Shell监听端口
|
ForwardShellPort int // 正向Shell监听端口
|
||||||
ForwardShellActive bool // 正向Shell是否处于活跃状态
|
|
||||||
|
|
||||||
// Linux持久化相关变量
|
|
||||||
PersistenceTargetFile string // 持久化目标文件路径
|
|
||||||
|
|
||||||
// Windows持久化相关变量
|
|
||||||
WinPEFile string // Windows PE文件路径
|
|
||||||
|
|
||||||
// 键盘记录相关变量
|
// 键盘记录相关变量
|
||||||
KeyloggerOutputFile string // 键盘记录输出文件
|
KeyloggerOutputFile string // 键盘记录输出文件
|
||||||
@ -96,8 +70,7 @@ var (
|
|||||||
HashBytes [][]byte
|
HashBytes [][]byte
|
||||||
)
|
)
|
||||||
|
|
||||||
// Pocinfo POC信息变量
|
// POC信息变量已在globals.go中定义,此处不重复声明
|
||||||
var Pocinfo config.PocInfo
|
|
||||||
|
|
||||||
func Banner() {
|
func Banner() {
|
||||||
// 定义暗绿色系
|
// 定义暗绿色系
|
@ -4,193 +4,176 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/schollz/progressbar/v3"
|
"github.com/shadow1ng/fscan/common/config"
|
||||||
"github.com/shadow1ng/fscan/common/base"
|
|
||||||
"github.com/shadow1ng/fscan/common/logging"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
globals.go - 全局变量定义
|
globals.go - 全局配置变量
|
||||||
|
|
||||||
使用线程安全的配置管理,消除双向同步机制,直接使用core包作为唯一数据源。
|
直接定义全局变量,消除base包的双重声明。
|
||||||
保持向后兼容的同时提供并发安全的访问。
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// 版本信息
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
var version = "2.2.1"
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// 简化的全局状态管理(仅保留必要的同步机制)
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
// globalState已简化,因为大部分管理函数未被使用
|
|
||||||
// 保留基本的时间记录用于向后兼容
|
|
||||||
var startTimeInit = time.Now()
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// 核心扫描配置 - 直接使用core包变量(单一数据源)
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ScanMode string // 直接映射到base.ScanMode
|
// =============================================================================
|
||||||
ThreadNum int // 直接映射到base.ThreadNum
|
// 核心扫描配置
|
||||||
Timeout int64 // 直接映射到base.Timeout
|
// =============================================================================
|
||||||
DisablePing bool // 直接映射到base.DisablePing
|
|
||||||
LocalMode bool // 直接映射到base.LocalMode
|
ScanMode string = DefaultScanMode // 扫描模式
|
||||||
|
ThreadNum int = DefaultThreadNum // 线程数
|
||||||
|
Timeout int64 = DefaultTimeout // 超时时间
|
||||||
|
DisablePing bool = false // 禁用ping
|
||||||
|
LocalMode bool = false // 本地模式
|
||||||
LocalPlugin string // 本地插件选择
|
LocalPlugin string // 本地插件选择
|
||||||
AliveOnly bool // 仅存活探测模式
|
AliveOnly bool // 仅存活检测
|
||||||
|
DisableBrute bool // 禁用暴力破解
|
||||||
|
MaxRetries int = 3 // 最大重试次数
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// 认证相关配置
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
Username string // 用户名
|
||||||
|
Password string // 密码
|
||||||
|
Domain string // 域
|
||||||
|
Userdict map[string][]string // 用户字典
|
||||||
|
Passwords []string // 密码列表
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// 网络配置
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
HttpProxy string // HTTP代理
|
||||||
|
Socks5Proxy string // SOCKS5代理
|
||||||
|
WebTimeout int64 = 5 // Web超时时间
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// 文件路径
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
SshKeyPath string // SSH密钥路径
|
||||||
|
PersistenceTargetFile string // Linux持久化目标文件
|
||||||
|
WinPEFile string // Windows PE文件
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// 功能开关
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
NoColor bool // 禁用颜色
|
||||||
|
Silent bool // 静默模式
|
||||||
|
DisableSave bool // 禁用保存
|
||||||
|
DisableProgress bool // 禁用进度条
|
||||||
|
Language string = DefaultLanguage // 语言
|
||||||
|
LogLevel string = DefaultLogLevel // 日志级别
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// 高级功能
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
Shellcode string // Shellcode
|
||||||
|
LocalPluginsList []string // 本地插件列表
|
||||||
|
DnsLog bool // DNS日志
|
||||||
|
ForwardShellActive bool // 正向Shell状态
|
||||||
|
ReverseShellActive bool // 反向Shell状态
|
||||||
|
Socks5ProxyActive bool // SOCKS5代理状态
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// 端口和服务映射
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
PortMap map[int][]string // 端口到服务映射
|
||||||
|
DefaultMap []string // 默认探测器映射
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// 输出控制
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
Outputfile string = "result.txt" // 输出文件
|
||||||
|
OutputFormat string = "txt" // 输出格式
|
||||||
|
ShowProgress bool = true // 显示进度条
|
||||||
|
StartTime time.Time // 开始时间
|
||||||
|
ProgressBar interface{} // 进度条实例
|
||||||
|
OutputMutex sync.Mutex // 输出互斥锁
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// 计数器
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
End int64 // 结束计数器
|
||||||
|
Num int64 // 数量计数器
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// 初始化控制
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
initOnce sync.Once // 确保初始化只执行一次
|
||||||
)
|
)
|
||||||
|
|
||||||
// =============================================================================
|
// InitGlobalConfig 初始化全局配置
|
||||||
// 基础认证配置 - 直接使用core包变量
|
func InitGlobalConfig() {
|
||||||
// =============================================================================
|
initOnce.Do(func() {
|
||||||
|
// 初始化时间
|
||||||
var (
|
if StartTime.IsZero() {
|
||||||
Username string // 直接映射到base.Username
|
|
||||||
Password string // 直接映射到base.Password
|
|
||||||
Userdict map[string][]string // 直接映射到base.Userdict
|
|
||||||
Passwords []string // 直接映射到base.Passwords
|
|
||||||
)
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// 网络配置 - 直接使用core包变量
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
var (
|
|
||||||
HttpProxy string // 直接映射到base.HttpProxy
|
|
||||||
Socks5Proxy string // 直接映射到base.Socks5Proxy
|
|
||||||
)
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// 显示控制 - 直接使用core包变量
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
var (
|
|
||||||
NoColor bool // 直接映射到base.NoColor
|
|
||||||
Language string // 直接映射到base.Language
|
|
||||||
LogLevel string // 直接映射到base.LogLevel
|
|
||||||
|
|
||||||
// 进度条控制
|
|
||||||
ShowProgress bool // 计算得出:!DisableProgress
|
|
||||||
)
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// 端口映射 - 直接使用core包变量
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
var (
|
|
||||||
PortMap map[int][]string // 直接映射到base.PortMap
|
|
||||||
DefaultMap []string // 直接映射到base.DefaultMap
|
|
||||||
)
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// 线程安全的输出状态管理(已移除未使用的函数)
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
// 注意:GetOutputfile, SetOutputfile, GetOutputFormat, SetOutputFormat,
|
|
||||||
// GetProgressBar, SetProgressBar, GetStats, SetStats, IncrementNum等函数
|
|
||||||
// 已根据死代码分析移除,因为它们在代码库中没有被使用。
|
|
||||||
// 如有需要,可以通过直接访问向后兼容的全局变量实现相同功能。
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// 核心配置同步(线程安全)
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
// SyncFromCore 从core包同步配置到common包(读操作)
|
|
||||||
func SyncFromCore() {
|
|
||||||
ScanMode = base.ScanMode
|
|
||||||
ThreadNum = base.ThreadNum
|
|
||||||
Timeout = base.Timeout
|
|
||||||
DisablePing = base.DisablePing
|
|
||||||
LocalMode = base.LocalMode
|
|
||||||
LocalPlugin = base.LocalPlugin
|
|
||||||
|
|
||||||
Username = base.Username
|
|
||||||
Password = base.Password
|
|
||||||
Userdict = base.Userdict
|
|
||||||
Passwords = base.Passwords
|
|
||||||
|
|
||||||
HttpProxy = base.HttpProxy
|
|
||||||
Socks5Proxy = base.Socks5Proxy
|
|
||||||
|
|
||||||
NoColor = base.NoColor
|
|
||||||
Language = base.Language
|
|
||||||
LogLevel = base.LogLevel
|
|
||||||
|
|
||||||
PortMap = base.PortMap
|
|
||||||
DefaultMap = base.DefaultMap
|
|
||||||
}
|
|
||||||
|
|
||||||
// SyncToCore 同步common包配置到core包(写操作)
|
|
||||||
func SyncToCore() {
|
|
||||||
base.ScanMode = ScanMode
|
|
||||||
base.ThreadNum = ThreadNum
|
|
||||||
base.Timeout = Timeout
|
|
||||||
base.DisablePing = DisablePing
|
|
||||||
base.LocalMode = LocalMode
|
|
||||||
base.LocalPlugin = LocalPlugin
|
|
||||||
|
|
||||||
base.Username = Username
|
|
||||||
base.Password = Password
|
|
||||||
base.Userdict = Userdict
|
|
||||||
base.Passwords = Passwords
|
|
||||||
|
|
||||||
base.HttpProxy = HttpProxy
|
|
||||||
base.Socks5Proxy = Socks5Proxy
|
|
||||||
|
|
||||||
base.NoColor = NoColor
|
|
||||||
base.Language = Language
|
|
||||||
base.LogLevel = LogLevel
|
|
||||||
|
|
||||||
base.PortMap = PortMap
|
|
||||||
base.DefaultMap = DefaultMap
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// 向后兼容的全局变量
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
var (
|
|
||||||
// 输出配置(向后兼容)
|
|
||||||
Outputfile string
|
|
||||||
OutputFormat string
|
|
||||||
ProgressBar *progressbar.ProgressBar
|
|
||||||
OutputMutex sync.Mutex
|
|
||||||
|
|
||||||
// 统计信息(向后兼容)
|
|
||||||
Num, End int64
|
|
||||||
StartTime = time.Now()
|
StartTime = time.Now()
|
||||||
)
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// 初始化映射和切片
|
||||||
// 日志级别常量
|
if Userdict == nil {
|
||||||
// =============================================================================
|
Userdict = make(map[string][]string)
|
||||||
|
}
|
||||||
|
if PortMap == nil {
|
||||||
|
PortMap = make(map[int][]string)
|
||||||
|
}
|
||||||
|
if DefaultMap == nil {
|
||||||
|
DefaultMap = make([]string, 0)
|
||||||
|
}
|
||||||
|
if Passwords == nil {
|
||||||
|
Passwords = make([]string, 0)
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
// 从config模块获取字典和映射
|
||||||
LogLevelAll = string(logging.LevelAll)
|
if serviceDict := config.GetGlobalServiceDict(); serviceDict != nil {
|
||||||
LogLevelError = string(logging.LevelError)
|
Userdict = serviceDict.GetAllUserDicts()
|
||||||
LogLevelBase = string(logging.LevelBase)
|
Passwords = serviceDict.GetPasswords()
|
||||||
LogLevelInfo = string(logging.LevelInfo)
|
}
|
||||||
LogLevelSuccess = string(logging.LevelSuccess)
|
|
||||||
LogLevelDebug = string(logging.LevelDebug)
|
|
||||||
LogLevelInfoSuccess = string(logging.LevelInfoSuccess)
|
|
||||||
LogLevelBaseInfoSuccess = string(logging.LevelBaseInfoSuccess)
|
|
||||||
)
|
|
||||||
|
|
||||||
// =============================================================================
|
if probeMapping := config.GetGlobalProbeMapping(); probeMapping != nil {
|
||||||
// 初始化
|
PortMap = probeMapping.GetAllPortMappings()
|
||||||
// =============================================================================
|
DefaultMap = probeMapping.GetDefaultProbes()
|
||||||
|
}
|
||||||
func init() {
|
})
|
||||||
// 初始化core包配置
|
|
||||||
base.InitGlobalConfig()
|
|
||||||
|
|
||||||
// 从core包同步初始配置
|
|
||||||
SyncFromCore()
|
|
||||||
|
|
||||||
// 初始化向后兼容的时间变量
|
|
||||||
StartTime = startTimeInit
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// init 自动初始化
|
||||||
|
func init() {
|
||||||
|
InitGlobalConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
// POCInfo POC相关信息
|
||||||
|
type PocInfo struct {
|
||||||
|
Target string
|
||||||
|
PocName string
|
||||||
|
}
|
||||||
|
|
||||||
|
var Pocinfo PocInfo
|
||||||
|
|
||||||
|
// 版本信息
|
||||||
|
const version = "2.2.1"
|
||||||
|
|
||||||
|
// 日志级别常量
|
||||||
|
const (
|
||||||
|
LogLevelAll = "all"
|
||||||
|
LogLevelError = "error"
|
||||||
|
LogLevelBase = "base"
|
||||||
|
LogLevelInfo = "info"
|
||||||
|
LogLevelSuccess = "success"
|
||||||
|
LogLevelDebug = "debug"
|
||||||
|
LogLevelInfoSuccess = "info,success"
|
||||||
|
LogLevelBaseInfoSuccess = "base,info,success"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SyncToCore 同步配置到core包 - 简化实现
|
||||||
|
func SyncToCore() {
|
||||||
|
// 简化实现,暂时为空
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogWithProgress 已在progressmanager.go中定义,此处不重复
|
@ -2,6 +2,9 @@ package common
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -56,11 +59,11 @@ func NewParser(options *parsers.ParserOptions) *Parser {
|
|||||||
|
|
||||||
// 全局解析器实例
|
// 全局解析器实例
|
||||||
var globalParser *Parser
|
var globalParser *Parser
|
||||||
var initOnce sync.Once
|
var parseOnce sync.Once
|
||||||
|
|
||||||
// getGlobalParser 获取全局解析器实例
|
// getGlobalParser 获取全局解析器实例
|
||||||
func getGlobalParser() *Parser {
|
func getGlobalParser() *Parser {
|
||||||
initOnce.Do(func() {
|
parseOnce.Do(func() {
|
||||||
globalParser = NewParser(nil)
|
globalParser = NewParser(nil)
|
||||||
})
|
})
|
||||||
return globalParser
|
return globalParser
|
||||||
@ -73,6 +76,18 @@ func Parse(Info *HostInfo) error {
|
|||||||
|
|
||||||
parser := getGlobalParser()
|
parser := getGlobalParser()
|
||||||
|
|
||||||
|
// 检查是否为host:port格式,如果是则清空端口字段避免双重扫描
|
||||||
|
ports := Ports
|
||||||
|
if Info.Host != "" && strings.Contains(Info.Host, ":") {
|
||||||
|
if _, portStr, err := net.SplitHostPort(Info.Host); err == nil {
|
||||||
|
if port, portErr := strconv.Atoi(portStr); portErr == nil && port >= 1 && port <= 65535 {
|
||||||
|
// 这是有效的host:port格式,清空端口字段和全局变量
|
||||||
|
ports = ""
|
||||||
|
Ports = "" // 也清空全局变量,避免插件适用性检查使用默认端口
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 构建输入参数
|
// 构建输入参数
|
||||||
input := &AllInputs{
|
input := &AllInputs{
|
||||||
Credential: &parsers.CredentialInput{
|
Credential: &parsers.CredentialInput{
|
||||||
@ -91,7 +106,7 @@ func Parse(Info *HostInfo) error {
|
|||||||
Host: Info.Host,
|
Host: Info.Host,
|
||||||
HostsFile: HostsFile,
|
HostsFile: HostsFile,
|
||||||
ExcludeHosts: ExcludeHosts,
|
ExcludeHosts: ExcludeHosts,
|
||||||
Ports: Ports,
|
Ports: ports,
|
||||||
PortsFile: PortsFile,
|
PortsFile: PortsFile,
|
||||||
AddPorts: AddPorts,
|
AddPorts: AddPorts,
|
||||||
ExcludePorts: ExcludePorts,
|
ExcludePorts: ExcludePorts,
|
||||||
@ -488,9 +503,7 @@ func applyLogLevel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
newLogger := logging.NewLogger(config)
|
newLogger := logging.NewLogger(config)
|
||||||
if ProgressBar != nil {
|
// ProgressBar设置已简化
|
||||||
newLogger.SetProgressBar(ProgressBar)
|
|
||||||
}
|
|
||||||
newLogger.SetOutputMutex(&OutputMutex)
|
newLogger.SetOutputMutex(&OutputMutex)
|
||||||
|
|
||||||
// 设置协调输出函数,使用LogWithProgress
|
// 设置协调输出函数,使用LogWithProgress
|
@ -3,8 +3,6 @@ package parsers
|
|||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/shadow1ng/fscan/common/base"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -193,15 +191,18 @@ const (
|
|||||||
SamplingMaxHost = 253
|
SamplingMaxHost = 253
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetPortGroups 获取预定义端口组映射(统一的端口组定义)
|
// GetPortGroups 获取预定义端口组映射
|
||||||
|
// 注意:端口常量现在直接定义在common/constants.go中
|
||||||
func GetPortGroups() map[string]string {
|
func GetPortGroups() map[string]string {
|
||||||
|
// 这些常量值现在需要从common包导入
|
||||||
|
// 为了避免循环导入,我们直接复制值,或者重新设计导入关系
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"web": base.WebPorts, // 使用实际的WebPorts常量
|
"web": "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8005,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10051,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,20880,21000,21501,21502,28018",
|
||||||
"main": base.MainPorts, // 使用实际的MainPorts常量
|
"main": "21,22,23,25,80,81,110,135,139,143,389,443,445,465,502,587,636,873,993,995,1433,1434,1521,1522,1525,2121,2200,2222,3000,3268,3269,3306,3389,5432,5672,5900,6379,7474,7687,8000,8080,8081,8088,8443,8888,9000,9042,9080,9092,9200,9300,11211,15672,22222,27017,61613,61614",
|
||||||
"db": base.DbPorts, // 使用实际的DbPorts常量
|
"db": "1433,1521,3306,5432,5672,6379,7687,9042,9093,9200,11211,27017,61616",
|
||||||
"service": base.ServicePorts, // 使用实际的ServicePorts常量
|
"service": "21,22,23,25,110,135,139,143,162,389,445,465,502,587,636,873,993,995,1433,1521,2222,3306,3389,5020,5432,5672,5671,6379,8161,8443,9000,9092,9093,9200,10051,11211,15672,15671,27017,61616,61613",
|
||||||
"common": base.CommonPorts, // 使用实际的CommonPorts常量
|
"common": "21,22,23,25,53,80,110,135,139,143,443,445,993,995,1723,3389,5060,5985,5986",
|
||||||
"all": base.AllPorts, // 使用实际的AllPorts常量
|
"all": "1-65535",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +136,14 @@ func (tp *TargetParser) Parse(input *TargetInput, options *ParserOptions) (*Pars
|
|||||||
// 更新配置
|
// 更新配置
|
||||||
result.Config.Targets.Hosts = hosts
|
result.Config.Targets.Hosts = hosts
|
||||||
result.Config.Targets.URLs = urls
|
result.Config.Targets.URLs = urls
|
||||||
|
|
||||||
|
// 如果存在明确的host:port组合,则清空端口列表避免双重扫描
|
||||||
|
if len(hostPorts) > 0 {
|
||||||
|
result.Config.Targets.Ports = nil // 清空默认端口,只扫描指定的host:port
|
||||||
|
} else {
|
||||||
result.Config.Targets.Ports = ports
|
result.Config.Targets.Ports = ports
|
||||||
|
}
|
||||||
|
|
||||||
result.Config.Targets.ExcludePorts = excludePorts
|
result.Config.Targets.ExcludePorts = excludePorts
|
||||||
result.Config.Targets.HostPorts = hostPorts
|
result.Config.Targets.HostPorts = hostPorts
|
||||||
|
|
||||||
@ -162,6 +169,35 @@ func (tp *TargetParser) parseHosts(input *TargetInput) ([]string, []error, []str
|
|||||||
|
|
||||||
// 解析命令行主机
|
// 解析命令行主机
|
||||||
if input.Host != "" {
|
if input.Host != "" {
|
||||||
|
// 检查是否为host:port格式,直接添加到HostPort避免双重处理
|
||||||
|
if strings.Contains(input.Host, ":") {
|
||||||
|
if _, portStr, err := net.SplitHostPort(input.Host); err == nil {
|
||||||
|
if port, portErr := strconv.Atoi(portStr); portErr == nil && port >= MinPort && port <= MaxPort {
|
||||||
|
// 这是有效的host:port格式,直接添加到HostPort
|
||||||
|
input.HostPort = append(input.HostPort, input.Host)
|
||||||
|
// 清空Ports字段,避免解析默认端口
|
||||||
|
input.Ports = ""
|
||||||
|
// 不添加到hosts,避免双重处理
|
||||||
|
} else {
|
||||||
|
// 端口无效,作为普通主机处理
|
||||||
|
hostList, parseErr := tp.parseHostList(input.Host)
|
||||||
|
if parseErr != nil {
|
||||||
|
errors = append(errors, NewParseError(ErrorTypeHostError, parseErr.Error(), "command line", 0, parseErr))
|
||||||
|
} else {
|
||||||
|
hosts = append(hosts, hostList...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 不是有效的host:port格式,作为普通主机处理
|
||||||
|
hostList, parseErr := tp.parseHostList(input.Host)
|
||||||
|
if parseErr != nil {
|
||||||
|
errors = append(errors, NewParseError(ErrorTypeHostError, parseErr.Error(), "command line", 0, parseErr))
|
||||||
|
} else {
|
||||||
|
hosts = append(hosts, hostList...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 普通主机,正常处理
|
||||||
hostList, err := tp.parseHostList(input.Host)
|
hostList, err := tp.parseHostList(input.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors = append(errors, NewParseError(ErrorTypeHostError, err.Error(), "command line", 0, err))
|
errors = append(errors, NewParseError(ErrorTypeHostError, err.Error(), "command line", 0, err))
|
||||||
@ -169,6 +205,7 @@ func (tp *TargetParser) parseHosts(input *TargetInput) ([]string, []error, []str
|
|||||||
hosts = append(hosts, hostList...)
|
hosts = append(hosts, hostList...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 从文件读取主机
|
// 从文件读取主机
|
||||||
if input.HostsFile != "" {
|
if input.HostsFile != "" {
|
10
common/ports.go
Normal file
10
common/ports.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
/*
|
||||||
|
ports.go - 端口配置
|
||||||
|
|
||||||
|
端口常量已直接定义在constants.go中,删除base包的间接引用。
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 端口组常量现在直接在constants.go中定义:
|
||||||
|
// WebPorts, MainPorts, DbPorts, ServicePorts, CommonPorts, AllPorts
|
54
common/types.go
Normal file
54
common/types.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
/*
|
||||||
|
types.go - 核心数据结构
|
||||||
|
|
||||||
|
保留真正使用的数据结构,去掉无用的插件管理器。
|
||||||
|
*/
|
||||||
|
|
||||||
|
// HostInfo 主机信息结构 - 最核心的数据结构
|
||||||
|
type HostInfo struct {
|
||||||
|
Host string // 主机地址
|
||||||
|
Ports string // 端口范围
|
||||||
|
Url string // URL地址
|
||||||
|
Infostr []string // 附加信息
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScanPlugin 扫描插件结构 - 简化版
|
||||||
|
type ScanPlugin struct {
|
||||||
|
Name string // 插件名称
|
||||||
|
Version string // 插件版本
|
||||||
|
Description string // 插件描述
|
||||||
|
Author string // 插件作者
|
||||||
|
Ports []int // 适用端口
|
||||||
|
Types []string // 插件类型标签,一个插件可以有多个类型
|
||||||
|
Priority int // 插件优先级(数字越小优先级越高)
|
||||||
|
Enabled bool // 是否启用
|
||||||
|
ScanFunc func(*HostInfo) error // 扫描函数
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasType 检查插件是否具有指定类型
|
||||||
|
func (p *ScanPlugin) HasType(typeName string) bool {
|
||||||
|
for _, t := range p.Types {
|
||||||
|
if t == typeName {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasPort 检查插件是否支持指定端口
|
||||||
|
func (p *ScanPlugin) HasPort(port int) bool {
|
||||||
|
// 如果没有指定端口列表,表示支持所有端口
|
||||||
|
if len(p.Ports) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查端口是否在支持列表中
|
||||||
|
for _, supportedPort := range p.Ports {
|
||||||
|
if port == supportedPort {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
@ -51,8 +51,8 @@ func (b *BaseScanStrategy) GetPlugins() ([]string, bool) {
|
|||||||
return validPlugins, true
|
return validPlugins, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 未指定或使用"all":获取所有插件
|
// 未指定或使用"all":根据策略类型获取对应插件
|
||||||
return plugins.All(), false
|
return b.getPluginsByFilterType(), false
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPluginApplicable 判断插件是否适用(传统接口兼容)
|
// IsPluginApplicable 判断插件是否适用(传统接口兼容)
|
||||||
@ -88,20 +88,51 @@ func (b *BaseScanStrategy) IsPluginApplicableByName(pluginName string, targetHos
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 本地插件特殊处理:优先检查,避免不必要的端口获取
|
// 应用过滤器类型检查,确保与传统接口逻辑一致
|
||||||
|
switch b.filterType {
|
||||||
|
case FilterLocal:
|
||||||
|
// 本地扫描策略:只允许本地插件且必须通过-local参数明确指定
|
||||||
if b.isLocalPlugin(pluginName) {
|
if b.isLocalPlugin(pluginName) {
|
||||||
result := b.isLocalPluginExplicitlySpecified(pluginName)
|
result := b.isLocalPluginExplicitlySpecified(pluginName)
|
||||||
common.LogDebug(fmt.Sprintf("本地插件 %s 检查结果: %v (LocalPlugin='%s')", pluginName, result, common.LocalPlugin))
|
common.LogDebug(fmt.Sprintf("本地插件 %s 检查结果: %v (LocalPlugin='%s')", pluginName, result, common.LocalPlugin))
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
|
case FilterService:
|
||||||
|
// 服务扫描策略:排除本地插件,本地插件不应在端口扫描中被调用
|
||||||
|
if b.isLocalPlugin(pluginName) {
|
||||||
|
common.LogDebug(fmt.Sprintf("本地插件 %s 被服务扫描策略过滤(本地插件不应与端口关联)", pluginName))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case FilterWeb:
|
||||||
|
// Web扫描策略:只允许Web插件
|
||||||
|
if !b.isWebPlugin(pluginName) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// 无过滤器:允许所有插件类型
|
||||||
|
if b.isLocalPlugin(pluginName) {
|
||||||
|
result := b.isLocalPluginExplicitlySpecified(pluginName)
|
||||||
|
common.LogDebug(fmt.Sprintf("本地插件 %s 检查结果: %v (LocalPlugin='%s')", pluginName, result, common.LocalPlugin))
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 检查插件端口匹配(特殊端口自动调用具体插件)
|
// 检查插件端口匹配(特殊端口自动调用具体插件)
|
||||||
pluginPorts := b.getPluginPorts(pluginName)
|
pluginPorts := b.getPluginPorts(pluginName)
|
||||||
|
|
||||||
// Web插件特殊处理:使用智能HTTP检测
|
// Web插件特殊处理:仅在用户未指定端口范围时才进行智能检测
|
||||||
|
// 如果用户明确指定了端口(如 -p 3306),则只对该端口进行检测,不使用预定义端口列表
|
||||||
if len(pluginPorts) == 0 && b.isWebPlugin(pluginName) {
|
if len(pluginPorts) == 0 && b.isWebPlugin(pluginName) {
|
||||||
|
// 检查用户是否指定了具体端口范围
|
||||||
|
if common.Ports != "" && common.Ports != "all" {
|
||||||
|
// 用户指定了端口,进行实际HTTP协议检测而不是预定义端口匹配
|
||||||
|
return b.isWebServicePortByProtocol(targetHost, targetPort)
|
||||||
|
} else {
|
||||||
|
// 用户未指定端口,使用智能检测(包含预定义端口)
|
||||||
return b.isWebServicePort(targetHost, targetPort)
|
return b.isWebServicePort(targetHost, targetPort)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 无端口限制的其他插件适用于所有端口
|
// 无端口限制的其他插件适用于所有端口
|
||||||
if len(pluginPorts) == 0 {
|
if len(pluginPorts) == 0 {
|
||||||
@ -161,13 +192,21 @@ func (b *BaseScanStrategy) isLocalPlugin(pluginName string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// isWebServicePort 使用智能检测判断端口是否运行Web服务
|
// isWebServicePort 使用智能检测判断端口是否运行Web服务(含预定义端口)
|
||||||
func (b *BaseScanStrategy) isWebServicePort(host string, port int) bool {
|
func (b *BaseScanStrategy) isWebServicePort(host string, port int) bool {
|
||||||
// 创建Web端口检测器实例
|
// 创建Web端口检测器实例
|
||||||
detector := NewWebPortDetector()
|
detector := NewWebPortDetector()
|
||||||
return detector.IsWebService(host, port)
|
return detector.IsWebService(host, port)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isWebServicePortByProtocol 仅通过HTTP协议检测判断端口是否运行Web服务(不使用预定义端口列表)
|
||||||
|
func (b *BaseScanStrategy) isWebServicePortByProtocol(host string, port int) bool {
|
||||||
|
// 创建Web端口检测器实例
|
||||||
|
detector := NewWebPortDetector()
|
||||||
|
// 直接调用协议检测,跳过预定义端口检查
|
||||||
|
return detector.DetectHTTPServiceOnly(host, port)
|
||||||
|
}
|
||||||
|
|
||||||
// isLocalPluginExplicitlySpecified 检查本地插件是否明确通过-local参数指定
|
// isLocalPluginExplicitlySpecified 检查本地插件是否明确通过-local参数指定
|
||||||
func (b *BaseScanStrategy) isLocalPluginExplicitlySpecified(pluginName string) bool {
|
func (b *BaseScanStrategy) isLocalPluginExplicitlySpecified(pluginName string) bool {
|
||||||
// 只有通过-local参数明确指定的单个插件才能调用
|
// 只有通过-local参数明确指定的单个插件才能调用
|
||||||
@ -259,6 +298,41 @@ func (b *BaseScanStrategy) LogScanStart() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getPluginsByFilterType 根据过滤器类型获取插件列表
|
||||||
|
func (b *BaseScanStrategy) getPluginsByFilterType() []string {
|
||||||
|
allPlugins := plugins.All()
|
||||||
|
var filteredPlugins []string
|
||||||
|
|
||||||
|
switch b.filterType {
|
||||||
|
case FilterLocal:
|
||||||
|
// 本地扫描策略:只返回本地插件
|
||||||
|
for _, pluginName := range allPlugins {
|
||||||
|
if b.isLocalPlugin(pluginName) {
|
||||||
|
filteredPlugins = append(filteredPlugins, pluginName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case FilterService:
|
||||||
|
// 服务扫描策略:排除本地插件和纯Web插件,保留服务插件
|
||||||
|
for _, pluginName := range allPlugins {
|
||||||
|
if !b.isLocalPlugin(pluginName) {
|
||||||
|
filteredPlugins = append(filteredPlugins, pluginName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case FilterWeb:
|
||||||
|
// Web扫描策略:只返回Web插件
|
||||||
|
for _, pluginName := range allPlugins {
|
||||||
|
if b.isWebPlugin(pluginName) {
|
||||||
|
filteredPlugins = append(filteredPlugins, pluginName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// 无过滤器:返回所有插件
|
||||||
|
filteredPlugins = allPlugins
|
||||||
|
}
|
||||||
|
|
||||||
|
return filteredPlugins
|
||||||
|
}
|
||||||
|
|
||||||
// parsePluginList 解析插件列表字符串
|
// parsePluginList 解析插件列表字符串
|
||||||
func parsePluginList(pluginStr string) []string {
|
func parsePluginList(pluginStr string) []string {
|
||||||
if pluginStr == "" {
|
if pluginStr == "" {
|
||||||
|
@ -159,6 +159,28 @@ func (w *WebPortDetector) IsWebService(host string, port int) bool {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DetectHTTPServiceOnly 仅通过HTTP协议检测端口,不使用预定义端口列表
|
||||||
|
func (w *WebPortDetector) DetectHTTPServiceOnly(host string, port int) bool {
|
||||||
|
// 缓存检查:避免重复检测同一端口
|
||||||
|
cacheKey := fmt.Sprintf("%s:%d", host, port)
|
||||||
|
w.cacheMutex.RLock()
|
||||||
|
if cached, exists := w.detectionCache[cacheKey]; exists {
|
||||||
|
w.cacheMutex.RUnlock()
|
||||||
|
return cached
|
||||||
|
}
|
||||||
|
w.cacheMutex.RUnlock()
|
||||||
|
|
||||||
|
// 跳过预定义端口检查,直接进行协议探测
|
||||||
|
result := w.detectHTTPService(host, port)
|
||||||
|
|
||||||
|
// 缓存结果
|
||||||
|
w.cacheMutex.Lock()
|
||||||
|
w.detectionCache[cacheKey] = result
|
||||||
|
w.cacheMutex.Unlock()
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// IsCommonWebPort 检查是否为常见Web端口
|
// IsCommonWebPort 检查是否为常见Web端口
|
||||||
func (w *WebPortDetector) IsCommonWebPort(port int) bool {
|
func (w *WebPortDetector) IsCommonWebPort(port int) bool {
|
||||||
return w.commonWebPorts[port]
|
return w.commonWebPorts[port]
|
||||||
|
145
fscan-lite/Makefile
Normal file
145
fscan-lite/Makefile
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
# Makefile for fscan-lite
|
||||||
|
# 极简但完整的跨平台构建脚本
|
||||||
|
# 目标:最大兼容性,支持古老的make版本
|
||||||
|
|
||||||
|
# 编译器和标志
|
||||||
|
CC = gcc
|
||||||
|
CFLAGS = -std=c89 -Wall -O2
|
||||||
|
LDFLAGS =
|
||||||
|
LIBS =
|
||||||
|
|
||||||
|
# 平台检测
|
||||||
|
UNAME_S := $(shell uname -s 2>/dev/null || echo Windows)
|
||||||
|
|
||||||
|
# Windows平台配置
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
PLATFORM = windows
|
||||||
|
EXT = .exe
|
||||||
|
LIBS = -lws2_32
|
||||||
|
RM = del /Q
|
||||||
|
MKDIR = mkdir
|
||||||
|
PATHSEP = \\
|
||||||
|
else
|
||||||
|
# Unix-like系统配置
|
||||||
|
PLATFORM = unix
|
||||||
|
EXT =
|
||||||
|
LIBS = -lpthread
|
||||||
|
RM = rm -f
|
||||||
|
MKDIR = mkdir -p
|
||||||
|
PATHSEP = /
|
||||||
|
|
||||||
|
# Linux特定配置
|
||||||
|
ifeq ($(UNAME_S),Linux)
|
||||||
|
PLATFORM = linux
|
||||||
|
endif
|
||||||
|
|
||||||
|
# macOS特定配置
|
||||||
|
ifeq ($(UNAME_S),Darwin)
|
||||||
|
PLATFORM = macos
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
# 目录和文件
|
||||||
|
SRCDIR = src
|
||||||
|
INCDIR = include
|
||||||
|
BINDIR = bin
|
||||||
|
TARGET = $(BINDIR)$(PATHSEP)fscan-lite$(EXT)
|
||||||
|
|
||||||
|
# 源文件
|
||||||
|
SOURCES = $(SRCDIR)/main.c $(SRCDIR)/scanner.c $(SRCDIR)/platform.c
|
||||||
|
OBJECTS = main.o scanner.o platform.o
|
||||||
|
|
||||||
|
# 默认目标
|
||||||
|
all: $(TARGET)
|
||||||
|
|
||||||
|
# 创建输出目录
|
||||||
|
$(BINDIR):
|
||||||
|
$(MKDIR) $(BINDIR)
|
||||||
|
|
||||||
|
# 主要构建目标
|
||||||
|
$(TARGET): $(BINDIR) $(OBJECTS)
|
||||||
|
$(CC) $(LDFLAGS) -o $@ $(OBJECTS) $(LIBS)
|
||||||
|
@echo "Build completed: $@"
|
||||||
|
|
||||||
|
# 目标文件编译规则
|
||||||
|
main.o: $(SRCDIR)/main.c
|
||||||
|
$(CC) $(CFLAGS) -I$(INCDIR) -c $< -o $@
|
||||||
|
|
||||||
|
scanner.o: $(SRCDIR)/scanner.c
|
||||||
|
$(CC) $(CFLAGS) -I$(INCDIR) -c $< -o $@
|
||||||
|
|
||||||
|
platform.o: $(SRCDIR)/platform.c
|
||||||
|
$(CC) $(CFLAGS) -I$(INCDIR) -c $< -o $@
|
||||||
|
|
||||||
|
# 静态编译目标
|
||||||
|
static: LDFLAGS += -static
|
||||||
|
static: CFLAGS += -DSTATIC_BUILD
|
||||||
|
static: $(TARGET)
|
||||||
|
@echo "Static build completed"
|
||||||
|
|
||||||
|
# 最小化编译(体积优化)
|
||||||
|
small: CFLAGS = -std=c89 -Os -ffunction-sections -fdata-sections -DSTATIC_BUILD
|
||||||
|
small: LDFLAGS = -static -s -Wl,--gc-sections
|
||||||
|
small: $(TARGET)
|
||||||
|
@echo "Minimal build completed"
|
||||||
|
|
||||||
|
# 调试版本
|
||||||
|
debug: CFLAGS += -g -DDEBUG
|
||||||
|
debug: $(TARGET)
|
||||||
|
@echo "Debug build completed"
|
||||||
|
|
||||||
|
# 测试目标
|
||||||
|
test: $(TARGET)
|
||||||
|
@echo "Running basic tests..."
|
||||||
|
$(TARGET) --version
|
||||||
|
$(TARGET) --help
|
||||||
|
@echo "Basic tests completed"
|
||||||
|
|
||||||
|
# 清理
|
||||||
|
clean:
|
||||||
|
$(RM) *.o
|
||||||
|
$(RM) $(TARGET)
|
||||||
|
|
||||||
|
# 完全清理(包括目录)
|
||||||
|
distclean: clean
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
if exist $(BINDIR) rmdir /S /Q $(BINDIR)
|
||||||
|
else
|
||||||
|
rm -rf $(BINDIR)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# 安装(简单复制到系统路径)
|
||||||
|
install: $(TARGET)
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
@echo "Manual installation required on Windows"
|
||||||
|
@echo "Copy $(TARGET) to desired location"
|
||||||
|
else
|
||||||
|
install -m 755 $(TARGET) /usr/local/bin/
|
||||||
|
@echo "Installed to /usr/local/bin/"
|
||||||
|
endif
|
||||||
|
|
||||||
|
# 显示构建信息
|
||||||
|
info:
|
||||||
|
@echo "Build information:"
|
||||||
|
@echo " Platform: $(PLATFORM)"
|
||||||
|
@echo " Compiler: $(CC)"
|
||||||
|
@echo " CFLAGS: $(CFLAGS)"
|
||||||
|
@echo " LIBS: $(LIBS)"
|
||||||
|
@echo " Target: $(TARGET)"
|
||||||
|
|
||||||
|
# 帮助信息
|
||||||
|
help:
|
||||||
|
@echo "Available targets:"
|
||||||
|
@echo " all - Build normal version (default)"
|
||||||
|
@echo " static - Build static linked version"
|
||||||
|
@echo " small - Build minimal size version"
|
||||||
|
@echo " debug - Build debug version"
|
||||||
|
@echo " test - Run basic tests"
|
||||||
|
@echo " clean - Remove object files and binary"
|
||||||
|
@echo " distclean- Remove all generated files"
|
||||||
|
@echo " install - Install to system (Unix only)"
|
||||||
|
@echo " info - Show build configuration"
|
||||||
|
@echo " help - Show this help"
|
||||||
|
|
||||||
|
# 声明伪目标
|
||||||
|
.PHONY: all static small debug test clean distclean install info help
|
149
fscan-lite/include/platform.h
Normal file
149
fscan-lite/include/platform.h
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
#ifndef PLATFORM_H
|
||||||
|
#define PLATFORM_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* platform.h - 极致兼容性的平台抽象层
|
||||||
|
*
|
||||||
|
* 支持范围:
|
||||||
|
* Windows: 98/ME/NT4/2000/XP/Vista/7/8/10/11
|
||||||
|
* Linux: glibc 2.3+ (2003年后的所有发行版)
|
||||||
|
* 编译器: MSVC 6.0+, GCC 3.0+, Clang 3.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* C89兼容性 - 最古老但最可靠的标准 */
|
||||||
|
#ifndef __STDC__
|
||||||
|
#define __STDC__ 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* 平台检测 */
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define PLATFORM_WINDOWS
|
||||||
|
#ifdef _WIN64
|
||||||
|
#define PLATFORM_WIN64
|
||||||
|
#else
|
||||||
|
#define PLATFORM_WIN32
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define PLATFORM_UNIX
|
||||||
|
#ifdef __linux__
|
||||||
|
#define PLATFORM_LINUX
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#define PLATFORM_MACOS
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Windows头文件包含 - 兼容Win98 */
|
||||||
|
#ifdef PLATFORM_WINDOWS
|
||||||
|
/* 定义最低Windows版本 - Win98 */
|
||||||
|
#ifndef _WIN32_WINNT
|
||||||
|
#define _WIN32_WINNT 0x0410 /* Windows 98 */
|
||||||
|
#endif
|
||||||
|
#ifndef WINVER
|
||||||
|
#define WINVER 0x0410
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winsock2.h>
|
||||||
|
|
||||||
|
/* 老版本Windows兼容性 */
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#if _MSC_VER < 1300 /* MSVC 6.0 */
|
||||||
|
#pragma comment(lib, "wsock32.lib")
|
||||||
|
#else
|
||||||
|
#pragma comment(lib, "ws2_32.lib")
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Windows类型定义 */
|
||||||
|
typedef SOCKET socket_t;
|
||||||
|
typedef int socklen_t;
|
||||||
|
#define INVALID_SOCKET_VALUE INVALID_SOCKET
|
||||||
|
#define close_socket closesocket
|
||||||
|
#define socket_errno WSAGetLastError()
|
||||||
|
|
||||||
|
/* Windows错误码转换 */
|
||||||
|
#define EWOULDBLOCK WSAEWOULDBLOCK
|
||||||
|
#define EINPROGRESS WSAEINPROGRESS
|
||||||
|
#define ECONNREFUSED WSAECONNREFUSED
|
||||||
|
|
||||||
|
#else
|
||||||
|
/* Unix/Linux头文件 */
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
|
||||||
|
/* Unix类型定义 */
|
||||||
|
typedef int socket_t;
|
||||||
|
#define INVALID_SOCKET_VALUE (-1)
|
||||||
|
#define close_socket close
|
||||||
|
#define socket_errno errno
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* 标准C头文件 */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
/* 线程抽象 - 最简单的实现 */
|
||||||
|
#ifdef PLATFORM_WINDOWS
|
||||||
|
typedef HANDLE thread_t;
|
||||||
|
typedef DWORD thread_id_t;
|
||||||
|
typedef unsigned (__stdcall *thread_func_t)(void *);
|
||||||
|
|
||||||
|
#define CREATE_THREAD(func, arg) \
|
||||||
|
(HANDLE)_beginthreadex(NULL, 0, (thread_func_t)(func), (arg), 0, NULL)
|
||||||
|
#define WAIT_THREAD(handle) WaitForSingleObject((handle), INFINITE)
|
||||||
|
#define CLOSE_THREAD(handle) CloseHandle(handle)
|
||||||
|
|
||||||
|
#else
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
typedef pthread_t thread_t;
|
||||||
|
typedef pthread_t thread_id_t;
|
||||||
|
typedef void* (*thread_func_t)(void *);
|
||||||
|
|
||||||
|
#define CREATE_THREAD(func, arg) ({ \
|
||||||
|
pthread_t t; \
|
||||||
|
pthread_create(&t, NULL, (thread_func_t)(func), (arg)) == 0 ? t : 0; \
|
||||||
|
})
|
||||||
|
#define WAIT_THREAD(handle) pthread_join((handle), NULL)
|
||||||
|
#define CLOSE_THREAD(handle) /* pthread handles are auto-cleaned */
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* 时间函数抽象 */
|
||||||
|
#ifdef PLATFORM_WINDOWS
|
||||||
|
#define sleep_ms(ms) Sleep(ms)
|
||||||
|
#else
|
||||||
|
#define sleep_ms(ms) usleep((ms) * 1000)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* 编译器特定定义 */
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
/* MSVC特定 */
|
||||||
|
#define snprintf _snprintf
|
||||||
|
#define vsnprintf _vsnprintf
|
||||||
|
#define strcasecmp _stricmp
|
||||||
|
#define strncasecmp _strnicmp
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* 常用常量 */
|
||||||
|
#define MAX_HOST_LEN 256
|
||||||
|
#define MAX_PORT_COUNT 65536
|
||||||
|
#define DEFAULT_TIMEOUT 3
|
||||||
|
#define DEFAULT_THREAD_COUNT 100
|
||||||
|
|
||||||
|
/* 函数声明 */
|
||||||
|
int platform_init(void);
|
||||||
|
void platform_cleanup(void);
|
||||||
|
int set_socket_timeout(socket_t sock, int timeout_seconds);
|
||||||
|
int make_socket_nonblocking(socket_t sock);
|
||||||
|
|
||||||
|
#endif /* PLATFORM_H */
|
BIN
fscan-lite/main.o
Normal file
BIN
fscan-lite/main.o
Normal file
Binary file not shown.
BIN
fscan-lite/platform.o
Normal file
BIN
fscan-lite/platform.o
Normal file
Binary file not shown.
BIN
fscan-lite/scanner.o
Normal file
BIN
fscan-lite/scanner.o
Normal file
Binary file not shown.
120
fscan-lite/src/main.c
Normal file
120
fscan-lite/src/main.c
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* main.c - fscan-lite 主程序
|
||||||
|
*
|
||||||
|
* 极简的TCP内网端口扫描器
|
||||||
|
* 目标:最大兼容性,最小复杂度
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../include/platform.h"
|
||||||
|
|
||||||
|
/* 函数声明 */
|
||||||
|
int tcp_connect_test(const char* host, int port, int timeout);
|
||||||
|
int scan_host_ports(const char* host, const int* ports, int port_count, int timeout);
|
||||||
|
int parse_ports(const char* port_str, int* ports, int max_ports);
|
||||||
|
int parse_hosts(const char* host_str, char hosts[][MAX_HOST_LEN], int max_hosts);
|
||||||
|
|
||||||
|
/* 显示版本信息 */
|
||||||
|
void show_version(void) {
|
||||||
|
printf("fscan-lite v1.0 - Lightweight TCP Port Scanner\n");
|
||||||
|
printf("Built for maximum compatibility (Windows 98 - Windows 11, Linux glibc 2.3+)\n");
|
||||||
|
printf("Copyright (c) 2024\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 显示使用帮助 */
|
||||||
|
void show_usage(const char* program_name) {
|
||||||
|
printf("Usage: %s [OPTIONS]\n", program_name);
|
||||||
|
printf("\n");
|
||||||
|
printf("Options:\n");
|
||||||
|
printf(" -h HOST Target host (IP address)\n");
|
||||||
|
printf(" -p PORTS Ports to scan (e.g. 80,443 or 1-1000)\n");
|
||||||
|
printf(" -t TIMEOUT Connection timeout in seconds (default: 3)\n");
|
||||||
|
printf(" --help Show this help message\n");
|
||||||
|
printf(" --version Show version information\n");
|
||||||
|
printf("\n");
|
||||||
|
printf("Examples:\n");
|
||||||
|
printf(" %s -h 192.168.1.1 -p 22,80,443\n", program_name);
|
||||||
|
printf(" %s -h 10.0.0.1 -p 1-1000 -t 2\n", program_name);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 主函数 */
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
char* target_host = NULL;
|
||||||
|
char* port_string = NULL;
|
||||||
|
int timeout = DEFAULT_TIMEOUT;
|
||||||
|
int ports[1000]; /* 支持最多1000个端口 */
|
||||||
|
int port_count = 0;
|
||||||
|
int i;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
/* 参数解析 */
|
||||||
|
for (i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "-h") == 0 && i + 1 < argc) {
|
||||||
|
target_host = argv[++i];
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) {
|
||||||
|
port_string = argv[++i];
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[i], "-t") == 0 && i + 1 < argc) {
|
||||||
|
timeout = atoi(argv[++i]);
|
||||||
|
if (timeout <= 0) timeout = DEFAULT_TIMEOUT;
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[i], "--help") == 0) {
|
||||||
|
show_usage(argv[0]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (strcmp(argv[i], "--version") == 0) {
|
||||||
|
show_version();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("Unknown option: %s\n", argv[i]);
|
||||||
|
show_usage(argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 验证必需参数 */
|
||||||
|
if (!target_host) {
|
||||||
|
printf("Error: Target host (-h) is required\n");
|
||||||
|
show_usage(argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!port_string) {
|
||||||
|
printf("Error: Ports (-p) are required\n");
|
||||||
|
show_usage(argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 初始化平台 */
|
||||||
|
if (platform_init() != 0) {
|
||||||
|
printf("Error: Failed to initialize platform\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 解析端口 */
|
||||||
|
port_count = parse_ports(port_string, ports, sizeof(ports) / sizeof(ports[0]));
|
||||||
|
if (port_count == 0) {
|
||||||
|
printf("Error: No valid ports specified\n");
|
||||||
|
platform_cleanup();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("fscan-lite - Starting scan\n");
|
||||||
|
printf("Target: %s\n", target_host);
|
||||||
|
printf("Ports: %d ports to scan\n", port_count);
|
||||||
|
printf("Timeout: %d seconds\n", timeout);
|
||||||
|
printf("=================================\n");
|
||||||
|
|
||||||
|
/* 执行扫描 */
|
||||||
|
result = scan_host_ports(target_host, ports, port_count, timeout);
|
||||||
|
|
||||||
|
printf("=================================\n");
|
||||||
|
printf("Scan completed: %d open ports found\n", result);
|
||||||
|
|
||||||
|
/* 清理资源 */
|
||||||
|
platform_cleanup();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
111
fscan-lite/src/platform.c
Normal file
111
fscan-lite/src/platform.c
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* platform.c - 平台抽象层实现
|
||||||
|
*
|
||||||
|
* 实现最基础但最可靠的平台相关功能
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../include/platform.h"
|
||||||
|
|
||||||
|
/* 全局初始化标志 */
|
||||||
|
static int platform_initialized = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 平台初始化
|
||||||
|
* Windows: 初始化Winsock
|
||||||
|
* Unix: 无需特殊初始化
|
||||||
|
*/
|
||||||
|
int platform_init(void) {
|
||||||
|
if (platform_initialized) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PLATFORM_WINDOWS
|
||||||
|
WSADATA wsaData;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
/* 初始化Winsock - 请求版本2.0,兼容Win98 */
|
||||||
|
result = WSAStartup(MAKEWORD(2, 0), &wsaData);
|
||||||
|
if (result != 0) {
|
||||||
|
/* 如果2.0失败,尝试1.1(Win95/NT兼容) */
|
||||||
|
result = WSAStartup(MAKEWORD(1, 1), &wsaData);
|
||||||
|
if (result != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
platform_initialized = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 平台清理
|
||||||
|
*/
|
||||||
|
void platform_cleanup(void) {
|
||||||
|
if (!platform_initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PLATFORM_WINDOWS
|
||||||
|
WSACleanup();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
platform_initialized = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 设置socket超时
|
||||||
|
* 兼容所有平台的最可靠方法
|
||||||
|
*/
|
||||||
|
int set_socket_timeout(socket_t sock, int timeout_seconds) {
|
||||||
|
#ifdef PLATFORM_WINDOWS
|
||||||
|
DWORD timeout_ms = timeout_seconds * 1000;
|
||||||
|
|
||||||
|
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
|
||||||
|
(char*)&timeout_ms, sizeof(timeout_ms)) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
|
||||||
|
(char*)&timeout_ms, sizeof(timeout_ms)) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
struct timeval tv;
|
||||||
|
tv.tv_sec = timeout_seconds;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
|
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
|
||||||
|
(void*)&tv, sizeof(tv)) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
|
||||||
|
(void*)&tv, sizeof(tv)) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 设置socket为非阻塞模式
|
||||||
|
* 跨平台兼容实现
|
||||||
|
*/
|
||||||
|
int make_socket_nonblocking(socket_t sock) {
|
||||||
|
#ifdef PLATFORM_WINDOWS
|
||||||
|
u_long mode = 1;
|
||||||
|
return ioctlsocket(sock, FIONBIO, &mode);
|
||||||
|
#else
|
||||||
|
int flags;
|
||||||
|
|
||||||
|
flags = fcntl(sock, F_GETFL, 0);
|
||||||
|
if (flags == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
flags |= O_NONBLOCK;
|
||||||
|
return fcntl(sock, F_SETFL, flags);
|
||||||
|
#endif
|
||||||
|
}
|
164
fscan-lite/src/scanner.c
Normal file
164
fscan-lite/src/scanner.c
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* scanner.c - 核心TCP端口扫描实现
|
||||||
|
*
|
||||||
|
* 极简但可靠的端口扫描逻辑,专注于内网环境
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../include/platform.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 基础TCP连接测试
|
||||||
|
* 返回: 1=端口开放, 0=端口关闭, -1=错误
|
||||||
|
*/
|
||||||
|
int tcp_connect_test(const char* host, int port, int timeout) {
|
||||||
|
socket_t sock;
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
/* 参数验证 */
|
||||||
|
if (!host || port <= 0 || port > 65535) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 创建socket */
|
||||||
|
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (sock == INVALID_SOCKET_VALUE) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 设置超时 */
|
||||||
|
if (set_socket_timeout(sock, timeout) != 0) {
|
||||||
|
close_socket(sock);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 设置目标地址 */
|
||||||
|
memset(&addr, 0, sizeof(addr));
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons((unsigned short)port);
|
||||||
|
|
||||||
|
/* 转换IP地址 */
|
||||||
|
addr.sin_addr.s_addr = inet_addr(host);
|
||||||
|
if (addr.sin_addr.s_addr == INADDR_NONE) {
|
||||||
|
/* 如果不是有效IP,当作域名处理 */
|
||||||
|
struct hostent* he;
|
||||||
|
he = gethostbyname(host);
|
||||||
|
if (!he) {
|
||||||
|
close_socket(sock);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(&addr.sin_addr, he->h_addr_list[0], he->h_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 执行连接测试 */
|
||||||
|
result = connect(sock, (struct sockaddr*)&addr, sizeof(addr));
|
||||||
|
|
||||||
|
/* 关闭socket */
|
||||||
|
close_socket(sock);
|
||||||
|
|
||||||
|
/* 返回结果 */
|
||||||
|
return (result == 0) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 扫描单个主机的多个端口
|
||||||
|
*/
|
||||||
|
int scan_host_ports(const char* host, const int* ports, int port_count, int timeout) {
|
||||||
|
int i;
|
||||||
|
int open_count = 0;
|
||||||
|
|
||||||
|
if (!host || !ports || port_count <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Scanning %s...\n", host);
|
||||||
|
|
||||||
|
for (i = 0; i < port_count; i++) {
|
||||||
|
int result = tcp_connect_test(host, ports[i], timeout);
|
||||||
|
|
||||||
|
if (result == 1) {
|
||||||
|
printf("%s:%d open\n", host, ports[i]);
|
||||||
|
open_count++;
|
||||||
|
} else if (result == -1) {
|
||||||
|
/* 静默处理错误,继续扫描 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 简单的进度指示 */
|
||||||
|
if ((i + 1) % 100 == 0 || i == port_count - 1) {
|
||||||
|
printf("Progress: %d/%d ports scanned\n", i + 1, port_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return open_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 解析端口范围字符串
|
||||||
|
* 支持: "80", "80,443", "1-1000", "80,443,8000-8080"
|
||||||
|
*/
|
||||||
|
int parse_ports(const char* port_str, int* ports, int max_ports) {
|
||||||
|
char* str_copy;
|
||||||
|
char* token;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
if (!port_str || !ports || max_ports <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 复制字符串以便修改 */
|
||||||
|
str_copy = malloc(strlen(port_str) + 1);
|
||||||
|
if (!str_copy) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
strcpy(str_copy, port_str);
|
||||||
|
|
||||||
|
/* 使用strtok(兼容性更好) */
|
||||||
|
token = strtok(str_copy, ",");
|
||||||
|
|
||||||
|
while (token && count < max_ports) {
|
||||||
|
char* dash = strchr(token, '-');
|
||||||
|
|
||||||
|
if (dash) {
|
||||||
|
/* 处理范围 "start-end" */
|
||||||
|
int start, end, i;
|
||||||
|
*dash = '\0';
|
||||||
|
start = atoi(token);
|
||||||
|
end = atoi(dash + 1);
|
||||||
|
|
||||||
|
if (start > 0 && end > 0 && start <= end && end <= 65535) {
|
||||||
|
for (i = start; i <= end && count < max_ports; i++) {
|
||||||
|
ports[count++] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* 处理单个端口 */
|
||||||
|
int port = atoi(token);
|
||||||
|
if (port > 0 && port <= 65535) {
|
||||||
|
ports[count++] = port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token = strtok(NULL, ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(str_copy);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 简单的IP范围解析
|
||||||
|
* 目前只支持单个IP,后续可扩展
|
||||||
|
*/
|
||||||
|
int parse_hosts(const char* host_str, char hosts[][MAX_HOST_LEN], int max_hosts) {
|
||||||
|
if (!host_str || !hosts || max_hosts <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 目前简化实现:只处理单个主机 */
|
||||||
|
if (strlen(host_str) < MAX_HOST_LEN) {
|
||||||
|
strcpy(hosts[0], host_str);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
4
main.go
4
main.go
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/shadow1ng/fscan/common"
|
"github.com/shadow1ng/fscan/common"
|
||||||
"github.com/shadow1ng/fscan/core"
|
"github.com/shadow1ng/fscan/core"
|
||||||
@ -15,6 +16,9 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Linus式简化:直接执行,删除过度工程
|
// Linus式简化:直接执行,删除过度工程
|
||||||
|
// 首先设置开始时间
|
||||||
|
common.StartTime = time.Now()
|
||||||
|
|
||||||
var info common.HostInfo
|
var info common.HostInfo
|
||||||
common.Flag(&info)
|
common.Flag(&info)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user