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 }