1 /* etl.c
2 *
3 * Copyright 2020, Odysseus Yang
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 * Reads an ETL file and writes out a pcap file with LINKTYPE_ETW.
14 *
15 * https://docs.microsoft.com/en-us/windows/win32/etw/event-tracing-portal
16 */
17
18 #include "config.h"
19 #include "etl.h"
20 #include "wsutil/ws_getopt.h"
21 #include "wsutil/strtoi.h"
22 #include "etw_message.h"
23
24 #include <rpc.h>
25 #include <winevt.h>
26
27 #define MAX_PACKET_SIZE 0xFFFF
28 #define G_NSEC_PER_SEC 1000000000
29 #define ADD_OFFSET_TO_POINTER(buffer, offset) (((PBYTE)buffer) + offset)
30 #define ROUND_UP_COUNT(Count,Pow2) \
31 ( ((Count)+(Pow2)-1) & (~(((int)(Pow2))-1)) )
32
33 extern int g_include_undecidable_event;
34
35 const GUID mbb_provider = { 0xA42FE227, 0xA7BF, 0x4483, {0xA5, 0x02, 0x6B, 0xCD, 0xA4, 0x28, 0xCD, 0x96} };
36
37 EXTERN_C const GUID DECLSPEC_SELECTANY EventTraceGuid = { 0x68fdd900, 0x4a3e, 0x11d1, {0x84, 0xf4, 0x00, 0x00, 0xf8, 0x04, 0x64, 0xe3} };
38 EXTERN_C const GUID DECLSPEC_SELECTANY ImageIdGuid = { 0xb3e675d7, 0x2554, 0x4f18, { 0x83, 0xb, 0x27, 0x62, 0x73, 0x25, 0x60, 0xde } };
39 EXTERN_C const GUID DECLSPEC_SELECTANY SystemConfigExGuid = { 0x9b79ee91, 0xb5fd, 0x41c0, { 0xa2, 0x43, 0x42, 0x48, 0xe2, 0x66, 0xe9, 0xd0 } };
40 EXTERN_C const GUID DECLSPEC_SELECTANY EventMetadataGuid = { 0xbbccf6c1, 0x6cd1, 0x48c4, {0x80, 0xff, 0x83, 0x94, 0x82, 0xe3, 0x76, 0x71 } };
41 EXTERN_C const GUID DECLSPEC_SELECTANY ZeroGuid = { 0 };
42
43 typedef struct _WTAP_ETL_RECORD {
44 EVENT_HEADER EventHeader; // Event header
45 ETW_BUFFER_CONTEXT BufferContext; // Buffer context
46 ULONG UserDataLength;
47 ULONG MessageLength;
48 ULONG ProviderLength;
49 } WTAP_ETL_RECORD;
50
51 enum {
52 OPT_PROVIDER,
53 OPT_KEYWORD,
54 OPT_LEVEL,
55 };
56
57 static struct ws_option longopts[] = {
58 { "p", ws_required_argument, NULL, OPT_PROVIDER},
59 { "k", ws_required_argument, NULL, OPT_KEYWORD},
60 { "l", ws_required_argument, NULL, OPT_LEVEL},
61 { 0, 0, 0, 0 }
62 };
63
64 typedef struct _PROVIDER_FILTER {
65 GUID ProviderId;
66 ULONG64 Keyword;
67 UCHAR Level;
68 } PROVIDER_FILTER;
69
70 static gchar g_err_info[FILENAME_MAX] = { 0 };
71 static int g_err = ERROR_SUCCESS;
72 static wtap_dumper* g_pdh = NULL;
73 extern ULONGLONG g_num_events;
74 static PROVIDER_FILTER g_provider_filters[32] = { 0 };
75 static BOOL g_is_live_session = FALSE;
76
77 static void WINAPI event_callback(PEVENT_RECORD ev);
78 void etw_dump_write_opn_event(PEVENT_RECORD ev, ULARGE_INTEGER timestamp);
79 void etw_dump_write_general_event(PEVENT_RECORD ev, ULARGE_INTEGER timestamp);
80 void etw_dump_write_event_head_only(PEVENT_RECORD ev, ULARGE_INTEGER timestamp);
81 void wtap_etl_rec_dump(ULARGE_INTEGER timestamp, WTAP_ETL_RECORD* etl_record, ULONG total_packet_length, BOOLEAN is_inbound);
82 wtap_dumper* etw_dump_open(const char* pcapng_filename, int* err, gchar** err_info);
83
GetPropertyValue(WCHAR * ProviderId,EVT_PUBLISHER_METADATA_PROPERTY_ID PropertyId,PEVT_VARIANT * Value)84 DWORD GetPropertyValue(WCHAR* ProviderId, EVT_PUBLISHER_METADATA_PROPERTY_ID PropertyId, PEVT_VARIANT* Value)
85 {
86 BOOL bRet;
87 DWORD err = ERROR_SUCCESS;
88 PEVT_VARIANT value = NULL;
89 DWORD bufSize = 0;
90 DWORD bufUsedOrReqd = 0;
91
92 EVT_HANDLE pubHandle = EvtOpenPublisherMetadata(NULL, ProviderId, NULL, GetThreadLocale(), 0);
93 if (pubHandle == NULL)
94 {
95 return GetLastError();
96 }
97
98 /*
99 * Get required size for property
100 */
101 bRet = EvtGetPublisherMetadataProperty(
102 pubHandle,
103 PropertyId,
104 0,
105 bufSize,
106 value,
107 &bufUsedOrReqd);
108
109 if (!bRet && ((err = GetLastError()) != ERROR_INSUFFICIENT_BUFFER))
110 {
111 return err;
112 }
113 else if (bRet) /* Didn't expect this to succeed */
114 {
115 return ERROR_INVALID_STATE;
116 }
117
118 value = (PEVT_VARIANT)g_malloc(bufUsedOrReqd);
119 if (!value)
120 {
121 return ERROR_INSUFFICIENT_BUFFER;
122 }
123 bufSize = bufUsedOrReqd;
124
125 /*
126 * Get the property value
127 */
128 bRet = EvtGetPublisherMetadataProperty(
129 pubHandle,
130 PropertyId,
131 0,
132 bufSize,
133 value,
134 &bufUsedOrReqd);
135 if (!bRet)
136 {
137 g_free(value);
138 return GetLastError();
139 }
140
141 *Value = value;
142 return ERROR_SUCCESS;
143 }
144
etw_dump(const char * etl_filename,const char * pcapng_filename,const char * params,int * err,gchar ** err_info)145 wtap_open_return_val etw_dump(const char* etl_filename, const char* pcapng_filename, const char* params, int* err, gchar** err_info)
146 {
147 EVENT_TRACE_LOGFILE log_file = { 0 };
148 WCHAR w_etl_filename[FILENAME_MAX] = { 0 };
149 wtap_open_return_val returnVal = WTAP_OPEN_MINE;
150
151 SUPER_EVENT_TRACE_PROPERTIES super_trace_properties = { 0 };
152 super_trace_properties.prop.Wnode.BufferSize = sizeof(SUPER_EVENT_TRACE_PROPERTIES);
153 super_trace_properties.prop.Wnode.ClientContext = 2;
154 super_trace_properties.prop.Wnode.Flags = WNODE_FLAG_TRACED_GUID;
155 super_trace_properties.prop.LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
156 super_trace_properties.prop.LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
157 TRACEHANDLE traceControllerHandle = (TRACEHANDLE)INVALID_HANDLE_VALUE;
158 TRACEHANDLE trace_handle = INVALID_PROCESSTRACE_HANDLE;
159
160 SecureZeroMemory(g_provider_filters, sizeof(g_provider_filters));
161 SecureZeroMemory(g_err_info, FILENAME_MAX);
162 g_err = ERROR_SUCCESS;
163 g_num_events = 0;
164 g_is_live_session = FALSE;
165
166 if (params)
167 {
168 int opt_result = 0;
169 int option_idx = 0;
170 int provider_idx = 0;
171 char** params_array = NULL;
172 int params_array_num = 0;
173 WCHAR provider_id[FILENAME_MAX] = { 0 };
174 ULONG convert_level = 0;
175
176 params_array = g_strsplit(params, " ", -1);
177 while (params_array[params_array_num])
178 {
179 params_array_num++;
180 }
181
182 ws_optind = 0;
183 while ((opt_result = ws_getopt_long(params_array_num, params_array, ":", longopts, &option_idx)) != -1) {
184 switch (opt_result) {
185 case OPT_PROVIDER:
186 mbstowcs(provider_id, ws_optarg, FILENAME_MAX);
187 if (UuidFromString(provider_id, &g_provider_filters[provider_idx].ProviderId) == RPC_S_INVALID_STRING_UUID)
188 {
189 PEVT_VARIANT value = NULL;
190
191 *err = GetPropertyValue(
192 provider_id,
193 EvtPublisherMetadataPublisherGuid,
194 &value);
195
196 /*
197 * Copy returned GUID locally
198 */
199 if (*err == ERROR_SUCCESS)
200 {
201 if (value->Type == EvtVarTypeGuid && value->GuidVal)
202 {
203 g_provider_filters[provider_idx].ProviderId = *(value->GuidVal);
204 }
205 else
206 {
207 *err = ERROR_INVALID_DATA;
208 }
209 }
210 else
211 {
212 *err_info = g_strdup_printf("Cannot convert provider %s to a GUID, err is 0x%x", ws_optarg, *err);
213 return WTAP_OPEN_ERROR;
214 }
215
216 g_free(value);
217 }
218
219 if (IsEqualGUID(&g_provider_filters[0].ProviderId, &ZeroGuid))
220 {
221 *err = ERROR_INVALID_PARAMETER;
222 *err_info = g_strdup_printf("Provider %s is zero, err is 0x%x", ws_optarg, *err);
223 return WTAP_OPEN_ERROR;
224 }
225 provider_idx++;
226 break;
227 case OPT_KEYWORD:
228 if (provider_idx == 0)
229 {
230 *err = ERROR_INVALID_PARAMETER;
231 *err_info = g_strdup_printf("-k parameter must follow -p, err is 0x%x", *err);
232 return WTAP_OPEN_ERROR;
233 }
234
235 g_provider_filters[provider_idx - 1].Keyword = _strtoui64(ws_optarg, NULL, 0);
236 if (!g_provider_filters[provider_idx - 1].Keyword)
237 {
238 *err = ERROR_INVALID_PARAMETER;
239 *err_info = g_strdup_printf("Keyword %s cannot be converted, err is 0x%x", ws_optarg, *err);
240 return WTAP_OPEN_ERROR;
241 }
242 break;
243 case OPT_LEVEL:
244 if (provider_idx == 0)
245 {
246 *err = ERROR_INVALID_PARAMETER;
247 *err_info = g_strdup_printf("-l parameter must follow -p, err is 0x%x", *err);
248 return WTAP_OPEN_ERROR;
249 }
250
251 convert_level = strtoul(ws_optarg, NULL, 0);
252 if (convert_level > UCHAR_MAX)
253 {
254 *err = ERROR_INVALID_PARAMETER;
255 *err_info = g_strdup_printf("Level %s is bigger than 0xff, err is 0x%x", ws_optarg, *err);
256 return WTAP_OPEN_ERROR;
257 }
258 if (!convert_level)
259 {
260 *err = ERROR_INVALID_PARAMETER;
261 *err_info = g_strdup_printf("Level %s cannot be converted, err is 0x%x", ws_optarg, *err);
262 return WTAP_OPEN_ERROR;
263 }
264
265 g_provider_filters[provider_idx - 1].Level = (UCHAR)convert_level;
266 break;
267 }
268 }
269 g_strfreev(params_array);
270 }
271
272 /* do/while(FALSE) is used to jump out of loop so no complex nested if/else is needed */
273 do
274 {
275 /* Read ETW from an etl file */
276 if (etl_filename)
277 {
278 mbstowcs(w_etl_filename, etl_filename, FILENAME_MAX);
279
280 log_file.LogFileName = w_etl_filename;
281 log_file.ProcessTraceMode = PROCESS_TRACE_MODE_EVENT_RECORD;
282 log_file.EventRecordCallback = event_callback;
283 log_file.Context = NULL;
284 }
285 else
286 {
287 /*
288 * Try the best to stop the leftover session since extcap has no way to cleanup when stop capturing. See issue
289 * https://gitlab.com/wireshark/wireshark/-/issues/17131
290 */
291 ControlTrace((TRACEHANDLE)NULL, LOGGER_NAME, &super_trace_properties.prop, EVENT_TRACE_CONTROL_STOP);
292
293 g_is_live_session = TRUE;
294
295 log_file.LoggerName = LOGGER_NAME;
296 log_file.LogFileName = NULL;
297 log_file.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD;
298 log_file.EventRecordCallback = event_callback;
299 log_file.BufferCallback = NULL;
300 log_file.Context = NULL;
301
302 *err = StartTrace(
303 &traceControllerHandle,
304 log_file.LoggerName,
305 &super_trace_properties.prop);
306 if (*err != ERROR_SUCCESS)
307 {
308 *err_info = g_strdup_printf("StartTrace failed with %u", *err);
309 returnVal = WTAP_OPEN_ERROR;
310 break;
311 }
312
313 for (int i = 0; i < ARRAYSIZE(g_provider_filters); i++)
314 {
315 if (IsEqualGUID(&g_provider_filters[i].ProviderId, &ZeroGuid))
316 {
317 break;
318 }
319 *err = EnableTraceEx(
320 &g_provider_filters[i].ProviderId,
321 NULL,
322 traceControllerHandle,
323 TRUE,
324 g_provider_filters[i].Level,
325 g_provider_filters[i].Keyword,
326 0,
327 0,
328 NULL);
329 if (*err != ERROR_SUCCESS)
330 {
331 *err_info = g_strdup_printf("EnableTraceEx failed with %u", *err);
332 returnVal = WTAP_OPEN_ERROR;
333 break;
334 }
335 }
336 }
337
338 trace_handle = OpenTrace(&log_file);
339 if (trace_handle == INVALID_PROCESSTRACE_HANDLE) {
340 *err = GetLastError();
341 *err_info = g_strdup_printf("OpenTrace failed with %u", err);
342 returnVal = WTAP_OPEN_NOT_MINE;
343 break;
344 }
345
346 g_pdh = etw_dump_open(pcapng_filename, err, err_info);
347 if (g_pdh == NULL)
348 {
349 returnVal = WTAP_OPEN_ERROR;
350 break;
351 }
352
353 *err = ProcessTrace(&trace_handle, 1, 0, 0);
354 if (*err != ERROR_SUCCESS) {
355 returnVal = WTAP_OPEN_ERROR;
356 *err_info = g_strdup_printf("ProcessTrace failed with %u", err);
357 break;
358 }
359
360 if (g_err != ERROR_SUCCESS)
361 {
362 *err = g_err;
363 *err_info = g_strdup(g_err_info);
364 returnVal = WTAP_OPEN_ERROR;
365 break;
366 }
367
368 if (!g_num_events) {
369 *err = ERROR_NO_DATA;
370 *err_info = g_strdup_printf("Didn't find any etw event");
371 returnVal = WTAP_OPEN_NOT_MINE;
372 break;
373 }
374 } while (FALSE);
375
376 if (trace_handle != INVALID_PROCESSTRACE_HANDLE)
377 {
378 CloseTrace(trace_handle);
379 }
380 if (g_pdh != NULL)
381 {
382 if (*err == ERROR_SUCCESS)
383 {
384 if (!wtap_dump_close(g_pdh, err, err_info))
385 {
386 returnVal = WTAP_OPEN_ERROR;
387 }
388 }
389 else
390 {
391 int err_ignore;
392 gchar* err_info_ignore = NULL;
393 if (!wtap_dump_close(g_pdh, &err_ignore, &err_info_ignore))
394 {
395 returnVal = WTAP_OPEN_ERROR;
396 g_free(err_info_ignore);
397 }
398 }
399 }
400 return returnVal;
401 }
402
is_event_filtered_out(PEVENT_RECORD ev)403 BOOL is_event_filtered_out(PEVENT_RECORD ev)
404 {
405 if (g_is_live_session)
406 {
407 return FALSE;
408 }
409
410 if (IsEqualGUID(&g_provider_filters[0].ProviderId, &ZeroGuid))
411 {
412 return FALSE;
413 }
414
415 for (int i = 0; i < ARRAYSIZE(g_provider_filters); i++)
416 {
417 if (IsEqualGUID(&g_provider_filters[i].ProviderId, &ev->EventHeader.ProviderId))
418 {
419 return FALSE;
420 }
421 if (IsEqualGUID(&g_provider_filters[i].ProviderId, &ZeroGuid))
422 {
423 break;
424 }
425 }
426
427 return TRUE;
428 }
429
event_callback(PEVENT_RECORD ev)430 static void WINAPI event_callback(PEVENT_RECORD ev)
431 {
432 ULARGE_INTEGER timestamp;
433 g_num_events++;
434
435 if (is_event_filtered_out(ev))
436 {
437 return;
438 }
439
440 /*
441 * 100ns since 1/1/1601 -> usec since 1/1/1970.
442 * The offset of 11644473600 seconds can be calculated with a couple of calls to SystemTimeToFileTime.
443 */
444 timestamp.QuadPart = (ev->EventHeader.TimeStamp.QuadPart / 10) - 11644473600000000ll;
445
446 /* Write OPN events that needs mbim sub dissector */
447 if (IsEqualGUID(&ev->EventHeader.ProviderId, &mbb_provider))
448 {
449 etw_dump_write_opn_event(ev, timestamp);
450 }
451 /* TODO:: You can write events from other providers that needs specific sub dissector */
452 #if 0
453 else if (IsEqualGUID(&ev->EventHeader.ProviderId, &ndis_packcapture_provider))
454 {
455 etw_dump_write_packet_event(ev, timestamp);
456 }
457 #endif
458 /* Write any event form other providers other than above */
459 else
460 {
461 etw_dump_write_general_event(ev, timestamp);
462 }
463 }
464
etw_dump_open(const char * pcapng_filename,int * err,gchar ** err_info)465 wtap_dumper* etw_dump_open(const char* pcapng_filename, int* err, gchar** err_info)
466 {
467 wtap_dump_params params = { 0 };
468 GArray* shb_hdrs = NULL;
469 wtap_block_t shb_hdr;
470 wtapng_iface_descriptions_t* idb_info;
471 GArray* idb_datas;
472 wtap_block_t idb_data;
473 wtapng_if_descr_mandatory_t* descr_mand;
474
475 wtap_dumper* pdh = NULL;
476
477 shb_hdrs = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
478 shb_hdr = wtap_block_create(WTAP_BLOCK_SECTION);
479 g_array_append_val(shb_hdrs, shb_hdr);
480
481 /* In the future, may create multiple WTAP_BLOCK_IF_ID_AND_INFO separately for IP packet */
482 idb_info = g_new(wtapng_iface_descriptions_t, 1);
483 idb_datas = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
484 idb_data = wtap_block_create(WTAP_BLOCK_IF_ID_AND_INFO);
485 descr_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(idb_data);
486 descr_mand->tsprecision = WTAP_TSPREC_USEC;
487 descr_mand->wtap_encap = WTAP_ENCAP_ETW;
488 /* Timestamp for each pcapng packet is usec units, so time_units_per_second need be set to 10^6 */
489 descr_mand->time_units_per_second = G_USEC_PER_SEC;
490 g_array_append_val(idb_datas, idb_data);
491 idb_info->interface_data = idb_datas;
492
493 params.encap = WTAP_ENCAP_ETW;
494 params.snaplen = 0;
495 params.tsprec = WTAP_TSPREC_USEC;
496 params.shb_hdrs = shb_hdrs;
497 params.idb_inf = idb_info;
498
499 pdh = wtap_dump_open(pcapng_filename, wtap_pcapng_file_type_subtype(), WTAP_UNCOMPRESSED, ¶ms, err, err_info);
500
501 if (shb_hdrs)
502 {
503 wtap_block_array_free(shb_hdrs);
504 }
505 if (params.idb_inf)
506 {
507 if (params.idb_inf->interface_data)
508 {
509 wtap_block_array_free(params.idb_inf->interface_data);
510 }
511 g_free(params.idb_inf);
512 params.idb_inf = NULL;
513 }
514
515 return pdh;
516 }
517
wtap_etl_record_buffer_init(WTAP_ETL_RECORD ** out_etl_record,PEVENT_RECORD ev,BOOLEAN include_user_data,WCHAR * message,WCHAR * provider_name)518 ULONG wtap_etl_record_buffer_init(WTAP_ETL_RECORD** out_etl_record, PEVENT_RECORD ev, BOOLEAN include_user_data, WCHAR* message, WCHAR* provider_name)
519 {
520 ULONG total_packet_length = sizeof(WTAP_ETL_RECORD);
521 WTAP_ETL_RECORD* etl_record = NULL;
522 ULONG user_data_length = 0;
523 ULONG user_data_offset = 0;
524 ULONG message_offset = 0;
525 ULONG provider_name_offset = 0;
526 ULONG message_length = 0;
527 ULONG provider_name_length = 0;
528
529 if (include_user_data)
530 {
531 if (ev->UserDataLength < MAX_PACKET_SIZE)
532 {
533 user_data_length = ev->UserDataLength;
534 }
535 else
536 {
537 user_data_length = MAX_PACKET_SIZE;
538 }
539 user_data_offset = sizeof(WTAP_ETL_RECORD);
540 total_packet_length += ROUND_UP_COUNT(user_data_length, sizeof(LONG));
541 }
542 if (message && message[0] != L'\0')
543 {
544 message_offset = total_packet_length;
545 message_length = (ULONG)((wcslen(message) + 1) * sizeof(WCHAR));
546 total_packet_length += ROUND_UP_COUNT(message_length, sizeof(LONG));
547 }
548 if (provider_name && provider_name[0] != L'\0')
549 {
550 provider_name_offset = total_packet_length;
551 provider_name_length = (ULONG)((wcslen(provider_name) + 1) * sizeof(WCHAR));
552 total_packet_length += ROUND_UP_COUNT(provider_name_length, sizeof(LONG));
553 }
554
555 etl_record = g_malloc(total_packet_length);
556 SecureZeroMemory(etl_record, total_packet_length);
557 etl_record->EventHeader = ev->EventHeader;
558 etl_record->BufferContext = ev->BufferContext;
559 etl_record->UserDataLength = user_data_length;
560 etl_record->MessageLength = message_length;
561 etl_record->ProviderLength = provider_name_length;
562
563 if (user_data_offset)
564 {
565 memcpy(ADD_OFFSET_TO_POINTER(etl_record, user_data_offset), ev->UserData, user_data_length);
566 }
567 if (message_offset)
568 {
569 memcpy(ADD_OFFSET_TO_POINTER(etl_record, message_offset), message, message_length);
570 }
571 if (provider_name_offset)
572 {
573 memcpy(ADD_OFFSET_TO_POINTER(etl_record, provider_name_offset), provider_name, provider_name_length);
574 }
575
576 *out_etl_record = etl_record;
577 return total_packet_length;
578 }
579
wtap_etl_rec_dump(ULARGE_INTEGER timestamp,WTAP_ETL_RECORD * etl_record,ULONG total_packet_length,BOOLEAN is_inbound)580 void wtap_etl_rec_dump(ULARGE_INTEGER timestamp, WTAP_ETL_RECORD* etl_record, ULONG total_packet_length, BOOLEAN is_inbound)
581 {
582 gchar* err_info;
583 int err;
584 wtap_rec rec = { 0 };
585
586 wtap_rec_init(&rec);
587 rec.rec_header.packet_header.caplen = total_packet_length;
588 rec.rec_header.packet_header.len = total_packet_length;
589 rec.rec_header.packet_header.pkt_encap = WTAP_ENCAP_ETW;
590 rec.block = wtap_block_create(WTAP_BLOCK_PACKET);
591 wtap_block_add_uint32_option(rec.block, OPT_PKT_FLAGS, is_inbound ? PACK_FLAGS_DIRECTION_INBOUND : PACK_FLAGS_DIRECTION_OUTBOUND);
592 /* Convert usec of the timestamp into nstime_t */
593 rec.ts.secs = (time_t)(timestamp.QuadPart / G_USEC_PER_SEC);
594 rec.ts.nsecs = (int)(((timestamp.QuadPart % G_USEC_PER_SEC) * G_NSEC_PER_SEC) / G_USEC_PER_SEC);
595
596 /* and save the packet */
597 if (!wtap_dump(g_pdh, &rec, (guint8*)etl_record, &err, &err_info)) {
598 g_err = err;
599 sprintf_s(g_err_info, sizeof(g_err_info), "wtap_dump failed, %s", err_info);
600 g_free(err_info);
601 }
602
603 /* Only flush when live session */
604 if (g_is_live_session && !wtap_dump_flush(g_pdh, &err)) {
605 g_err = err;
606 sprintf_s(g_err_info, sizeof(g_err_info), "wtap_dump failed, %d", err);
607 }
608 wtap_rec_cleanup(&rec);
609 }
610
etw_dump_write_opn_event(PEVENT_RECORD ev,ULARGE_INTEGER timestamp)611 void etw_dump_write_opn_event(PEVENT_RECORD ev, ULARGE_INTEGER timestamp)
612 {
613 WTAP_ETL_RECORD* etl_record = NULL;
614 ULONG total_packet_length = 0;
615 BOOLEAN is_inbound = FALSE;
616 /* 0x80000000 mask the function to host message */
617 is_inbound = ((*(INT32*)(ev->UserData)) & 0x80000000) ? TRUE : FALSE;
618 total_packet_length = wtap_etl_record_buffer_init(&etl_record, ev, TRUE, NULL, NULL);
619 wtap_etl_rec_dump(timestamp, etl_record, total_packet_length, is_inbound);
620 g_free(etl_record);
621 }
622
etw_dump_write_event_head_only(PEVENT_RECORD ev,ULARGE_INTEGER timestamp)623 void etw_dump_write_event_head_only(PEVENT_RECORD ev, ULARGE_INTEGER timestamp)
624 {
625 WTAP_ETL_RECORD* etl_record = NULL;
626 ULONG total_packet_length = 0;
627 total_packet_length = wtap_etl_record_buffer_init(&etl_record, ev, FALSE, NULL, NULL);
628 wtap_etl_rec_dump(timestamp, etl_record, total_packet_length, FALSE);
629 g_free(etl_record);
630 }
631
etw_dump_write_general_event(PEVENT_RECORD ev,ULARGE_INTEGER timestamp)632 void etw_dump_write_general_event(PEVENT_RECORD ev, ULARGE_INTEGER timestamp)
633 {
634 PTRACE_EVENT_INFO pInfo = NULL;
635 PBYTE pUserData = NULL;
636 PBYTE pEndOfUserData = NULL;
637 DWORD PointerSize = 0;
638 PROPERTY_KEY_VALUE* prop_arr = NULL;
639 DWORD dwTopLevelPropertyCount = 0;
640 DWORD dwSizeofArray = 0;
641 WCHAR wszMessageBuffer[MAX_LOG_LINE_LENGTH] = { 0 };
642 WCHAR formatMessage[MAX_LOG_LINE_LENGTH] = { 0 };
643
644 WTAP_ETL_RECORD* etl_record = NULL;
645 ULONG total_packet_length = 0;
646 BOOLEAN is_message_dumped = FALSE;
647
648 do
649 {
650 /* Skip EventTrace events */
651 if (ev->EventHeader.Flags & EVENT_HEADER_FLAG_CLASSIC_HEADER &&
652 IsEqualGUID(&ev->EventHeader.ProviderId, &EventTraceGuid))
653 {
654 /*
655 * The first event in every ETL file contains the data from the file header.
656 * This is the same data as was returned in the EVENT_TRACE_LOGFILEW by
657 * OpenTrace. Since we've already seen this information, we'll skip this
658 * event.
659 */
660 break;
661 }
662
663 /* Skip events injected by the XPerf tracemerger - they will never be decodable */
664 if (IsEqualGUID(&ev->EventHeader.ProviderId, &ImageIdGuid) ||
665 IsEqualGUID(&ev->EventHeader.ProviderId, &SystemConfigExGuid) ||
666 IsEqualGUID(&ev->EventHeader.ProviderId, &EventMetadataGuid))
667 {
668 break;
669 }
670
671 if (!get_event_information(ev, &pInfo))
672 {
673 break;
674 }
675
676 /* Skip those events without format message since most of them need special logic to decode like NDIS-PackCapture */
677 if (pInfo->EventMessageOffset <= 0)
678 {
679 break;
680 }
681
682 if (EVENT_HEADER_FLAG_32_BIT_HEADER == (ev->EventHeader.Flags & EVENT_HEADER_FLAG_32_BIT_HEADER))
683 {
684 PointerSize = 4;
685 }
686 else
687 {
688 PointerSize = 8;
689 }
690
691 pUserData = (PBYTE)ev->UserData;
692 pEndOfUserData = (PBYTE)ev->UserData + ev->UserDataLength;
693
694 dwTopLevelPropertyCount = pInfo->TopLevelPropertyCount;
695 if (dwTopLevelPropertyCount > 0)
696 {
697 prop_arr = g_malloc(sizeof(PROPERTY_KEY_VALUE) * dwTopLevelPropertyCount);
698 dwSizeofArray = dwTopLevelPropertyCount * sizeof(PROPERTY_KEY_VALUE);
699 SecureZeroMemory(prop_arr, dwSizeofArray);
700 }
701
702 StringCbCopy(formatMessage, MAX_LOG_LINE_LENGTH, (LPWSTR)ADD_OFFSET_TO_POINTER(pInfo, pInfo->EventMessageOffset));
703
704 for (USHORT i = 0; i < dwTopLevelPropertyCount; i++)
705 {
706 pUserData = extract_properties(ev, pInfo, PointerSize, i, pUserData, pEndOfUserData, &prop_arr[i]);
707 if (NULL == pUserData)
708 {
709 break;
710 }
711 }
712
713 format_message(formatMessage, prop_arr, dwTopLevelPropertyCount, wszMessageBuffer, sizeof(wszMessageBuffer));
714
715 total_packet_length = wtap_etl_record_buffer_init(&etl_record, ev, FALSE, wszMessageBuffer, (WCHAR*)ADD_OFFSET_TO_POINTER(pInfo, pInfo->ProviderNameOffset));
716 wtap_etl_rec_dump(timestamp, etl_record, total_packet_length, FALSE);
717 g_free(etl_record);
718
719 is_message_dumped = TRUE;
720 } while (FALSE);
721
722 if (NULL != prop_arr)
723 {
724 g_free(prop_arr);
725 prop_arr = NULL;
726 }
727 if (NULL != pInfo)
728 {
729 g_free(pInfo);
730 pInfo = NULL;
731 }
732
733 if (!is_message_dumped && g_include_undecidable_event)
734 {
735 etw_dump_write_event_head_only(ev, timestamp);
736 }
737 }
738
739 /*
740 * Editor modelines - https://www.wireshark.org/tools/modelines.html
741 *
742 * Local variables:
743 * c-basic-offset: 4
744 * tab-width: 8
745 * indent-tabs-mode: nil
746 * End:
747 *
748 * vi: set shiftwidth=4 tabstop=8 expandtab:
749 * :indentSize=4:tabSize=8:noTabs=true:
750 */
751