2 * Routines for the EGFX RDP channel
3 * Copyright 2021, David Fort <contact@hardening-consulting.com>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
13 * See: "[MS-RDPEGFX] "
18 #include <epan/packet.h>
19 #include <epan/prefs.h>
20 #include <epan/conversation.h>
21 #include <epan/expert.h>
22 #include <epan/proto_data.h>
23 #include <epan/tvbuff_rdp.h>
24 #include <epan/crc32-tvb.h>
26 #include "packet-rdp.h"
27 #include "packet-rdpudp.h"
29 void proto_register_rdp_egfx(void);
30 void proto_reg_handoff_rdp_egfx(void);
32 static int proto_rdp_egfx
;
34 static int hf_egfx_cmdId
;
35 static int hf_egfx_flags
;
36 static int hf_egfx_pduLength
;
38 static int hf_egfx_caps_capsSetCount
;
39 static int hf_egfx_cap_version
;
40 static int hf_egfx_cap_length
;
42 static int hf_egfx_reset_width
;
43 static int hf_egfx_reset_height
;
44 static int hf_egfx_reset_monitorCount
;
45 static int hf_egfx_reset_monitorDefLeft
;
46 static int hf_egfx_reset_monitorDefTop
;
47 static int hf_egfx_reset_monitorDefRight
;
48 static int hf_egfx_reset_monitorDefBottom
;
49 static int hf_egfx_reset_monitorDefFlags
;
52 static int hf_egfx_ack_queue_depth
;
53 static int hf_egfx_ack_frame_id
;
54 static int hf_egfx_ack_total_decoded
;
55 static int hf_egfx_ack_frame_start
;
56 static int hf_egfx_ack_frame_end
;
58 static int hf_egfx_ackqoe_frame_id
;
59 static int hf_egfx_ackqoe_timestamp
;
60 static int hf_egfx_ackqoe_timediffse
;
61 static int hf_egfx_ackqoe_timediffedr
;
62 static int hf_egfx_ackqoe_frame_start
;
63 static int hf_egfx_ackqoe_frame_end
;
65 static int hf_egfx_start_timestamp
;
66 static int hf_egfx_start_frameid
;
67 static int hf_egfx_start_acked_in
;
69 static int hf_egfx_end_frameid
;
70 static int hf_egfx_end_acked_in
;
73 static int ett_rdp_egfx
;
74 static int ett_egfx_caps
;
75 static int ett_egfx_capsconfirm
;
76 static int ett_egfx_cap
;
77 static int ett_egfx_cap_version
;
78 static int ett_egfx_ack
;
79 static int ett_egfx_ackqoe
;
80 static int ett_egfx_reset
;
81 static int ett_egfx_monitors
;
82 static int ett_egfx_monitordef
;
85 static expert_field ei_egfx_pdulen_invalid
;
86 static expert_field ei_egfx_invalid_compression
;
89 #define PNAME "RDP Graphic pipeline channel Protocol"
91 #define PFNAME "rdp_egfx"
94 RDPGFX_CMDID_WIRETOSURFACE_1
= 0x0001,
95 RDPGFX_CMDID_WIRETOSURFACE_2
= 0x0002,
96 RDPGFX_CMDID_DELETEENCODINGCONTEXT
= 0x0003,
97 RDPGFX_CMDID_SOLIDFILL
= 0x0004,
98 RDPGFX_CMDID_SURFACETOSURFACE
= 0x0005,
99 RDPGFX_CMDID_SURFACETOCACHE
= 0x0006,
100 RDPGFX_CMDID_CACHETOSURFACE
= 0x0007,
101 RDPGFX_CMDID_EVICTCACHEENTRY
= 0x0008,
102 RDPGFX_CMDID_CREATESURFACE
= 0x0009,
103 RDPGFX_CMDID_DELETESURFACE
= 0x000a,
104 RDPGFX_CMDID_STARTFRAME
= 0x000b,
105 RDPGFX_CMDID_ENDFRAME
= 0x000c,
106 RDPGFX_CMDID_FRAMEACKNOWLEDGE
= 0x000d,
107 RDPGFX_CMDID_RESETGRAPHICS
= 0x000e,
108 RDPGFX_CMDID_MAPSURFACETOOUTPUT
= 0x000f,
109 RDPGFX_CMDID_CACHEIMPORTOFFER
= 0x0010,
110 RDPGFX_CMDID_CACHEIMPORTREPLY
= 0x0011,
111 RDPGFX_CMDID_CAPSADVERTISE
= 0x0012,
112 RDPGFX_CMDID_CAPSCONFIRM
= 0x0013,
113 RDPGFX_CMDID_MAPSURFACETOWINDOW
= 0x0015,
114 RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE
= 0x0016,
115 RDPGFX_CMDID_MAPSURFACETOSCALEDOUTPUT
= 0x0017,
116 RDPGFX_CMDID_MAPSURFACETOSCALEDWINDOW
= 0x0018,
120 RDPGFX_CAPVERSION_8
= 0x00080004,
121 RDPGFX_CAPVERSION_81
= 0x00080105,
122 RDPGFX_CAPVERSION_10
= 0x000A0002,
123 RDPGFX_CAPVERSION_101
= 0x000A0100,
124 RDPGFX_CAPVERSION_102
= 0x000A0200,
125 RDPGFX_CAPVERSION_103
= 0x000A0301,
126 RDPGFX_CAPVERSION_104
= 0x000A0400,
127 RDPGFX_CAPVERSION_105
= 0x000A0502,
128 RDPGFX_CAPVERSION_106_ERROR
= 0x000A0600,
129 RDPGFX_CAPVERSION_106
= 0x000A0601,
130 RDPGFX_CAPVERSION_107
= 0x000A0701
133 static const value_string rdp_egfx_cmd_vals
[] = {
134 { RDPGFX_CMDID_WIRETOSURFACE_1
, "Wire to surface 1" },
135 { RDPGFX_CMDID_WIRETOSURFACE_2
, "Wire to surface 2" },
136 { RDPGFX_CMDID_DELETEENCODINGCONTEXT
, "delete encoding context" },
137 { RDPGFX_CMDID_SOLIDFILL
, "Solid fill" },
138 { RDPGFX_CMDID_SURFACETOSURFACE
, "Surface to surface" },
139 { RDPGFX_CMDID_SURFACETOCACHE
, "Surface to cache" },
140 { RDPGFX_CMDID_CACHETOSURFACE
, "Cache to surface" },
141 { RDPGFX_CMDID_EVICTCACHEENTRY
, "Evict cache entry" },
142 { RDPGFX_CMDID_CREATESURFACE
, "Create surface" },
143 { RDPGFX_CMDID_DELETESURFACE
, "Delete surface" },
144 { RDPGFX_CMDID_STARTFRAME
, "Start frame" },
145 { RDPGFX_CMDID_ENDFRAME
, "End frame" },
146 { RDPGFX_CMDID_FRAMEACKNOWLEDGE
, "Frame acknowledge" },
147 { RDPGFX_CMDID_RESETGRAPHICS
, "Reset graphics" },
148 { RDPGFX_CMDID_MAPSURFACETOOUTPUT
, "Map Surface to output" },
149 { RDPGFX_CMDID_CACHEIMPORTOFFER
, "Cache import offer" },
150 { RDPGFX_CMDID_CACHEIMPORTREPLY
, "Cache import reply" },
151 { RDPGFX_CMDID_CAPSADVERTISE
, "Caps advertise" },
152 { RDPGFX_CMDID_CAPSCONFIRM
, "Caps confirm" },
153 { RDPGFX_CMDID_MAPSURFACETOWINDOW
, "Map surface to window" },
154 { RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE
, "Qoe frame acknowledge" },
155 { RDPGFX_CMDID_MAPSURFACETOSCALEDOUTPUT
, "Map surface to scaled output" },
156 { RDPGFX_CMDID_MAPSURFACETOSCALEDWINDOW
, "Map surface to scaled window" },
160 static const value_string rdp_egfx_caps_version_vals
[] = {
161 { RDPGFX_CAPVERSION_8
, "8.0" },
162 { RDPGFX_CAPVERSION_81
, "8.1" } ,
163 { RDPGFX_CAPVERSION_10
, "10.0" } ,
164 { RDPGFX_CAPVERSION_101
, "10.1" },
165 { RDPGFX_CAPVERSION_102
, "10.2" },
166 { RDPGFX_CAPVERSION_103
, "10.3" },
167 { RDPGFX_CAPVERSION_104
, "10.4" },
168 { RDPGFX_CAPVERSION_105
, "10.5" },
169 { RDPGFX_CAPVERSION_106_ERROR
, "10.6 bogus" },
170 { RDPGFX_CAPVERSION_106
, "10.6" },
171 { RDPGFX_CAPVERSION_107
, "10.7" },
175 static const value_string rdp_egfx_monitor_flags_vals
[] = {
176 { 0x00000000, "is secondary" },
177 { 0x00000001, "is primary" },
183 zgfx_context_t
*zgfx
;
202 find_egfx_version(uint32_t v
) {
203 const value_string
*vs
= rdp_egfx_caps_version_vals
;
204 for ( ; vs
->strptr
; vs
++)
211 static egfx_conv_info_t
*
212 egfx_get_conversation_data(packet_info
*pinfo
)
214 conversation_t
*conversation
, *conversation_tcp
;
215 egfx_conv_info_t
*info
;
217 conversation
= find_or_create_conversation(pinfo
);
219 info
= (egfx_conv_info_t
*)conversation_get_proto_data(conversation
, proto_rdp_egfx
);
221 conversation_tcp
= rdp_find_tcp_conversation_from_udp(conversation
);
222 if (conversation_tcp
)
223 info
= (egfx_conv_info_t
*)conversation_get_proto_data(conversation_tcp
, proto_rdp_egfx
);
227 info
= wmem_new0(wmem_file_scope(), egfx_conv_info_t
);
228 info
->zgfx
= zgfx_context_new(wmem_file_scope());
229 info
->frames
= wmem_map_new(wmem_file_scope(), g_direct_hash
, g_direct_equal
);
230 conversation_add_proto_data(conversation
, proto_rdp_egfx
, info
);
238 dissect_rdp_egfx_payload(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*parent_tree
, egfx_conv_info_t
*conv
, void *data _U_
)
249 parent_tree
= proto_tree_get_root(parent_tree
);
250 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "EGFX");
251 col_clear(pinfo
->cinfo
, COL_INFO
);
253 while (tvb_captured_length_remaining(tvb
, offset
) > 8) {
254 pduLength
= tvb_get_uint32(tvb
, offset
+ 4, ENC_LITTLE_ENDIAN
);
256 item
= proto_tree_add_item(parent_tree
, proto_rdp_egfx
, tvb
, offset
, pduLength
, ENC_NA
);
257 tree
= proto_item_add_subtree(item
, ett_rdp_egfx
);
259 proto_tree_add_item_ret_uint(tree
, hf_egfx_cmdId
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
, &cmdId
);
262 proto_tree_add_item(tree
, hf_egfx_flags
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
265 proto_tree_add_item(tree
, hf_egfx_pduLength
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
269 expert_add_info_format(pinfo
, item
, &ei_egfx_pdulen_invalid
, "pduLength is %u, not < 8", pduLength
);
273 int nextOffset
= offset
+ (pduLength
- 8);
275 case RDPGFX_CMDID_CAPSADVERTISE
: {
276 uint16_t capsSetCount
= tvb_get_uint16(tvb
, offset
, ENC_LITTLE_ENDIAN
);
278 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Caps advertise");
279 proto_tree_add_item(tree
, hf_egfx_caps_capsSetCount
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
281 subtree
= proto_tree_add_subtree(tree
, tvb
, offset
, pduLength
-8, ett_egfx_caps
, NULL
, "Caps");
284 for (i
= 0; i
< capsSetCount
; i
++) {
285 uint32_t version
= tvb_get_uint32(tvb
, offset
, ENC_LITTLE_ENDIAN
);
286 uint32_t capsDataLength
= tvb_get_uint32(tvb
, offset
+ 4, ENC_LITTLE_ENDIAN
);
287 proto_tree
* vtree
= proto_tree_add_subtree(subtree
, tvb
, offset
, 8 + capsDataLength
, ett_egfx_cap_version
, NULL
, find_egfx_version(version
));
289 proto_tree_add_item(vtree
, hf_egfx_cap_version
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
292 proto_tree_add_item(vtree
, hf_egfx_cap_length
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
295 offset
+= capsDataLength
;
300 case RDPGFX_CMDID_CAPSCONFIRM
: {
301 uint32_t capsDataLength
;
303 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Caps confirm");
305 subtree
= proto_tree_add_subtree(tree
, tvb
, offset
, pduLength
-8, ett_egfx_capsconfirm
, NULL
, "Caps confirm");
306 proto_tree_add_item(subtree
, hf_egfx_cap_version
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
309 proto_tree_add_item_ret_uint(subtree
, hf_egfx_cap_length
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
, &capsDataLength
);
313 case RDPGFX_CMDID_RESETGRAPHICS
: {
315 proto_tree
*monitors_tree
;
316 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Reset graphics");
318 subtree
= proto_tree_add_subtree(tree
, tvb
, offset
, pduLength
-8, ett_egfx_reset
, NULL
, "Reset graphics");
319 proto_tree_add_item(subtree
, hf_egfx_reset_width
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
322 proto_tree_add_item(subtree
, hf_egfx_reset_height
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
325 proto_tree_add_item_ret_uint(subtree
, hf_egfx_reset_monitorCount
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
, &nmonitor
);
328 monitors_tree
= proto_tree_add_subtree(subtree
, tvb
, offset
, nmonitor
* 20, ett_egfx_monitors
, NULL
, "Monitors");
329 for (i
= 0; i
< nmonitor
; i
++) {
330 proto_item
*monitor_tree
;
331 uint32_t left
, top
, right
, bottom
;
332 left
= tvb_get_uint32(tvb
, offset
, ENC_LITTLE_ENDIAN
);
333 top
= tvb_get_uint32(tvb
, offset
+4, ENC_LITTLE_ENDIAN
);
334 right
= tvb_get_uint32(tvb
, offset
+8, ENC_LITTLE_ENDIAN
);
335 bottom
= tvb_get_uint32(tvb
, offset
+12, ENC_LITTLE_ENDIAN
);
337 monitor_tree
= proto_tree_add_subtree_format(monitors_tree
, tvb
, offset
, 20, ett_egfx_monitordef
, NULL
,
338 "(%d,%d) - (%d,%d)", left
, top
, right
, bottom
);
340 proto_tree_add_item(monitor_tree
, hf_egfx_reset_monitorDefLeft
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
343 proto_tree_add_item(monitor_tree
, hf_egfx_reset_monitorDefTop
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
346 proto_tree_add_item(monitor_tree
, hf_egfx_reset_monitorDefRight
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
349 proto_tree_add_item(monitor_tree
, hf_egfx_reset_monitorDefBottom
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
352 proto_tree_add_item(monitor_tree
, hf_egfx_reset_monitorDefFlags
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
358 case RDPGFX_CMDID_STARTFRAME
: {
361 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Start frame");
362 proto_tree_add_item(tree
, hf_egfx_start_timestamp
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
363 // TODO: dissect timestamp
366 proto_tree_add_item_ret_uint(tree
, hf_egfx_start_frameid
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
, &frameId
);
367 frame
= wmem_map_lookup(conv
->frames
, GUINT_TO_POINTER(frameId
));
369 frame
= wmem_alloc0(wmem_file_scope(), sizeof(*frame
));
370 frame
->startNum
= pinfo
->num
;
373 wmem_map_insert(conv
->frames
, GUINT_TO_POINTER(frameId
), frame
);
376 if (PINFO_FD_VISITED(pinfo
) && frame
->ackNum
!= -1) {
377 pi
= proto_tree_add_uint(tree
, hf_egfx_start_acked_in
, tvb
, 0, 0, frame
->ackNum
);
378 proto_item_set_generated(pi
);
383 case RDPGFX_CMDID_ENDFRAME
: {
387 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "End frame");
388 proto_tree_add_item_ret_uint(tree
, hf_egfx_end_frameid
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
, &frameId
);
390 frame
= wmem_map_lookup(conv
->frames
, GUINT_TO_POINTER(frameId
));
392 frame
= wmem_alloc0(wmem_file_scope(), sizeof(*frame
));
393 frame
->startNum
= -1;
395 wmem_map_insert(conv
->frames
, GUINT_TO_POINTER(frameId
), frame
);
398 frame
->endNum
= pinfo
->num
;
400 if (PINFO_FD_VISITED(pinfo
) && frame
->ackNum
!= -1) {
401 pi
= proto_tree_add_uint(tree
, hf_egfx_end_acked_in
, tvb
, 0, 0, frame
->ackNum
);
402 proto_item_set_generated(pi
);
408 case RDPGFX_CMDID_FRAMEACKNOWLEDGE
: {
412 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Frame acknowledge");
413 subtree
= proto_tree_add_subtree(tree
, tvb
, offset
, -1, ett_egfx_ack
, NULL
, "Frame acknowledge");
414 proto_tree_add_item(subtree
, hf_egfx_ack_queue_depth
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
417 proto_tree_add_item_ret_uint(subtree
, hf_egfx_ack_frame_id
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
, &frameId
);
420 proto_tree_add_item(subtree
, hf_egfx_ack_total_decoded
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
422 frame
= wmem_map_lookup(conv
->frames
, GUINT_TO_POINTER(frameId
));
424 frame
= wmem_alloc0(wmem_file_scope(), sizeof(*frame
));
425 frame
->startNum
= -1;
427 frame
->ackNum
= frameId
;
428 wmem_map_insert(conv
->frames
, GUINT_TO_POINTER(frameId
), frame
);
431 frame
->ackNum
= pinfo
->num
;
433 if (PINFO_FD_VISITED(pinfo
) && frame
->startNum
!= -1) {
434 pi
= proto_tree_add_uint(tree
, hf_egfx_ack_frame_start
, tvb
, 0, 0, frame
->startNum
);
435 proto_item_set_generated(pi
);
438 if (PINFO_FD_VISITED(pinfo
) && frame
->endNum
!= -1) {
439 pi
= proto_tree_add_uint(tree
, hf_egfx_ack_frame_end
, tvb
, 0, 0, frame
->endNum
);
440 proto_item_set_generated(pi
);
445 case RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE
: {
449 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Frame acknowledge QoE");
450 subtree
= proto_tree_add_subtree(tree
, tvb
, offset
, -1, ett_egfx_ackqoe
, NULL
, "Frame acknowledge QoE");
451 proto_tree_add_item_ret_uint(subtree
, hf_egfx_ackqoe_frame_id
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
, &frameId
);
454 proto_tree_add_item(subtree
, hf_egfx_ackqoe_timestamp
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
457 proto_tree_add_item(subtree
, hf_egfx_ackqoe_timediffse
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
460 proto_tree_add_item(subtree
, hf_egfx_ackqoe_timediffedr
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
461 frame
= wmem_map_lookup(conv
->frames
, GUINT_TO_POINTER(frameId
));
463 frame
= wmem_alloc0(wmem_file_scope(), sizeof(*frame
));
464 frame
->startNum
= -1;
466 frame
->ackNum
= frameId
;
467 wmem_map_insert(conv
->frames
, GUINT_TO_POINTER(frameId
), frame
);
470 frame
->ackNum
= pinfo
->num
;
472 if (PINFO_FD_VISITED(pinfo
) && frame
->startNum
!= -1) {
473 pi
= proto_tree_add_uint(tree
, hf_egfx_ackqoe_frame_start
, tvb
, 0, 0, frame
->startNum
);
474 proto_item_set_generated(pi
);
477 if (PINFO_FD_VISITED(pinfo
) && frame
->endNum
!= -1) {
478 pi
= proto_tree_add_uint(tree
, hf_egfx_ackqoe_frame_end
, tvb
, 0, 0, frame
->endNum
);
479 proto_item_set_generated(pi
);
485 case RDPGFX_CMDID_CREATESURFACE
:
486 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Create Surface");
489 case RDPGFX_CMDID_MAPSURFACETOOUTPUT
:
490 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Map Surface To Output");
493 case RDPGFX_CMDID_WIRETOSURFACE_1
:
494 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Wire To Surface 1");
497 case RDPGFX_CMDID_WIRETOSURFACE_2
:
498 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Wire To Surface 2");
501 case RDPGFX_CMDID_DELETEENCODINGCONTEXT
:
502 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Delete Encoding Context");
505 case RDPGFX_CMDID_SOLIDFILL
:
506 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Solid Fill");
509 case RDPGFX_CMDID_SURFACETOSURFACE
:
510 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Surface To Surface");
513 case RDPGFX_CMDID_SURFACETOCACHE
:
514 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Surface To Cache");
517 case RDPGFX_CMDID_CACHETOSURFACE
:
518 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Cache To Surface");
521 case RDPGFX_CMDID_EVICTCACHEENTRY
:
522 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Evict Cache Entry");
525 case RDPGFX_CMDID_DELETESURFACE
:
526 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Delete Surface");
529 case RDPGFX_CMDID_CACHEIMPORTOFFER
:
530 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Cache Import Offer");
533 case RDPGFX_CMDID_CACHEIMPORTREPLY
:
534 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Cache Import Reply");
537 case RDPGFX_CMDID_MAPSURFACETOWINDOW
:
538 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Map Surface To Window");
541 case RDPGFX_CMDID_MAPSURFACETOSCALEDOUTPUT
:
542 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Map Surface To Scaled Output");
545 case RDPGFX_CMDID_MAPSURFACETOSCALEDWINDOW
:
546 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ",", "Map Surface To Scaled Window");
559 dissect_rdp_egfx(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*parent_tree
, void *data
)
561 tvbuff_t
*work_tvb
= tvb
;
562 egfx_conv_info_t
*infos
= egfx_get_conversation_data(pinfo
);
564 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "EGFX");
565 col_clear(pinfo
->cinfo
, COL_INFO
);
567 parent_tree
= proto_tree_get_root(parent_tree
);
569 if (!rdp_isServerAddressTarget(pinfo
)) {
570 uint32_t hash
= crc32_ccitt_tvb(tvb
, tvb_captured_length_remaining(tvb
, 0));
571 egfx_pdu_info_t
*pdu_infos
= p_get_proto_data(wmem_file_scope(), pinfo
, proto_rdp_egfx
, EGFX_PDU_KEY
);
573 pdu_infos
= wmem_alloc(wmem_file_scope(), sizeof(*pdu_infos
));
574 pdu_infos
->pdus
= wmem_tree_new(wmem_file_scope());
575 p_set_proto_data(wmem_file_scope(), pinfo
, proto_rdp_egfx
, EGFX_PDU_KEY
, pdu_infos
);
578 if (!PINFO_FD_VISITED(pinfo
)) {
579 work_tvb
= rdp8_decompress(infos
->zgfx
, wmem_file_scope(), tvb
, 0);
581 //printf("%d: zgfx sz=%d\n", pinfo->num, tvb_captured_length(work_tvb));
582 wmem_tree_insert32(pdu_infos
->pdus
, hash
, work_tvb
);
585 pdu_infos
= p_get_proto_data(wmem_file_scope(), pinfo
, proto_rdp_egfx
, EGFX_PDU_KEY
);
586 work_tvb
= wmem_tree_lookup32(pdu_infos
->pdus
, hash
);
590 add_new_data_source(pinfo
, work_tvb
, "Uncompressed GFX");
594 dissect_rdp_egfx_payload(work_tvb
, pinfo
, parent_tree
, infos
, data
);
597 expert_add_info_format(pinfo
, parent_tree
->last_child
, &ei_egfx_invalid_compression
, "invalid compression");
600 return tvb_reported_length(tvb
);
604 void proto_register_rdp_egfx(void) {
605 static hf_register_info hf
[] = {
607 { "CmdId", "rdp_egfx.cmdid",
608 FT_UINT16
, BASE_HEX
, VALS(rdp_egfx_cmd_vals
), 0x0,
612 { "flags", "rdp_egfx.flags",
613 FT_UINT16
, BASE_HEX
, NULL
, 0x0,
616 { &hf_egfx_pduLength
,
617 { "pduLength", "rdp_egfx.pdulength",
618 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
621 { &hf_egfx_caps_capsSetCount
,
622 { "capsSetCount", "rdp_egfx.caps.setcount",
623 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
626 { &hf_egfx_cap_version
,
627 { "Version", "rdp_egfx.cap.version",
628 FT_UINT32
, BASE_HEX
, VALS(rdp_egfx_caps_version_vals
), 0x0,
631 { &hf_egfx_cap_length
,
632 { "capsDataLength", "rdp_egfx.cap.length",
633 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
636 { &hf_egfx_ack_queue_depth
,
637 { "queueDepth", "rdp_egfx.ack.queuedepth",
638 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
641 { &hf_egfx_ack_frame_id
,
642 { "frameId", "rdp_egfx.ack.frameid",
643 FT_UINT32
, BASE_HEX
, NULL
, 0x0,
646 { &hf_egfx_ack_total_decoded
,
647 { "Total frames decoded", "rdp_egfx.ack.totalframesdecoded",
648 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
651 { &hf_egfx_ack_frame_start
,
652 { "Frame starts in", "rdp_egfx.ack.framestart",
653 FT_FRAMENUM
, BASE_NONE
, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST
), 0x0,
656 { &hf_egfx_ack_frame_end
,
657 { "Frame ends in", "rdp_egfx.ack.frameend",
658 FT_FRAMENUM
, BASE_NONE
, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST
), 0x0,
661 { &hf_egfx_ackqoe_frame_id
,
662 { "frameId", "rdp_egfx.ackqoe.frameid",
663 FT_UINT32
, BASE_HEX
, NULL
, 0x0,
666 { &hf_egfx_ackqoe_timestamp
,
667 { "Timestamp", "rdp_egfx.ackqoe.timestamp",
668 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
671 { &hf_egfx_ackqoe_timediffse
,
672 { "TimeDiffSE", "rdp_egfx.ackqoe.timediffse",
673 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
676 { &hf_egfx_ackqoe_timediffedr
,
677 { "TimeDiffEDR", "rdp_egfx.ackqoe.timediffedr",
678 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
681 { &hf_egfx_ackqoe_frame_start
,
682 { "Frame starts in", "rdp_egfx.ackqoe.framestart",
683 FT_FRAMENUM
, BASE_NONE
, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST
), 0x0,
686 { &hf_egfx_ackqoe_frame_end
,
687 { "Frame ends in", "rdp_egfx.ackqoe.frameend",
688 FT_FRAMENUM
, BASE_NONE
, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST
), 0x0,
691 { &hf_egfx_reset_width
,
692 { "Width", "rdp_egfx.reset.width",
693 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
696 { &hf_egfx_reset_height
,
697 { "Height", "rdp_egfx.reset.height",
698 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
701 { &hf_egfx_reset_monitorCount
,
702 { "Monitor count", "rdp_egfx.reset.monitorcount",
703 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
706 { &hf_egfx_reset_monitorDefLeft
,
707 { "Left", "rdp_egfx.monitor.left",
708 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
711 { &hf_egfx_reset_monitorDefTop
,
712 { "Top", "rdp_egfx.monitor.top",
713 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
716 { &hf_egfx_reset_monitorDefRight
,
717 { "Right", "rdp_egfx.monitor.right",
718 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
721 { &hf_egfx_reset_monitorDefBottom
,
722 { "Bottom", "rdp_egfx.monitor.bottom",
723 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
726 { &hf_egfx_reset_monitorDefFlags
,
727 { "Flags", "rdp_egfx.monitor.flags",
728 FT_UINT32
, BASE_HEX
, VALS(rdp_egfx_monitor_flags_vals
), 0x0,
731 { &hf_egfx_start_timestamp
,
732 { "Timestamp", "rdp_egfx.startframe.timestamp",
733 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
736 { &hf_egfx_start_frameid
,
737 { "Frame id", "rdp_egfx.startframe.frameid",
738 FT_UINT32
, BASE_HEX
, NULL
, 0x0,
741 { &hf_egfx_start_acked_in
,
742 { "Frame acked in", "rdp_egfx.startframe.ackedin",
743 FT_FRAMENUM
, BASE_NONE
, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE
), 0x0,
747 { &hf_egfx_end_frameid
,
748 { "Frame id", "rdp_egfx.endframe.frameid",
749 FT_UINT32
, BASE_HEX
, NULL
, 0x0,
752 { &hf_egfx_end_acked_in
,
753 { "Frame acked in", "rdp_egfx.endframe.ackedin",
754 FT_FRAMENUM
, BASE_NONE
, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE
), 0x0,
759 static int *ett
[] = {
763 &ett_egfx_cap_version
,
767 &ett_egfx_capsconfirm
,
769 &ett_egfx_monitordef
,
772 static ei_register_info ei
[] = {
773 { &ei_egfx_pdulen_invalid
, { "rdp_egfx.pdulength.invalid", PI_PROTOCOL
, PI_ERROR
, "Invalid length", EXPFILL
}},
774 { &ei_egfx_invalid_compression
, { "rdp_egfx.compression.invalid", PI_PROTOCOL
, PI_ERROR
, "Invalid compression", EXPFILL
}},
776 expert_module_t
* expert_egfx
;
779 proto_rdp_egfx
= proto_register_protocol(PNAME
, PSNAME
, PFNAME
);
780 /* Register fields and subtrees */
781 proto_register_field_array(proto_rdp_egfx
, hf
, array_length(hf
));
782 proto_register_subtree_array(ett
, array_length(ett
));
783 expert_egfx
= expert_register_protocol(proto_rdp_egfx
);
784 expert_register_field_array(expert_egfx
, ei
, array_length(ei
));
786 register_dissector("rdp_egfx", dissect_rdp_egfx
, proto_rdp_egfx
);
789 void proto_reg_handoff_rdp_egfx(void) {