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

     1  package fingers
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"fmt"
     7  	"github.com/chainreactors/fingers/alias"
     8  	"github.com/chainreactors/fingers/common"
     9  	"github.com/chainreactors/fingers/ehole"
    10  	"github.com/chainreactors/fingers/favicon"
    11  	"github.com/chainreactors/fingers/fingerprinthub"
    12  	"github.com/chainreactors/fingers/fingers"
    13  	"github.com/chainreactors/fingers/goby"
    14  	gonmap "github.com/chainreactors/fingers/nmap"
    15  	"github.com/chainreactors/fingers/resources"
    16  	wappalyzer "github.com/chainreactors/fingers/wappalyzer"
    17  	xrayengine "github.com/chainreactors/fingers/xray"
    18  	"github.com/chainreactors/utils/httputils"
    19  	"github.com/pkg/errors"
    20  	"net/http"
    21  	"strings"
    22  )
    23  
    24  const (
    25  	FaviconEngine     = "favicon"
    26  	FingersEngine     = "fingers"
    27  	FingerPrintEngine = "fingerprinthub"
    28  	WappalyzerEngine  = "wappalyzer"
    29  	EHoleEngine       = "ehole"
    30  	GobyEngine        = "goby"
    31  	NmapEngine        = "nmap"
    32  	XrayEngine        = "xray"
    33  )
    34  
    35  var (
    36  	AllEngines           = []string{FingersEngine, FingerPrintEngine, WappalyzerEngine, EHoleEngine, GobyEngine, NmapEngine, XrayEngine, FaviconEngine}
    37  	DefaultEnableEngines = AllEngines
    38  
    39  	NotFoundEngine = errors.New("engine not found")
    40  )
    41  
    42  func NewEngine(engines ...string) (*Engine, error) {
    43  	if engines == nil {
    44  		engines = DefaultEnableEngines
    45  	}
    46  	engine := &Engine{
    47  		EnginesImpl:  make(map[string]EngineImpl),
    48  		Enabled:      make(map[string]bool),
    49  		Capabilities: make(map[string]common.EngineCapability),
    50  	}
    51  	var err error
    52  
    53  	err = engine.InitEngine(FaviconEngine)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  	for _, name := range engines {
    58  		err = engine.InitEngine(name)
    59  		if err != nil {
    60  			return nil, err
    61  		}
    62  	}
    63  
    64  	err = engine.Compile()
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  	return engine, nil
    69  }
    70  
    71  type EngineImpl interface {
    72  	Name() string
    73  	Compile() error
    74  	Len() int
    75  	Capability() common.EngineCapability
    76  
    77  	// Web指纹匹配 - 基于HTTP响应内容
    78  	WebMatch(content []byte) common.Frameworks
    79  
    80  	// Service指纹匹配 - 主动探测服务
    81  	ServiceMatch(host string, portStr string, level int, sender common.ServiceSender, callback common.ServiceCallback) *common.ServiceResult
    82  }
    83  
    84  type Engine struct {
    85  	EnginesImpl map[string]EngineImpl
    86  	*alias.Aliases
    87  	Enabled      map[string]bool
    88  	Capabilities map[string]common.EngineCapability // 新增:记录各引擎能力
    89  }
    90  
    91  func (engine *Engine) String() string {
    92  	var s strings.Builder
    93  	for name, impl := range engine.EnginesImpl {
    94  		s.WriteString(fmt.Sprintf(" %s:%d", name, impl.Len()))
    95  	}
    96  	return strings.TrimSpace(s.String())
    97  }
    98  
    99  func (engine *Engine) Compile() error {
   100  	// 从所有引擎中填充Favicon引擎的数据
   101  	if impl := engine.Fingers(); impl != nil {
   102  		for hash, name := range impl.Favicons.Md5Fingers {
   103  			engine.Favicon().Md5Fingers[hash] = name
   104  		}
   105  		for hash, name := range impl.Favicons.Mmh3Fingers {
   106  			engine.Favicon().Mmh3Fingers[hash] = name
   107  		}
   108  	}
   109  
   110  	// FingerPrintHub (v4) 使用 neutron 内置的 favicon 匹配,不需要单独处理
   111  
   112  	if impl := engine.EHole(); impl != nil {
   113  		for hash, name := range impl.FaviconMap {
   114  			engine.Favicon().Mmh3Fingers[hash] = name
   115  		}
   116  	}
   117  
   118  	engine.Enabled[FaviconEngine] = false // 默认faviconEngine与其他引擎不同时使用
   119  
   120  	// 将fingers指纹库的数据作为未配置alias的基准值
   121  	var aliases []*alias.Alias
   122  	if impl := engine.Fingers(); impl != nil {
   123  		for _, finger := range impl.HTTPFingers {
   124  			aliases = append(aliases, &alias.Alias{
   125  				Name:       finger.Name,
   126  				Attributes: finger.Attributes,
   127  				AliasMap: map[string][]string{
   128  					"fingers": []string{finger.Name},
   129  				},
   130  			})
   131  		}
   132  	}
   133  
   134  	var err error
   135  	engine.Aliases, err = alias.NewAliases(aliases...)
   136  	if err != nil {
   137  		return err
   138  	}
   139  	return nil
   140  }
   141  
   142  func (engine *Engine) Register(impl EngineImpl) bool {
   143  	if impl == nil {
   144  		return false
   145  	}
   146  	name := impl.Name()
   147  	engine.EnginesImpl[name] = impl
   148  	engine.Enabled[name] = true
   149  	engine.Capabilities[name] = impl.Capability() // 自动记录引擎能力
   150  	return true
   151  }
   152  
   153  func (engine *Engine) InitEngine(name string) error {
   154  	var err error
   155  	var impl EngineImpl
   156  	if _, ok := engine.EnginesImpl[name]; !ok {
   157  		switch name {
   158  		case FingersEngine:
   159  			impl, err = fingers.NewFingersEngine(
   160  				resources.FingersHTTPData,
   161  				resources.FingersSocketData,
   162  				resources.PortData,
   163  			)
   164  		case FingerPrintEngine:
   165  			impl, err = fingerprinthub.NewFingerPrintHubEngine(
   166  				resources.FingerprinthubWebData,
   167  				resources.FingerprinthubServiceData,
   168  			)
   169  		case WappalyzerEngine:
   170  			impl, err = wappalyzer.NewWappalyzeEngine(resources.WappalyzerData)
   171  		case EHoleEngine:
   172  			impl, err = ehole.NewEHoleEngine(resources.EholeData)
   173  		case GobyEngine:
   174  			impl, err = goby.NewGobyEngine(resources.GobyData)
   175  		case NmapEngine:
   176  			impl, err = gonmap.NewNmapEngine(
   177  				resources.NmapServiceProbesData,
   178  				resources.NmapServicesData,
   179  			)
   180  		case XrayEngine:
   181  			impl, err = xrayengine.NewXrayEngine(resources.XrayWebData)
   182  		case FaviconEngine:
   183  			impl = favicon.NewFavicons()
   184  		default:
   185  			return NotFoundEngine
   186  		}
   187  		if err != nil {
   188  			return err
   189  		}
   190  		engine.Register(impl)
   191  	}
   192  
   193  	engine.Enabled[name] = true
   194  	return nil
   195  }
   196  
   197  func (engine *Engine) Enable(name string) {
   198  	if _, ok := engine.EnginesImpl[name]; ok {
   199  		engine.Enabled[name] = true
   200  	}
   201  }
   202  
   203  func (engine *Engine) Disable(name string) {
   204  	engine.Enabled[name] = false
   205  }
   206  
   207  func (engine *Engine) Fingers() *fingers.FingersEngine {
   208  	if impl, ok := engine.EnginesImpl[FingersEngine]; ok {
   209  		return impl.(*fingers.FingersEngine)
   210  	}
   211  	return nil
   212  }
   213  
   214  func (engine *Engine) Favicon() *favicon.FaviconsEngine {
   215  	if impl, ok := engine.EnginesImpl[FaviconEngine]; ok {
   216  		return impl.(*favicon.FaviconsEngine)
   217  	}
   218  	return nil
   219  }
   220  
   221  func (engine *Engine) FingerPrintHub() *fingerprinthub.FingerPrintHubEngine {
   222  	if impl, ok := engine.EnginesImpl[FingerPrintEngine]; ok {
   223  		return impl.(*fingerprinthub.FingerPrintHubEngine)
   224  	}
   225  	return nil
   226  }
   227  
   228  func (engine *Engine) Wappalyzer() *wappalyzer.Wappalyze {
   229  	if impl, ok := engine.EnginesImpl[WappalyzerEngine]; ok {
   230  		return impl.(*wappalyzer.Wappalyze)
   231  	}
   232  	return nil
   233  }
   234  
   235  func (engine *Engine) EHole() *ehole.EHoleEngine {
   236  	if impl, ok := engine.EnginesImpl[EHoleEngine]; ok {
   237  		return impl.(*ehole.EHoleEngine)
   238  	}
   239  	return nil
   240  }
   241  
   242  func (engine *Engine) Goby() *goby.GobyEngine {
   243  	if impl, ok := engine.EnginesImpl[GobyEngine]; ok {
   244  		return impl.(*goby.GobyEngine)
   245  	}
   246  	return nil
   247  }
   248  
   249  func (engine *Engine) Xray() *xrayengine.XrayEngine {
   250  	if impl, ok := engine.EnginesImpl[XrayEngine]; ok {
   251  		return impl.(*xrayengine.XrayEngine)
   252  	}
   253  	return nil
   254  }
   255  
   256  func (engine *Engine) Nmap() *gonmap.NmapEngine {
   257  	if impl, ok := engine.EnginesImpl[NmapEngine]; ok {
   258  		return impl.(*gonmap.NmapEngine)
   259  	}
   260  	return nil
   261  }
   262  
   263  func (engine *Engine) GetEngine(name string) EngineImpl {
   264  	if enabled, _ := engine.Enabled[name]; enabled {
   265  		return engine.EnginesImpl[name]
   266  	}
   267  	return nil
   268  }
   269  
   270  // GetEnginesByType 根据指纹类型获取支持的引擎列表
   271  func (engine *Engine) GetEnginesByType(fpType common.FingerprintType) []string {
   272  	var engines []string
   273  	for name, capability := range engine.Capabilities {
   274  		if !engine.Enabled[name] {
   275  			continue
   276  		}
   277  		switch fpType {
   278  		case common.WebFingerprint:
   279  			if capability.SupportWeb {
   280  				engines = append(engines, name)
   281  			}
   282  		case common.ServiceFingerprint:
   283  			if capability.SupportService {
   284  				engines = append(engines, name)
   285  			}
   286  		}
   287  	}
   288  	return engines
   289  }
   290  
   291  // MatchByType 根据指纹类型进行匹配
   292  func (engine *Engine) MatchByType(resp *http.Response, fpType common.FingerprintType) common.Frameworks {
   293  	engines := engine.GetEnginesByType(fpType)
   294  	return engine.MatchWithEngines(resp, engines...)
   295  }
   296  
   297  // Match use http.Response for web fingerprinting (deprecated, use WebMatch instead)
   298  func (engine *Engine) Match(resp *http.Response) common.Frameworks {
   299  	return engine.WebMatch(resp)
   300  }
   301  
   302  // WebMatch 专门用于Web指纹识别 - 保留原有性能优化
   303  func (engine *Engine) WebMatch(resp *http.Response) common.Frameworks {
   304  	content := httputils.ReadRaw(resp)
   305  	// lower content for performance optimization
   306  	lower := bytes.ToLower(content)
   307  	body, header, _ := httputils.SplitHttpRaw(lower)
   308  	combined := make(common.Frameworks)
   309  
   310  	for name, ok := range engine.Enabled {
   311  		if !ok {
   312  			continue
   313  		}
   314  
   315  		// Check if engine supports web fingerprinting
   316  		if !engine.Capabilities[name].SupportWeb {
   317  			continue
   318  		}
   319  
   320  		var fs common.Frameworks
   321  		switch name {
   322  		case FingersEngine:
   323  			var cert string
   324  			if resp.TLS != nil && len(resp.TLS.PeerCertificates) > 0 {
   325  				cert = strings.Join(resp.TLS.PeerCertificates[0].DNSNames, ",")
   326  			}
   327  			fs, _ = engine.Fingers().HTTPMatch(lower, cert)
   328  		case WappalyzerEngine:
   329  			fs = engine.Wappalyzer().Fingerprint(resp.Header, body)
   330  		case FingerPrintEngine:
   331  			// 新版 fingerprinthub 使用 WebMatch 接口
   332  			fs = engine.FingerPrintHub().WebMatch(content)
   333  		case EHoleEngine:
   334  			fs = engine.EHole().MatchWithHeaderAndBody(string(header), string(body))
   335  		case GobyEngine:
   336  			fs = engine.Goby().MatchRaw(string(lower))
   337  		case FaviconEngine:
   338  			// Favicon engine is handled separately via MatchFavicon
   339  			continue
   340  		default:
   341  			// For any other engines, use the generic WebMatch interface
   342  			if impl, exists := engine.EnginesImpl[name]; exists {
   343  				fs = impl.WebMatch(content)
   344  			}
   345  		}
   346  
   347  		combined = engine.MergeFrameworks(combined, fs)
   348  	}
   349  	return combined
   350  }
   351  
   352  // ServiceMatch 专门用于Service指纹识别
   353  func (engine *Engine) ServiceMatch(host string, portStr string, level int, sender common.ServiceSender, callback common.ServiceCallback) []*common.ServiceResult {
   354  	var results []*common.ServiceResult
   355  	engines := engine.GetEnginesByType(common.ServiceFingerprint)
   356  
   357  	for _, engineName := range engines {
   358  		if eng := engine.GetEngine(engineName); eng != nil {
   359  			result := eng.ServiceMatch(host, portStr, level, sender, callback)
   360  			if result != nil && result.Framework != nil {
   361  				results = append(results, result)
   362  			}
   363  		}
   364  	}
   365  	return results
   366  }
   367  
   368  // WebMatchWithEngines 用指定的引擎进行Web指纹匹配
   369  func (engine *Engine) WebMatchWithEngines(content []byte, engines ...string) common.Frameworks {
   370  	combined := make(common.Frameworks)
   371  	for _, name := range engines {
   372  		if impl, ok := engine.EnginesImpl[name]; ok && engine.Capabilities[name].SupportWeb {
   373  			fs := impl.WebMatch(content)
   374  			combined = engine.MergeFrameworks(combined, fs)
   375  		}
   376  	}
   377  	return combined
   378  }
   379  
   380  // MatchWithEngines (deprecated, use WebMatchWithEngines instead)
   381  func (engine *Engine) MatchWithEngines(resp *http.Response, engines ...string) common.Frameworks {
   382  	content := httputils.ReadRaw(resp)
   383  	return engine.WebMatchWithEngines(content, engines...)
   384  }
   385  
   386  func (engine *Engine) MatchFavicon(content []byte) common.Frameworks {
   387  	favEngine := engine.Favicon()
   388  	if favEngine != nil {
   389  		return favEngine.WebMatch(content)
   390  	}
   391  	return make(common.Frameworks)
   392  }
   393  
   394  func (engine *Engine) MergeFrameworks(origin, other common.Frameworks) common.Frameworks {
   395  	for _, frame := range other {
   396  		aliasFrame, ok := engine.Aliases.FindFramework(frame)
   397  		if aliasFrame != nil {
   398  			if ok {
   399  				frame.Name = aliasFrame.Name
   400  				frame.UpdateAttributes(aliasFrame.ToWFN())
   401  			}
   402  			if aliasFrame.IsBlocked(frame.From.String()) {
   403  				continue
   404  			}
   405  		}
   406  		origin.Add(frame)
   407  	}
   408  	return origin
   409  }
   410  
   411  // DetectResponse Web指纹检测 - 基于HTTP响应
   412  func (engine *Engine) DetectResponse(resp *http.Response) (common.Frameworks, error) {
   413  	return engine.WebMatch(resp), nil
   414  }
   415  
   416  // DetectContent Web指纹检测 - 基于原始HTTP内容
   417  func (engine *Engine) DetectContent(content []byte) (common.Frameworks, error) {
   418  	resp, err := httputils.ReadResponse(bufio.NewReader(bytes.NewReader(content)))
   419  	if err != nil {
   420  		return nil, err
   421  	}
   422  	return engine.WebMatch(resp), nil
   423  }
   424  
   425  // DetectService Service指纹检测 - 基于主动探测
   426  func (engine *Engine) DetectService(host string, portStr string, level int, sender common.ServiceSender, callback common.ServiceCallback) ([]*common.ServiceResult, error) {
   427  	results := engine.ServiceMatch(host, portStr, level, sender, callback)
   428  	return results, nil
   429  }
   430  
   431  // DetectFavicon Favicon指纹检测
   432  func (engine *Engine) DetectFavicon(content []byte) *common.Framework {
   433  	return engine.Favicon().WebMatch(content).One()
   434  }