Skip to content

Commit

Permalink
Add a dedicated dissector for Zoom
Browse files Browse the repository at this point in the history
Move it from the RTP code and extend it
  • Loading branch information
IvanNardi committed Jan 18, 2024
1 parent 248f3d5 commit 954e792
Show file tree
Hide file tree
Showing 76 changed files with 259 additions and 177 deletions.
1 change: 1 addition & 0 deletions src/include/ndpi_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,7 @@ void init_ceph_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int
void init_roughtime_dissector(struct ndpi_detection_module_struct *ndpi_struct, u_int32_t *id);
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_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 @@ -5865,6 +5865,9 @@ static int ndpi_callback_init(struct ndpi_detection_module_struct *ndpi_str) {
/* KCP */
init_kcp_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 find_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(find_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(find_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: 524 (6.31 diss/flow)
Num dissector calls: 525 (6.33 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: 4882 (24.78 diss/flow)
Num dissector calls: 4896 (24.85 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: 185 (185.00 diss/flow)
Num dissector calls: 186 (186.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: 140 (140.00 diss/flow)
Num dissector calls: 141 (141.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: 4749 (153.19 diss/flow)
Num dissector calls: 4778 (154.13 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: 149 (29.80 diss/flow)
Num dissector calls: 150 (30.00 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: 1178 (58.90 diss/flow)
Num dissector calls: 1180 (59.00 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: 860 (12.46 diss/flow)
Num dissector calls: 861 (12.48 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

0 comments on commit 954e792

Please sign in to comment.