github.com/chainreactors/fingers@v1.2.1/nmap/type-probe.go (about) 1 package gonmap 2 3 import ( 4 "bytes" 5 "errors" 6 "regexp" 7 "strconv" 8 "strings" 9 ) 10 11 type Probe struct { 12 //探针级别 13 Rarity int `json:"rarity"` 14 //探针名称 15 Name string `json:"name"` 16 //探针适用默认端口号 17 Ports PortList `json:"ports"` 18 //探针适用SSL端口号 19 SSLPorts PortList `json:"ssl_ports"` 20 21 //totalwaitms time.Duration 22 //tcpwrappedms time.Duration 23 24 //探针对应指纹库 25 MatchGroup []*Match `json:"matches"` 26 //探针指纹库若匹配失败,则会尝试使用fallback指定探针的指纹库 27 Fallback string `json:"fallback,omitempty"` 28 29 //探针发送协议类型 30 Protocol string `json:"protocol"` 31 //探针发送数据 32 SendRaw []byte `json:"probe_string"` 33 } 34 35 // buildRequest 构建探测请求数据 36 func (p *Probe) buildRequest(host string) []byte { 37 sendRaw := p.SendRaw 38 // 替换模板变量 39 sendRaw = bytes.ReplaceAll(sendRaw, []byte("{Host}"), []byte(host)) 40 return sendRaw 41 } 42 43 // 原scan方法保留但现在不使用timeout 44 //func (p *Probe) scan(host string, port int, tls bool, timeout time.Duration, size int) (string, bool, error) { 45 // uri := fmt.Sprintf("%s:%d", host, port) 46 // 47 // sendRaw := strings.Replace(p.SendRaw, "{Host}", fmt.Sprintf("%s:%d", host, port), -1) 48 // 49 // text, err := simplenet.Send(p.Protocol, tls, uri, sendRaw, timeout, size) 50 // if err == nil { 51 // return text, tls, nil 52 // } 53 // if strings.Contains(err.Error(), "STEP1") && tls == true { 54 // text, err := simplenet.Send(p.Protocol, false, uri, p.SendRaw, timeout, size) 55 // return text, false, err 56 // } 57 // return text, tls, err 58 //} 59 60 func (p *Probe) match(data []byte) *FingerPrint { 61 var f = &FingerPrint{} 62 var softFilter string 63 64 // Convert []byte to string for regex matching using safe conversion 65 s := convResponseBytes(data) 66 67 for _, m := range p.MatchGroup { 68 //实现软筛选 69 if softFilter != "" { 70 if m.Service != softFilter { 71 continue 72 } 73 } 74 //logger.Println("开始匹配正则:", m.service, m.patternRegexp.String()) 75 isMatch, _ := m.PatternRegexp.MatchString(s) 76 if isMatch { 77 //标记当前正则 78 f.MatchRegexString = m.PatternRegexp.String() 79 if m.Soft { 80 //如果为软捕获,这设置筛选器 81 f.Service = m.Service 82 softFilter = m.Service 83 continue 84 } else { 85 //如果为硬捕获则直接获取指纹信息 86 m.makeVersionInfo(s, f) 87 f.Service = m.Service 88 return f 89 } 90 } 91 } 92 return f 93 } 94 95 // convResponseBytes 将[]byte安全转换为string用于正则匹配 96 // 为了适配go语言的正则,将二进制强行转换成UTF-8兼容格式 97 func convResponseBytes(b1 []byte) string { 98 var r1 []rune 99 for _, i := range b1 { 100 r1 = append(r1, rune(i)) 101 } 102 return string(r1) 103 } 104 105 var probeExprRegx = regexp.MustCompile("^(UDP|TCP) ([a-zA-Z0-9-_./]+) (?:q\\|([^|]*)\\|)(?:\\s+.*)?$") 106 var probeIntRegx = regexp.MustCompile(`^(\d+)$`) 107 var probeStrRegx = regexp.MustCompile(`^([a-zA-Z0-9-_./, ]+)$`) 108 109 func parseProbe(lines []string) *Probe { 110 var p = &Probe{ 111 Ports: emptyPortList, 112 SSLPorts: emptyPortList, 113 } 114 115 for _, line := range lines { 116 p.loadLine(line) 117 } 118 return p 119 } 120 121 func (p *Probe) loadLine(s string) { 122 //分解命令 123 i := strings.Index(s, " ") 124 commandName := s[:i] 125 commandArgs := s[i+1:] 126 //逐行处理 127 switch commandName { 128 case "Probe": 129 p.loadProbe(commandArgs) 130 case "match": 131 p.loadMatch(commandArgs, false) 132 case "softmatch": 133 p.loadMatch(commandArgs, true) 134 case "ports": 135 p.loadPorts(commandArgs, false) 136 case "sslports": 137 p.loadPorts(commandArgs, true) 138 case "totalwaitms": 139 //p.totalwaitms = time.Duration(p.getInt(commandArgs)) * time.Millisecond 140 case "tcpwrappedms": 141 //p.tcpwrappedms = time.Duration(p.getInt(commandArgs)) * time.Millisecond 142 case "rarity": 143 p.Rarity = p.getInt(commandArgs) 144 case "fallback": 145 p.Fallback = p.getString(commandArgs) 146 } 147 } 148 149 func (p *Probe) loadProbe(s string) { 150 //Probe <protocol> <probename> <probestring> 151 if !probeExprRegx.MatchString(s) { 152 panic(errors.New(s + " probe 语句格式不正确")) 153 } 154 args := probeExprRegx.FindStringSubmatch(s) 155 if args[1] == "" || args[2] == "" { 156 panic(errors.New("probe 参数格式不正确")) 157 } 158 p.Protocol = args[1] 159 p.Name = args[1] + "_" + args[2] 160 str := args[3] 161 str = strings.ReplaceAll(str, `\0`, `\x00`) 162 str = strings.ReplaceAll(str, `"`, `${double-quoted}`) 163 str = `"` + str + `"` 164 str, _ = strconv.Unquote(str) 165 str = strings.ReplaceAll(str, `${double-quoted}`, `"`) 166 p.SendRaw = []byte(str) 167 } 168 169 func (p *Probe) loadMatch(s string, soft bool) { 170 //"match": misc.MakeRegexpCompile("^([a-zA-Z0-9-_./]+) m\\|([^|]+)\\|([is]{0,2}) (.*)$"), 171 //match <Service> <pattern>|<patternopt> [<versioninfo>] 172 // "matchVersioninfoProductname": misc.MakeRegexpCompile("p/([^/]+)/"), 173 // "matchVersioninfoVersion": misc.MakeRegexpCompile("v/([^/]+)/"), 174 // "matchVersioninfoInfo": misc.MakeRegexpCompile("i/([^/]+)/"), 175 // "matchVersioninfoHostname": misc.MakeRegexpCompile("h/([^/]+)/"), 176 // "matchVersioninfoOS": misc.MakeRegexpCompile("o/([^/]+)/"), 177 // "matchVersioninfoDevice": misc.MakeRegexpCompile("d/([^/]+)/"), 178 179 p.MatchGroup = append(p.MatchGroup, parseMatch(s, soft)) 180 } 181 182 func (p *Probe) loadPorts(expr string, ssl bool) { 183 if ssl { 184 p.SSLPorts = parsePortList(expr) 185 } else { 186 p.Ports = parsePortList(expr) 187 } 188 } 189 190 func (p *Probe) getInt(expr string) int { 191 if !probeIntRegx.MatchString(expr) { 192 panic(errors.New("totalwaitms or tcpwrappedms 语句参数不正确")) 193 } 194 i, _ := strconv.Atoi(probeIntRegx.FindStringSubmatch(expr)[1]) 195 return i 196 } 197 198 func (p *Probe) getString(expr string) string { 199 if !probeStrRegx.MatchString(expr) { 200 panic(errors.New(expr + " fallback 语句参数不正确")) 201 } 202 203 // 获取匹配的字符串 204 matched := probeStrRegx.FindStringSubmatch(expr)[1] 205 206 // 如果有多个fallback值(逗号分隔),只取第一个 207 if strings.Contains(matched, ",") { 208 parts := strings.Split(matched, ",") 209 return strings.TrimSpace(parts[0]) 210 } 211 212 return matched 213 } 214 215 // LoadMatch 导出的loadMatch方法,供transform工具使用 216 func (p *Probe) LoadMatch(expr string, isExclude bool) { 217 p.loadMatch(expr, isExclude) 218 }