github.com/chainreactors/fingers@v1.2.1/nmap/engine.go (about)

     1  package gonmap
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/chainreactors/fingers/common"
     8  	"github.com/chainreactors/fingers/resources"
     9  )
    10  
    11  type NmapEngine struct {
    12  	nmap *Nmap
    13  }
    14  
    15  // NewNmapEngine 创建新的 nmap 引擎实例
    16  func NewNmapEngine(probesData, servicesData []byte) (*NmapEngine, error) {
    17  	// 解压缩 gzip 数据
    18  	decompressedProbes, err := resources.DecompressGzip(probesData)
    19  	if err != nil {
    20  		// 如果解压失败,尝试直接使用原始数据(可能已经是解压后的)
    21  		decompressedProbes = probesData
    22  	}
    23  
    24  	decompressedServices, err := resources.DecompressGzip(servicesData)
    25  	if err != nil {
    26  		// 如果解压失败,尝试直接使用原始数据(可能已经是解压后的)
    27  		decompressedServices = servicesData
    28  	}
    29  
    30  	// 手动初始化nmap实例,传入已解压的数据
    31  	n := NewWithData(decompressedProbes, decompressedServices)
    32  
    33  	return &NmapEngine{
    34  		nmap: n,
    35  	}, nil
    36  }
    37  
    38  // Name 实现 EngineImpl 接口
    39  func (e *NmapEngine) Name() string {
    40  	return "nmap"
    41  }
    42  
    43  // Compile 实现 EngineImpl 接口
    44  func (e *NmapEngine) Compile() error {
    45  	// gonmap 在 init() 中已经完成编译,这里不需要额外操作
    46  	return nil
    47  }
    48  
    49  // Len 实现 EngineImpl 接口
    50  func (e *NmapEngine) Len() int {
    51  	// 返回 nmap 指纹库的总指纹数
    52  	return len(e.nmap.probeNameMap)
    53  }
    54  
    55  // WebMatch 实现Web指纹匹配 - nmap不支持Web指纹
    56  func (e *NmapEngine) WebMatch(content []byte) common.Frameworks {
    57  	// nmap不支持Web指纹识别
    58  	return make(common.Frameworks)
    59  }
    60  
    61  // ServiceMatch 实现Service指纹匹配
    62  func (e *NmapEngine) ServiceMatch(host string, portStr string, level int, sender common.ServiceSender, callback common.ServiceCallback) *common.ServiceResult {
    63  	if sender == nil || level <= 0 {
    64  		return nil
    65  	}
    66  
    67  	// 创建适配器将common.ServiceSender转换为nmap内部sender格式
    68  	// 注意:这个adapter需要支持probe的Protocol字段(TCP/UDP)
    69  	nmapSender := func(host string, port int, data []byte, requestTLS bool, probeProtocol string) ([]byte, bool, error) {
    70  		// 根据probe的Protocol字段、TLS需求和端口特性选择网络协议
    71  		network := "tcp"
    72  
    73  		// 首先检查probe的协议类型
    74  		if strings.ToUpper(probeProtocol) == "UDP" {
    75  			network = "udp"
    76  		} else if requestTLS || isHTTPSPort(port) {
    77  			network = "tls"
    78  		}
    79  
    80  		// 构造正确的端口字符串,UDP需要加U:前缀
    81  		actualPortStr := portStr
    82  		if network == "udp" {
    83  			// 确保UDP端口有U:前缀
    84  			if !strings.HasPrefix(strings.ToUpper(portStr), "U:") {
    85  				actualPortStr = fmt.Sprintf("U:%s", portStr)
    86  			}
    87  		}
    88  
    89  		// 使用ServiceSender发送数据
    90  		response, err := sender.Send(host, actualPortStr, data, network)
    91  		if err != nil {
    92  			// 如果TLS失败,尝试普通TCP
    93  			if network == "tls" {
    94  				response, err = sender.Send(host, portStr, data, "tcp")
    95  				if err == nil {
    96  					return response, false, nil // 成功但不是TLS
    97  				}
    98  			}
    99  			return nil, false, err
   100  		}
   101  
   102  		// 返回响应和实际使用的协议类型
   103  		actualTLS := (network == "tls")
   104  		return response, actualTLS, nil
   105  	}
   106  
   107  	// 解析端口字符串获取端口号(用于其他逻辑)
   108  	portNum, _, _ := e.nmap.parsePortString(portStr)
   109  	
   110  	// 使用nmap的完整扫描逻辑,但网络发送由外部sender控制
   111  	status, response := e.nmap.Scan(host, portStr, level, nmapSender)
   112  
   113  
   114  	var framework *common.Framework
   115  
   116  	if status == Matched && response != nil && response.FingerPrint != nil {
   117  		// 扫描成功,获取多个Framework(支持多个CPE app)
   118  		frameworks := response.FingerPrint.ToFrameworks()
   119  		if len(frameworks) > 0 {
   120  			framework = frameworks[0] // 取第一个Framework作为主要结果
   121  		}
   122  	} else if status == Open && !common.NoGuess {
   123  		// 端口开放但无法识别服务,使用guess功能猜测服务
   124  		guessedProtocol := e.nmap.GuessProtocol(portNum)
   125  		if guessedProtocol != "" && guessedProtocol != "unknown" {
   126  			// 创建基于猜测的Framework
   127  			framework = common.NewFramework(FixProtocol(guessedProtocol), common.FrameFromGUESS)
   128  			// 添加guess标记(使用AddTag避免重复)
   129  			framework.AddTag("guess")
   130  		}
   131  	}
   132  	// 如果status是Closed或其他状态,framework保持为nil,表示端口未开放或无法连接
   133  
   134  	if framework == nil {
   135  		return nil
   136  	}
   137  
   138  	result := &common.ServiceResult{
   139  		Framework: framework,
   140  		Vuln:      nil, // nmap一般不直接返回漏洞信息
   141  	}
   142  
   143  	// 调用回调函数
   144  	if callback != nil {
   145  		callback(result)
   146  	}
   147  
   148  	return result
   149  }
   150  
   151  // matchResponse 使用nmap指纹库分析响应数据
   152  func (e *NmapEngine) matchResponse(responseData []byte, host string, port int) *common.Framework {
   153  	// 使用nmap的指纹匹配逻辑
   154  	// 调用nmap的核心指纹识别函数,不涉及网络请求
   155  	fingerPrint := e.nmap.getFinger(responseData, false, "")
   156  
   157  	if fingerPrint != nil && fingerPrint.Service != "" {
   158  		frameworks := fingerPrint.ToFrameworks()
   159  		if len(frameworks) > 0 {
   160  			return frameworks[0] // 返回第一个Framework
   161  		}
   162  	}
   163  	return nil
   164  }
   165  
   166  // Capability 实现 EngineImpl 接口 - 返回引擎能力
   167  func (e *NmapEngine) Capability() common.EngineCapability {
   168  	return common.EngineCapability{
   169  		SupportWeb:     false, // nmap不支持Web指纹
   170  		SupportService: true,  // nmap支持Service指纹
   171  	}
   172  }
   173  
   174  // isHTTPSPort 判断是否是常见的HTTPS端口
   175  func isHTTPSPort(port int) bool {
   176  	httpsports := []int{443, 8443, 993, 995, 465, 636, 989, 990, 992, 993, 994, 995, 5986}
   177  	for _, p := range httpsports {
   178  		if port == p {
   179  			return true
   180  		}
   181  	}
   182  	return false
   183  }