Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 122 additions & 15 deletions wireshark/source/packet-ja4.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ static int hf_ja4h_raw_original = -1;
static int hf_ja4l = -1;
static int hf_ja4ls = -1;
static int hf_ja4ssh = -1;

static int hf_ja4t = -1;
static int hf_ja4ts = -1;
static int hf_ja4d = -1;

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

Expand All @@ -90,7 +90,7 @@ const value_string ssl_versions[] = {
{0x00, NULL}
};

#define HFIDS 53
#define HFIDS 61
const char *interesting_hfids[HFIDS] = {
"tls.handshake.type",
"dtls.handshake.type",
Expand Down Expand Up @@ -144,7 +144,15 @@ const char *interesting_hfids[HFIDS] = {
"frame.time_epoch",
"frame.time_delta_displayed",
"ssh.direction",
"quic.long.packet_type"
"quic.long.packet_type",
"dhcp.option.dhcp",
"dhcp.option.dhcp_max_message_size",
"dhcp.option.type",
"dhcp.option.request_list_item",
"dhcpv6.msgtype",
"dhcpv6.duid.bytes",
"dhcpv6.option.type",
"dhcpv6.requested_option_code",
};

typedef struct {
Expand Down Expand Up @@ -189,6 +197,14 @@ typedef struct {
int window_size;
} ja4t_info_t;

typedef struct {
gchar proto;
guint32 type;
wmem_strbuf_t *size;
wmem_strbuf_t *options;
wmem_strbuf_t *request_list;
} ja4d_info_t;

typedef struct {
int stream;

Expand Down Expand Up @@ -589,6 +605,25 @@ char *ja4t(ja4t_info_t *data, conn_info_t *conn) {
return (char *)wmem_strbuf_get_str(display);
}

char *ja4d(ja4d_info_t *data) {
wmem_strbuf_t *display = wmem_strbuf_new(wmem_file_scope(), "");
if (wmem_strbuf_get_len(data->size) == 0)
wmem_strbuf_append_printf(data->size, "00");
if (wmem_strbuf_get_len(data->options) == 0)
wmem_strbuf_append_printf(data->options, "00");
if (wmem_strbuf_get_len(data->request_list) == 0)
wmem_strbuf_append_printf(data->request_list, "00");
wmem_strbuf_append_printf(
display, "%c-%d-%s_%s_%s",
data->proto,
data->type,
wmem_strbuf_get_str(data->size),
wmem_strbuf_get_str(data->options),
wmem_strbuf_get_str(data->request_list)
);
return (char *)wmem_strbuf_get_str(display);
}

static void init_ja4_data(packet_info *pinfo, ja4_info_t *ja4_data) {
ja4_data->version = 0;
ja4_data->ext_len = 0;
Expand Down Expand Up @@ -672,6 +707,7 @@ static void set_ja4_ciphers(proto_tree *tree, ja4_info_t *data) {
static int dissect_ja4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *dummy _U_) {
guint32 handshake_type = 0;
gboolean alpn_visited = false;
gboolean dhcpv6_option_type_1_exists = false;
proto_tree *ja4_tree = NULL;

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

ja4_info_t ja4_data;
ja4h_info_t ja4h_data;

init_ja4_data(pinfo, &ja4_data);

// JA4T data
ja4t_info_t ja4t_data;
ja4t_data.tcp_options = wmem_strbuf_new(wmem_file_scope(), "");
ja4t_data.mss_val = 0;
ja4t_data.window_scale = 0;
ja4t_data.window_size = 0;
// End of JA4T data

ja4h_info_t ja4h_data;
ja4h_data.version = wmem_strbuf_new(pinfo->pool, "");
ja4h_data.headers = wmem_strbuf_new(pinfo->pool, "");
ja4h_data.lang = wmem_strbuf_new(pinfo->pool, "");
Expand All @@ -730,6 +757,19 @@ static int dissect_ja4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void
ja4h_data.sorted_cookie_fields = wmem_strbuf_new(pinfo->pool, "");
ja4h_data.sorted_cookie_values = wmem_strbuf_new(pinfo->pool, "");

ja4t_info_t ja4t_data;
ja4t_data.tcp_options = wmem_strbuf_new(wmem_file_scope(), "");
ja4t_data.mss_val = 0;
ja4t_data.window_scale = 0;
ja4t_data.window_size = 0;

ja4d_info_t ja4d_data;
ja4d_data.proto = 0;
ja4d_data.type = 0;
ja4d_data.size = wmem_strbuf_new(pinfo->pool, "");
ja4d_data.options = wmem_strbuf_new(pinfo->pool, "");
ja4d_data.request_list = wmem_strbuf_new(pinfo->pool, "");

char *proto = "tls";
switch (ja4_data.proto) {
case 'q': {
Expand Down Expand Up @@ -1267,6 +1307,56 @@ static int dissect_ja4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void
wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
}
}

// JA4D processing

// DHCPv4
if (strcmp(field->hfinfo->abbrev, "dhcp.option.dhcp") == 0) {
ja4d_data.proto = '4';
ja4d_data.type = fvalue_get_uinteger(get_value_ptr(field));
}
if (strcmp(field->hfinfo->abbrev, "dhcp.option.dhcp_max_message_size") == 0) {
wmem_strbuf_append_printf(
ja4d_data.size, "%d", fvalue_get_uinteger(get_value_ptr(field))
);
}
if (strcmp(field->hfinfo->abbrev, "dhcp.option.type") == 0) {
guint val = fvalue_get_uinteger(get_value_ptr(field));
if (val != 0 && val != 53) {
wmem_strbuf_append_printf(ja4d_data.options, "%d-", val);
}
}
if (strcmp(field->hfinfo->abbrev, "dhcp.option.request_list_item") == 0) {
wmem_strbuf_append_printf(
ja4d_data.request_list, "%d-", fvalue_get_uinteger(get_value_ptr(field))
);
}

// DHCPv6
if (strcmp(field->hfinfo->abbrev, "dhcpv6.msgtype") == 0) {
ja4d_data.proto = '6';
ja4d_data.type = fvalue_get_uinteger(get_value_ptr(field));
}
if (strcmp(field->hfinfo->abbrev, "dhcpv6.duid.bytes") == 0 &&
wmem_strbuf_get_len(ja4d_data.size) == 0 &&
dhcpv6_option_type_1_exists == true) {
wmem_strbuf_append_printf(
ja4d_data.size, "%d", field->length
);
}
if (strcmp(field->hfinfo->abbrev, "dhcpv6.option.type") == 0) {
guint val = fvalue_get_uinteger(get_value_ptr(field));
if (val == 1)
dhcpv6_option_type_1_exists = true;
wmem_strbuf_append_printf(
ja4d_data.options, "%d-", val
);
}
if (strcmp(field->hfinfo->abbrev, "dhcpv6.requested_option_code") == 0) {
wmem_strbuf_append_printf(
ja4d_data.request_list, "%d-", fvalue_get_uinteger(get_value_ptr(field))
);
}
}
g_ptr_array_free(items, TRUE);
}
Expand Down Expand Up @@ -1351,6 +1441,18 @@ static int dissect_ja4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void
);
}

if (ja4d_data.proto != 0) {
wmem_strbuf_truncate(ja4d_data.options, wmem_strbuf_get_len(ja4d_data.options) - 1);
wmem_strbuf_truncate(ja4d_data.request_list, wmem_strbuf_get_len(ja4d_data.request_list) - 1);

char *dhcp_proto = "dhcp";
if (ja4d_data.proto == '6') {
dhcp_proto = "dhcpv6";
}

update_tree_item(tvb, tree, &ja4_tree, hf_ja4d, ja4d(&ja4d_data), dhcp_proto);
}

return tvb_reported_length(tvb);
}

Expand All @@ -1361,7 +1463,11 @@ static void init_globals(void) {
GArray *wanted_hfids = g_array_new(FALSE, FALSE, (guint)sizeof(int));
for (int i = 0; i < HFIDS; i++) {
int id = proto_registrar_get_id_byname(interesting_hfids[i]);
g_array_append_val(wanted_hfids, id);
if (id != -1) {
g_array_append_val(wanted_hfids, id);
} else {
g_warning("JA4: Unknown field: %s", interesting_hfids[i]);
}
}

set_postdissector_wanted_hfids(ja4_handle, wanted_hfids);
Expand Down Expand Up @@ -1390,7 +1496,8 @@ void proto_register_ja4(void) {
{&hf_ja4ls, {"JA4LS", "ja4.ja4ls", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
{&hf_ja4ssh, {"JA4SSH", "ja4.ja4ssh", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
{&hf_ja4t, {"JA4T", "ja4.ja4t", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
{&hf_ja4ts, {"JA4T-S", "ja4.ja4ts", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} }
{&hf_ja4ts, {"JA4T-S", "ja4.ja4ts", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} },
{&hf_ja4d, {"JA4D", "ja4.ja4d", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL} }
};

static gint *ett[] = {
Expand Down