mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-22 03:10:42 +08:00
refactor: 大量重构
This commit is contained in:
+87
-75
@@ -864,93 +864,105 @@ type PocInfo struct {
|
||||
}
|
||||
|
||||
var (
|
||||
// 目标配置
|
||||
Ports string
|
||||
ExcludePorts string // 原NoPorts
|
||||
ExcludeHosts string
|
||||
AddPorts string // 原PortAdd
|
||||
// =========================================================
|
||||
// 扫描目标配置
|
||||
// =========================================================
|
||||
Ports string // 要扫描的端口列表,如"80,443,8080"
|
||||
ExcludePorts string // 要排除的端口列表
|
||||
ExcludeHosts string // 要排除的主机列表
|
||||
AddPorts string // 额外添加的端口列表
|
||||
HostPort []string // 主机:端口格式的目标列表
|
||||
|
||||
// 认证配置
|
||||
Username string
|
||||
Password string
|
||||
Domain string
|
||||
SshKeyPath string // 原SshKey
|
||||
AddUsers string // 原UserAdd
|
||||
AddPasswords string // 原PassAdd
|
||||
// =========================================================
|
||||
// 认证与凭据配置
|
||||
// =========================================================
|
||||
Username string // 用于认证的用户名
|
||||
Password string // 用于认证的密码
|
||||
AddUsers string // 额外添加的用户名列表
|
||||
AddPasswords string // 额外添加的密码列表
|
||||
|
||||
// 扫描配置
|
||||
ScanMode string // 原Scantype
|
||||
ThreadNum int // 原Threads
|
||||
ModuleThreadNum int = 10
|
||||
Timeout int64 = 3
|
||||
GlobalTimeout int64 = 180
|
||||
LiveTop int
|
||||
DisablePing bool // 原NoPing
|
||||
UsePing bool // 原Ping
|
||||
Command string
|
||||
SkipFingerprint bool
|
||||
// 特定服务认证
|
||||
Domain string // Active Directory/SMB域名
|
||||
HashValue string // 用于哈希认证的单个哈希值
|
||||
HashValues []string // 哈希值列表
|
||||
HashBytes [][]byte // 二进制格式的哈希值列表
|
||||
HashFile string // 包含哈希值的文件路径
|
||||
SshKeyPath string // SSH私钥文件路径
|
||||
|
||||
// 文件配置
|
||||
HostsFile string // 原HostFile
|
||||
UsersFile string // 原Userfile
|
||||
PasswordsFile string // 原Passfile
|
||||
HashFile string // 原Hashfile
|
||||
PortsFile string // 原PortFile
|
||||
// =========================================================
|
||||
// 扫描控制配置
|
||||
// =========================================================
|
||||
ScanMode string // 扫描模式或指定的插件列表
|
||||
ThreadNum int // 并发扫描线程数
|
||||
ModuleThreadNum int // 模块内部线程数
|
||||
Timeout int64 // 单个扫描操作超时时间(秒)
|
||||
GlobalTimeout int64 // 整体扫描超时时间(秒)
|
||||
LiveTop int // 显示的存活主机排名数量
|
||||
DisablePing bool // 是否禁用主机存活性检测
|
||||
UsePing bool // 是否使用ICMP Ping检测主机存活
|
||||
EnableFingerprint bool // 是否跳过服务指纹识别
|
||||
LocalMode bool // 是否启用本地信息收集模式
|
||||
|
||||
// Web配置
|
||||
TargetURL string // 原URL
|
||||
URLsFile string // 原UrlFile
|
||||
URLs []string // 原Urls
|
||||
WebTimeout int64 = 5
|
||||
HttpProxy string // 原Proxy
|
||||
Socks5Proxy string
|
||||
// =========================================================
|
||||
// 输入文件配置
|
||||
// =========================================================
|
||||
HostsFile string // 包含目标主机的文件路径
|
||||
UsersFile string // 包含用户名列表的文件路径
|
||||
PasswordsFile string // 包含密码列表的文件路径
|
||||
PortsFile string // 包含端口列表的文件路径
|
||||
|
||||
LocalMode bool // -local 本地模式
|
||||
// =========================================================
|
||||
// Web扫描配置
|
||||
// =========================================================
|
||||
TargetURL string // 单个目标URL
|
||||
URLsFile string // 包含URL列表的文件路径
|
||||
URLs []string // 解析后的URL目标列表
|
||||
WebTimeout int64 // Web请求超时时间(秒),默认5秒
|
||||
HttpProxy string // HTTP代理地址
|
||||
Socks5Proxy string // SOCKS5代理地址
|
||||
|
||||
// =========================================================
|
||||
// POC与漏洞利用配置
|
||||
// =========================================================
|
||||
// POC配置
|
||||
PocPath string
|
||||
Pocinfo PocInfo
|
||||
PocPath string // POC脚本路径
|
||||
Pocinfo PocInfo // POC详细信息结构
|
||||
|
||||
// Redis配置
|
||||
RedisFile string
|
||||
RedisShell string
|
||||
DisableRedis bool // 原Noredistest
|
||||
RedisWritePath string
|
||||
RedisWriteContent string
|
||||
RedisWriteFile string
|
||||
// Redis利用
|
||||
RedisFile string // Redis利用目标文件
|
||||
RedisShell string // Redis反弹Shell命令
|
||||
DisableRedis bool // 是否禁用Redis利用测试
|
||||
RedisWritePath string // Redis文件写入路径
|
||||
RedisWriteContent string // Redis文件写入内容
|
||||
RedisWriteFile string // Redis写入的源文件
|
||||
|
||||
// 爆破配置
|
||||
DisableBrute bool // 原IsBrute
|
||||
//BruteThreads int // 原BruteThread
|
||||
MaxRetries int // 最大重试次数
|
||||
// 其他漏洞利用
|
||||
Shellcode string // 用于MS17010等漏洞利用的Shellcode
|
||||
|
||||
// 其他配置
|
||||
RemotePath string // 原Path
|
||||
HashValue string // 原Hash
|
||||
HashValues []string // 原Hashs
|
||||
HashBytes [][]byte
|
||||
HostPort []string
|
||||
Shellcode string // 原SC
|
||||
EnableWmi bool // 原IsWmi
|
||||
// =========================================================
|
||||
// 暴力破解控制
|
||||
// =========================================================
|
||||
DisableBrute bool // 是否禁用暴力破解模块
|
||||
MaxRetries int // 连接失败最大重试次数
|
||||
|
||||
// 输出配置
|
||||
DisableSave bool // 禁止保存结果
|
||||
Silent bool // 静默模式
|
||||
NoColor bool // 禁用彩色输出
|
||||
JsonFormat bool // JSON格式输出
|
||||
LogLevel string // 日志输出级别
|
||||
ShowProgress bool // 是否显示进度条
|
||||
|
||||
Language string // 语言
|
||||
// =========================================================
|
||||
// 输出与显示配置
|
||||
// =========================================================
|
||||
DisableSave bool // 是否禁止保存扫描结果
|
||||
Silent bool // 是否启用静默模式
|
||||
NoColor bool // 是否禁用彩色输出
|
||||
LogLevel string // 日志输出级别
|
||||
ShowProgress bool // 是否显示进度条
|
||||
ShowScanPlan bool // 是否显示扫描计划详情
|
||||
SlowLogOutput bool // 是否启用慢速日志输出
|
||||
Language string // 界面语言设置
|
||||
)
|
||||
|
||||
var (
|
||||
UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36"
|
||||
Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
|
||||
DnsLog bool
|
||||
PocNum int
|
||||
PocFull bool
|
||||
CeyeDomain string
|
||||
ApiKey string
|
||||
Cookie string
|
||||
UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36"
|
||||
Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
|
||||
DnsLog bool
|
||||
PocNum int
|
||||
PocFull bool
|
||||
Cookie string
|
||||
)
|
||||
|
||||
+107
-41
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"github.com/fatih/color"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -56,40 +55,50 @@ func Banner() {
|
||||
c.Printf(" Fscan Version: %s\n\n", version)
|
||||
}
|
||||
|
||||
// Flag 解析命令行参数并配置扫描选项
|
||||
func Flag(Info *HostInfo) {
|
||||
Banner()
|
||||
|
||||
// 目标配置
|
||||
// ═════════════════════════════════════════════════
|
||||
// 目标配置参数
|
||||
// ═════════════════════════════════════════════════
|
||||
flag.StringVar(&Info.Host, "h", "", GetText("flag_host"))
|
||||
flag.StringVar(&ExcludeHosts, "eh", "", GetText("flag_exclude_hosts"))
|
||||
flag.StringVar(&Ports, "p", MainPorts, GetText("flag_ports"))
|
||||
flag.StringVar(&HostsFile, "hf", "", GetText("flag_hosts_file"))
|
||||
flag.StringVar(&PortsFile, "pf", "", GetText("flag_ports_file"))
|
||||
|
||||
// 认证配置
|
||||
flag.StringVar(&AddUsers, "usera", "", GetText("flag_add_users"))
|
||||
flag.StringVar(&AddPasswords, "pwda", "", GetText("flag_add_passwords"))
|
||||
flag.StringVar(&Username, "user", "", GetText("flag_username"))
|
||||
flag.StringVar(&Password, "pwd", "", GetText("flag_password"))
|
||||
flag.StringVar(&Domain, "domain", "", GetText("flag_domain"))
|
||||
flag.StringVar(&SshKeyPath, "sshkey", "", GetText("flag_ssh_key"))
|
||||
|
||||
// 扫描配置
|
||||
// ═════════════════════════════════════════════════
|
||||
// 扫描控制参数
|
||||
// ═════════════════════════════════════════════════
|
||||
flag.StringVar(&ScanMode, "m", "All", GetText("flag_scan_mode"))
|
||||
flag.IntVar(&ThreadNum, "t", 60, GetText("flag_thread_num"))
|
||||
flag.IntVar(&ThreadNum, "t", 10, GetText("flag_thread_num"))
|
||||
flag.Int64Var(&Timeout, "time", 3, GetText("flag_timeout"))
|
||||
flag.IntVar(&ModuleThreadNum, "mt", 10, GetText("flag_module_thread_num"))
|
||||
flag.Int64Var(&GlobalTimeout, "gt", 180, GetText("flag_global_timeout"))
|
||||
flag.IntVar(&LiveTop, "top", 10, GetText("flag_live_top"))
|
||||
flag.BoolVar(&DisablePing, "np", false, GetText("flag_disable_ping"))
|
||||
flag.BoolVar(&UsePing, "ping", false, GetText("flag_use_ping"))
|
||||
flag.StringVar(&Command, "c", "", GetText("flag_command"))
|
||||
flag.BoolVar(&SkipFingerprint, "skip", false, GetText("flag_skip_fingerprint"))
|
||||
flag.BoolVar(&EnableFingerprint, "fingerprint", false, GetText("flag_enable_fingerprint"))
|
||||
flag.BoolVar(&LocalMode, "local", false, GetText("flag_local_mode"))
|
||||
|
||||
// 文件配置
|
||||
flag.StringVar(&HostsFile, "hf", "", GetText("flag_hosts_file"))
|
||||
// ═════════════════════════════════════════════════
|
||||
// 认证与凭据参数
|
||||
// ═════════════════════════════════════════════════
|
||||
flag.StringVar(&Username, "user", "", GetText("flag_username"))
|
||||
flag.StringVar(&Password, "pwd", "", GetText("flag_password"))
|
||||
flag.StringVar(&AddUsers, "usera", "", GetText("flag_add_users"))
|
||||
flag.StringVar(&AddPasswords, "pwda", "", GetText("flag_add_passwords"))
|
||||
flag.StringVar(&UsersFile, "userf", "", GetText("flag_users_file"))
|
||||
flag.StringVar(&PasswordsFile, "pwdf", "", GetText("flag_passwords_file"))
|
||||
flag.StringVar(&HashFile, "hashf", "", GetText("flag_hash_file"))
|
||||
flag.StringVar(&PortsFile, "portf", "", GetText("flag_ports_file"))
|
||||
flag.StringVar(&HashValue, "hash", "", GetText("flag_hash_value"))
|
||||
flag.StringVar(&Domain, "domain", "", GetText("flag_domain")) // SMB扫描用
|
||||
flag.StringVar(&SshKeyPath, "sshkey", "", GetText("flag_ssh_key")) // SSH扫描用
|
||||
|
||||
// Web配置
|
||||
// ═════════════════════════════════════════════════
|
||||
// Web扫描参数
|
||||
// ═════════════════════════════════════════════════
|
||||
flag.StringVar(&TargetURL, "u", "", GetText("flag_target_url"))
|
||||
flag.StringVar(&URLsFile, "uf", "", GetText("flag_urls_file"))
|
||||
flag.StringVar(&Cookie, "cookie", "", GetText("flag_cookie"))
|
||||
@@ -97,55 +106,112 @@ func Flag(Info *HostInfo) {
|
||||
flag.StringVar(&HttpProxy, "proxy", "", GetText("flag_http_proxy"))
|
||||
flag.StringVar(&Socks5Proxy, "socks5", "", GetText("flag_socks5_proxy"))
|
||||
|
||||
// 本地扫描配置
|
||||
flag.BoolVar(&LocalMode, "local", false, GetText("flag_local_mode"))
|
||||
|
||||
// POC配置
|
||||
// ═════════════════════════════════════════════════
|
||||
// POC测试参数
|
||||
// ═════════════════════════════════════════════════
|
||||
flag.StringVar(&PocPath, "pocpath", "", GetText("flag_poc_path"))
|
||||
flag.StringVar(&Pocinfo.PocName, "pocname", "", GetText("flag_poc_name"))
|
||||
flag.BoolVar(&PocFull, "full", false, GetText("flag_poc_full"))
|
||||
flag.BoolVar(&DnsLog, "dns", false, GetText("flag_dns_log"))
|
||||
flag.IntVar(&PocNum, "num", 20, GetText("flag_poc_num"))
|
||||
|
||||
// Redis利用配置
|
||||
// ═════════════════════════════════════════════════
|
||||
// Redis利用参数
|
||||
// ═════════════════════════════════════════════════
|
||||
flag.StringVar(&RedisFile, "rf", "", GetText("flag_redis_file"))
|
||||
flag.StringVar(&RedisShell, "rs", "", GetText("flag_redis_shell"))
|
||||
flag.BoolVar(&DisableRedis, "noredis", false, GetText("flag_disable_redis"))
|
||||
// Redis任意文件写入配置
|
||||
flag.StringVar(&RedisWritePath, "rwp", "", GetText("flag_redis_write_path"))
|
||||
flag.StringVar(&RedisWriteContent, "rwc", "", GetText("flag_redis_write_content"))
|
||||
flag.StringVar(&RedisWriteFile, "rwf", "", GetText("flag_redis_write_file"))
|
||||
|
||||
// 暴力破解配置
|
||||
// ═════════════════════════════════════════════════
|
||||
// 暴力破解控制参数
|
||||
// ═════════════════════════════════════════════════
|
||||
flag.BoolVar(&DisableBrute, "nobr", false, GetText("flag_disable_brute"))
|
||||
flag.IntVar(&MaxRetries, "retry", 3, GetText("flag_max_retries"))
|
||||
|
||||
// 其他配置
|
||||
flag.StringVar(&RemotePath, "path", "", GetText("flag_remote_path"))
|
||||
flag.StringVar(&HashValue, "hash", "", GetText("flag_hash_value"))
|
||||
flag.StringVar(&Shellcode, "sc", "", GetText("flag_shellcode"))
|
||||
flag.BoolVar(&EnableWmi, "wmi", false, GetText("flag_enable_wmi"))
|
||||
|
||||
// 输出配置
|
||||
// ═════════════════════════════════════════════════
|
||||
// 输出与显示控制参数
|
||||
// ═════════════════════════════════════════════════
|
||||
flag.StringVar(&Outputfile, "o", "result.txt", GetText("flag_output_file"))
|
||||
flag.StringVar(&OutputFormat, "f", "txt", GetText("flag_output_format"))
|
||||
flag.BoolVar(&DisableSave, "no", false, GetText("flag_disable_save"))
|
||||
flag.BoolVar(&Silent, "silent", false, GetText("flag_silent_mode"))
|
||||
flag.BoolVar(&NoColor, "nocolor", false, GetText("flag_no_color"))
|
||||
flag.BoolVar(&JsonFormat, "json", false, GetText("flag_json_format"))
|
||||
flag.StringVar(&LogLevel, "log", LogLevelSuccess, GetText("flag_log_level"))
|
||||
flag.BoolVar(&ShowProgress, "pg", false, GetText("flag_show_progress"))
|
||||
flag.BoolVar(&ShowScanPlan, "sp", false, GetText("flag_show_scan_plan"))
|
||||
flag.BoolVar(&SlowLogOutput, "slow", false, GetText("flag_slow_log_output"))
|
||||
|
||||
// ═════════════════════════════════════════════════
|
||||
// 其他参数
|
||||
// ═════════════════════════════════════════════════
|
||||
flag.StringVar(&Shellcode, "sc", "", GetText("flag_shellcode"))
|
||||
flag.StringVar(&Language, "lang", "zh", GetText("flag_language"))
|
||||
|
||||
envArgsString := os.Getenv("FS_ARGS")
|
||||
if envArgsString != "" && runtime.GOOS != "windows" {
|
||||
envArgs := strings.Split(envArgsString, " ")
|
||||
flag.CommandLine.Parse(envArgs)
|
||||
os.Unsetenv("FS_ARGS")
|
||||
} else {
|
||||
flag.Parse()
|
||||
}
|
||||
// 解析命令行参数
|
||||
parseCommandLineArgs()
|
||||
|
||||
// 设置语言
|
||||
SetLanguage()
|
||||
}
|
||||
|
||||
// parseCommandLineArgs 处理来自环境变量和命令行的参数
|
||||
func parseCommandLineArgs() {
|
||||
// 首先检查环境变量中的参数
|
||||
envArgsString := os.Getenv("FS_ARGS")
|
||||
if envArgsString != "" {
|
||||
// 解析环境变量参数 (跨平台支持)
|
||||
envArgs, err := parseEnvironmentArgs(envArgsString)
|
||||
if err == nil && len(envArgs) > 0 {
|
||||
flag.CommandLine.Parse(envArgs)
|
||||
os.Unsetenv("FS_ARGS") // 使用后清除环境变量
|
||||
return
|
||||
}
|
||||
// 如果环境变量解析失败,继续使用命令行参数
|
||||
}
|
||||
|
||||
// 解析命令行参数
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
// parseEnvironmentArgs 安全地解析环境变量中的参数
|
||||
func parseEnvironmentArgs(argsString string) ([]string, error) {
|
||||
if strings.TrimSpace(argsString) == "" {
|
||||
return nil, fmt.Errorf("empty arguments string")
|
||||
}
|
||||
|
||||
// 使用更安全的参数分割方法
|
||||
var args []string
|
||||
var currentArg strings.Builder
|
||||
inQuote := false
|
||||
quoteChar := ' '
|
||||
|
||||
for _, char := range argsString {
|
||||
switch {
|
||||
case char == '"' || char == '\'':
|
||||
if inQuote && char == quoteChar {
|
||||
inQuote = false
|
||||
} else if !inQuote {
|
||||
inQuote = true
|
||||
quoteChar = char
|
||||
} else {
|
||||
currentArg.WriteRune(char)
|
||||
}
|
||||
case char == ' ' && !inQuote:
|
||||
if currentArg.Len() > 0 {
|
||||
args = append(args, currentArg.String())
|
||||
currentArg.Reset()
|
||||
}
|
||||
default:
|
||||
currentArg.WriteRune(char)
|
||||
}
|
||||
}
|
||||
|
||||
if currentArg.Len() > 0 {
|
||||
args = append(args, currentArg.String())
|
||||
}
|
||||
|
||||
return args, nil
|
||||
}
|
||||
|
||||
+4
-2
@@ -122,8 +122,10 @@ func printLog(entry *LogEntry) {
|
||||
fmt.Println(logMsg)
|
||||
}
|
||||
|
||||
// 等待日志输出完成
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
// 根据慢速输出设置决定是否添加延迟
|
||||
if SlowLogOutput {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
// 重新显示进度条
|
||||
if ProgressBar != nil {
|
||||
|
||||
+313
-133
@@ -10,25 +10,37 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Parse 配置解析的总入口函数
|
||||
// 协调调用各解析子函数,完成完整的配置处理流程
|
||||
func Parse(Info *HostInfo) error {
|
||||
ParseUser()
|
||||
ParsePass(Info)
|
||||
if err := ParseInput(Info); err != nil {
|
||||
return err
|
||||
// 按照依赖顺序解析各类配置
|
||||
if err := ParseUser(); err != nil {
|
||||
return fmt.Errorf("用户名解析错误: %v", err)
|
||||
}
|
||||
|
||||
if err := ParsePass(Info); err != nil {
|
||||
return fmt.Errorf("密码与目标解析错误: %v", err)
|
||||
}
|
||||
|
||||
if err := ParseInput(Info); err != nil {
|
||||
return fmt.Errorf("输入参数解析错误: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseUser 解析用户名配置
|
||||
// 处理直接指定的用户名和从文件加载的用户名,更新全局用户字典
|
||||
func ParseUser() error {
|
||||
// 如果未指定用户名和用户名文件,直接返回
|
||||
// 如果未指定用户名和用户名文件,无需处理
|
||||
if Username == "" && UsersFile == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 收集所有用户名
|
||||
var usernames []string
|
||||
|
||||
// 处理直接指定的用户名列表
|
||||
// 处理命令行参数指定的用户名列表
|
||||
if Username != "" {
|
||||
usernames = strings.Split(Username, ",")
|
||||
LogInfo(GetText("no_username_specified", len(usernames)))
|
||||
@@ -36,25 +48,25 @@ func ParseUser() error {
|
||||
|
||||
// 从文件加载用户名列表
|
||||
if UsersFile != "" {
|
||||
users, err := Readfile(UsersFile)
|
||||
fileUsers, err := ReadFileLines(UsersFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("读取用户名文件失败: %v", err)
|
||||
}
|
||||
|
||||
// 过滤空用户名
|
||||
for _, user := range users {
|
||||
// 添加非空用户名
|
||||
for _, user := range fileUsers {
|
||||
if user != "" {
|
||||
usernames = append(usernames, user)
|
||||
}
|
||||
}
|
||||
LogInfo(GetText("load_usernames_from_file", len(users)))
|
||||
LogInfo(GetText("load_usernames_from_file", len(fileUsers)))
|
||||
}
|
||||
|
||||
// 去重处理
|
||||
usernames = RemoveDuplicate(usernames)
|
||||
LogInfo(GetText("total_usernames", len(usernames)))
|
||||
|
||||
// 更新用户字典
|
||||
// 更新所有字典的用户名列表
|
||||
for name := range Userdict {
|
||||
Userdict[name] = usernames
|
||||
}
|
||||
@@ -62,10 +74,37 @@ func ParseUser() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParsePass 解析密码、哈希值、URL和端口配置
|
||||
// ParsePass 解析密码、URL、主机和端口等目标配置
|
||||
// 处理多种输入源的配置,并更新全局目标信息
|
||||
func ParsePass(Info *HostInfo) error {
|
||||
// 处理直接指定的密码列表
|
||||
// 处理密码配置
|
||||
parsePasswords()
|
||||
|
||||
// 处理哈希值配置
|
||||
parseHashes()
|
||||
|
||||
// 处理URL配置
|
||||
parseURLs()
|
||||
|
||||
// 处理主机配置
|
||||
if err := parseHosts(Info); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 处理端口配置
|
||||
if err := parsePorts(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// parsePasswords 解析密码配置
|
||||
// 处理直接指定的密码和从文件加载的密码
|
||||
func parsePasswords() {
|
||||
var pwdList []string
|
||||
|
||||
// 处理命令行参数指定的密码列表
|
||||
if Password != "" {
|
||||
passes := strings.Split(Password, ",")
|
||||
for _, pass := range passes {
|
||||
@@ -79,10 +118,12 @@ func ParsePass(Info *HostInfo) error {
|
||||
|
||||
// 从文件加载密码列表
|
||||
if PasswordsFile != "" {
|
||||
passes, err := Readfile(PasswordsFile)
|
||||
passes, err := ReadFileLines(PasswordsFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("读取密码文件失败: %v", err)
|
||||
LogError(fmt.Sprintf("读取密码文件失败: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
for _, pass := range passes {
|
||||
if pass != "" {
|
||||
pwdList = append(pwdList, pass)
|
||||
@@ -91,109 +132,152 @@ func ParsePass(Info *HostInfo) error {
|
||||
Passwords = pwdList
|
||||
LogInfo(GetText("load_passwords_from_file", len(passes)))
|
||||
}
|
||||
}
|
||||
|
||||
// parseHashes 解析哈希值配置
|
||||
// 验证并处理哈希文件中的哈希值
|
||||
func parseHashes() {
|
||||
// 处理哈希文件
|
||||
if HashFile != "" {
|
||||
hashes, err := Readfile(HashFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("读取哈希文件失败: %v", err)
|
||||
}
|
||||
|
||||
validCount := 0
|
||||
for _, line := range hashes {
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
if len(line) == 32 {
|
||||
HashValues = append(HashValues, line)
|
||||
validCount++
|
||||
} else {
|
||||
LogError(GetText("invalid_hash", line))
|
||||
}
|
||||
}
|
||||
LogInfo(GetText("load_valid_hashes", validCount))
|
||||
if HashFile == "" {
|
||||
return
|
||||
}
|
||||
|
||||
// 处理直接指定的URL列表
|
||||
hashes, err := ReadFileLines(HashFile)
|
||||
if err != nil {
|
||||
LogError(fmt.Sprintf("读取哈希文件失败: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
validCount := 0
|
||||
for _, line := range hashes {
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
// 验证哈希长度(MD5哈希为32位)
|
||||
if len(line) == 32 {
|
||||
HashValues = append(HashValues, line)
|
||||
validCount++
|
||||
} else {
|
||||
LogError(GetText("invalid_hash", line))
|
||||
}
|
||||
}
|
||||
LogInfo(GetText("load_valid_hashes", validCount))
|
||||
}
|
||||
|
||||
// parseURLs 解析URL目标配置
|
||||
// 处理命令行和文件指定的URL列表,去重后更新全局URL列表
|
||||
func parseURLs() {
|
||||
urlMap := make(map[string]struct{})
|
||||
|
||||
// 处理命令行参数指定的URL列表
|
||||
if TargetURL != "" {
|
||||
urls := strings.Split(TargetURL, ",")
|
||||
tmpUrls := make(map[string]struct{})
|
||||
for _, url := range urls {
|
||||
if url != "" {
|
||||
if _, ok := tmpUrls[url]; !ok {
|
||||
tmpUrls[url] = struct{}{}
|
||||
URLs = append(URLs, url)
|
||||
}
|
||||
urlMap[url] = struct{}{}
|
||||
}
|
||||
}
|
||||
LogInfo(GetText("load_urls", len(URLs)))
|
||||
}
|
||||
|
||||
// 从文件加载URL列表
|
||||
if URLsFile != "" {
|
||||
urls, err := Readfile(URLsFile)
|
||||
urls, err := ReadFileLines(URLsFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("读取URL文件失败: %v", err)
|
||||
LogError(fmt.Sprintf("读取URL文件失败: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
tmpUrls := make(map[string]struct{})
|
||||
for _, url := range urls {
|
||||
if url != "" {
|
||||
if _, ok := tmpUrls[url]; !ok {
|
||||
tmpUrls[url] = struct{}{}
|
||||
URLs = append(URLs, url)
|
||||
}
|
||||
urlMap[url] = struct{}{}
|
||||
}
|
||||
}
|
||||
LogInfo(GetText("load_urls_from_file", len(urls)))
|
||||
}
|
||||
|
||||
// 从文件加载主机列表
|
||||
if HostsFile != "" {
|
||||
hosts, err := Readfile(HostsFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("读取主机文件失败: %v", err)
|
||||
// 更新全局URL列表(已去重)
|
||||
URLs = make([]string, 0, len(urlMap))
|
||||
for u := range urlMap {
|
||||
URLs = append(URLs, u)
|
||||
}
|
||||
|
||||
if len(URLs) > 0 {
|
||||
LogInfo(GetText("load_urls", len(URLs)))
|
||||
}
|
||||
}
|
||||
|
||||
// parseHosts 解析主机配置
|
||||
// 从文件加载主机列表并更新目标信息
|
||||
func parseHosts(Info *HostInfo) error {
|
||||
// 如果未指定主机文件,无需处理
|
||||
if HostsFile == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
hosts, err := ReadFileLines(HostsFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("读取主机文件失败: %v", err)
|
||||
}
|
||||
|
||||
// 去重处理
|
||||
hostMap := make(map[string]struct{})
|
||||
for _, host := range hosts {
|
||||
if host != "" {
|
||||
hostMap[host] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// 构建主机列表并更新Info.Host
|
||||
if len(hostMap) > 0 {
|
||||
var hostList []string
|
||||
for host := range hostMap {
|
||||
hostList = append(hostList, host)
|
||||
}
|
||||
|
||||
tmpHosts := make(map[string]struct{})
|
||||
for _, host := range hosts {
|
||||
if host != "" {
|
||||
if _, ok := tmpHosts[host]; !ok {
|
||||
tmpHosts[host] = struct{}{}
|
||||
if Info.Host == "" {
|
||||
Info.Host = host
|
||||
} else {
|
||||
Info.Host += "," + host
|
||||
}
|
||||
}
|
||||
}
|
||||
hostStr := strings.Join(hostList, ",")
|
||||
if Info.Host == "" {
|
||||
Info.Host = hostStr
|
||||
} else {
|
||||
Info.Host += "," + hostStr
|
||||
}
|
||||
|
||||
LogInfo(GetText("load_hosts_from_file", len(hosts)))
|
||||
}
|
||||
|
||||
// 从文件加载端口列表
|
||||
if PortsFile != "" {
|
||||
ports, err := Readfile(PortsFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("读取端口文件失败: %v", err)
|
||||
}
|
||||
|
||||
var newport strings.Builder
|
||||
for _, port := range ports {
|
||||
if port != "" {
|
||||
newport.WriteString(port)
|
||||
newport.WriteString(",")
|
||||
}
|
||||
}
|
||||
Ports = newport.String()
|
||||
LogInfo(GetText("load_ports_from_file"))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Readfile 读取文件内容并返回非空行的切片
|
||||
func Readfile(filename string) ([]string, error) {
|
||||
// parsePorts 解析端口配置
|
||||
// 从文件加载端口列表并更新全局端口配置
|
||||
func parsePorts() error {
|
||||
// 如果未指定端口文件,无需处理
|
||||
if PortsFile == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
ports, err := ReadFileLines(PortsFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("读取端口文件失败: %v", err)
|
||||
}
|
||||
|
||||
// 构建端口列表字符串
|
||||
var portBuilder strings.Builder
|
||||
for _, port := range ports {
|
||||
if port != "" {
|
||||
portBuilder.WriteString(port)
|
||||
portBuilder.WriteString(",")
|
||||
}
|
||||
}
|
||||
|
||||
// 更新全局端口配置
|
||||
Ports = portBuilder.String()
|
||||
LogInfo(GetText("load_ports_from_file"))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadFileLines 读取文件内容并返回非空行的切片
|
||||
// 通用的文件读取函数,处理文件打开、读取和错误报告
|
||||
func ReadFileLines(filename string) ([]string, error) {
|
||||
// 打开文件
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
@@ -206,7 +290,7 @@ func Readfile(filename string) ([]string, error) {
|
||||
scanner := bufio.NewScanner(file)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
|
||||
// 逐行读取文件内容
|
||||
// 逐行读取文件内容,忽略空行
|
||||
lineCount := 0
|
||||
for scanner.Scan() {
|
||||
text := strings.TrimSpace(scanner.Text())
|
||||
@@ -227,19 +311,48 @@ func Readfile(filename string) ([]string, error) {
|
||||
}
|
||||
|
||||
// ParseInput 解析和验证输入参数配置
|
||||
// 处理多种配置的冲突检查、格式验证和参数处理
|
||||
func ParseInput(Info *HostInfo) error {
|
||||
// 检查互斥的扫描模式
|
||||
// 检查扫描模式冲突
|
||||
if err := validateScanMode(Info); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 处理端口配置组合
|
||||
processPortsConfig()
|
||||
|
||||
// 处理额外用户名和密码
|
||||
processExtraCredentials()
|
||||
|
||||
// 处理代理配置
|
||||
if err := processProxySettings(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 处理哈希值
|
||||
if err := processHashValues(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateScanMode 验证扫描模式
|
||||
// 检查互斥的扫描模式配置,避免参数冲突
|
||||
func validateScanMode(Info *HostInfo) error {
|
||||
// 检查互斥的扫描模式(主机扫描、URL扫描、本地模式)
|
||||
modes := 0
|
||||
if Info.Host != "" || HostsFile != "" {
|
||||
modes++
|
||||
}
|
||||
if TargetURL != "" || URLsFile != "" {
|
||||
if len(URLs) > 0 || TargetURL != "" || URLsFile != "" {
|
||||
modes++
|
||||
}
|
||||
if LocalMode {
|
||||
modes++
|
||||
}
|
||||
|
||||
// 处理扫描模式验证结果
|
||||
if modes == 0 {
|
||||
// 无参数时显示帮助
|
||||
flag.Usage()
|
||||
@@ -248,17 +361,18 @@ func ParseInput(Info *HostInfo) error {
|
||||
return fmt.Errorf(GetText("params_conflict"))
|
||||
}
|
||||
|
||||
//// 处理爆破线程配置
|
||||
//if BruteThreads <= 0 {
|
||||
// BruteThreads = 1
|
||||
// LogInfo(GetText("brute_threads", BruteThreads))
|
||||
//}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 处理端口配置
|
||||
// processPortsConfig 处理端口配置
|
||||
// 合并默认端口和附加端口配置
|
||||
func processPortsConfig() {
|
||||
// 如果使用主要端口,添加Web端口
|
||||
if Ports == MainPorts {
|
||||
Ports += "," + WebPorts
|
||||
}
|
||||
|
||||
// 处理附加端口
|
||||
if AddPorts != "" {
|
||||
if strings.HasSuffix(Ports, ",") {
|
||||
Ports += AddPorts
|
||||
@@ -267,8 +381,12 @@ func ParseInput(Info *HostInfo) error {
|
||||
}
|
||||
LogInfo(GetText("extra_ports", AddPorts))
|
||||
}
|
||||
}
|
||||
|
||||
// 处理用户名配置
|
||||
// processExtraCredentials 处理额外的用户名和密码
|
||||
// 添加命令行指定的额外用户名和密码到现有配置
|
||||
func processExtraCredentials() {
|
||||
// 处理额外用户名
|
||||
if AddUsers != "" {
|
||||
users := strings.Split(AddUsers, ",")
|
||||
for dict := range Userdict {
|
||||
@@ -278,67 +396,112 @@ func ParseInput(Info *HostInfo) error {
|
||||
LogInfo(GetText("extra_usernames", AddUsers))
|
||||
}
|
||||
|
||||
// 处理密码配置
|
||||
// 处理额外密码
|
||||
if AddPasswords != "" {
|
||||
passes := strings.Split(AddPasswords, ",")
|
||||
Passwords = append(Passwords, passes...)
|
||||
Passwords = RemoveDuplicate(Passwords)
|
||||
LogInfo(GetText("extra_passwords", AddPasswords))
|
||||
}
|
||||
}
|
||||
|
||||
// 处理Socks5代理配置
|
||||
// processProxySettings 处理代理设置
|
||||
// 解析并验证Socks5和HTTP代理配置
|
||||
func processProxySettings() error {
|
||||
// 处理Socks5代理
|
||||
if Socks5Proxy != "" {
|
||||
if !strings.HasPrefix(Socks5Proxy, "socks5://") {
|
||||
if !strings.Contains(Socks5Proxy, ":") {
|
||||
Socks5Proxy = "socks5://127.0.0.1" + Socks5Proxy
|
||||
} else {
|
||||
Socks5Proxy = "socks5://" + Socks5Proxy
|
||||
}
|
||||
if err := setupSocks5Proxy(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := url.Parse(Socks5Proxy)
|
||||
if err != nil {
|
||||
return fmt.Errorf(GetText("socks5_proxy_error", err))
|
||||
}
|
||||
DisablePing = true
|
||||
LogInfo(GetText("socks5_proxy", Socks5Proxy))
|
||||
}
|
||||
|
||||
// 处理HTTP代理配置
|
||||
// 处理HTTP代理
|
||||
if HttpProxy != "" {
|
||||
switch HttpProxy {
|
||||
case "1":
|
||||
HttpProxy = "http://127.0.0.1:8080"
|
||||
case "2":
|
||||
HttpProxy = "socks5://127.0.0.1:1080"
|
||||
default:
|
||||
if !strings.Contains(HttpProxy, "://") {
|
||||
HttpProxy = "http://127.0.0.1:" + HttpProxy
|
||||
}
|
||||
if err := setupHttpProxy(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(HttpProxy, "socks") && !strings.HasPrefix(HttpProxy, "http") {
|
||||
return fmt.Errorf(GetText("unsupported_proxy"))
|
||||
}
|
||||
|
||||
_, err := url.Parse(HttpProxy)
|
||||
if err != nil {
|
||||
return fmt.Errorf(GetText("proxy_format_error", err))
|
||||
}
|
||||
LogInfo(GetText("http_proxy", HttpProxy))
|
||||
}
|
||||
|
||||
// 处理Hash配置
|
||||
return nil
|
||||
}
|
||||
|
||||
// setupSocks5Proxy 设置Socks5代理
|
||||
// 格式化和验证Socks5代理URL
|
||||
func setupSocks5Proxy() error {
|
||||
// 规范化Socks5代理URL格式
|
||||
if !strings.HasPrefix(Socks5Proxy, "socks5://") {
|
||||
if !strings.Contains(Socks5Proxy, ":") {
|
||||
// 仅指定端口时使用本地地址
|
||||
Socks5Proxy = "socks5://127.0.0.1:" + Socks5Proxy
|
||||
} else {
|
||||
// 指定IP:PORT时添加协议前缀
|
||||
Socks5Proxy = "socks5://" + Socks5Proxy
|
||||
}
|
||||
}
|
||||
|
||||
// 验证代理URL格式
|
||||
_, err := url.Parse(Socks5Proxy)
|
||||
if err != nil {
|
||||
return fmt.Errorf(GetText("socks5_proxy_error", err))
|
||||
}
|
||||
|
||||
// 使用Socks5代理时禁用Ping(无法通过代理进行ICMP)
|
||||
DisablePing = true
|
||||
LogInfo(GetText("socks5_proxy", Socks5Proxy))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setupHttpProxy 设置HTTP代理
|
||||
// 处理多种HTTP代理简写形式并验证URL格式
|
||||
func setupHttpProxy() error {
|
||||
// 处理HTTP代理简写形式
|
||||
switch HttpProxy {
|
||||
case "1":
|
||||
// 快捷方式1: 本地8080端口(常用代理工具默认端口)
|
||||
HttpProxy = "http://127.0.0.1:8080"
|
||||
case "2":
|
||||
// 快捷方式2: 本地1080端口(常见SOCKS端口)
|
||||
HttpProxy = "socks5://127.0.0.1:1080"
|
||||
default:
|
||||
// 仅指定端口时使用本地HTTP代理
|
||||
if !strings.Contains(HttpProxy, "://") {
|
||||
HttpProxy = "http://127.0.0.1:" + HttpProxy
|
||||
}
|
||||
}
|
||||
|
||||
// 验证代理协议
|
||||
if !strings.HasPrefix(HttpProxy, "socks") && !strings.HasPrefix(HttpProxy, "http") {
|
||||
return fmt.Errorf(GetText("unsupported_proxy"))
|
||||
}
|
||||
|
||||
// 验证代理URL格式
|
||||
_, err := url.Parse(HttpProxy)
|
||||
if err != nil {
|
||||
return fmt.Errorf(GetText("proxy_format_error", err))
|
||||
}
|
||||
|
||||
LogInfo(GetText("http_proxy", HttpProxy))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// processHashValues 处理哈希值
|
||||
// 验证单个哈希值并处理哈希列表
|
||||
func processHashValues() error {
|
||||
// 处理单个哈希值
|
||||
if HashValue != "" {
|
||||
// MD5哈希必须是32位十六进制字符
|
||||
if len(HashValue) != 32 {
|
||||
return fmt.Errorf(GetText("hash_length_error"))
|
||||
}
|
||||
HashValues = append(HashValues, HashValue)
|
||||
}
|
||||
|
||||
// 处理Hash列表
|
||||
// 处理哈希值列表
|
||||
HashValues = RemoveDuplicate(HashValues)
|
||||
for _, hash := range HashValues {
|
||||
// 将十六进制字符串转换为字节数组
|
||||
hashByte, err := hex.DecodeString(hash)
|
||||
if err != nil {
|
||||
LogError(GetText("hash_decode_failed", hash))
|
||||
@@ -346,7 +509,24 @@ func ParseInput(Info *HostInfo) error {
|
||||
}
|
||||
HashBytes = append(HashBytes, hashByte)
|
||||
}
|
||||
|
||||
// 清空原始哈希值列表,仅保留字节形式
|
||||
HashValues = []string{}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveDuplicate 对字符串切片进行去重
|
||||
func RemoveDuplicate(old []string) []string {
|
||||
temp := make(map[string]struct{})
|
||||
var result []string
|
||||
|
||||
for _, item := range old {
|
||||
if _, exists := temp[item]; !exists {
|
||||
temp[item] = struct{}{}
|
||||
result = append(result, item)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
+423
-208
@@ -13,26 +13,37 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var ParseIPErr = errors.New(GetText("parse_ip_error"))
|
||||
// IP解析相关错误
|
||||
var (
|
||||
ErrParseIP = errors.New(GetText("parse_ip_error")) // IP解析失败的统一错误
|
||||
)
|
||||
|
||||
// ParseIP 解析IP地址配置
|
||||
// ParseIP 解析各种格式的IP地址
|
||||
// 参数:
|
||||
// - host: 主机地址(可以是单个IP、IP范围、CIDR或常用网段简写)
|
||||
// - filename: 包含主机地址的文件名
|
||||
// - nohosts: 需要排除的主机地址列表
|
||||
//
|
||||
// 返回:
|
||||
// - []string: 解析后的IP地址列表
|
||||
// - error: 解析过程中的错误
|
||||
func ParseIP(host string, filename string, nohosts ...string) (hosts []string, err error) {
|
||||
// 处理主机和端口组合的情况
|
||||
// 处理主机和端口组合的情况 (格式: IP:PORT)
|
||||
if filename == "" && strings.Contains(host, ":") {
|
||||
hostport := strings.Split(host, ":")
|
||||
if len(hostport) == 2 {
|
||||
host = hostport[0]
|
||||
hosts = ParseIPs(host)
|
||||
hosts = parseIPList(host)
|
||||
Ports = hostport[1]
|
||||
LogInfo(GetText("host_port_parsed", Ports))
|
||||
}
|
||||
} else {
|
||||
// 解析主机地址
|
||||
hosts = ParseIPs(host)
|
||||
hosts = parseIPList(host)
|
||||
|
||||
// 从文件加载额外主机
|
||||
if filename != "" {
|
||||
fileHosts, err := Readipfile(filename)
|
||||
fileHosts, err := readIPFile(filename)
|
||||
if err != nil {
|
||||
LogError(GetText("read_host_file_failed", err))
|
||||
} else {
|
||||
@@ -42,177 +53,137 @@ func ParseIP(host string, filename string, nohosts ...string) (hosts []string, e
|
||||
}
|
||||
}
|
||||
|
||||
// 处理排除主机
|
||||
if len(nohosts) > 0 && nohosts[0] != "" {
|
||||
excludeHosts := ParseIPs(nohosts[0])
|
||||
if len(excludeHosts) > 0 {
|
||||
// 使用map存储有效主机
|
||||
temp := make(map[string]struct{})
|
||||
for _, host := range hosts {
|
||||
temp[host] = struct{}{}
|
||||
}
|
||||
// 处理需要排除的主机
|
||||
hosts = excludeHosts(hosts, nohosts)
|
||||
|
||||
// 删除需要排除的主机
|
||||
for _, host := range excludeHosts {
|
||||
delete(temp, host)
|
||||
}
|
||||
|
||||
// 重建主机列表
|
||||
var newHosts []string
|
||||
for host := range temp {
|
||||
newHosts = append(newHosts, host)
|
||||
}
|
||||
hosts = newHosts
|
||||
sort.Strings(hosts)
|
||||
LogInfo(GetText("hosts_excluded", len(excludeHosts)))
|
||||
}
|
||||
}
|
||||
|
||||
// 去重处理
|
||||
hosts = RemoveDuplicate(hosts)
|
||||
// 去重并排序
|
||||
hosts = removeDuplicateIPs(hosts)
|
||||
LogInfo(GetText("final_valid_hosts", len(hosts)))
|
||||
|
||||
// 检查解析结果
|
||||
if len(hosts) == 0 && len(HostPort) == 0 && (host != "" || filename != "") {
|
||||
return nil, ParseIPErr
|
||||
return nil, ErrParseIP
|
||||
}
|
||||
|
||||
return hosts, nil
|
||||
}
|
||||
|
||||
func ParseIPs(ip string) (hosts []string) {
|
||||
if strings.Contains(ip, ",") {
|
||||
IPList := strings.Split(ip, ",")
|
||||
var ips []string
|
||||
for _, ip := range IPList {
|
||||
ips = parseIP(ip)
|
||||
hosts = append(hosts, ips...)
|
||||
// parseIPList 解析逗号分隔的IP地址列表
|
||||
// 参数:
|
||||
// - ipList: 逗号分隔的IP地址列表字符串
|
||||
//
|
||||
// 返回:
|
||||
// - []string: 解析后的IP地址列表
|
||||
func parseIPList(ipList string) []string {
|
||||
var result []string
|
||||
|
||||
// 处理逗号分隔的IP列表
|
||||
if strings.Contains(ipList, ",") {
|
||||
ips := strings.Split(ipList, ",")
|
||||
for _, ip := range ips {
|
||||
if parsed := parseSingleIP(ip); len(parsed) > 0 {
|
||||
result = append(result, parsed...)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hosts = parseIP(ip)
|
||||
} else if ipList != "" {
|
||||
// 解析单个IP地址或范围
|
||||
result = parseSingleIP(ipList)
|
||||
}
|
||||
return hosts
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func parseIP(ip string) []string {
|
||||
reg := regexp.MustCompile(`[a-zA-Z]+`)
|
||||
// parseSingleIP 解析单个IP地址或IP范围
|
||||
// 支持多种格式:
|
||||
// - 普通IP: 192.168.1.1
|
||||
// - 简写网段: 192, 172, 10
|
||||
// - CIDR: 192.168.0.0/24
|
||||
// - 范围: 192.168.1.1-192.168.1.100 或 192.168.1.1-100
|
||||
// - 域名: example.com
|
||||
// 参数:
|
||||
// - ip: IP地址或范围字符串
|
||||
//
|
||||
// 返回:
|
||||
// - []string: 解析后的IP地址列表
|
||||
func parseSingleIP(ip string) []string {
|
||||
// 检测是否包含字母(可能是域名)
|
||||
isAlpha := regexp.MustCompile(`[a-zA-Z]+`).MatchString(ip)
|
||||
|
||||
// 根据不同格式解析IP
|
||||
switch {
|
||||
case ip == "192":
|
||||
return parseIP("192.168.0.0/16")
|
||||
// 常用内网段简写
|
||||
return parseSingleIP("192.168.0.0/16")
|
||||
case ip == "172":
|
||||
return parseIP("172.16.0.0/12")
|
||||
// 常用内网段简写
|
||||
return parseSingleIP("172.16.0.0/12")
|
||||
case ip == "10":
|
||||
return parseIP("10.0.0.0/8")
|
||||
// 常用内网段简写
|
||||
return parseSingleIP("10.0.0.0/8")
|
||||
case strings.HasSuffix(ip, "/8"):
|
||||
return parseIP8(ip)
|
||||
// 处理/8网段(使用采样方式)
|
||||
return parseSubnet8(ip)
|
||||
case strings.Contains(ip, "/"):
|
||||
return parseIP2(ip)
|
||||
case reg.MatchString(ip):
|
||||
// 处理CIDR格式
|
||||
return parseCIDR(ip)
|
||||
case isAlpha:
|
||||
// 处理域名,直接返回
|
||||
return []string{ip}
|
||||
case strings.Contains(ip, "-"):
|
||||
return parseIP1(ip)
|
||||
// 处理IP范围
|
||||
return parseIPRange(ip)
|
||||
default:
|
||||
testIP := net.ParseIP(ip)
|
||||
if testIP == nil {
|
||||
LogError(GetText("invalid_ip_format", ip))
|
||||
return nil
|
||||
// 尝试解析为单个IP地址
|
||||
if testIP := net.ParseIP(ip); testIP != nil {
|
||||
return []string{ip}
|
||||
}
|
||||
return []string{ip}
|
||||
LogError(GetText("invalid_ip_format", ip))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// parseIP2 解析CIDR格式的IP地址段
|
||||
func parseIP2(host string) []string {
|
||||
_, ipNet, err := net.ParseCIDR(host)
|
||||
// parseCIDR 解析CIDR格式的IP地址段
|
||||
// 例如: 192.168.1.0/24
|
||||
// 参数:
|
||||
// - cidr: CIDR格式的IP地址段
|
||||
//
|
||||
// 返回:
|
||||
// - []string: 展开后的IP地址列表
|
||||
func parseCIDR(cidr string) []string {
|
||||
// 解析CIDR格式
|
||||
_, ipNet, err := net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
LogError(GetText("cidr_parse_failed", host, err))
|
||||
LogError(GetText("cidr_parse_failed", cidr, err))
|
||||
return nil
|
||||
}
|
||||
|
||||
ipRange := IPRange(ipNet)
|
||||
hosts := parseIP1(ipRange)
|
||||
LogInfo(GetText("parse_cidr_to_range", host, ipRange))
|
||||
// 转换为IP范围
|
||||
ipRange := calculateIPRange(ipNet)
|
||||
hosts := parseIPRange(ipRange)
|
||||
LogInfo(GetText("parse_cidr_to_range", cidr, ipRange))
|
||||
return hosts
|
||||
}
|
||||
|
||||
// parseIP1 解析IP范围格式的地址
|
||||
func parseIP1(ip string) []string {
|
||||
ipRange := strings.Split(ip, "-")
|
||||
testIP := net.ParseIP(ipRange[0])
|
||||
var allIP []string
|
||||
// calculateIPRange 计算CIDR的起始IP和结束IP
|
||||
// 例如: 192.168.1.0/24 -> 192.168.1.0-192.168.1.255
|
||||
// 参数:
|
||||
// - cidr: 解析后的IPNet对象
|
||||
//
|
||||
// 返回:
|
||||
// - string: 格式为"起始IP-结束IP"的范围字符串
|
||||
func calculateIPRange(cidr *net.IPNet) string {
|
||||
// 获取网络起始IP
|
||||
start := cidr.IP.String()
|
||||
mask := cidr.Mask
|
||||
|
||||
// 处理简写格式 (192.168.111.1-255)
|
||||
if len(ipRange[1]) < 4 {
|
||||
endNum, err := strconv.Atoi(ipRange[1])
|
||||
if testIP == nil || endNum > 255 || err != nil {
|
||||
LogError(GetText("ip_range_format_error", ip))
|
||||
return nil
|
||||
}
|
||||
|
||||
splitIP := strings.Split(ipRange[0], ".")
|
||||
startNum, err1 := strconv.Atoi(splitIP[3])
|
||||
endNum, err2 := strconv.Atoi(ipRange[1])
|
||||
prefixIP := strings.Join(splitIP[0:3], ".")
|
||||
|
||||
if startNum > endNum || err1 != nil || err2 != nil {
|
||||
LogError(GetText("invalid_ip_range", startNum, endNum))
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := startNum; i <= endNum; i++ {
|
||||
allIP = append(allIP, prefixIP+"."+strconv.Itoa(i))
|
||||
}
|
||||
|
||||
LogInfo(GetText("generate_ip_range", prefixIP, startNum, prefixIP, endNum))
|
||||
} else {
|
||||
// 处理完整IP范围格式
|
||||
splitIP1 := strings.Split(ipRange[0], ".")
|
||||
splitIP2 := strings.Split(ipRange[1], ".")
|
||||
|
||||
if len(splitIP1) != 4 || len(splitIP2) != 4 {
|
||||
LogError(GetText("ip_format_error", ip))
|
||||
return nil
|
||||
}
|
||||
|
||||
start, end := [4]int{}, [4]int{}
|
||||
for i := 0; i < 4; i++ {
|
||||
ip1, err1 := strconv.Atoi(splitIP1[i])
|
||||
ip2, err2 := strconv.Atoi(splitIP2[i])
|
||||
if ip1 > ip2 || err1 != nil || err2 != nil {
|
||||
LogError(GetText("invalid_ip_range", ipRange[0], ipRange[1]))
|
||||
return nil
|
||||
}
|
||||
start[i], end[i] = ip1, ip2
|
||||
}
|
||||
|
||||
startNum := start[0]<<24 | start[1]<<16 | start[2]<<8 | start[3]
|
||||
endNum := end[0]<<24 | end[1]<<16 | end[2]<<8 | end[3]
|
||||
|
||||
for num := startNum; num <= endNum; num++ {
|
||||
ip := strconv.Itoa((num>>24)&0xff) + "." +
|
||||
strconv.Itoa((num>>16)&0xff) + "." +
|
||||
strconv.Itoa((num>>8)&0xff) + "." +
|
||||
strconv.Itoa((num)&0xff)
|
||||
allIP = append(allIP, ip)
|
||||
}
|
||||
|
||||
LogInfo(GetText("generate_ip_range", ipRange[0], ipRange[1]))
|
||||
}
|
||||
|
||||
return allIP
|
||||
}
|
||||
|
||||
// IPRange 计算CIDR的起始IP和结束IP
|
||||
func IPRange(c *net.IPNet) string {
|
||||
start := c.IP.String()
|
||||
mask := c.Mask
|
||||
bcst := make(net.IP, len(c.IP))
|
||||
copy(bcst, c.IP)
|
||||
// 计算广播地址(最后一个IP)
|
||||
bcst := make(net.IP, len(cidr.IP))
|
||||
copy(bcst, cidr.IP)
|
||||
|
||||
// 将网络掩码按位取反,然后与IP地址按位或,得到广播地址
|
||||
for i := 0; i < len(mask); i++ {
|
||||
ipIdx := len(bcst) - i - 1
|
||||
bcst[ipIdx] = c.IP[ipIdx] | ^mask[len(mask)-i-1]
|
||||
bcst[ipIdx] = cidr.IP[ipIdx] | ^mask[len(mask)-i-1]
|
||||
}
|
||||
end := bcst.String()
|
||||
|
||||
@@ -221,8 +192,218 @@ func IPRange(c *net.IPNet) string {
|
||||
return result
|
||||
}
|
||||
|
||||
// Readipfile 从文件中按行读取IP地址
|
||||
func Readipfile(filename string) ([]string, error) {
|
||||
// parseIPRange 解析IP范围格式的地址
|
||||
// 支持两种格式:
|
||||
// - 完整格式: 192.168.1.1-192.168.1.100
|
||||
// - 简写格式: 192.168.1.1-100
|
||||
// 参数:
|
||||
// - ipRange: IP范围字符串
|
||||
//
|
||||
// 返回:
|
||||
// - []string: 展开后的IP地址列表
|
||||
func parseIPRange(ipRange string) []string {
|
||||
parts := strings.Split(ipRange, "-")
|
||||
if len(parts) != 2 {
|
||||
LogError(GetText("ip_range_format_error", ipRange))
|
||||
return nil
|
||||
}
|
||||
|
||||
startIP := parts[0]
|
||||
endIP := parts[1]
|
||||
|
||||
// 验证起始IP
|
||||
if net.ParseIP(startIP) == nil {
|
||||
LogError(GetText("invalid_ip_format", startIP))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 处理简写格式 (如: 192.168.1.1-100)
|
||||
if len(endIP) < 4 || !strings.Contains(endIP, ".") {
|
||||
return parseShortIPRange(startIP, endIP)
|
||||
} else {
|
||||
// 处理完整格式 (如: 192.168.1.1-192.168.1.100)
|
||||
return parseFullIPRange(startIP, endIP)
|
||||
}
|
||||
}
|
||||
|
||||
// parseShortIPRange 解析简写格式的IP范围
|
||||
// 例如: 192.168.1.1-100 表示从192.168.1.1到192.168.1.100
|
||||
// 参数:
|
||||
// - startIP: 起始IP
|
||||
// - endSuffix: 结束IP的最后一部分
|
||||
//
|
||||
// 返回:
|
||||
// - []string: 展开后的IP地址列表
|
||||
func parseShortIPRange(startIP, endSuffix string) []string {
|
||||
var allIP []string
|
||||
|
||||
// 将结束段转换为数字
|
||||
endNum, err := strconv.Atoi(endSuffix)
|
||||
if err != nil || endNum > 255 {
|
||||
LogError(GetText("ip_range_format_error", startIP+"-"+endSuffix))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 分解起始IP
|
||||
ipParts := strings.Split(startIP, ".")
|
||||
if len(ipParts) != 4 {
|
||||
LogError(GetText("ip_format_error", startIP))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取前缀和起始IP的最后一部分
|
||||
prefixIP := strings.Join(ipParts[0:3], ".")
|
||||
startNum, err := strconv.Atoi(ipParts[3])
|
||||
if err != nil || startNum > endNum {
|
||||
LogError(GetText("invalid_ip_range", startNum, endNum))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 生成IP范围
|
||||
for i := startNum; i <= endNum; i++ {
|
||||
allIP = append(allIP, fmt.Sprintf("%s.%d", prefixIP, i))
|
||||
}
|
||||
|
||||
LogInfo(GetText("generate_ip_range", prefixIP, startNum, prefixIP, endNum))
|
||||
return allIP
|
||||
}
|
||||
|
||||
// parseFullIPRange 解析完整格式的IP范围
|
||||
// 例如: 192.168.1.1-192.168.2.100
|
||||
// 参数:
|
||||
// - startIP: 起始IP
|
||||
// - endIP: 结束IP
|
||||
//
|
||||
// 返回:
|
||||
// - []string: 展开后的IP地址列表
|
||||
func parseFullIPRange(startIP, endIP string) []string {
|
||||
var allIP []string
|
||||
|
||||
// 验证结束IP
|
||||
if net.ParseIP(endIP) == nil {
|
||||
LogError(GetText("invalid_ip_format", endIP))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 分解起始IP和结束IP
|
||||
startParts := strings.Split(startIP, ".")
|
||||
endParts := strings.Split(endIP, ".")
|
||||
|
||||
if len(startParts) != 4 || len(endParts) != 4 {
|
||||
LogError(GetText("ip_format_error", startIP+"-"+endIP))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 转换为整数数组
|
||||
var start, end [4]int
|
||||
for i := 0; i < 4; i++ {
|
||||
var err1, err2 error
|
||||
start[i], err1 = strconv.Atoi(startParts[i])
|
||||
end[i], err2 = strconv.Atoi(endParts[i])
|
||||
|
||||
if err1 != nil || err2 != nil || start[i] > 255 || end[i] > 255 {
|
||||
LogError(GetText("ip_format_error", startIP+"-"+endIP))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// 计算IP地址的整数表示
|
||||
startInt := (start[0] << 24) | (start[1] << 16) | (start[2] << 8) | start[3]
|
||||
endInt := (end[0] << 24) | (end[1] << 16) | (end[2] << 8) | end[3]
|
||||
|
||||
// 检查范围的有效性
|
||||
if startInt > endInt {
|
||||
LogError(GetText("invalid_ip_range", startIP, endIP))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 限制IP范围的大小,防止生成过多IP导致内存问题
|
||||
if endInt-startInt > 65535 {
|
||||
LogError(GetText("ip_range_too_large", startIP, endIP))
|
||||
// 可以考虑在这里实现采样或截断策略
|
||||
}
|
||||
|
||||
// 生成IP范围
|
||||
for ipInt := startInt; ipInt <= endInt; ipInt++ {
|
||||
ip := fmt.Sprintf("%d.%d.%d.%d",
|
||||
(ipInt>>24)&0xFF,
|
||||
(ipInt>>16)&0xFF,
|
||||
(ipInt>>8)&0xFF,
|
||||
ipInt&0xFF)
|
||||
allIP = append(allIP, ip)
|
||||
}
|
||||
|
||||
LogInfo(GetText("generate_ip_range_full", startIP, endIP, len(allIP)))
|
||||
return allIP
|
||||
}
|
||||
|
||||
// parseSubnet8 解析/8网段的IP地址,生成采样IP列表
|
||||
// 由于/8网段包含1600多万个IP,因此采用采样方式
|
||||
// 参数:
|
||||
// - subnet: CIDR格式的/8网段
|
||||
//
|
||||
// 返回:
|
||||
// - []string: 采样的IP地址列表
|
||||
func parseSubnet8(subnet string) []string {
|
||||
// 去除CIDR后缀获取基础IP
|
||||
baseIP := subnet[:len(subnet)-2]
|
||||
if net.ParseIP(baseIP) == nil {
|
||||
LogError(GetText("invalid_ip_format", baseIP))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取/8网段的第一段
|
||||
firstOctet := strings.Split(baseIP, ".")[0]
|
||||
var sampleIPs []string
|
||||
|
||||
LogInfo(GetText("parse_subnet", firstOctet))
|
||||
|
||||
// 预分配足够的容量以提高性能
|
||||
// 每个二级网段10个IP,共256*256个二级网段
|
||||
sampleIPs = make([]string, 0, 10)
|
||||
|
||||
// 对常用网段进行更全面的扫描
|
||||
commonSecondOctets := []int{0, 1, 2, 10, 100, 200, 254}
|
||||
|
||||
// 对于每个选定的第二段,采样部分第三段
|
||||
for _, secondOctet := range commonSecondOctets {
|
||||
for thirdOctet := 0; thirdOctet < 256; thirdOctet += 10 {
|
||||
// 添加常见的网关和服务器IP
|
||||
sampleIPs = append(sampleIPs, fmt.Sprintf("%s.%d.%d.1", firstOctet, secondOctet, thirdOctet)) // 默认网关
|
||||
sampleIPs = append(sampleIPs, fmt.Sprintf("%s.%d.%d.254", firstOctet, secondOctet, thirdOctet)) // 通常用于路由器/交换机
|
||||
|
||||
// 随机采样不同范围的主机IP
|
||||
fourthOctet := randomInt(2, 253)
|
||||
sampleIPs = append(sampleIPs, fmt.Sprintf("%s.%d.%d.%d", firstOctet, secondOctet, thirdOctet, fourthOctet))
|
||||
}
|
||||
}
|
||||
|
||||
// 对其他二级网段进行稀疏采样
|
||||
samplingStep := 32 // 每32个二级网段采样1个
|
||||
for secondOctet := 0; secondOctet < 256; secondOctet += samplingStep {
|
||||
for thirdOctet := 0; thirdOctet < 256; thirdOctet += samplingStep {
|
||||
// 对于采样的网段,取几个代表性IP
|
||||
sampleIPs = append(sampleIPs, fmt.Sprintf("%s.%d.%d.1", firstOctet, secondOctet, thirdOctet))
|
||||
sampleIPs = append(sampleIPs, fmt.Sprintf("%s.%d.%d.%d", firstOctet, secondOctet, thirdOctet, randomInt(2, 253)))
|
||||
}
|
||||
}
|
||||
|
||||
LogInfo(GetText("sample_ip_generated", len(sampleIPs)))
|
||||
return sampleIPs
|
||||
}
|
||||
|
||||
// readIPFile 从文件中按行读取IP地址
|
||||
// 支持两种格式:
|
||||
// - 每行一个IP或IP范围
|
||||
// - IP:PORT 格式指定端口
|
||||
// 参数:
|
||||
// - filename: 包含IP地址的文件路径
|
||||
//
|
||||
// 返回:
|
||||
// - []string: 解析后的IP地址列表
|
||||
// - error: 读取和解析过程中的错误
|
||||
func readIPFile(filename string) ([]string, error) {
|
||||
// 打开文件
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
LogError(GetText("open_file_failed", filename, err))
|
||||
@@ -230,105 +411,139 @@ func Readipfile(filename string) ([]string, error) {
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var content []string
|
||||
var ipList []string
|
||||
scanner := bufio.NewScanner(file)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
|
||||
// 逐行处理
|
||||
lineCount := 0
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
if line == "" {
|
||||
continue
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue // 跳过空行和注释行
|
||||
}
|
||||
|
||||
text := strings.Split(line, ":")
|
||||
if len(text) == 2 {
|
||||
port := strings.Split(text[1], " ")[0]
|
||||
num, err := strconv.Atoi(port)
|
||||
if err != nil || num < 1 || num > 65535 {
|
||||
LogError(GetText("invalid_port", line))
|
||||
continue
|
||||
}
|
||||
lineCount++
|
||||
|
||||
hosts := ParseIPs(text[0])
|
||||
for _, host := range hosts {
|
||||
HostPort = append(HostPort, fmt.Sprintf("%s:%s", host, port))
|
||||
// 处理IP:PORT格式
|
||||
if strings.Contains(line, ":") {
|
||||
parts := strings.Split(line, ":")
|
||||
if len(parts) == 2 {
|
||||
// 提取端口部分,处理可能的注释
|
||||
portPart := strings.Split(parts[1], " ")[0]
|
||||
portPart = strings.Split(portPart, "#")[0]
|
||||
port, err := strconv.Atoi(portPart)
|
||||
|
||||
// 验证端口有效性
|
||||
if err != nil || port < 1 || port > 65535 {
|
||||
LogError(GetText("invalid_port", line))
|
||||
continue
|
||||
}
|
||||
|
||||
// 解析IP部分并与端口组合
|
||||
hosts := parseIPList(parts[0])
|
||||
for _, host := range hosts {
|
||||
HostPort = append(HostPort, fmt.Sprintf("%s:%s", host, portPart))
|
||||
}
|
||||
LogInfo(GetText("parse_ip_port", line))
|
||||
} else {
|
||||
LogError(GetText("invalid_ip_port_format", line))
|
||||
}
|
||||
LogInfo(GetText("parse_ip_port", line))
|
||||
} else {
|
||||
hosts := ParseIPs(line)
|
||||
content = append(content, hosts...)
|
||||
// 处理纯IP格式
|
||||
hosts := parseIPList(line)
|
||||
ipList = append(ipList, hosts...)
|
||||
LogInfo(GetText("parse_ip_address", line))
|
||||
}
|
||||
}
|
||||
|
||||
// 检查扫描过程中的错误
|
||||
if err := scanner.Err(); err != nil {
|
||||
LogError(GetText("read_file_error", err))
|
||||
return content, err
|
||||
return ipList, err
|
||||
}
|
||||
|
||||
LogInfo(GetText("file_parse_complete", len(content)))
|
||||
return content, nil
|
||||
LogInfo(GetText("file_parse_complete", len(ipList)))
|
||||
return ipList, nil
|
||||
}
|
||||
|
||||
// RemoveDuplicate 对字符串切片进行去重
|
||||
func RemoveDuplicate(old []string) []string {
|
||||
temp := make(map[string]struct{})
|
||||
var result []string
|
||||
|
||||
for _, item := range old {
|
||||
if _, exists := temp[item]; !exists {
|
||||
temp[item] = struct{}{}
|
||||
result = append(result, item)
|
||||
}
|
||||
// excludeHosts 从主机列表中排除指定的主机
|
||||
// 参数:
|
||||
// - hosts: 原始主机列表
|
||||
// - nohosts: 需要排除的主机列表(可选)
|
||||
//
|
||||
// 返回:
|
||||
// - []string: 排除后的主机列表
|
||||
func excludeHosts(hosts []string, nohosts []string) []string {
|
||||
// 如果没有需要排除的主机,直接返回原列表
|
||||
if len(nohosts) == 0 || nohosts[0] == "" {
|
||||
return hosts
|
||||
}
|
||||
|
||||
// 解析排除列表
|
||||
excludeList := parseIPList(nohosts[0])
|
||||
if len(excludeList) == 0 {
|
||||
return hosts
|
||||
}
|
||||
|
||||
// 使用map存储有效主机,提高查找效率
|
||||
hostMap := make(map[string]struct{}, len(hosts))
|
||||
for _, host := range hosts {
|
||||
hostMap[host] = struct{}{}
|
||||
}
|
||||
|
||||
// 从map中删除需要排除的主机
|
||||
for _, host := range excludeList {
|
||||
delete(hostMap, host)
|
||||
}
|
||||
|
||||
// 重建主机列表
|
||||
result := make([]string, 0, len(hostMap))
|
||||
for host := range hostMap {
|
||||
result = append(result, host)
|
||||
}
|
||||
|
||||
// 排序以保持结果的稳定性
|
||||
sort.Strings(result)
|
||||
LogInfo(GetText("hosts_excluded", len(excludeList)))
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// parseIP8 解析/8网段的IP地址
|
||||
func parseIP8(ip string) []string {
|
||||
// 去除CIDR后缀获取基础IP
|
||||
realIP := ip[:len(ip)-2]
|
||||
testIP := net.ParseIP(realIP)
|
||||
|
||||
if testIP == nil {
|
||||
LogError(GetText("invalid_ip_format", realIP))
|
||||
return nil
|
||||
// removeDuplicateIPs 去除重复的IP地址
|
||||
// 参数:
|
||||
// - ips: 包含可能重复项的IP地址列表
|
||||
//
|
||||
// 返回:
|
||||
// - []string: 去重后的IP地址列表
|
||||
func removeDuplicateIPs(ips []string) []string {
|
||||
// 使用map去重
|
||||
ipMap := make(map[string]struct{}, len(ips))
|
||||
for _, ip := range ips {
|
||||
ipMap[ip] = struct{}{}
|
||||
}
|
||||
|
||||
// 获取/8网段的第一段
|
||||
ipRange := strings.Split(ip, ".")[0]
|
||||
var allIP []string
|
||||
|
||||
LogInfo(GetText("parse_subnet", ipRange))
|
||||
|
||||
// 遍历所有可能的第二、三段
|
||||
for a := 0; a <= 255; a++ {
|
||||
for b := 0; b <= 255; b++ {
|
||||
// 添加常用网关IP
|
||||
allIP = append(allIP, fmt.Sprintf("%s.%d.%d.1", ipRange, a, b)) // 默认网关
|
||||
allIP = append(allIP, fmt.Sprintf("%s.%d.%d.2", ipRange, a, b)) // 备用网关
|
||||
allIP = append(allIP, fmt.Sprintf("%s.%d.%d.4", ipRange, a, b)) // 常用服务器
|
||||
allIP = append(allIP, fmt.Sprintf("%s.%d.%d.5", ipRange, a, b)) // 常用服务器
|
||||
|
||||
// 随机采样不同范围的IP
|
||||
allIP = append(allIP, fmt.Sprintf("%s.%d.%d.%d", ipRange, a, b, RandInt(6, 55))) // 低段随机
|
||||
allIP = append(allIP, fmt.Sprintf("%s.%d.%d.%d", ipRange, a, b, RandInt(56, 100))) // 中低段随机
|
||||
allIP = append(allIP, fmt.Sprintf("%s.%d.%d.%d", ipRange, a, b, RandInt(101, 150))) // 中段随机
|
||||
allIP = append(allIP, fmt.Sprintf("%s.%d.%d.%d", ipRange, a, b, RandInt(151, 200))) // 中高段随机
|
||||
allIP = append(allIP, fmt.Sprintf("%s.%d.%d.%d", ipRange, a, b, RandInt(201, 253))) // 高段随机
|
||||
allIP = append(allIP, fmt.Sprintf("%s.%d.%d.254", ipRange, a, b)) // 广播地址前
|
||||
}
|
||||
// 创建结果切片并添加唯一的IP
|
||||
result := make([]string, 0, len(ipMap))
|
||||
for ip := range ipMap {
|
||||
result = append(result, ip)
|
||||
}
|
||||
|
||||
LogInfo(GetText("sample_ip_generated", len(allIP)))
|
||||
return allIP
|
||||
// 排序以保持结果的稳定性
|
||||
sort.Strings(result)
|
||||
return result
|
||||
}
|
||||
|
||||
// RandInt 生成指定范围内的随机整数
|
||||
func RandInt(min, max int) int {
|
||||
if min >= max || min == 0 || max == 0 {
|
||||
// randomInt 生成指定范围内的随机整数
|
||||
// 参数:
|
||||
// - min: 最小值(包含)
|
||||
// - max: 最大值(包含)
|
||||
//
|
||||
// 返回:
|
||||
// - int: 生成的随机数
|
||||
func randomInt(min, max int) int {
|
||||
if min >= max || min < 0 || max <= 0 {
|
||||
return max
|
||||
}
|
||||
return rand.Intn(max-min) + min
|
||||
return rand.Intn(max-min+1) + min
|
||||
}
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
package Common
|
||||
|
||||
// 扫描模式常量 - 使用大写开头表示这是一个预设的扫描模式
|
||||
const (
|
||||
ModeAll = "All" // 全量扫描
|
||||
ModeBasic = "Basic" // 基础扫描
|
||||
ModeDatabase = "Database" // 数据库扫描
|
||||
ModeWeb = "Web" // Web扫描
|
||||
ModeService = "Service" // 服务扫描
|
||||
ModeVul = "Vul" // 漏洞扫描
|
||||
ModePort = "Port" // 端口扫描
|
||||
ModeICMP = "ICMP" // ICMP探测
|
||||
ModeLocal = "Local" // 本地信息收集
|
||||
)
|
||||
|
||||
// 插件分类映射表 - 所有插件名使用小写
|
||||
var PluginGroups = map[string][]string{
|
||||
ModeAll: {
|
||||
"webtitle", "webpoc", // web类
|
||||
"mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类
|
||||
"oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", "cassandra", "neo4j", // 数据库类
|
||||
"ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "snmp", "modbus", "rsync", // 服务类
|
||||
"ms17010", "smbghost", "smb2", // 漏洞类
|
||||
"findnet", // 其他
|
||||
},
|
||||
ModeBasic: {
|
||||
"webtitle", "ftp", "ssh", "smb", "findnet",
|
||||
},
|
||||
ModeDatabase: {
|
||||
"mysql", "mssql", "redis", "mongodb",
|
||||
"postgres", "oracle", "memcached", "elasticsearch", "rabbitmq", "kafka", "activemq", "cassandra", "neo4j",
|
||||
},
|
||||
ModeWeb: {
|
||||
"webtitle", "webpoc",
|
||||
},
|
||||
ModeService: {
|
||||
"ftp", "ssh", "telnet", "smb", "rdp", "vnc", "netbios", "ldap", "smtp", "imap", "pop3", "modbus", "rsync",
|
||||
},
|
||||
ModeVul: {
|
||||
"ms17010", "smbghost", "smb2",
|
||||
},
|
||||
ModeLocal: {
|
||||
"localinfo", "minidump", "dcinfo",
|
||||
},
|
||||
}
|
||||
|
||||
// ParseScanMode 解析扫描模式
|
||||
func ParseScanMode(mode string) {
|
||||
LogInfo(GetText("parse_scan_mode", mode))
|
||||
|
||||
// 检查是否是预设模式
|
||||
presetModes := []string{
|
||||
ModeAll, ModeBasic, ModeDatabase, ModeWeb,
|
||||
ModeService, ModeVul, ModePort, ModeICMP, ModeLocal,
|
||||
}
|
||||
|
||||
for _, presetMode := range presetModes {
|
||||
if mode == presetMode {
|
||||
ScanMode = mode
|
||||
if plugins := GetPluginsForMode(mode); plugins != nil {
|
||||
LogInfo(GetText("using_preset_mode_plugins", mode, plugins))
|
||||
} else {
|
||||
LogInfo(GetText("using_preset_mode", mode))
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否是有效的插件名
|
||||
if _, exists := PluginManager[mode]; exists {
|
||||
ScanMode = mode
|
||||
LogInfo(GetText("using_single_plugin", mode))
|
||||
return
|
||||
}
|
||||
|
||||
// 默认使用All模式
|
||||
ScanMode = ModeAll
|
||||
LogInfo(GetText("using_default_mode", ModeAll))
|
||||
LogInfo(GetText("included_plugins", PluginGroups[ModeAll]))
|
||||
}
|
||||
|
||||
// GetPluginsForMode 获取指定模式下的插件列表
|
||||
func GetPluginsForMode(mode string) []string {
|
||||
plugins, exists := PluginGroups[mode]
|
||||
if exists {
|
||||
return plugins
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 辅助函数
|
||||
func IsPortScan() bool { return ScanMode == ModePort }
|
||||
func IsICMPScan() bool { return ScanMode == ModeICMP }
|
||||
func IsWebScan() bool { return ScanMode == ModeWeb }
|
||||
func GetScanMode() string { return ScanMode }
|
||||
+72
-117
@@ -234,70 +234,51 @@ var i18nMap = map[string]map[string]string{
|
||||
"Пример: -p main, -p 80,443, -p 1-1000",
|
||||
},
|
||||
"flag_scan_mode": {
|
||||
LangZH: "指定扫描模式:\n" +
|
||||
"预设模式:\n" +
|
||||
" - All: 全量扫描\n" +
|
||||
" - Basic: 基础扫描(Web/FTP/SSH等)\n" +
|
||||
" - Database: 数据库扫描\n" +
|
||||
" - Web: Web服务扫描\n" +
|
||||
" - Service: 常见服务扫描\n" +
|
||||
" - Vul: 漏洞扫描\n" +
|
||||
" - Port: 端口扫描\n" +
|
||||
" - ICMP: 存活探测\n" +
|
||||
" - Local: 本地信息\n" +
|
||||
"单项扫描:\n" +
|
||||
" - web/db: mysql,redis等\n" +
|
||||
" - service: ftp,ssh等\n" +
|
||||
" - vul: ms17010等",
|
||||
LangZH: "指定要使用的扫描插件:\n" +
|
||||
" - All: 使用所有非敏感插件\n" +
|
||||
" - 单个插件: 如 ssh, redis, mysql\n" +
|
||||
" - 多个插件: 使用逗号分隔,如 ssh,ftp,redis\n\n" +
|
||||
"插件分类:\n" +
|
||||
" - 服务类: ssh, ftp, telnet, smb, rdp, vnc...\n" +
|
||||
" - 数据库类: mysql, redis, mongodb, postgres...\n" +
|
||||
" - Web类: webtitle, webpoc...\n" +
|
||||
" - 漏洞类: ms17010...\n" +
|
||||
" - 本地类: localinfo, dcinfo, minidump (需明确指定)",
|
||||
|
||||
LangEN: "Specify scan mode:\n" +
|
||||
"Preset modes:\n" +
|
||||
" - All: Full scan\n" +
|
||||
" - Basic: Basic scan(Web/FTP/SSH)\n" +
|
||||
" - Database: Database scan\n" +
|
||||
" - Web: Web service scan\n" +
|
||||
" - Service: Common service scan\n" +
|
||||
" - Vul: Vulnerability scan\n" +
|
||||
" - Port: Port scan\n" +
|
||||
" - ICMP: Alive detection\n" +
|
||||
" - Local: Local info\n" +
|
||||
"Single scan:\n" +
|
||||
" - web/db: mysql,redis etc\n" +
|
||||
" - service: ftp,ssh etc\n" +
|
||||
" - vul: ms17010 etc",
|
||||
LangEN: "Specify scan plugins to use:\n" +
|
||||
" - All: Use all non-sensitive plugins\n" +
|
||||
" - Single plugin: e.g., ssh, redis, mysql\n" +
|
||||
" - Multiple plugins: comma-separated, e.g., ssh,ftp,redis\n\n" +
|
||||
"Plugin categories:\n" +
|
||||
" - Services: ssh, ftp, telnet, smb, rdp, vnc...\n" +
|
||||
" - Databases: mysql, redis, mongodb, postgres...\n" +
|
||||
" - Web: webtitle, webpoc...\n" +
|
||||
" - Vulnerabilities: ms17010...\n" +
|
||||
" - Local: localinfo, dcinfo, minidump (must be explicitly specified)",
|
||||
|
||||
LangJA: "スキャンモードを指定:\n" +
|
||||
"プリセットモード:\n" +
|
||||
" - All: フルスキャン\n" +
|
||||
" - Basic: 基本スキャン(Web/FTP/SSH)\n" +
|
||||
" - Database: データベーススキャン\n" +
|
||||
" - Web: Webサービススキャン\n" +
|
||||
" - Service: 一般サービススキャン\n" +
|
||||
" - Vul: 脆弱性スキャン\n" +
|
||||
" - Port: ポートスキャン\n" +
|
||||
" - ICMP: 生存確認\n" +
|
||||
" - Local: ローカル情報\n" +
|
||||
"単一スキャン:\n" +
|
||||
" - web/db: mysql,redis など\n" +
|
||||
" - service: ftp,ssh など\n" +
|
||||
" - vul: ms17010 など",
|
||||
LangJA: "使用するスキャンプラグインを指定:\n" +
|
||||
" - All: すべての非機密プラグインを使用\n" +
|
||||
" - 単一プラグイン: 例 ssh, redis, mysql\n" +
|
||||
" - 複数プラグイン: カンマ区切り、例 ssh,ftp,redis\n\n" +
|
||||
"プラグインカテゴリ:\n" +
|
||||
" - サービス: ssh, ftp, telnet, smb, rdp, vnc...\n" +
|
||||
" - データベース: mysql, redis, mongodb, postgres...\n" +
|
||||
" - Web: webtitle, webpoc...\n" +
|
||||
" - 脆弱性: ms17010...\n" +
|
||||
" - ローカル: localinfo, dcinfo, minidump (明示的に指定が必要)",
|
||||
|
||||
LangRU: "Укажите режим сканирования:\n" +
|
||||
"Предустановки:\n" +
|
||||
" - All: Полное сканирование\n" +
|
||||
" - Basic: Базовое сканирование(Web/FTP/SSH)\n" +
|
||||
" - Database: Сканирование БД\n" +
|
||||
" - Web: Веб-сервисы\n" +
|
||||
" - Service: Общие службы\n" +
|
||||
" - Vul: Уязвимости\n" +
|
||||
" - Port: Порты\n" +
|
||||
" - ICMP: Обнаружение\n" +
|
||||
" - Local: Локальная информация\n" +
|
||||
"Одиночное сканирование:\n" +
|
||||
" - web/db: mysql,redis и др\n" +
|
||||
" - service: ftp,ssh и др\n" +
|
||||
" - vul: ms17010 и др",
|
||||
LangRU: "Укажите используемые плагины сканирования:\n" +
|
||||
" - All: Использовать все неконфиденциальные плагины\n" +
|
||||
" - Один плагин: например, ssh, redis, mysql\n" +
|
||||
" - Несколько плагинов: через запятую, например ssh,ftp,redis\n\n" +
|
||||
"Категории плагинов:\n" +
|
||||
" - Сервисы: ssh, ftp, telnet, smb, rdp, vnc...\n" +
|
||||
" - Базы данных: mysql, redis, mongodb, postgres...\n" +
|
||||
" - Веб: webtitle, webpoc...\n" +
|
||||
" - Уязвимости: ms17010...\n" +
|
||||
" - Локальные: localinfo, dcinfo, minidump (требуется явное указание)",
|
||||
},
|
||||
|
||||
"flag_exclude_hosts": {
|
||||
LangZH: "排除指定主机范围,支持CIDR格式,如: 192.168.1.1/24",
|
||||
LangEN: "Exclude host ranges, supports CIDR format, e.g.: 192.168.1.1/24",
|
||||
@@ -368,6 +349,20 @@ var i18nMap = map[string]map[string]string{
|
||||
LangRU: "Показать только указанное количество активных хостов",
|
||||
},
|
||||
|
||||
"flag_module_thread_num": {
|
||||
LangZH: "设置每个模块的最大线程数(默认:10)",
|
||||
LangEN: "Set maximum threads per module (default:10)",
|
||||
LangJA: "モジュールごとの最大スレッド数を設定(デフォルト:10)",
|
||||
LangRU: "Установить максимальное количество потоков на модуль (по умолчанию:10)",
|
||||
},
|
||||
|
||||
"flag_global_timeout": {
|
||||
LangZH: "设置全局扫描超时时间(单位:秒,默认:180)",
|
||||
LangEN: "Set global scan timeout (in seconds, default:180)",
|
||||
LangJA: "グローバルスキャンのタイムアウトを設定(秒単位、デフォルト:180)",
|
||||
LangRU: "Установить глобальный таймаут сканирования (в секундах, по умолчанию:180)",
|
||||
},
|
||||
|
||||
"flag_disable_ping": {
|
||||
LangZH: "禁用主机存活探测",
|
||||
LangEN: "Disable host alive detection",
|
||||
@@ -382,14 +377,7 @@ var i18nMap = map[string]map[string]string{
|
||||
LangRU: "Использовать системную команду ping вместо ICMP-зондирования",
|
||||
},
|
||||
|
||||
"flag_command": {
|
||||
LangZH: "指定要执行的系统命令(支持ssh和wmiexec)",
|
||||
LangEN: "Specify system command to execute (supports ssh and wmiexec)",
|
||||
LangJA: "実行するシステムコマンドを指定(sshとwmiexecをサポート)",
|
||||
LangRU: "Указать системную команду для выполнения (поддерживает ssh и wmiexec)",
|
||||
},
|
||||
|
||||
"flag_skip_fingerprint": {
|
||||
"flag_enable_fingerprint": {
|
||||
LangZH: "跳过端口指纹识别",
|
||||
LangEN: "Skip port fingerprint identification",
|
||||
LangJA: "ポートフィンガープリント識別をスキップ",
|
||||
@@ -638,13 +626,6 @@ var i18nMap = map[string]map[string]string{
|
||||
LangRU: "Отключить цветной вывод",
|
||||
},
|
||||
|
||||
"flag_json_format": {
|
||||
LangZH: "以JSON格式输出结果",
|
||||
LangEN: "Output results in JSON format",
|
||||
LangJA: "結果をJSON形式で出力",
|
||||
LangRU: "Вывести результаты в формате JSON",
|
||||
},
|
||||
|
||||
"flag_log_level": {
|
||||
LangZH: "日志输出级别(ALL/SUCCESS/ERROR/INFO/DEBUG)",
|
||||
LangEN: "Log output level (ALL/SUCCESS/ERROR/INFO/DEBUG)",
|
||||
@@ -658,6 +639,21 @@ var i18nMap = map[string]map[string]string{
|
||||
LangJA: "プログレスバー表示を有効化",
|
||||
LangRU: "Включить отображение индикатора выполнения",
|
||||
},
|
||||
|
||||
"flag_show_scan_plan": {
|
||||
LangZH: "显示扫描计划详情",
|
||||
LangEN: "Show scan plan details",
|
||||
LangJA: "スキャン計画の詳細を表示する",
|
||||
LangRU: "Показать детали плана сканирования",
|
||||
},
|
||||
|
||||
"flag_slow_log_output": {
|
||||
LangZH: "启用慢速日志输出,便于肉眼观察",
|
||||
LangEN: "Enable slow log output for better visual observation",
|
||||
LangJA: "目視観察のための低速ログ出力を有効にする",
|
||||
LangRU: "Включить медленный вывод журнала для лучшего визуального наблюдения",
|
||||
},
|
||||
|
||||
"no_username_specified": {
|
||||
LangZH: "加载用户名: %d 个",
|
||||
LangEN: "Loaded usernames: %d",
|
||||
@@ -754,12 +750,7 @@ var i18nMap = map[string]map[string]string{
|
||||
LangJA: "パラメータ -h、-u、-local は同時に使用できません",
|
||||
LangRU: "Параметры -h, -u, -local нельзя использовать одновременно",
|
||||
},
|
||||
//"brute_threads": {
|
||||
// LangZH: "暴力破解线程数: %d",
|
||||
// LangEN: "Brute force threads: %d",
|
||||
// LangJA: "ブルートフォーススレッド数: %d",
|
||||
// LangRU: "Потоков для брутфорса: %d",
|
||||
//},
|
||||
|
||||
"extra_ports": {
|
||||
LangZH: "额外端口: %s",
|
||||
LangEN: "Extra ports: %s",
|
||||
@@ -999,42 +990,6 @@ var i18nMap = map[string]map[string]string{
|
||||
LangJA: "有効なポート数: %d",
|
||||
LangRU: "Количество действительных портов: %d",
|
||||
},
|
||||
"parse_scan_mode": {
|
||||
LangZH: "解析扫描模式: %s",
|
||||
LangEN: "Parse scan mode: %s",
|
||||
LangJA: "スキャンモードを解析: %s",
|
||||
LangRU: "Разбор режима сканирования: %s",
|
||||
},
|
||||
"using_preset_mode": {
|
||||
LangZH: "使用预设模式: %s",
|
||||
LangEN: "Using preset mode: %s",
|
||||
LangJA: "プリセットモードを使用: %s",
|
||||
LangRU: "Использование предустановленного режима: %s",
|
||||
},
|
||||
"using_preset_mode_plugins": {
|
||||
LangZH: "使用预设模式: %s, 包含插件: %v",
|
||||
LangEN: "Using preset mode: %s, included plugins: %v",
|
||||
LangJA: "プリセットモードを使用: %s, 含まれるプラグイン: %v",
|
||||
LangRU: "Использование предустановленного режима: %s, включенные плагины: %v",
|
||||
},
|
||||
"using_single_plugin": {
|
||||
LangZH: "使用单个插件: %s",
|
||||
LangEN: "Using single plugin: %s",
|
||||
LangJA: "単一のプラグインを使用: %s",
|
||||
LangRU: "Использование одного плагина: %s",
|
||||
},
|
||||
"using_default_mode": {
|
||||
LangZH: "未识别的模式,使用默认模式: %s",
|
||||
LangEN: "Unrecognized mode, using default mode: %s",
|
||||
LangJA: "認識できないモード、デフォルトモードを使用: %s",
|
||||
LangRU: "Нераспознанный режим, использование режима по умолчанию: %s",
|
||||
},
|
||||
"included_plugins": {
|
||||
LangZH: "包含插件: %v",
|
||||
LangEN: "Included plugins: %v",
|
||||
LangJA: "含まれるプラグイン: %v",
|
||||
LangRU: "Включенные плагины: %v",
|
||||
},
|
||||
"tcp_conn_failed": {
|
||||
LangZH: "建立TCP连接失败: %v",
|
||||
LangEN: "Failed to establish TCP connection: %v",
|
||||
|
||||
Reference in New Issue
Block a user