Skip to content

Commit 459b6f4

Browse files
committed
add example
1 parent c776c0d commit 459b6f4

4 files changed

Lines changed: 326 additions & 9 deletions

File tree

example/README.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Fingers Example
2+
3+
这是一个使用 Fingers 引擎进行指纹识别的命令行工具示例。
4+
5+
## 功能特性
6+
7+
- 支持多引擎指纹识别
8+
- 支持 SSL 证书验证跳过
9+
- 支持详细输出模式
10+
- 支持 Favicon 专项检测
11+
- 支持自定义资源文件覆盖
12+
13+
## 使用方法
14+
15+
### 基本用法
16+
17+
```bash
18+
# 基本使用
19+
go run example.go -u https://example.com
20+
21+
# 使用特定引擎
22+
go run example.go -u https://example.com -e fingers,wappalyzer
23+
24+
# 显示详细信息
25+
go run example.go -u https://example.com -v
26+
27+
# 忽略SSL证书验证
28+
go run example.go -u https://example.com -k
29+
30+
# 仅检测favicon
31+
go run example.go -u https://example.com -f
32+
```
33+
34+
### 资源文件覆盖
35+
36+
工具支持覆盖所有内置的指纹库文件:
37+
38+
```bash
39+
# Goby 指纹库
40+
go run example.go -u https://example.com --goby custom_goby.json
41+
42+
# FingerPrintHub 指纹库
43+
go run example.go -u https://example.com --fingerprinthub custom_fingerprinthub.json
44+
45+
# EHole 指纹库
46+
go run example.go -u https://example.com --ehole custom_ehole.json
47+
48+
# Fingers 指纹库
49+
go run example.go -u https://example.com --fingers custom_fingers.json
50+
51+
# Wappalyzer 指纹库
52+
go run example.go -u https://example.com --wappalyzer custom_wappalyzer.json
53+
54+
# Aliases 配置
55+
go run example.go -u https://example.com --aliases custom_aliases.yaml
56+
```
57+
58+
资源文件说明:
59+
60+
- 支持 JSON 和 YAML 格式的资源文件
61+
- JSON 文件会自动转换为 YAML 格式
62+
- 非压缩文件会自动进行 gzip 压缩
63+
- 可以同时覆盖多个资源文件
64+
65+
## 命令行参数
66+
67+
```
68+
应用选项:
69+
-e, --engines= 指定要使用的引擎,多个引擎用逗号分隔 (默认: fingers,fingerprinthub,wappalyzer,ehole,goby)
70+
-k, --insecure 跳过 SSL 证书验证
71+
-u, --url= 要检测的目标 URL (必需)
72+
-v, --verbose 显示详细调试信息
73+
-f, --favicon 仅检测 favicon
74+
--goby= 覆盖 goby.json.gz
75+
--fingerprinthub= 覆盖 fingerprinthub_v3.json.gz
76+
--ehole= 覆盖 ehole.json.gz
77+
--fingers= 覆盖 fingers_http.json.gz
78+
--wappalyzer= 覆盖 wappalyzer.json.gz
79+
--aliases= 覆盖 aliases.yaml
80+
```
81+
82+
## 输出示例
83+
84+
普通模式:
85+
86+
```
87+
nginx/1.18.0 ubuntu/20.04
88+
```
89+
90+
详细模式 (-v):
91+
92+
```
93+
Loaded engines: fingers:1000 fingerprinthub:500 wappalyzer:300
94+
95+
Detected frameworks for https://example.com:
96+
Name: nginx
97+
Vendor: nginx
98+
Product: nginx
99+
Version: 1.18.0
100+
CPE: cpe:/a:nginx:nginx:1.18.0
101+
---
102+
Name: ubuntu
103+
Vendor: canonical
104+
Product: ubuntu
105+
Version: 20.04
106+
CPE: cpe:/o:canonical:ubuntu:20.04
107+
---
108+
```
109+
110+
## 注意事项
111+
112+
1. 自定义资源文件必须符合对应引擎的数据格式要求
113+
2. 建议先使用小规模数据测试自定义资源文件是否正确
114+
3. 覆盖资源文件会影响所有使用该资源的引擎
115+
4. 建议在测试环境中充分验证自定义资源文件后再在生产环境使用

example/example.go

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
package main
2+
3+
import (
4+
"crypto/tls"
5+
"encoding/json"
6+
"fmt"
7+
"io/ioutil"
8+
"net/http"
9+
"os"
10+
"path/filepath"
11+
"strings"
12+
13+
"github.com/chainreactors/fingers"
14+
"github.com/chainreactors/fingers/common"
15+
"github.com/chainreactors/fingers/resources"
16+
"github.com/chainreactors/utils/encode"
17+
"github.com/chainreactors/utils/httputils"
18+
"github.com/jessevdk/go-flags"
19+
"gopkg.in/yaml.v3"
20+
)
21+
22+
var opts struct {
23+
// 指定要使用的引擎,多个引擎用逗号分隔
24+
Engines string `short:"e" long:"engines" description:"Specify engines to use (comma separated)" default:"fingers,fingerprinthub,wappalyzer,ehole,goby"`
25+
26+
// 是否忽略SSL证书验证
27+
InsecureSSL bool `short:"k" long:"insecure" description:"Skip SSL certificate verification"`
28+
29+
// 目标URL
30+
URL string `short:"u" long:"url" description:"Target URL to fingerprint" required:"true"`
31+
32+
// 是否显示详细信息
33+
Verbose bool `short:"v" long:"verbose" description:"Show verbose debug information"`
34+
35+
// 是否只检测favicon
36+
FaviconOnly bool `short:"f" long:"favicon" description:"Only detect favicon"`
37+
38+
// 资源文件覆盖
39+
GobyFile string `long:"goby" description:"Override goby.json.gz with custom file"`
40+
FingerprintHubFile string `long:"fingerprinthub" description:"Override fingerprinthub_v3.json.gz with custom file"`
41+
EholeFile string `long:"ehole" description:"Override ehole.json.gz with custom file"`
42+
FingersFile string `long:"fingers" description:"Override fingers_http.json.gz with custom file"`
43+
WappalyzerFile string `long:"wappalyzer" description:"Override wappalyzer.json.gz with custom file"`
44+
AliasesFile string `long:"aliases" description:"Override aliases.yaml with custom file"`
45+
}
46+
47+
// 处理资源文件覆盖
48+
func processResourceFile(filePath string) ([]byte, error) {
49+
if filePath == "" {
50+
return nil, nil
51+
}
52+
53+
data, err := ioutil.ReadFile(filePath)
54+
if err != nil {
55+
return nil, fmt.Errorf("failed to read file %s: %v", filePath, err)
56+
}
57+
58+
ext := strings.ToLower(filepath.Ext(filePath))
59+
60+
// 如果是JSON文件,转换为YAML
61+
if ext == ".json" {
62+
var jsonData interface{}
63+
if err := json.Unmarshal(data, &jsonData); err != nil {
64+
return nil, fmt.Errorf("failed to parse JSON file %s: %v", filePath, err)
65+
}
66+
67+
data, err = yaml.Marshal(jsonData)
68+
if err != nil {
69+
return nil, fmt.Errorf("failed to convert JSON to YAML for file %s: %v", filePath, err)
70+
}
71+
}
72+
73+
// 如果文件不是.gz结尾,进行gzip压缩
74+
if !strings.HasSuffix(filePath, ".gz") {
75+
compressedData, err := encode.GzipCompress(data)
76+
if err != nil {
77+
return nil, fmt.Errorf("failed to compress file %s: %v", filePath, err)
78+
}
79+
data = compressedData
80+
}
81+
82+
return data, nil
83+
}
84+
85+
func main() {
86+
// 解析命令行参数
87+
_, err := flags.Parse(&opts)
88+
if err != nil {
89+
os.Exit(1)
90+
}
91+
92+
// 处理资源文件覆盖
93+
if opts.GobyFile != "" {
94+
if data, err := processResourceFile(opts.GobyFile); err != nil {
95+
fmt.Println(err)
96+
os.Exit(1)
97+
} else {
98+
resources.GobyData = data
99+
}
100+
}
101+
102+
if opts.FingerprintHubFile != "" {
103+
if data, err := processResourceFile(opts.FingerprintHubFile); err != nil {
104+
fmt.Println(err)
105+
os.Exit(1)
106+
} else {
107+
resources.Fingerprinthubdata = data
108+
}
109+
}
110+
111+
if opts.EholeFile != "" {
112+
if data, err := processResourceFile(opts.EholeFile); err != nil {
113+
fmt.Println(err)
114+
os.Exit(1)
115+
} else {
116+
resources.EholeData = data
117+
}
118+
}
119+
120+
if opts.FingersFile != "" {
121+
if data, err := processResourceFile(opts.FingersFile); err != nil {
122+
fmt.Println(err)
123+
os.Exit(1)
124+
} else {
125+
resources.FingersHTTPData = data
126+
}
127+
}
128+
129+
if opts.WappalyzerFile != "" {
130+
if data, err := processResourceFile(opts.WappalyzerFile); err != nil {
131+
fmt.Println(err)
132+
os.Exit(1)
133+
} else {
134+
resources.WappalyzerData = data
135+
}
136+
}
137+
138+
if opts.AliasesFile != "" {
139+
if data, err := processResourceFile(opts.AliasesFile); err != nil {
140+
fmt.Println(err)
141+
os.Exit(1)
142+
} else {
143+
resources.AliasesData = data
144+
}
145+
}
146+
147+
// 创建HTTP客户端
148+
client := &http.Client{
149+
Transport: &http.Transport{
150+
TLSClientConfig: &tls.Config{
151+
InsecureSkipVerify: opts.InsecureSSL,
152+
},
153+
},
154+
}
155+
156+
// 创建引擎实例
157+
var engineNames []string
158+
if opts.Engines != "" {
159+
engineNames = strings.Split(opts.Engines, ",")
160+
}
161+
162+
engine, err := fingers.NewEngine(engineNames...)
163+
if err != nil {
164+
fmt.Printf("Failed to create engine: %v\n", err)
165+
os.Exit(1)
166+
}
167+
168+
if opts.Verbose {
169+
fmt.Printf("Loaded engines: %s\n", engine.String())
170+
}
171+
172+
// 发送HTTP请求
173+
resp, err := client.Get(opts.URL)
174+
if err != nil {
175+
fmt.Printf("Failed to request URL %s: %v\n", opts.URL, err)
176+
os.Exit(1)
177+
}
178+
defer resp.Body.Close()
179+
180+
// 检测指纹
181+
var frames common.Frameworks
182+
if opts.FaviconOnly {
183+
content := httputils.ReadBody(resp)
184+
frame := engine.DetectFavicon(content)
185+
if frame != nil {
186+
frames.Add(frame)
187+
}
188+
} else {
189+
frames = engine.Match(resp)
190+
}
191+
192+
// 输出结果
193+
if opts.Verbose {
194+
fmt.Printf("\nDetected frameworks for %s:\n", opts.URL)
195+
for _, frame := range frames {
196+
fmt.Printf("Name: %s\n", frame.Name)
197+
fmt.Printf("Vendor: %s\n", frame.Vendor)
198+
fmt.Printf("Product: %s\n", frame.Product)
199+
fmt.Printf("Version: %s\n", frame.Version)
200+
fmt.Printf("CPE: %s\n", frame.CPE())
201+
fmt.Printf("---\n")
202+
}
203+
} else {
204+
fmt.Println(frames.String())
205+
}
206+
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ require (
1313
github.com/chainreactors/files v0.0.0-20240716182835-7884ee1e77f0 // indirect
1414
github.com/chainreactors/words v0.0.0-20241002061906-25d8893158d9
1515
github.com/facebookincubator/nvdtools v0.1.5
16+
github.com/jessevdk/go-flags v1.6.1
1617
github.com/mozillazg/go-pinyin v0.20.0
1718
github.com/pkg/errors v0.9.1
1819
gopkg.in/yaml.v3 v3.0.1

go.sum

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,16 +76,7 @@ github.com/chainreactors/files v0.0.0-20240716182835-7884ee1e77f0 h1:cU3sGEODXZs
7676
github.com/chainreactors/files v0.0.0-20240716182835-7884ee1e77f0/go.mod h1:NSxGNMRWryAyrDzZpVwmujI22wbGw6c52bQOd5zEvyU=
7777
github.com/chainreactors/logs v0.0.0-20240207121836-c946f072f81f h1:tcfp+CEdgiMvjyUzWab5edJtxUwRMSMEIkLybupIx0k=
7878
github.com/chainreactors/logs v0.0.0-20240207121836-c946f072f81f/go.mod h1:6Mv6W70JrtL6VClulZhmMRZnoYpcTahcDTKLMNEjK0o=
79-
github.com/chainreactors/utils v0.0.0-20240716182459-e85f2b01ee16 h1:TCOshCp7PrWqhP/HSAM5kT3VxoOe7EoJbRseyoSX3RM=
8079
github.com/chainreactors/utils v0.0.0-20240716182459-e85f2b01ee16/go.mod h1:LajXuvESQwP+qCMAvlcoSXppQCjuLlBrnQpu9XQ1HtU=
81-
github.com/chainreactors/utils v0.0.0-20241002093511-ef3a209f5bf1 h1:2TM+9SSDUrXKI6AYrKIBKTI7sd3+FxFbfO+hIfEKI3k=
82-
github.com/chainreactors/utils v0.0.0-20241002093511-ef3a209f5bf1/go.mod h1:LajXuvESQwP+qCMAvlcoSXppQCjuLlBrnQpu9XQ1HtU=
83-
github.com/chainreactors/utils v0.0.0-20241203060824-513698912dc5 h1:6dAsBdLuHIroeiodCupFLXj7AcXRzJOxnVUdr0khxU0=
84-
github.com/chainreactors/utils v0.0.0-20241203060824-513698912dc5/go.mod h1:LajXuvESQwP+qCMAvlcoSXppQCjuLlBrnQpu9XQ1HtU=
85-
github.com/chainreactors/utils v0.0.0-20241208110424-7b432b8cfe2c h1:Tn+LIgPsiu1A3D62WL5fNNzHMSMXtYFd4F2NeXy59tw=
86-
github.com/chainreactors/utils v0.0.0-20241208110424-7b432b8cfe2c/go.mod h1:LajXuvESQwP+qCMAvlcoSXppQCjuLlBrnQpu9XQ1HtU=
87-
github.com/chainreactors/utils v0.0.0-20241208112827-c16555f42500 h1:9BTgMYHV13/04M/rqeEMRBQH2DXK++ROsUMBT4PwLC0=
88-
github.com/chainreactors/utils v0.0.0-20241208112827-c16555f42500/go.mod h1:LajXuvESQwP+qCMAvlcoSXppQCjuLlBrnQpu9XQ1HtU=
8980
github.com/chainreactors/utils v0.0.0-20241209140746-65867d2f78b2 h1:YRQRjgb3MUOOqT0CdUDC51dsXbWpI3l6w3H4xexqKr8=
9081
github.com/chainreactors/utils v0.0.0-20241209140746-65867d2f78b2/go.mod h1:LajXuvESQwP+qCMAvlcoSXppQCjuLlBrnQpu9XQ1HtU=
9182
github.com/chainreactors/words v0.0.0-20241002061906-25d8893158d9 h1:BXaPgD1pfBPvFSy9DHZDxW0bHX96ONZT6HW+WbXlZCk=
@@ -256,6 +247,8 @@ github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47
256247
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
257248
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
258249
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
250+
github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4=
251+
github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc=
259252
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
260253
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
261254
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -582,6 +575,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
582575
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
583576
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
584577
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
578+
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
579+
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
585580
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
586581
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
587582
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=

0 commit comments

Comments
 (0)