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, &params, 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