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  }