Skip to content

Commit 904b075

Browse files
authored
Add JA4D fingerprint to Wireshark plugin (#238)
* Add ja4d fingerprint * Handle special cases * Add pcap files for testing * Handle empty options * Remove pcap files
1 parent c62b572 commit 904b075

File tree

1 file changed

+122
-15
lines changed

1 file changed

+122
-15
lines changed

wireshark/source/packet-ja4.c

Lines changed: 122 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ static int hf_ja4h_raw_original = -1;
6767
static int hf_ja4l = -1;
6868
static int hf_ja4ls = -1;
6969
static int hf_ja4ssh = -1;
70-
7170
static int hf_ja4t = -1;
7271
static int hf_ja4ts = -1;
72+
static int hf_ja4d = -1;
7373

7474
static int dissect_ja4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *dummy);
7575

@@ -90,7 +90,7 @@ const value_string ssl_versions[] = {
9090
{0x00, NULL}
9191
};
9292

93-
#define HFIDS 53
93+
#define HFIDS 61
9494
const char *interesting_hfids[HFIDS] = {
9595
"tls.handshake.type",
9696
"dtls.handshake.type",
@@ -144,7 +144,15 @@ const char *interesting_hfids[HFIDS] = {
144144
"frame.time_epoch",
145145
"frame.time_delta_displayed",
146146
"ssh.direction",
147-
"quic.long.packet_type"
147+
"quic.long.packet_type",
148+
"dhcp.option.dhcp",
149+
"dhcp.option.dhcp_max_message_size",
150+
"dhcp.option.type",
151+
"dhcp.option.request_list_item",
152+
"dhcpv6.msgtype",
153+
"dhcpv6.duid.bytes",
154+
"dhcpv6.option.type",
155+
"dhcpv6.requested_option_code",
148156
};
149157

150158
typedef struct {
@@ -189,6 +197,14 @@ typedef struct {
189197
int window_size;
190198
} ja4t_info_t;
191199

200+
typedef struct {
201+
gchar proto;
202+
guint32 type;
203+
wmem_strbuf_t *size;
204+
wmem_strbuf_t *options;
205+
wmem_strbuf_t *request_list;
206+
} ja4d_info_t;
207+
192208
typedef struct {
193209
int stream;
194210

@@ -589,6 +605,25 @@ char *ja4t(ja4t_info_t *data, conn_info_t *conn) {
589605
return (char *)wmem_strbuf_get_str(display);
590606
}
591607

608+
char *ja4d(ja4d_info_t *data) {
609+
wmem_strbuf_t *display = wmem_strbuf_new(wmem_file_scope(), "");
610+
if (wmem_strbuf_get_len(data->size) == 0)
611+
wmem_strbuf_append_printf(data->size, "00");
612+
if (wmem_strbuf_get_len(data->options) == 0)
613+
wmem_strbuf_append_printf(data->options, "00");
614+
if (wmem_strbuf_get_len(data->request_list) == 0)
615+
wmem_strbuf_append_printf(data->request_list, "00");
616+
wmem_strbuf_append_printf(
617+
display, "%c-%d-%s_%s_%s",
618+
data->proto,
619+
data->type,
620+
wmem_strbuf_get_str(data->size),
621+
wmem_strbuf_get_str(data->options),
622+
wmem_strbuf_get_str(data->request_list)
623+
);
624+
return (char *)wmem_strbuf_get_str(display);
625+
}
626+
592627
static void init_ja4_data(packet_info *pinfo, ja4_info_t *ja4_data) {
593628
ja4_data->version = 0;
594629
ja4_data->ext_len = 0;
@@ -672,6 +707,7 @@ static void set_ja4_ciphers(proto_tree *tree, ja4_info_t *data) {
672707
static int dissect_ja4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *dummy _U_) {
673708
guint32 handshake_type = 0;
674709
gboolean alpn_visited = false;
710+
gboolean dhcpv6_option_type_1_exists = false;
675711
proto_tree *ja4_tree = NULL;
676712

677713
// For JA4C/S, record signature algorithms only when extension type == 13
@@ -704,18 +740,9 @@ static int dissect_ja4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void
704740
return tvb_captured_length(tvb);
705741

706742
ja4_info_t ja4_data;
707-
ja4h_info_t ja4h_data;
708-
709743
init_ja4_data(pinfo, &ja4_data);
710744

711-
// JA4T data
712-
ja4t_info_t ja4t_data;
713-
ja4t_data.tcp_options = wmem_strbuf_new(wmem_file_scope(), "");
714-
ja4t_data.mss_val = 0;
715-
ja4t_data.window_scale = 0;
716-
ja4t_data.window_size = 0;
717-
// End of JA4T data
718-
745+
ja4h_info_t ja4h_data;
719746
ja4h_data.version = wmem_strbuf_new(pinfo->pool, "");
720747
ja4h_data.headers = wmem_strbuf_new(pinfo->pool, "");
721748
ja4h_data.lang = wmem_strbuf_new(pinfo->pool, "");
@@ -730,6 +757,19 @@ static int dissect_ja4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void
730757
ja4h_data.sorted_cookie_fields = wmem_strbuf_new(pinfo->pool, "");
731758
ja4h_data.sorted_cookie_values = wmem_strbuf_new(pinfo->pool, "");
732759

760+
ja4t_info_t ja4t_data;
761+
ja4t_data.tcp_options = wmem_strbuf_new(wmem_file_scope(), "");
762+
ja4t_data.mss_val = 0;
763+
ja4t_data.window_scale = 0;
764+
ja4t_data.window_size = 0;
765+
766+
ja4d_info_t ja4d_data;
767+
ja4d_data.proto = 0;
768+
ja4d_data.type = 0;
769+
ja4d_data.size = wmem_strbuf_new(pinfo->pool, "");
770+
ja4d_data.options = wmem_strbuf_new(pinfo->pool, "");
771+
ja4d_data.request_list = wmem_strbuf_new(pinfo->pool, "");
772+
733773
char *proto = "tls";
734774
switch (ja4_data.proto) {
735775
case 'q': {
@@ -1267,6 +1307,56 @@ static int dissect_ja4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void
12671307
wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
12681308
}
12691309
}
1310+
1311+
// JA4D processing
1312+
1313+
// DHCPv4
1314+
if (strcmp(field->hfinfo->abbrev, "dhcp.option.dhcp") == 0) {
1315+
ja4d_data.proto = '4';
1316+
ja4d_data.type = fvalue_get_uinteger(get_value_ptr(field));
1317+
}
1318+
if (strcmp(field->hfinfo->abbrev, "dhcp.option.dhcp_max_message_size") == 0) {
1319+
wmem_strbuf_append_printf(
1320+
ja4d_data.size, "%d", fvalue_get_uinteger(get_value_ptr(field))
1321+
);
1322+
}
1323+
if (strcmp(field->hfinfo->abbrev, "dhcp.option.type") == 0) {
1324+
guint val = fvalue_get_uinteger(get_value_ptr(field));
1325+
if (val != 0 && val != 53) {
1326+
wmem_strbuf_append_printf(ja4d_data.options, "%d-", val);
1327+
}
1328+
}
1329+
if (strcmp(field->hfinfo->abbrev, "dhcp.option.request_list_item") == 0) {
1330+
wmem_strbuf_append_printf(
1331+
ja4d_data.request_list, "%d-", fvalue_get_uinteger(get_value_ptr(field))
1332+
);
1333+
}
1334+
1335+
// DHCPv6
1336+
if (strcmp(field->hfinfo->abbrev, "dhcpv6.msgtype") == 0) {
1337+
ja4d_data.proto = '6';
1338+
ja4d_data.type = fvalue_get_uinteger(get_value_ptr(field));
1339+
}
1340+
if (strcmp(field->hfinfo->abbrev, "dhcpv6.duid.bytes") == 0 &&
1341+
wmem_strbuf_get_len(ja4d_data.size) == 0 &&
1342+
dhcpv6_option_type_1_exists == true) {
1343+
wmem_strbuf_append_printf(
1344+
ja4d_data.size, "%d", field->length
1345+
);
1346+
}
1347+
if (strcmp(field->hfinfo->abbrev, "dhcpv6.option.type") == 0) {
1348+
guint val = fvalue_get_uinteger(get_value_ptr(field));
1349+
if (val == 1)
1350+
dhcpv6_option_type_1_exists = true;
1351+
wmem_strbuf_append_printf(
1352+
ja4d_data.options, "%d-", val
1353+
);
1354+
}
1355+
if (strcmp(field->hfinfo->abbrev, "dhcpv6.requested_option_code") == 0) {
1356+
wmem_strbuf_append_printf(
1357+
ja4d_data.request_list, "%d-", fvalue_get_uinteger(get_value_ptr(field))
1358+
);
1359+
}
12701360
}
12711361
g_ptr_array_free(items, TRUE);
12721362
}
@@ -1351,6 +1441,18 @@ static int dissect_ja4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void
13511441
);
13521442
}
13531443

1444+
if (ja4d_data.proto != 0) {
1445+
wmem_strbuf_truncate(ja4d_data.options, wmem_strbuf_get_len(ja4d_data.options) - 1);
1446+
wmem_strbuf_truncate(ja4d_data.request_list, wmem_strbuf_get_len(ja4d_data.request_list) - 1);
1447+
1448+
char *dhcp_proto = "dhcp";
1449+
if (ja4d_data.proto == '6') {
1450+
dhcp_proto = "dhcpv6";
1451+
}
1452+
1453+
update_tree_item(tvb, tree, &ja4_tree, hf_ja4d, ja4d(&ja4d_data), dhcp_proto);
1454+
}
1455+
13541456
return tvb_reported_length(tvb);
13551457
}
13561458

@@ -1361,7 +1463,11 @@ static void init_globals(void) {
13611463
GArray *wanted_hfids = g_array_new(FALSE, FALSE, (guint)sizeof(int));
13621464
for (int i = 0; i < HFIDS; i++) {
13631465
int id = proto_registrar_get_id_byname(interesting_hfids[i]);
1364-
g_array_append_val(wanted_hfids, id);
1466+
if (id != -1) {
1467+
g_array_append_val(wanted_hfids, id);
1468+
} else {
1469+
g_warning("JA4: Unknown field: %s", interesting_hfids[i]);
1470+
}
13651471
}
13661472

13671473
set_postdissector_wanted_hfids(ja4_handle, wanted_hfids);
@@ -1390,7 +1496,8 @@ void proto_register_ja4(void) {
13901496
{&hf_ja4ls, {"JA4LS", "ja4.ja4ls", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
13911497
{&hf_ja4ssh, {"JA4SSH", "ja4.ja4ssh", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
13921498
{&hf_ja4t, {"JA4T", "ja4.ja4t", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
1393-
{&hf_ja4ts, {"JA4T-S", "ja4.ja4ts", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} }
1499+
{&hf_ja4ts, {"JA4T-S", "ja4.ja4ts", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
1500+
{&hf_ja4d, {"JA4D", "ja4.ja4d", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} }
13941501
};
13951502

13961503
static gint *ett[] = {

0 commit comments

Comments
 (0)