Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a dedicated dissector for Zoom #2265

Merged
merged 1 commit into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions src/include/ndpi_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,7 @@ void init_roughtime_dissector(struct ndpi_detection_module_struct *ndpi_struct,
void init_kcp_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id);
void init_valve_sdr_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id);
void init_mumble_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id);
void init_zoom_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id);

#endif

Expand Down
3 changes: 3 additions & 0 deletions src/lib/ndpi_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -5872,6 +5872,9 @@ static int ndpi_callback_init(struct ndpi_detection_module_struct *ndpi_str) {
/* Mumble */
init_mumble_dissector(ndpi_str, &a);

/* Zoom */
init_zoom_dissector(ndpi_str, &a);

#ifdef CUSTOM_NDPI_PROTOCOLS
#include "../../../nDPI-custom/custom_ndpi_main_init.c"
#endif
Expand Down
108 changes: 2 additions & 106 deletions src/lib/protocols/rtp.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,90 +84,6 @@ static int is_valid_rtcp_payload_type(uint8_t type)
return (type >= 192 && type <= 213);
}

/* *************************************************************** */

/*
https://github.com/Princeton-Cabernet/zoom-analysis
https://citizenlab.ca/2020/04/move-fast-roll-your-own-crypto-a-quick-look-at-the-confidentiality-of-zoom-meetings/
https://github.com/marty90/rtc_pcap_cleaners
*/

PACK_ON struct zoom_sfu_encapsulation {
u_int8_t sfu_type; /* 3/4 = Zoom_0, 5 = RTCP/RTP */
u_int16_t sequence_num;
u_int32_t unknown;
u_int8_t direction; /* 0 = -> Zoom, 4 = <- Zoom */
} PACK_OFF;

PACK_ON struct zoom_media_encapsulation {
u_int8_t enc_type; /* 13/30 = Screen Share, 15 = Audio, 16 = Video, 33/34/35 = RTCP */
u_int32_t unknown_1, unknown_2;
u_int16_t sequence_num;
u_int32_t timestamp;
} PACK_OFF;

#define ZOOM_PORT 8801

static u_int8_t isZoom(struct ndpi_flow_struct *flow,
u_int16_t sport, u_int16_t dport,
const u_int8_t *payload, const u_int16_t payloadLen,
u_int8_t *is_rtp, u_int8_t *zoom_stream_type,
u_int16_t *payload_offset) {
u_int16_t header_offset = sizeof(struct zoom_sfu_encapsulation) + sizeof(struct zoom_media_encapsulation);

*payload_offset = 0;
if(payloadLen < header_offset)
return(0);

if((sport == ZOOM_PORT) || (dport == ZOOM_PORT)) {
struct zoom_sfu_encapsulation *enc = (struct zoom_sfu_encapsulation*)payload;

/* printf("==> %u <-> %u [type: %u]\n", sport, dport, enc->sfu_type); */

if((enc->sfu_type >= 3) && (enc->sfu_type <= 5)) {
struct zoom_media_encapsulation *enc = (struct zoom_media_encapsulation*)(&payload[sizeof(struct zoom_sfu_encapsulation)]);

*zoom_stream_type = enc->enc_type;

switch(enc->enc_type) {
case 13: /* Screen Share */
case 30: /* Screen Share */
*is_rtp = 0;
*payload_offset = 27;
flow->flow_multimedia_type = ndpi_multimedia_screen_sharing_flow;
break;

case 15: /* Audio */
*is_rtp = 1;
*payload_offset = 27;
flow->flow_multimedia_type = ndpi_multimedia_audio_flow;
break;

case 16: /* Video */
*is_rtp = 1;
*payload_offset = 32;
flow->flow_multimedia_type = ndpi_multimedia_video_flow;
break;

case 33: /* RTCP */
case 34: /* RTCP */
case 35: /* RTCP */
*is_rtp = 1;
*payload_offset = 36;
break;

default:
*is_rtp = 0;
break;
}

return(1);
}
}

return(0);
}

int is_rtp_or_rtcp(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow)
{
Expand Down Expand Up @@ -230,11 +146,10 @@ int is_rtp_or_rtcp(struct ndpi_detection_module_struct *ndpi_struct,

static void ndpi_rtp_search(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow) {
u_int8_t is_rtp, zoom_stream_type;
u_int16_t s_port = ntohs(ndpi_struct->packet.udp->source), d_port = ntohs(ndpi_struct->packet.udp->dest), payload_offset;
u_int8_t is_rtp;
u_int16_t d_port = ntohs(ndpi_struct->packet.udp->dest);
struct ndpi_packet_struct *packet = &ndpi_struct->packet;
const u_int8_t *payload = packet->payload;
const u_int16_t payload_len = packet->payload_packet_len;

NDPI_LOG_DBG(ndpi_struct, "search RTP\n");

Expand All @@ -245,25 +160,6 @@ static void ndpi_rtp_search(struct ndpi_detection_module_struct *ndpi_struct,
return;
}

/* TODO: should we move zoom stuff in a new, separated dissector? */
if(isZoom(flow, s_port, d_port, payload, payload_len,
&is_rtp, &zoom_stream_type, &payload_offset)) {
if(payload_offset < payload_len) {
/*
payload_len -= payload_offset;
payload = &payload[payload_offset];
*/

/* printf("->>> %u\n", zoom_stream_type); */

ndpi_set_detected_protocol(ndpi_struct, flow,
NDPI_PROTOCOL_ZOOM,
NDPI_PROTOCOL_SRTP,
NDPI_CONFIDENCE_DPI);
return;
}
}

/* * Let some "unknown" packets at the beginning
* search for 3 consecutive RTP/RTCP packets
*/
Expand Down
180 changes: 180 additions & 0 deletions src/lib/protocols/zoom.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/*
* zoom.c
*
* Copyright (C) 2024 - ntop.org
*
* nDPI is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nDPI is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with nDPI. If not, see <http://www.gnu.org/licenses/>.
*
*/

#include "ndpi_protocol_ids.h"

#define NDPI_CURRENT_PROTO NDPI_PROTOCOL_ZOOM

#include "ndpi_api.h"
#include "ndpi_private.h"

/*
https://github.com/Princeton-Cabernet/zoom-analysis
https://citizenlab.ca/2020/04/move-fast-roll-your-own-crypto-a-quick-look-at-the-confidentiality-of-zoom-meetings/
https://github.com/marty90/rtc_pcap_cleaners
*/

PACK_ON struct zoom_sfu_enc { /* Zoom SFU encapsulation */
u_int8_t sfu_type; /* 3/4 = Zoom_0, 5 = RTCP/RTP */
u_int16_t sequence_num;
u_int32_t unknown;
u_int8_t direction; /* 0 = -> Zoom, 4 = <- Zoom */
} PACK_OFF;

PACK_ON struct zoom_media_enc { /* Zoom media encapsulation */
u_int8_t enc_type; /* 13/30 = Screen Share, 15 = Audio, 16 = Video, 33/34/35 = RTCP */
u_int32_t unknown_1, unknown_2;
u_int16_t sequence_num;
u_int32_t timestamp;
} PACK_OFF;

static int zoom_search_again(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow);

static void ndpi_int_zoom_add_connection(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow,
u_int16_t master) {
NDPI_LOG_INFO(ndpi_struct, "found Zoom\n");
ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_ZOOM, master, NDPI_CONFIDENCE_DPI);

/* Keep looking for RTP if we are at the beginning of the flow (SFU 1 or 2).
* It is similar to the STUN logic... */
if(master == NDPI_PROTOCOL_UNKNOWN) {
flow->max_extra_packets_to_check = 4;
flow->extra_packets_func = zoom_search_again;
}
}

static int is_zoom_port(struct ndpi_flow_struct *flow)
{
/* https://support.zoom.com/hc/en/article?id=zm_kb&sysparm_article=KB0060548 */
if((ntohs(flow->c_port) >= 8801 && ntohs(flow->c_port) <= 8810) ||
(ntohs(flow->s_port) >= 8801 && ntohs(flow->s_port) <= 8810))
return 1;
return 0;
}

static int is_sfu_5(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow)
{
struct ndpi_packet_struct *packet = &ndpi_struct->packet;

/* SFU types 5 */
if(packet->payload[0] == 0x05 &&
packet->payload_packet_len > sizeof(struct zoom_sfu_enc) +
sizeof(struct zoom_media_enc)) {
struct zoom_media_enc *enc = (struct zoom_media_enc *)&packet->payload[sizeof(struct zoom_sfu_enc)];

switch(enc->enc_type) {
case 13: /* Screen Share */
case 30: /* Screen Share */
if(packet->payload_packet_len >= 27) {
flow->flow_multimedia_type = ndpi_multimedia_screen_sharing_flow;
return 1;
}
break;

case 15: /* RTP Audio */
if(packet->payload_packet_len >= 27) {
flow->flow_multimedia_type = ndpi_multimedia_audio_flow;
return 1;
}
break;

case 16: /* RTP Video */
if(packet->payload_packet_len >= 32) {
flow->flow_multimedia_type = ndpi_multimedia_video_flow;
return 1;
}
break;

case 33: /* RTCP */
case 34: /* RTCP */
case 35: /* RTCP */
if(packet->payload_packet_len >= 36) {
return 1;
}
break;

default:
return 1;
}
}
return 0;
}

static int zoom_search_again(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow)
{
if(is_sfu_5(ndpi_struct, flow)) {
ndpi_int_zoom_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_SRTP);
return 0; /* Stop */
}
return 1; /* Keep looking */
}

static void ndpi_search_zoom(struct ndpi_detection_module_struct *ndpi_struct,
struct ndpi_flow_struct *flow) {
struct ndpi_packet_struct *packet = &ndpi_struct->packet;
u_int8_t tomatch[] = { 0x01, 0x00, 0x03 }; /* Usually first pkt from the client */
u_int8_t tomatch_a[] = { 0x01, 0x00, 0x02 }; /* Other first pkt from the client */
u_int8_t tomatch2[] = { 0x02, 0x00, 0x03 }; /* Usually first pkt from the server: useful with asymmetric traffic */
u_int8_t tomatch2_a[] = { 0x02, 0x00, 0x02 }; /* Other first pkt from the server */

NDPI_LOG_DBG(ndpi_struct, "search Zoom\n");

if(is_zoom_port(flow) &&
packet->payload_packet_len > sizeof(struct zoom_sfu_enc)) {
/* SFU types 1 and 2 */
if(memcmp(packet->payload, tomatch, 3) == 0 ||
memcmp(packet->payload, tomatch_a, 3) == 0 ||
memcmp(packet->payload, tomatch2, 3) == 0 ||
memcmp(packet->payload, tomatch2_a, 3) == 0) {
ndpi_int_zoom_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN);
return;

/* SFU types 3 and 4. This check is quite weak but these packets are rare.
Wait for other kind of traffic */
} else if((packet->payload[0] == 0x03 || packet->payload[0] == 0x04) &&
flow->packet_counter < 3) {
return;

/* SFU types 5 */
} else if(is_sfu_5(ndpi_struct, flow)) {
ndpi_int_zoom_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_SRTP);
return;
}
}

NDPI_EXCLUDE_PROTO(ndpi_struct, flow);
}

/* *************************************************** */

void init_zoom_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id) {
ndpi_set_bitmask_protocol_detection("Zoom", ndpi_struct, *id,
NDPI_PROTOCOL_ZOOM,
ndpi_search_zoom,
NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_UDP_WITH_PAYLOAD,
SAVE_DETECTION_BITMASK_AS_UNKNOWN,
ADD_TO_DETECTION_BITMASK);

*id += 1;
}
2 changes: 1 addition & 1 deletion tests/cfgs/caches_cfg/result/teams.pcap.out
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ DPI Packets (other): 1 (1.00 pkts/flow)
Confidence Unknown : 1 (flows)
Confidence Match by port : 2 (flows)
Confidence DPI : 80 (flows)
Num dissector calls: 525 (6.33 diss/flow)
Num dissector calls: 526 (6.34 diss/flow)
LRU cache ookla: 0/0/0 (insert/search/found)
LRU cache bittorrent: 0/9/0 (insert/search/found)
LRU cache zoom: 0/0/0 (insert/search/found)
Expand Down
2 changes: 1 addition & 1 deletion tests/cfgs/default/result/1kxun.pcap.out
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ DPI Packets (UDP): 120 (1.21 pkts/flow)
Confidence Unknown : 14 (flows)
Confidence Match by port : 6 (flows)
Confidence DPI : 177 (flows)
Num dissector calls: 4896 (24.85 diss/flow)
Num dissector calls: 4910 (24.92 diss/flow)
LRU cache ookla: 0/0/0 (insert/search/found)
LRU cache bittorrent: 0/60/0 (insert/search/found)
LRU cache zoom: 0/0/0 (insert/search/found)
Expand Down
2 changes: 1 addition & 1 deletion tests/cfgs/default/result/4in4tunnel.pcap.out
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
DPI Packets (UDP): 5 (5.00 pkts/flow)
Confidence Unknown : 1 (flows)
Num dissector calls: 186 (186.00 diss/flow)
Num dissector calls: 187 (187.00 diss/flow)
LRU cache ookla: 0/0/0 (insert/search/found)
LRU cache bittorrent: 0/3/0 (insert/search/found)
LRU cache zoom: 0/0/0 (insert/search/found)
Expand Down
2 changes: 1 addition & 1 deletion tests/cfgs/default/result/6in6tunnel.pcap.out
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
DPI Packets (UDP): 2 (2.00 pkts/flow)
Confidence Unknown : 1 (flows)
Num dissector calls: 141 (141.00 diss/flow)
Num dissector calls: 142 (142.00 diss/flow)
LRU cache ookla: 0/0/0 (insert/search/found)
LRU cache bittorrent: 0/3/0 (insert/search/found)
LRU cache zoom: 0/0/0 (insert/search/found)
Expand Down
2 changes: 1 addition & 1 deletion tests/cfgs/default/result/EAQ.pcap.out
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
DPI Packets (TCP): 12 (6.00 pkts/flow)
DPI Packets (UDP): 116 (4.00 pkts/flow)
Confidence DPI : 31 (flows)
Num dissector calls: 4778 (154.13 diss/flow)
Num dissector calls: 4807 (155.06 diss/flow)
LRU cache ookla: 0/0/0 (insert/search/found)
LRU cache bittorrent: 0/0/0 (insert/search/found)
LRU cache zoom: 0/0/0 (insert/search/found)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
DPI Packets (UDP): 7 (1.40 pkts/flow)
Confidence DPI : 5 (flows)
Num dissector calls: 150 (30.00 diss/flow)
Num dissector calls: 151 (30.20 diss/flow)
LRU cache ookla: 0/0/0 (insert/search/found)
LRU cache bittorrent: 0/0/0 (insert/search/found)
LRU cache zoom: 0/0/0 (insert/search/found)
Expand Down
2 changes: 1 addition & 1 deletion tests/cfgs/default/result/KakaoTalk_talk.pcap.out
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ DPI Packets (UDP): 10 (2.00 pkts/flow)
Confidence Match by port : 8 (flows)
Confidence DPI : 11 (flows)
Confidence Match by IP : 1 (flows)
Num dissector calls: 1180 (59.00 diss/flow)
Num dissector calls: 1182 (59.10 diss/flow)
LRU cache ookla: 0/2/0 (insert/search/found)
LRU cache bittorrent: 0/27/0 (insert/search/found)
LRU cache zoom: 0/0/0 (insert/search/found)
Expand Down
2 changes: 1 addition & 1 deletion tests/cfgs/default/result/anyconnect-vpn.pcap.out
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ DPI Packets (other): 10 (1.00 pkts/flow)
Confidence Unknown : 2 (flows)
Confidence Match by port : 6 (flows)
Confidence DPI : 61 (flows)
Num dissector calls: 861 (12.48 diss/flow)
Num dissector calls: 862 (12.49 diss/flow)
LRU cache ookla: 0/0/0 (insert/search/found)
LRU cache bittorrent: 0/24/0 (insert/search/found)
LRU cache zoom: 0/0/0 (insert/search/found)
Expand Down
Loading
Loading