-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathja4.go
91 lines (82 loc) · 1.97 KB
/
ja4.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package ja4t
import (
"encoding/binary"
"fmt"
"strings"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
)
// JA4T is a struct to hold the JA4T fingerprint
type JA4T struct {
WindowSize uint16
Options []uint8
MaximumSegmentSize uint16
WindowScale uint8
}
func (ja4 *JA4T) String() string {
str := fmt.Sprintf("JA4T=%v_", ja4.WindowSize)
options := make([]string, len(ja4.Options))
for i, o := range ja4.Options {
options[i] = fmt.Sprintf("%v", o)
}
str += strings.Join(options, "-") + "_"
return str + fmt.Sprintf("%v_%v", ja4.MaximumSegmentSize, ja4.WindowScale)
}
func ParseFile(fileName string) ([]JA4T, error) {
handle, err := pcap.OpenOffline(fileName)
if err != nil {
return nil, err
}
defer handle.Close()
packets := gopacket.NewPacketSource(handle, handle.LinkType()).Packets()
packetJAs := []JA4T{}
for pkt := range packets {
jas, err := ParseLayers(pkt.Layers())
if err != nil {
return nil, err
}
packetJAs = append(packetJAs, jas...)
}
return packetJAs, nil
}
func ParseLayers(lrs []gopacket.Layer) ([]JA4T, error) {
jas := []JA4T{}
for _, layer := range lrs {
switch layer.LayerType() {
case layers.LayerTypeTCP:
tcp, ok := layer.(*layers.TCP)
if !ok {
continue
}
ja, err := ParseTCP(tcp)
if err != nil {
return nil, err
}
// TODO: this might be too rough
if len(ja.Options) > 0 && ja.WindowScale > 0 {
jas = append(jas, ja)
}
}
}
return jas, nil
}
func ParseTCP(tcp *layers.TCP) (JA4T, error) {
ja := JA4T{
WindowSize: tcp.Window,
}
// TODO: is this always true?
if len(tcp.Options) == 0 {
return ja, nil
}
for _, o := range tcp.Options {
ja.Options = append(ja.Options, uint8(o.OptionType))
switch o.OptionType {
case layers.TCPOptionKindWindowScale:
ja.WindowScale = o.OptionData[0]
case layers.TCPOptionKindMSS:
ja.MaximumSegmentSize = binary.BigEndian.Uint16(o.OptionData)
}
}
return ja, nil
}