refactor: 大量重构

This commit is contained in:
ZacharyZcR
2025-04-14 02:36:16 +08:00
parent a2c56ab106
commit 77705118d5
12 changed files with 1484 additions and 1545 deletions
+87 -75
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
-95
View File
@@ -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
View File
@@ -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",