1 /* Packet-rdp_egfx.c
2  * Routines for the EGFX RDP channel
3  * Copyright 2021, David Fort <contact@hardening-consulting.com>
4  *
5  * Wireshark - Network traffic analyzer
6  * By Gerald Combs <gerald@wireshark.org>
7  * Copyright 1998 Gerald Combs
8  *
9  * SPDX-License-Identifier: GPL-2.0-or-later
10  */
11 
12 /*
13  * See: "[MS-RDPEGFX] "
14  */
15 
16 #include "config.h"
17 
18 #include <epan/packet.h>
19 #include <epan/prefs.h>
20 #include <epan/conversation.h>
21 #include <epan/expert.h>
22 #include <epan/tvbuff_rdp.h>
23 
24 #include "packet-rdp.h"
25 #include "packet-rdpudp.h"
26 
27 void proto_register_rdp_egfx(void);
28 void proto_reg_handoff_rdp_egfx(void);
29 
30 static int proto_rdp_egfx = -1;
31 
32 static int hf_egfx_cmdId = -1;
33 static int hf_egfx_flags = -1;
34 static int hf_egfx_pduLength = -1;
35 
36 static int hf_egfx_caps_capsSetCount = -1;
37 static int hf_egfx_cap_version = -1;
38 static int hf_egfx_cap_length = -1;
39 
40 static int hf_egfx_reset_width = -1;
41 static int hf_egfx_reset_height = -1;
42 static int hf_egfx_reset_monitorCount = -1;
43 static int hf_egfx_reset_monitorDefLeft = -1;
44 static int hf_egfx_reset_monitorDefTop = -1;
45 static int hf_egfx_reset_monitorDefRight = -1;
46 static int hf_egfx_reset_monitorDefBottom = -1;
47 static int hf_egfx_reset_monitorDefFlags = -1;
48 
49 
50 static int hf_egfx_ack_queue_depth = -1;
51 static int hf_egfx_ack_frame_id = -1;
52 static int hf_egfx_ack_total_decoded = -1;
53 
54 static int hf_egfx_ackqoe_frame_id = -1;
55 static int hf_egfx_ackqoe_timestamp = -1;
56 static int hf_egfx_ackqoe_timediffse = -1;
57 static int hf_egfx_ackqoe_timediffedr = -1;
58 
59 static int hf_egfx_start_timestamp = -1;
60 static int hf_egfx_start_frameid = -1;
61 static int hf_egfx_end_frameid = -1;
62 
63 
64 static int ett_rdp_egfx = -1;
65 static int ett_egfx_caps = -1;
66 static int ett_egfx_capsconfirm = -1;
67 static int ett_egfx_cap = -1;
68 static int ett_egfx_ack = -1;
69 static int ett_egfx_ackqoe = -1;
70 static int ett_egfx_reset = -1;
71 static int ett_egfx_monitors = -1;
72 static int ett_egfx_monitordef = -1;
73 
74 
75 static expert_field ei_egfx_pdulen_invalid = EI_INIT;
76 static expert_field ei_egfx_invalid_compression = EI_INIT;
77 
78 
79 #define PNAME  "RDP Graphic pipeline channel Protocol"
80 #define PSNAME "EGFX"
81 #define PFNAME "rdp_egfx"
82 
83 enum {
84 	RDPGFX_CMDID_WIRETOSURFACE_1 		= 0x0001,
85 	RDPGFX_CMDID_WIRETOSURFACE_2 		= 0x0002,
86 	RDPGFX_CMDID_DELETEENCODINGCONTEXT 	= 0x0003,
87 	RDPGFX_CMDID_SOLIDFILL 				= 0x0004,
88 	RDPGFX_CMDID_SURFACETOSURFACE 		= 0x0005,
89 	RDPGFX_CMDID_SURFACETOCACHE 		= 0x0006,
90 	RDPGFX_CMDID_CACHETOSURFACE 		= 0x0007,
91 	RDPGFX_CMDID_EVICTCACHEENTRY 		= 0x0008,
92 	RDPGFX_CMDID_CREATESURFACE 			= 0x0009,
93 	RDPGFX_CMDID_DELETESURFACE 			= 0x000a,
94 	RDPGFX_CMDID_STARTFRAME 			= 0x000b,
95 	RDPGFX_CMDID_ENDFRAME 				= 0x000c,
96 	RDPGFX_CMDID_FRAMEACKNOWLEDGE 		= 0x000d,
97 	RDPGFX_CMDID_RESETGRAPHICS 			= 0x000e,
98 	RDPGFX_CMDID_MAPSURFACETOOUTPUT 	= 0x000f,
99 	RDPGFX_CMDID_CACHEIMPORTOFFER 		= 0x0010,
100 	RDPGFX_CMDID_CACHEIMPORTREPLY 		= 0x0011,
101 	RDPGFX_CMDID_CAPSADVERTISE 			= 0x0012,
102 	RDPGFX_CMDID_CAPSCONFIRM 			= 0x0013,
103 	RDPGFX_CMDID_MAPSURFACETOWINDOW 	= 0x0015,
104 	RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE 	= 0x0016,
105 	RDPGFX_CMDID_MAPSURFACETOSCALEDOUTPUT = 0x0017,
106 	RDPGFX_CMDID_MAPSURFACETOSCALEDWINDOW = 0x0018,
107 };
108 
109 enum {
110 	RDPGFX_CAPVERSION_8 = 0x00080004,
111 	RDPGFX_CAPVERSION_81 = 0x00080105,
112 	RDPGFX_CAPVERSION_101 = 0x000A0100,
113 	RDPGFX_CAPVERSION_102 = 0x000A0200,
114 	RDPGFX_CAPVERSION_103 = 0x000A0301,
115 	RDPGFX_CAPVERSION_104 = 0x000A0400,
116 	RDPGFX_CAPVERSION_105 = 0x000A0502,
117 	RDPGFX_CAPVERSION_106 = 0x000A0600
118 };
119 
120 static const value_string rdp_egfx_cmd_vals[] = {
121 	{ RDPGFX_CMDID_WIRETOSURFACE_1, "Wire to surface 1" },
122 	{ RDPGFX_CMDID_WIRETOSURFACE_2, "Wire to surface 2" },
123 	{ RDPGFX_CMDID_DELETEENCODINGCONTEXT, "delete encoding context" },
124 	{ RDPGFX_CMDID_SOLIDFILL, "Solid fill" },
125 	{ RDPGFX_CMDID_SURFACETOSURFACE, "Surface to surface" },
126 	{ RDPGFX_CMDID_SURFACETOCACHE, "Surface to cache" },
127 	{ RDPGFX_CMDID_CACHETOSURFACE, "Cache to surface" },
128 	{ RDPGFX_CMDID_EVICTCACHEENTRY, "Evict cache entry" },
129 	{ RDPGFX_CMDID_CREATESURFACE, "Create surface" },
130 	{ RDPGFX_CMDID_DELETESURFACE, "Delete surface" },
131 	{ RDPGFX_CMDID_STARTFRAME, "Start frame" },
132 	{ RDPGFX_CMDID_ENDFRAME, "End frame" },
133 	{ RDPGFX_CMDID_FRAMEACKNOWLEDGE, "Frame acknowlegde" },
134 	{ RDPGFX_CMDID_RESETGRAPHICS, "Reset graphics" },
135 	{ RDPGFX_CMDID_MAPSURFACETOOUTPUT, "Map Surface to output" },
136 	{ RDPGFX_CMDID_CACHEIMPORTOFFER, "Cache import offer" },
137 	{ RDPGFX_CMDID_CACHEIMPORTREPLY, "Cache import reply" },
138 	{ RDPGFX_CMDID_CAPSADVERTISE, "Caps advertise" },
139 	{ RDPGFX_CMDID_CAPSCONFIRM, "Caps confirm" },
140 	{ RDPGFX_CMDID_MAPSURFACETOWINDOW, "Map surface to window" },
141 	{ RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE, "Qoe frame acknowlegde" },
142 	{ RDPGFX_CMDID_MAPSURFACETOSCALEDOUTPUT, "Map surface to scaled output" },
143 	{ RDPGFX_CMDID_MAPSURFACETOSCALEDWINDOW, "Map surface to scaled window" },
144 	{ 0x0, NULL },
145 };
146 
147 static const value_string rdp_egfx_caps_version_vals[] = {
148 	{ RDPGFX_CAPVERSION_8, "8" },
149 	{ RDPGFX_CAPVERSION_81, "8.1" } ,
150 	{ RDPGFX_CAPVERSION_101, "10.1" },
151 	{ RDPGFX_CAPVERSION_102, "10.2" },
152 	{ RDPGFX_CAPVERSION_103, "10.3" },
153 	{ RDPGFX_CAPVERSION_104, "10.4" },
154 	{ RDPGFX_CAPVERSION_105, "10.5" },
155 	{ RDPGFX_CAPVERSION_106, "10.6" },
156 	{ 0x0, NULL },
157 };
158 
159 static const value_string rdp_egfx_monitor_flags_vals[] = {
160 	{ 0x00000000, "is secondary" },
161 	{ 0x00000001, "is primary" },
162 	{ 0x0, NULL },
163 };
164 
165 
166 typedef struct {
167 	zgfx_context_t *zgfx;
168 } egfx_conv_info_t;
169 
170 
171 static egfx_conv_info_t *
egfx_get_conversation_data(packet_info * pinfo)172 egfx_get_conversation_data(packet_info *pinfo)
173 {
174 	conversation_t  *conversation, *conversation_tcp;
175 	egfx_conv_info_t *info;
176 
177 	conversation = find_or_create_conversation(pinfo);
178 
179 	info = (egfx_conv_info_t *)conversation_get_proto_data(conversation, proto_rdp_egfx);
180 	if (!info) {
181 		conversation_tcp = rdp_find_tcp_conversation_from_udp(conversation);
182 		if (conversation_tcp)
183 			info = (egfx_conv_info_t *)conversation_get_proto_data(conversation_tcp, proto_rdp_egfx);
184 	}
185 
186 	if (info == NULL) {
187 		info = wmem_new0(wmem_file_scope(), egfx_conv_info_t);
188 		info->zgfx = zgfx_context_new(wmem_file_scope());
189 		conversation_add_proto_data(conversation, proto_rdp_egfx, info);
190 	}
191 
192 	return info;
193 }
194 
195 
196 static int
dissect_rdp_egfx_payload(tvbuff_t * tvb,packet_info * pinfo,proto_tree * parent_tree,void * data _U_)197 dissect_rdp_egfx_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void *data _U_)
198 {
199 	proto_item *item;
200 	proto_tree *tree;
201 	proto_tree *subtree;
202 	gint offset = 0;
203 	guint16 cmdId = 0;
204 	guint32 pduLength;
205 	guint32 i;
206 
207 
208 	parent_tree = proto_tree_get_root(parent_tree);
209 	col_set_str(pinfo->cinfo, COL_PROTOCOL, "EGFX");
210 	col_clear(pinfo->cinfo, COL_INFO);
211 
212 	while (tvb_captured_length_remaining(tvb, offset) > 8) {
213 		pduLength = tvb_get_guint32(tvb, offset + 4, ENC_LITTLE_ENDIAN);
214 
215 		item = proto_tree_add_item(parent_tree, proto_rdp_egfx, tvb, offset, pduLength, ENC_NA);
216 		tree = proto_item_add_subtree(item, ett_rdp_egfx);
217 
218 		cmdId = tvb_get_guint16(tvb, offset, ENC_LITTLE_ENDIAN);
219 		proto_tree_add_item(tree, hf_egfx_cmdId, tvb, offset, 2, ENC_LITTLE_ENDIAN);
220 		offset += 2;
221 
222 		proto_tree_add_item(tree, hf_egfx_flags, tvb, offset, 2, ENC_LITTLE_ENDIAN);
223 		offset += 2;
224 
225 		proto_tree_add_item(tree, hf_egfx_pduLength, tvb, offset, 4, ENC_LITTLE_ENDIAN);
226 		offset += 4;
227 
228 		if (pduLength < 8) {
229 			expert_add_info_format(pinfo, item, &ei_egfx_pdulen_invalid, "pduLength is %u, not < 8", pduLength);
230 			return offset;
231 		}
232 
233 		switch (cmdId) {
234 		case RDPGFX_CMDID_CAPSADVERTISE: {
235 			guint16 capsSetCount = tvb_get_guint16(tvb, offset, ENC_LITTLE_ENDIAN);
236 
237 			col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "Caps advertise");
238 			proto_tree_add_item(tree, hf_egfx_caps_capsSetCount, tvb, offset, 2, ENC_LITTLE_ENDIAN);
239 
240 			subtree = proto_tree_add_subtree(tree, tvb, offset, pduLength-8, ett_egfx_caps, NULL, "Caps");
241 			offset += 2;
242 
243 			for (i = 0; i < capsSetCount; i++) {
244 				guint32 capsDataLength;
245 
246 				proto_tree_add_item(subtree, hf_egfx_cap_version, tvb, offset, 4, ENC_LITTLE_ENDIAN);
247 				offset += 4;
248 
249 				proto_tree_add_item(subtree, hf_egfx_cap_length, tvb, offset, 4, ENC_LITTLE_ENDIAN);
250 				capsDataLength = tvb_get_guint32(tvb, offset, ENC_LITTLE_ENDIAN);
251 				offset += 4;
252 
253 				offset += capsDataLength;
254 			}
255 			break;
256 		}
257 
258 		case RDPGFX_CMDID_CAPSCONFIRM: {
259 			guint32 capsDataLength;
260 
261 			col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "Caps confirm");
262 
263 			subtree = proto_tree_add_subtree(tree, tvb, offset, pduLength-8, ett_egfx_capsconfirm, NULL, "Caps confirm");
264 			proto_tree_add_item(subtree, hf_egfx_cap_version, tvb, offset, 4, ENC_LITTLE_ENDIAN);
265 			offset += 4;
266 
267 			proto_tree_add_item_ret_uint(subtree, hf_egfx_cap_length, tvb, offset, 4, ENC_LITTLE_ENDIAN, &capsDataLength);
268 			offset += 4 + capsDataLength;
269 			break;
270 		}
271 
272 		case RDPGFX_CMDID_RESETGRAPHICS: {
273 			guint32 nmonitor;
274 			proto_tree *monitors_tree;
275 			col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "Reset graphics");
276 
277 			subtree = proto_tree_add_subtree(tree, tvb, offset, pduLength-4, ett_egfx_reset, NULL, "Reset graphics");
278 			proto_tree_add_item(subtree, hf_egfx_reset_width, tvb, offset, 4, ENC_LITTLE_ENDIAN);
279 			offset += 4;
280 
281 			proto_tree_add_item(subtree, hf_egfx_reset_height, tvb, offset, 4, ENC_LITTLE_ENDIAN);
282 			offset += 4;
283 
284 			proto_tree_add_item_ret_uint(subtree, hf_egfx_reset_monitorCount, tvb, offset, 4, ENC_LITTLE_ENDIAN, &nmonitor);
285 			offset += 4;
286 
287 			monitors_tree = proto_tree_add_subtree(subtree, tvb, offset, nmonitor * 20, ett_egfx_monitors, NULL, "Monitors");
288 			for (i = 0; i < nmonitor; i++) {
289 				proto_item *monitor_tree;
290 				guint32 left, top, right, bottom;
291 				left = tvb_get_guint32(tvb, offset, ENC_LITTLE_ENDIAN);
292 				top = tvb_get_guint32(tvb, offset+4, ENC_LITTLE_ENDIAN);
293 				right = tvb_get_guint32(tvb, offset+8, ENC_LITTLE_ENDIAN);
294 				bottom = tvb_get_guint32(tvb, offset+12, ENC_LITTLE_ENDIAN);
295 
296 				monitor_tree = proto_tree_add_subtree_format(monitors_tree, tvb, offset, 20, ett_egfx_monitordef, NULL,
297 						"(%d,%d) - (%d,%d)", left, top, right, bottom);
298 
299 				proto_tree_add_item(monitor_tree, hf_egfx_reset_monitorDefLeft, tvb, offset, 4, ENC_LITTLE_ENDIAN);
300 				offset += 4;
301 
302 				proto_tree_add_item(monitor_tree, hf_egfx_reset_monitorDefTop, tvb, offset, 4, ENC_LITTLE_ENDIAN);
303 				offset += 4;
304 
305 				proto_tree_add_item(monitor_tree, hf_egfx_reset_monitorDefRight, tvb, offset, 4, ENC_LITTLE_ENDIAN);
306 				offset += 4;
307 
308 				proto_tree_add_item(monitor_tree, hf_egfx_reset_monitorDefBottom, tvb, offset, 4, ENC_LITTLE_ENDIAN);
309 				offset += 4;
310 
311 				proto_tree_add_item(monitor_tree, hf_egfx_reset_monitorDefFlags, tvb, offset, 4, ENC_LITTLE_ENDIAN);
312 				offset += 4;
313 			}
314 
315 			offset += (pduLength - 8);
316 			break;
317 		}
318 
319 		case RDPGFX_CMDID_STARTFRAME: {
320 			col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "Start frame");
321 			proto_tree_add_item(tree, hf_egfx_start_timestamp, tvb, offset, 4, ENC_LITTLE_ENDIAN);
322 			// TODO: dissect timestamp
323 			offset += 4;
324 
325 			proto_tree_add_item(tree, hf_egfx_start_frameid, tvb, offset, 4, ENC_LITTLE_ENDIAN);
326 			offset += 4;
327 			break;
328 		}
329 
330 		case RDPGFX_CMDID_ENDFRAME:
331 			col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "End frame");
332 			proto_tree_add_item(tree, hf_egfx_end_frameid, tvb, offset, 4, ENC_LITTLE_ENDIAN);
333 			offset += 4;
334 			break;
335 
336 		case RDPGFX_CMDID_FRAMEACKNOWLEDGE:
337 			col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "Frame acknowledge");
338 			subtree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_egfx_ack, NULL, "Frame acknowledge");
339 			proto_tree_add_item(subtree, hf_egfx_ack_queue_depth, tvb, offset, 4, ENC_LITTLE_ENDIAN);
340 			offset += 4;
341 
342 			proto_tree_add_item(subtree, hf_egfx_ack_frame_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
343 			offset += 4;
344 
345 			proto_tree_add_item(subtree, hf_egfx_ack_total_decoded, tvb, offset, 4, ENC_LITTLE_ENDIAN);
346 			offset += 4;
347 			break;
348 
349 		case RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE:
350 			col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "Frame acknowledge QoE");
351 			subtree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_egfx_ackqoe, NULL, "Frame acknowledge QoE");
352 			proto_tree_add_item(subtree, hf_egfx_ackqoe_frame_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
353 			offset += 4;
354 
355 			proto_tree_add_item(subtree, hf_egfx_ackqoe_timestamp, tvb, offset, 4, ENC_LITTLE_ENDIAN);
356 			offset += 4;
357 
358 			proto_tree_add_item(subtree, hf_egfx_ackqoe_timediffse, tvb, offset, 2, ENC_LITTLE_ENDIAN);
359 			offset += 2;
360 
361 			proto_tree_add_item(subtree, hf_egfx_ackqoe_timediffedr, tvb, offset, 2, ENC_LITTLE_ENDIAN);
362 			offset += 2;
363 			break;
364 
365 		default:
366 			offset += (pduLength - 8);
367 			break;
368 		}
369 	}
370 	return offset;
371 }
372 
373 static int
dissect_rdp_egfx(tvbuff_t * tvb,packet_info * pinfo,proto_tree * parent_tree,void * data)374 dissect_rdp_egfx(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void *data)
375 {
376 	tvbuff_t *work_tvb = tvb;
377 
378 	col_set_str(pinfo->cinfo, COL_PROTOCOL, "EGFX");
379 	col_clear(pinfo->cinfo, COL_INFO);
380 
381 	parent_tree = proto_tree_get_root(parent_tree);
382 
383 	if (!rdp_isServerAddressTarget(pinfo)) {
384 		egfx_conv_info_t *infos = egfx_get_conversation_data(pinfo);
385 		work_tvb = rdp8_decompress(infos->zgfx, wmem_packet_scope(), tvb, 0);
386 		if (!work_tvb && parent_tree) {
387 			expert_add_info_format(pinfo, parent_tree->last_child, &ei_egfx_invalid_compression, "invalid compression");
388 			return 0;
389 		}
390 		add_new_data_source(pinfo, work_tvb, "Uncompressed GFX");
391 	}
392 
393 	dissect_rdp_egfx_payload(work_tvb, pinfo, parent_tree, data);
394 	return tvb_reported_length(tvb);
395 }
396 
397 
proto_register_rdp_egfx(void)398 void proto_register_rdp_egfx(void) {
399 	static hf_register_info hf[] = {
400 		{ &hf_egfx_cmdId,
401 		  { "CmdId", "rdp_egfx.cmdid",
402 		    FT_UINT16, BASE_HEX, VALS(rdp_egfx_cmd_vals), 0x0,
403 			NULL, HFILL }
404 		},
405 		{ &hf_egfx_flags,
406 		  { "flags", "rdp_egfx.flags",
407 			FT_UINT16, BASE_HEX, NULL, 0x0,
408 			NULL, HFILL }
409 		},
410 		{ &hf_egfx_pduLength,
411 		  { "pduLength", "rdp_egfx.pdulength",
412 			FT_UINT32, BASE_DEC, NULL, 0x0,
413 			NULL, HFILL }
414 		},
415 		{ &hf_egfx_caps_capsSetCount,
416 		  { "capsSetCount", "rdp_egfx.caps.setcount",
417 			FT_UINT16, BASE_DEC, NULL, 0x0,
418 			NULL, HFILL }
419 		},
420 		{ &hf_egfx_cap_version,
421 		  { "Version", "rdp_egfx.cap.version",
422 			FT_UINT32, BASE_HEX, VALS(rdp_egfx_caps_version_vals), 0x0,
423 			NULL, HFILL }
424 		},
425 		{ &hf_egfx_cap_length,
426 		  { "capsDataLength", "rdp_egfx.cap.length",
427 			FT_UINT32, BASE_DEC, NULL, 0x0,
428 			NULL, HFILL }
429 		},
430 		{ &hf_egfx_ack_queue_depth,
431 		  { "queueDepth", "rdp_egfx.ack.queuedepth",
432 			FT_UINT32, BASE_DEC, NULL, 0x0,
433 			NULL, HFILL }
434 		},
435 		{ &hf_egfx_ack_frame_id,
436 		  { "frameId", "rdp_egfx.ack.frameid",
437 			FT_UINT32, BASE_HEX, NULL, 0x0,
438 			NULL, HFILL }
439 		},
440 		{ &hf_egfx_ack_total_decoded,
441 		  { "Total frames decoded", "rdp_egfx.ack.totalframesdecoded",
442 			FT_UINT32, BASE_DEC, NULL, 0x0,
443 			NULL, HFILL }
444 		},
445 		{ &hf_egfx_ackqoe_frame_id,
446 		  { "frameId", "rdp_egfx.ackqoe.frameid",
447 			FT_UINT32, BASE_HEX, NULL, 0x0,
448 			NULL, HFILL }
449 		},
450 		{ &hf_egfx_ackqoe_timestamp,
451 		  { "Timestamp", "rdp_egfx.ackqoe.timestamp",
452 			FT_UINT32, BASE_DEC, NULL, 0x0,
453 			NULL, HFILL }
454 		},
455 		{ &hf_egfx_ackqoe_timediffse,
456 		  { "TimeDiffSE", "rdp_egfx.ackqoe.timediffse",
457 			FT_UINT16, BASE_DEC, NULL, 0x0,
458 			NULL, HFILL }
459 		},
460 		{ &hf_egfx_ackqoe_timediffedr,
461 		  { "TimeDiffEDR", "rdp_egfx.ackqoe.timediffedr",
462 			FT_UINT16, BASE_DEC, NULL, 0x0,
463 			NULL, HFILL }
464 		},
465 		{ &hf_egfx_reset_width,
466 		  { "Width", "rdp_egfx.reset.width",
467 			FT_UINT32, BASE_DEC, NULL, 0x0,
468 			NULL, HFILL }
469 		},
470 		{ &hf_egfx_reset_height,
471 		  { "Height", "rdp_egfx.reset.height",
472 			FT_UINT32, BASE_DEC, NULL, 0x0,
473 			NULL, HFILL }
474 		},
475 		{ &hf_egfx_reset_monitorCount,
476 		  { "Monitor count", "rdp_egfx.reset.monitorcount",
477 			FT_UINT32, BASE_DEC, NULL, 0x0,
478 			NULL, HFILL }
479 		},
480 		{ &hf_egfx_reset_monitorDefLeft,
481 		  { "Left", "rdp_egfx.monitor.left",
482 			FT_UINT32, BASE_DEC, NULL, 0x0,
483 			NULL, HFILL }
484 		},
485 		{ &hf_egfx_reset_monitorDefTop,
486 		  { "Top", "rdp_egfx.monitor.top",
487 			FT_UINT32, BASE_DEC, NULL, 0x0,
488 			NULL, HFILL }
489 		},
490 		{ &hf_egfx_reset_monitorDefRight,
491 		  { "Right", "rdp_egfx.monitor.right",
492 			FT_UINT32, BASE_DEC, NULL, 0x0,
493 			NULL, HFILL }
494 		},
495 		{ &hf_egfx_reset_monitorDefBottom,
496 		  { "Bottom", "rdp_egfx.monitor.bottom",
497 			FT_UINT32, BASE_DEC, NULL, 0x0,
498 			NULL, HFILL }
499 		},
500 		{ &hf_egfx_reset_monitorDefFlags,
501 		  { "Flags", "rdp_egfx.monitor.flags",
502 			FT_UINT32, BASE_DEC, VALS(rdp_egfx_monitor_flags_vals), 0x0,
503 			NULL, HFILL }
504 		},
505 		{ &hf_egfx_start_timestamp,
506 		  { "Timestamp", "rdp_egfx.startframe.timestamp",
507 			FT_UINT32, BASE_DEC, NULL, 0x0,
508 			NULL, HFILL }
509 		},
510 		{ &hf_egfx_start_frameid,
511 		  { "Frame id", "rdp_egfx.startframe.frameid",
512 			FT_UINT32, BASE_HEX, NULL, 0x0,
513 			NULL, HFILL }
514 		},
515 		{ &hf_egfx_end_frameid,
516 		  { "Frame id", "rdp_egfx.endframe.frameid",
517 			FT_UINT32, BASE_HEX, NULL, 0x0,
518 			NULL, HFILL }
519 		},
520 	};
521 
522 	static gint *ett[] = {
523 		&ett_rdp_egfx,
524 		&ett_egfx_caps,
525 		&ett_egfx_cap,
526 		&ett_egfx_ack,
527 		&ett_egfx_ackqoe,
528 		&ett_egfx_reset,
529 		&ett_egfx_capsconfirm,
530 		&ett_egfx_monitors,
531 		&ett_egfx_monitordef,
532 	};
533 
534 	static ei_register_info ei[] = {
535 		{ &ei_egfx_pdulen_invalid, { "rdp_egfx.pdulength.invalid", PI_PROTOCOL, PI_ERROR, "Invalid length", EXPFILL }},
536 		{ &ei_egfx_invalid_compression, { "rdp_egfx.compression.invalid", PI_PROTOCOL, PI_ERROR, "Invalid compression", EXPFILL }},
537 	};
538 	expert_module_t* expert_egfx;
539 
540 
541 	proto_rdp_egfx = proto_register_protocol(PNAME, PSNAME, PFNAME);
542 	/* Register fields and subtrees */
543 	proto_register_field_array(proto_rdp_egfx, hf, array_length(hf));
544 	proto_register_subtree_array(ett, array_length(ett));
545 	expert_egfx = expert_register_protocol(proto_rdp_egfx);
546 	expert_register_field_array(expert_egfx, ei, array_length(ei));
547 
548 	register_dissector("rdp_egfx", dissect_rdp_egfx, proto_rdp_egfx);
549 }
550 
proto_reg_handoff_rdp_egfx(void)551 void proto_reg_handoff_rdp_egfx(void) {
552 }
553