2 * Routines for .NET Message Framing Protocol (MC-NMF) dissection
3 * Copyright 2017-2020, Uli Heilmeier <uh@heilmeier.eu>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wieshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
13 * Basic dissector for .NET Message Framing Protocol based on protocol reference found at
14 * https://download.microsoft.com/download/9/5/E/95EF66AF-9026-4BB0-A41D-A4F81802D92C/[MC-NMF].pdf
15 * https://msdn.microsoft.com/en-us/library/cc219293.aspx
18 * - heuristic to detect .NET MFP
23 #include <epan/packet.h>
24 #include <epan/conversation.h>
25 #include <epan/expert.h>
28 void proto_reg_handoff_mc_nmf(void);
29 void proto_register_mc_nmf(void);
31 static dissector_handle_t ms_nns_handle
;
32 static dissector_handle_t tls_handle
;
34 static dissector_handle_t mc_nmf_handle
;
36 /* Initialize the protocol and registered fields */
38 #define MC_NMF_REC_VERSION 0
39 #define MC_NMF_REC_MODE 1
40 #define MC_NMF_REC_VIA 2
41 #define MC_NMF_REC_KNOWN_ENC 3
42 #define MC_NMF_REC_EXT_ENC 4
43 #define MC_NMF_REC_UNSIZED_ENV 5
44 #define MC_NMF_REC_SIZED_ENV 6
45 #define MC_NMF_REC_END 7
46 #define MC_NMF_REC_FAULT 8
47 #define MC_NMF_REC_UPGRADE_REQ 9
48 #define MC_NMF_REC_UPGRADE_RSP 10
49 #define MC_NMF_REC_PREAMBLE_ACK 11
50 #define MC_NMF_REC_PREAMBLE_END 12
52 static const value_string mc_nmf_record_type_vals
[] = {
53 { MC_NMF_REC_VERSION
, "Version Record" },
54 { MC_NMF_REC_MODE
, "Mode Record" },
55 { MC_NMF_REC_VIA
, "Via Record" },
56 { MC_NMF_REC_KNOWN_ENC
, "Known Encoding Record" },
57 { MC_NMF_REC_EXT_ENC
, "Extensible Encoding Record" },
58 { MC_NMF_REC_UNSIZED_ENV
, "Unsized Envelope Record" },
59 { MC_NMF_REC_SIZED_ENV
, "Sized Envelope Record" },
60 { MC_NMF_REC_END
, "End Record" },
61 { MC_NMF_REC_FAULT
, "Fault Record" },
62 { MC_NMF_REC_UPGRADE_REQ
, "Upgrade Request Record" },
63 { MC_NMF_REC_UPGRADE_RSP
, "Upgrade Response Record" },
64 { MC_NMF_REC_PREAMBLE_ACK
, "Preamble Ack Record" },
65 { MC_NMF_REC_PREAMBLE_END
, "Preamble End Record" },
69 static const value_string mc_nmf_mode_vals
[] = {
70 { 1, "Singleton-Unsized" },
73 { 4, "Singleton-Sized" },
77 static const value_string mc_nmf_encoding_vals
[] = {
80 { 2, "Unicode little-endian" },
83 { 5, "Unicode little-endian" },
86 { 8, "Binary with in-band dictionary" },
90 struct mc_nmf_session_state
{
97 static int proto_mc_nmf
;
98 static int hf_mc_nmf_record_type
;
99 static int hf_mc_nmf_major_version
;
100 static int hf_mc_nmf_minor_version
;
101 static int hf_mc_nmf_mode
;
102 static int hf_mc_nmf_known_encoding
;
103 static int hf_mc_nmf_via_length
;
104 static int hf_mc_nmf_via
;
105 static int hf_mc_nmf_encoding_length
;
106 static int hf_mc_nmf_encoding_type
;
107 static int hf_mc_nmf_fault_length
;
108 static int hf_mc_nmf_fault
;
109 static int hf_mc_nmf_upgrade_length
;
110 static int hf_mc_nmf_upgrade
;
111 static int hf_mc_nmf_chunk_length
;
112 static int hf_mc_nmf_chunk
;
113 static int hf_mc_nmf_terminator
;
114 static int hf_mc_nmf_payload_length
;
115 static int hf_mc_nmf_payload
;
116 static int hf_mc_nmf_upgrade_proto_data
;
118 static expert_field ei_mc_nmf_size_too_big
;
120 // [MC-NMF] does not have a defined port https://learn.microsoft.com/en-us/openspecs/windows_protocols/mc-nmf/51b5eb53-f488-4b74-b21d-8a498f016b61
121 // but 9389 is ADWS port https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adcap/cfff3d7f-e7cd-4529-86a0-4de89efe3855
122 // which relies on [MC-NMF], so by doing this, all ADWS traffic on port 9389 is properly dissected by default
123 #define MC_NMF_TCP_PORT 9389
125 /* Initialize the subtree pointers */
126 static int ett_mc_nmf
;
127 static int ett_mc_nmf_rec
;
129 #define MC_NMF_MIN_LENGTH 1
131 static bool get_size_length(tvbuff_t
*tvb
, int *offset
, unsigned *len_length
, packet_info
*pinfo
, uint32_t *out_size
) {
134 unsigned shiftcount
= 0;
136 lbyte
= tvb_get_uint8(tvb
, *offset
);
139 size
= ( lbyte
& 0x7F);
140 while ( lbyte
& 0x80 ) {
141 lbyte
= tvb_get_uint8(tvb
, *offset
);
143 /* Guard against the pathological case of a sequence of 0x80
144 * bytes (which add nothing to size).
146 if (*len_length
>= 5) {
147 expert_add_info(pinfo
, NULL
, &ei_mc_nmf_size_too_big
);
150 shiftcount
= 7 * *len_length
;
151 size
= ((lbyte
& UINT64_C(0x7F)) << shiftcount
) | (size
);
154 * Check if size if is too big to prevent against overflow.
155 * According to spec an implementation SHOULD support record sizes as
156 * large as 0xffffffff octets (encoded size requires five octets).
158 if (size
> 0xffffffff) {
159 expert_add_info(pinfo
, NULL
, &ei_mc_nmf_size_too_big
);
163 *out_size
= (uint32_t)size
;
168 dissect_mc_nmf(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
170 proto_item
*ti
, *rti
, *dti
;
171 proto_tree
*mc_nmf_tree
, *rec_tree
, *data_tree
;
173 uint32_t record_type
;
174 uint8_t *upgrade_protocol
;
177 uint8_t search_terminator
;
178 conversation_t
*conversation
;
180 struct mc_nmf_session_state
*session_state
;
182 if (tvb_reported_length(tvb
) < MC_NMF_MIN_LENGTH
)
185 conversation
= find_or_create_conversation(pinfo
);
187 session_state
= (struct mc_nmf_session_state
*)conversation_get_proto_data(conversation
, proto_mc_nmf
);
189 if (!session_state
) {
190 session_state
= wmem_new0(wmem_file_scope(), struct mc_nmf_session_state
);
192 conversation_add_proto_data(conversation
, proto_mc_nmf
, session_state
);
196 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "MC-NMF");
197 col_clear(pinfo
->cinfo
, COL_INFO
);
199 ti
= proto_tree_add_item(tree
, proto_mc_nmf
, tvb
, 0, -1, ENC_NA
);
201 mc_nmf_tree
= proto_item_add_subtree(ti
, ett_mc_nmf
);
203 if ( session_state
->upgrade_rsp
&& session_state
->upgrade_rsp
< pinfo
->num
&& session_state
->negotiate
) {
204 dti
= proto_tree_add_item(mc_nmf_tree
, hf_mc_nmf_upgrade_proto_data
, tvb
,
206 data_tree
= proto_item_add_subtree(dti
, ett_mc_nmf_rec
);
207 nt_tvb
= tvb_new_subset_remaining(tvb
, offset
);
208 call_dissector(ms_nns_handle
, nt_tvb
, pinfo
, data_tree
);
209 return offset
+ tvb_reported_length(nt_tvb
);
211 else if ( session_state
->upgrade_rsp
&& session_state
->upgrade_rsp
< pinfo
->num
&& session_state
->tls
) {
212 dti
= proto_tree_add_item(mc_nmf_tree
, hf_mc_nmf_upgrade_proto_data
, tvb
,
214 data_tree
= proto_item_add_subtree(dti
, ett_mc_nmf_rec
);
215 nt_tvb
= tvb_new_subset_remaining(tvb
, offset
);
216 call_dissector(tls_handle
, nt_tvb
, pinfo
, data_tree
);
217 return offset
+ tvb_reported_length(nt_tvb
);
220 while (tvb_reported_length(tvb
) > offset
)
222 rti
= proto_tree_add_item_ret_uint(mc_nmf_tree
, hf_mc_nmf_record_type
, tvb
,
223 offset
, 1, ENC_BIG_ENDIAN
, &record_type
);
225 col_append_sep_fstr(pinfo
->cinfo
, COL_INFO
, ", ", "%s", val_to_str_const(record_type
, mc_nmf_record_type_vals
, "Unknown Record"));
227 switch (record_type
) {
228 case MC_NMF_REC_VERSION
:
229 rec_tree
= proto_item_add_subtree(rti
, ett_mc_nmf_rec
);
230 proto_tree_add_item(rec_tree
, hf_mc_nmf_major_version
, tvb
,
231 offset
, 1, ENC_BIG_ENDIAN
);
233 proto_tree_add_item(rec_tree
, hf_mc_nmf_minor_version
, tvb
,
234 offset
, 1, ENC_BIG_ENDIAN
);
237 case MC_NMF_REC_MODE
:
238 rec_tree
= proto_item_add_subtree(rti
, ett_mc_nmf_rec
);
239 proto_tree_add_item(rec_tree
, hf_mc_nmf_mode
, tvb
,
240 offset
, 1, ENC_BIG_ENDIAN
);
246 rec_tree
= proto_item_add_subtree(rti
, ett_mc_nmf_rec
);
247 if (!get_size_length(tvb
, &offset
, &len_length
, pinfo
, &size
))
248 return tvb_reported_length(tvb
);
249 proto_tree_add_uint(rec_tree
, hf_mc_nmf_via_length
, tvb
, offset
- len_length
, len_length
, size
);
250 proto_tree_add_item(rec_tree
, hf_mc_nmf_via
, tvb
, offset
, size
, ENC_UTF_8
);
253 case MC_NMF_REC_KNOWN_ENC
:
254 rec_tree
= proto_item_add_subtree(rti
, ett_mc_nmf_rec
);
255 proto_tree_add_item(rec_tree
, hf_mc_nmf_known_encoding
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
258 case MC_NMF_REC_EXT_ENC
:
261 rec_tree
= proto_item_add_subtree(rti
, ett_mc_nmf_rec
);
262 if (!get_size_length(tvb
, &offset
, &len_length
, pinfo
, &size
))
263 return tvb_reported_length(tvb
);
264 proto_tree_add_uint(rec_tree
, hf_mc_nmf_encoding_length
, tvb
, offset
- len_length
, len_length
, size
);
265 proto_tree_add_item(rec_tree
, hf_mc_nmf_encoding_type
, tvb
, offset
, size
, ENC_UTF_8
);
268 case MC_NMF_REC_UNSIZED_ENV
:
269 rec_tree
= proto_item_add_subtree(rti
, ett_mc_nmf_rec
);
273 if (!get_size_length(tvb
, &offset
, &len_length
, pinfo
, &size
))
274 return tvb_reported_length(tvb
);
275 proto_tree_add_uint(rec_tree
, hf_mc_nmf_chunk_length
, tvb
, offset
- len_length
, len_length
, size
);
276 proto_tree_add_item(rec_tree
, hf_mc_nmf_chunk
, tvb
, offset
, size
, ENC_NA
);
278 search_terminator
= tvb_get_uint8(tvb
, offset
);
279 } while ( search_terminator
!= 0x00 );
280 proto_tree_add_item(rec_tree
, hf_mc_nmf_terminator
, tvb
,
284 case MC_NMF_REC_SIZED_ENV
:
287 rec_tree
= proto_item_add_subtree(rti
, ett_mc_nmf_rec
);
288 if (!get_size_length(tvb
, &offset
, &len_length
, pinfo
, &size
))
289 return tvb_reported_length(tvb
);
290 proto_tree_add_uint(rec_tree
, hf_mc_nmf_payload_length
, tvb
, offset
- len_length
, len_length
, size
);
291 proto_tree_add_item(rec_tree
, hf_mc_nmf_payload
, tvb
, offset
, size
, ENC_NA
);
294 case MC_NMF_REC_FAULT
:
297 rec_tree
= proto_item_add_subtree(rti
, ett_mc_nmf_rec
);
298 if (!get_size_length(tvb
, &offset
, &len_length
, pinfo
, &size
))
299 return tvb_reported_length(tvb
);
300 proto_tree_add_uint(rec_tree
, hf_mc_nmf_fault_length
, tvb
, offset
- len_length
, len_length
, size
);
301 proto_tree_add_item(rec_tree
, hf_mc_nmf_fault
, tvb
, offset
, size
, ENC_UTF_8
);
304 case MC_NMF_REC_UPGRADE_REQ
:
307 rec_tree
= proto_item_add_subtree(rti
, ett_mc_nmf_rec
);
308 if (!get_size_length(tvb
, &offset
, &len_length
, pinfo
, &size
))
309 return tvb_reported_length(tvb
);
310 proto_tree_add_uint(rec_tree
, hf_mc_nmf_upgrade_length
, tvb
, offset
- len_length
, len_length
, size
);
311 proto_tree_add_item(rec_tree
, hf_mc_nmf_upgrade
, tvb
, offset
, size
, ENC_UTF_8
);
312 upgrade_protocol
= tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, size
, ENC_UTF_8
|ENC_NA
);
314 if (strcmp((char*)upgrade_protocol
, "application/negotiate") == 0) {
315 session_state
->negotiate
= true;
317 else if (strcmp((char*)upgrade_protocol
, "application/ssl-tls") == 0) {
318 session_state
->tls
= true;
320 session_state
->upgrade_req
= true;
322 case MC_NMF_REC_UPGRADE_RSP
:
323 if ( session_state
->upgrade_req
== true) {
324 session_state
->upgrade_rsp
= pinfo
->num
;
328 case MC_NMF_REC_PREAMBLE_ACK
:
329 case MC_NMF_REC_PREAMBLE_END
:
336 void proto_register_mc_nmf(void)
338 static hf_register_info hf
[] = {
339 { &hf_mc_nmf_record_type
,
340 { "RecordType", "mc-nmf.record_type",
341 FT_UINT8
, BASE_DEC
, VALS(mc_nmf_record_type_vals
), 0x0,
344 { &hf_mc_nmf_major_version
,
345 { "Major Version", "mc-nmf.major_version",
346 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
349 { &hf_mc_nmf_minor_version
,
350 { "Minor Version", "mc-nmf.minor_version",
351 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
355 { "Mode", "mc-nmf.mode",
356 FT_UINT8
, BASE_DEC
, VALS(mc_nmf_mode_vals
), 0x0,
359 { &hf_mc_nmf_known_encoding
,
360 { "Known Encoding", "mc-nmf.known_encoding",
361 FT_UINT8
, BASE_DEC
, VALS(mc_nmf_encoding_vals
), 0x0,
364 { &hf_mc_nmf_via_length
,
365 { "Via Length", "mc-nmf.via_length",
366 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
370 { "Via", "mc-nmf.via",
371 FT_STRING
, BASE_NONE
, NULL
, 0x0,
374 { &hf_mc_nmf_encoding_length
,
375 { "Encoding Length", "mc-nmf.encoding_length",
376 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
379 { &hf_mc_nmf_encoding_type
,
380 { "Encoding Type", "mc-nmf.encoding_type",
381 FT_STRING
, BASE_NONE
, NULL
, 0x0,
382 "MIME Content-Type", HFILL
}
384 { &hf_mc_nmf_fault_length
,
385 { "Fault Length", "mc-nmf.fault_length",
386 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
390 { "Fault", "mc-nmf.fault",
391 FT_STRING
, BASE_NONE
, NULL
, 0x0,
394 { &hf_mc_nmf_upgrade_length
,
395 { "Upgrade Protocol Length", "mc-nmf.upgrade_length",
396 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
399 { &hf_mc_nmf_upgrade
,
400 { "Upgrade Protocol", "mc-nmf.upgrade",
401 FT_STRING
, BASE_NONE
, NULL
, 0x0,
404 { &hf_mc_nmf_chunk_length
,
405 { "DataChunk Length", "mc-nmf.chunk_length",
406 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
410 { "DataChunk", "mc-nmf.chunk",
411 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
414 { &hf_mc_nmf_terminator
,
415 { "Terminator", "mc-nmf.terminator",
416 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
419 { &hf_mc_nmf_payload_length
,
420 { "Size", "mc-nmf.payload_length",
421 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
424 { &hf_mc_nmf_payload
,
425 { "Payload", "mc-nmf.payload",
426 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
429 { &hf_mc_nmf_upgrade_proto_data
,
430 { "Upgrade Protocol Data", "mc-nmf.upgrade_protocol_data",
431 FT_NONE
, BASE_NONE
, NULL
, 0x0,
436 static int *ett
[] = {
441 static ei_register_info ei
[] = {
442 { &ei_mc_nmf_size_too_big
, { "mc-nmf.size_too_big", PI_MALFORMED
, PI_ERROR
, "Size too big", EXPFILL
}},
445 expert_module_t
* expert_mc_nmf
;
447 proto_mc_nmf
= proto_register_protocol(".NET Message Framing Protocol", "MC-NMF", "mc-nmf");
449 proto_register_field_array(proto_mc_nmf
, hf
, array_length(hf
));
450 proto_register_subtree_array(ett
, array_length(ett
));
451 expert_mc_nmf
= expert_register_protocol(proto_mc_nmf
);
452 expert_register_field_array(expert_mc_nmf
, ei
, array_length(ei
));
454 mc_nmf_handle
= register_dissector("mc-nmf", dissect_mc_nmf
, proto_mc_nmf
);
457 void proto_reg_handoff_mc_nmf(void)
459 dissector_add_uint_with_preference("tcp.port", MC_NMF_TCP_PORT
, mc_nmf_handle
);
460 ms_nns_handle
= find_dissector_add_dependency("ms-nns", proto_mc_nmf
);
461 tls_handle
= find_dissector("tls");
465 * Editor modelines - https://www.wireshark.org/tools/modelines.html
470 * indent-tabs-mode: nil
473 * vi: set shiftwidth=4 tabstop=8 expandtab:
474 * :indentSize=4:tabSize=8:noTabs=true: