2020-12-02 09:05:11 +00:00
|
|
|
/* etl.c
|
|
|
|
*
|
|
|
|
* Copyright 2020, Odysseus Yang
|
|
|
|
*
|
|
|
|
* Wireshark - Network traffic analyzer
|
|
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
|
|
* Copyright 1998 Gerald Combs
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
|
|
|
|
2021-01-27 22:33:09 +00:00
|
|
|
/*
|
|
|
|
* Reads an ETL file and writes out a pcap file with LINKTYPE_ETW.
|
|
|
|
*
|
|
|
|
* https://docs.microsoft.com/en-us/windows/win32/etw/event-tracing-portal
|
2020-12-02 09:05:11 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include "etl.h"
|
2021-07-26 16:22:36 +00:00
|
|
|
#include "wsutil/ws_getopt.h"
|
2021-01-11 18:45:24 +00:00
|
|
|
#include "wsutil/strtoi.h"
|
2020-12-02 09:05:11 +00:00
|
|
|
#include "etw_message.h"
|
|
|
|
|
2021-01-11 18:45:24 +00:00
|
|
|
#include <rpc.h>
|
|
|
|
#include <winevt.h>
|
|
|
|
|
2020-12-02 09:05:11 +00:00
|
|
|
#define MAX_PACKET_SIZE 0xFFFF
|
|
|
|
#define G_NSEC_PER_SEC 1000000000
|
|
|
|
#define ADD_OFFSET_TO_POINTER(buffer, offset) (((PBYTE)buffer) + offset)
|
|
|
|
#define ROUND_UP_COUNT(Count,Pow2) \
|
|
|
|
( ((Count)+(Pow2)-1) & (~(((int)(Pow2))-1)) )
|
|
|
|
|
|
|
|
extern int g_include_undecidable_event;
|
|
|
|
|
|
|
|
const GUID mbb_provider = { 0xA42FE227, 0xA7BF, 0x4483, {0xA5, 0x02, 0x6B, 0xCD, 0xA4, 0x28, 0xCD, 0x96} };
|
|
|
|
|
|
|
|
EXTERN_C const GUID DECLSPEC_SELECTANY EventTraceGuid = { 0x68fdd900, 0x4a3e, 0x11d1, {0x84, 0xf4, 0x00, 0x00, 0xf8, 0x04, 0x64, 0xe3} };
|
|
|
|
EXTERN_C const GUID DECLSPEC_SELECTANY ImageIdGuid = { 0xb3e675d7, 0x2554, 0x4f18, { 0x83, 0xb, 0x27, 0x62, 0x73, 0x25, 0x60, 0xde } };
|
|
|
|
EXTERN_C const GUID DECLSPEC_SELECTANY SystemConfigExGuid = { 0x9b79ee91, 0xb5fd, 0x41c0, { 0xa2, 0x43, 0x42, 0x48, 0xe2, 0x66, 0xe9, 0xd0 } };
|
|
|
|
EXTERN_C const GUID DECLSPEC_SELECTANY EventMetadataGuid = { 0xbbccf6c1, 0x6cd1, 0x48c4, {0x80, 0xff, 0x83, 0x94, 0x82, 0xe3, 0x76, 0x71 } };
|
2021-01-11 18:45:24 +00:00
|
|
|
EXTERN_C const GUID DECLSPEC_SELECTANY ZeroGuid = { 0 };
|
2020-12-02 09:05:11 +00:00
|
|
|
|
|
|
|
typedef struct _WTAP_ETL_RECORD {
|
|
|
|
EVENT_HEADER EventHeader; // Event header
|
|
|
|
ETW_BUFFER_CONTEXT BufferContext; // Buffer context
|
|
|
|
ULONG UserDataLength;
|
|
|
|
ULONG MessageLength;
|
|
|
|
ULONG ProviderLength;
|
|
|
|
} WTAP_ETL_RECORD;
|
|
|
|
|
2021-01-11 18:45:24 +00:00
|
|
|
enum {
|
|
|
|
OPT_PROVIDER,
|
|
|
|
OPT_KEYWORD,
|
|
|
|
OPT_LEVEL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct option longopts[] = {
|
|
|
|
{ "p", required_argument, NULL, OPT_PROVIDER},
|
|
|
|
{ "k", required_argument, NULL, OPT_KEYWORD},
|
|
|
|
{ "l", required_argument, NULL, OPT_LEVEL},
|
|
|
|
{ 0, 0, 0, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct _PROVIDER_FILTER {
|
|
|
|
GUID ProviderId;
|
|
|
|
ULONG64 Keyword;
|
|
|
|
UCHAR Level;
|
|
|
|
} PROVIDER_FILTER;
|
|
|
|
|
2020-12-02 09:05:11 +00:00
|
|
|
static gchar g_err_info[FILENAME_MAX] = { 0 };
|
|
|
|
static int g_err = ERROR_SUCCESS;
|
|
|
|
static wtap_dumper* g_pdh = NULL;
|
|
|
|
extern ULONGLONG g_num_events;
|
2021-01-11 18:45:24 +00:00
|
|
|
static PROVIDER_FILTER g_provider_filters[32] = { 0 };
|
|
|
|
static BOOL g_is_live_session = FALSE;
|
2020-12-02 09:05:11 +00:00
|
|
|
|
|
|
|
static void WINAPI event_callback(PEVENT_RECORD ev);
|
|
|
|
void etw_dump_write_opn_event(PEVENT_RECORD ev, ULARGE_INTEGER timestamp);
|
|
|
|
void etw_dump_write_general_event(PEVENT_RECORD ev, ULARGE_INTEGER timestamp);
|
|
|
|
void etw_dump_write_event_head_only(PEVENT_RECORD ev, ULARGE_INTEGER timestamp);
|
|
|
|
void wtap_etl_rec_dump(ULARGE_INTEGER timestamp, WTAP_ETL_RECORD* etl_record, ULONG total_packet_length, BOOLEAN is_inbound);
|
|
|
|
wtap_dumper* etw_dump_open(const char* pcapng_filename, int* err, gchar** err_info);
|
|
|
|
|
2021-01-11 18:45:24 +00:00
|
|
|
DWORD GetPropertyValue(WCHAR* ProviderId, EVT_PUBLISHER_METADATA_PROPERTY_ID PropertyId, PEVT_VARIANT* Value)
|
2020-12-02 09:05:11 +00:00
|
|
|
{
|
2021-01-11 18:45:24 +00:00
|
|
|
BOOL bRet;
|
|
|
|
DWORD err = ERROR_SUCCESS;
|
|
|
|
PEVT_VARIANT value = NULL;
|
|
|
|
DWORD bufSize = 0;
|
|
|
|
DWORD bufUsedOrReqd = 0;
|
|
|
|
|
|
|
|
EVT_HANDLE pubHandle = EvtOpenPublisherMetadata(NULL, ProviderId, NULL, GetThreadLocale(), 0);
|
|
|
|
if (pubHandle == NULL)
|
|
|
|
{
|
|
|
|
return GetLastError();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get required size for property
|
|
|
|
*/
|
|
|
|
bRet = EvtGetPublisherMetadataProperty(
|
|
|
|
pubHandle,
|
|
|
|
PropertyId,
|
|
|
|
0,
|
|
|
|
bufSize,
|
|
|
|
value,
|
|
|
|
&bufUsedOrReqd);
|
|
|
|
|
|
|
|
if (!bRet && ((err = GetLastError()) != ERROR_INSUFFICIENT_BUFFER))
|
|
|
|
{
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
else if (bRet) /* Didn't expect this to succeed */
|
|
|
|
{
|
|
|
|
return ERROR_INVALID_STATE;
|
|
|
|
}
|
|
|
|
|
|
|
|
value = (PEVT_VARIANT)g_malloc(bufUsedOrReqd);
|
|
|
|
if (!value)
|
|
|
|
{
|
|
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
|
|
}
|
|
|
|
bufSize = bufUsedOrReqd;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the property value
|
|
|
|
*/
|
|
|
|
bRet = EvtGetPublisherMetadataProperty(
|
|
|
|
pubHandle,
|
|
|
|
PropertyId,
|
|
|
|
0,
|
|
|
|
bufSize,
|
|
|
|
value,
|
|
|
|
&bufUsedOrReqd);
|
|
|
|
if (!bRet)
|
|
|
|
{
|
|
|
|
g_free(value);
|
|
|
|
return GetLastError();
|
|
|
|
}
|
|
|
|
|
|
|
|
*Value = value;
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
wtap_open_return_val etw_dump(const char* etl_filename, const char* pcapng_filename, const char* params, int* err, gchar** err_info)
|
|
|
|
{
|
|
|
|
EVENT_TRACE_LOGFILE log_file = { 0 };
|
2020-12-02 09:05:11 +00:00
|
|
|
WCHAR w_etl_filename[FILENAME_MAX] = { 0 };
|
|
|
|
wtap_open_return_val returnVal = WTAP_OPEN_MINE;
|
|
|
|
|
2021-01-11 18:45:24 +00:00
|
|
|
SUPER_EVENT_TRACE_PROPERTIES super_trace_properties = { 0 };
|
|
|
|
super_trace_properties.prop.Wnode.BufferSize = sizeof(SUPER_EVENT_TRACE_PROPERTIES);
|
|
|
|
super_trace_properties.prop.Wnode.ClientContext = 2;
|
|
|
|
super_trace_properties.prop.Wnode.Flags = WNODE_FLAG_TRACED_GUID;
|
|
|
|
super_trace_properties.prop.LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
|
|
|
|
super_trace_properties.prop.LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
|
|
|
|
TRACEHANDLE traceControllerHandle = (TRACEHANDLE)INVALID_HANDLE_VALUE;
|
|
|
|
TRACEHANDLE trace_handle = INVALID_PROCESSTRACE_HANDLE;
|
|
|
|
|
|
|
|
SecureZeroMemory(g_provider_filters, sizeof(g_provider_filters));
|
2020-12-02 09:05:11 +00:00
|
|
|
SecureZeroMemory(g_err_info, FILENAME_MAX);
|
|
|
|
g_err = ERROR_SUCCESS;
|
|
|
|
g_num_events = 0;
|
2021-01-11 18:45:24 +00:00
|
|
|
g_is_live_session = FALSE;
|
|
|
|
|
|
|
|
if (params)
|
|
|
|
{
|
|
|
|
int opt_result = 0;
|
|
|
|
int option_idx = 0;
|
|
|
|
int provider_idx = 0;
|
|
|
|
char** params_array = NULL;
|
|
|
|
int params_array_num = 0;
|
|
|
|
WCHAR provider_id[FILENAME_MAX] = { 0 };
|
|
|
|
ULONG convert_level = 0;
|
|
|
|
|
|
|
|
params_array = g_strsplit(params, " ", -1);
|
|
|
|
while (params_array[params_array_num])
|
|
|
|
{
|
|
|
|
params_array_num++;
|
|
|
|
}
|
|
|
|
|
2021-07-26 16:22:36 +00:00
|
|
|
ws_optind = 0;
|
|
|
|
while ((opt_result = ws_getopt_long(params_array_num, params_array, ":", longopts, &option_idx)) != -1) {
|
2021-01-11 18:45:24 +00:00
|
|
|
switch (opt_result) {
|
|
|
|
case OPT_PROVIDER:
|
2021-07-26 16:22:36 +00:00
|
|
|
mbstowcs(provider_id, ws_optarg, FILENAME_MAX);
|
2021-01-11 18:45:24 +00:00
|
|
|
if (UuidFromString(provider_id, &g_provider_filters[provider_idx].ProviderId) == RPC_S_INVALID_STRING_UUID)
|
|
|
|
{
|
|
|
|
PEVT_VARIANT value = NULL;
|
|
|
|
|
|
|
|
*err = GetPropertyValue(
|
|
|
|
provider_id,
|
|
|
|
EvtPublisherMetadataPublisherGuid,
|
|
|
|
&value);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy returned GUID locally
|
|
|
|
*/
|
|
|
|
if (*err == ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
if (value->Type == EvtVarTypeGuid && value->GuidVal)
|
|
|
|
{
|
|
|
|
g_provider_filters[provider_idx].ProviderId = *(value->GuidVal);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*err = ERROR_INVALID_DATA;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-07-26 16:22:36 +00:00
|
|
|
*err_info = g_strdup_printf("Cannot convert provider %s to a GUID, err is 0x%x", ws_optarg, *err);
|
2021-01-11 18:45:24 +00:00
|
|
|
return WTAP_OPEN_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IsEqualGUID(&g_provider_filters[0].ProviderId, &ZeroGuid))
|
|
|
|
{
|
|
|
|
*err = ERROR_INVALID_PARAMETER;
|
2021-07-26 16:22:36 +00:00
|
|
|
*err_info = g_strdup_printf("Provider %s is zero, err is 0x%x", ws_optarg, *err);
|
2021-01-11 18:45:24 +00:00
|
|
|
return WTAP_OPEN_ERROR;
|
|
|
|
}
|
|
|
|
provider_idx++;
|
|
|
|
break;
|
|
|
|
case OPT_KEYWORD:
|
|
|
|
if (provider_idx == 0)
|
|
|
|
{
|
|
|
|
*err = ERROR_INVALID_PARAMETER;
|
|
|
|
*err_info = g_strdup_printf("-k parameter must follow -p, err is 0x%x", *err);
|
|
|
|
return WTAP_OPEN_ERROR;
|
|
|
|
}
|
|
|
|
|
2021-07-26 16:22:36 +00:00
|
|
|
g_provider_filters[provider_idx - 1].Keyword = _strtoui64(ws_optarg, NULL, 0);
|
2021-01-11 18:45:24 +00:00
|
|
|
if (!g_provider_filters[provider_idx - 1].Keyword)
|
|
|
|
{
|
|
|
|
*err = ERROR_INVALID_PARAMETER;
|
2021-07-26 16:22:36 +00:00
|
|
|
*err_info = g_strdup_printf("Keyword %s cannot be converted, err is 0x%x", ws_optarg, *err);
|
2021-01-11 18:45:24 +00:00
|
|
|
return WTAP_OPEN_ERROR;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case OPT_LEVEL:
|
|
|
|
if (provider_idx == 0)
|
|
|
|
{
|
|
|
|
*err = ERROR_INVALID_PARAMETER;
|
|
|
|
*err_info = g_strdup_printf("-l parameter must follow -p, err is 0x%x", *err);
|
|
|
|
return WTAP_OPEN_ERROR;
|
|
|
|
}
|
|
|
|
|
2021-07-26 16:22:36 +00:00
|
|
|
convert_level = strtoul(ws_optarg, NULL, 0);
|
2021-01-11 18:45:24 +00:00
|
|
|
if (convert_level > UCHAR_MAX)
|
|
|
|
{
|
|
|
|
*err = ERROR_INVALID_PARAMETER;
|
2021-07-26 16:22:36 +00:00
|
|
|
*err_info = g_strdup_printf("Level %s is bigger than 0xff, err is 0x%x", ws_optarg, *err);
|
2021-01-11 18:45:24 +00:00
|
|
|
return WTAP_OPEN_ERROR;
|
|
|
|
}
|
|
|
|
if (!convert_level)
|
|
|
|
{
|
|
|
|
*err = ERROR_INVALID_PARAMETER;
|
2021-07-26 16:22:36 +00:00
|
|
|
*err_info = g_strdup_printf("Level %s cannot be converted, err is 0x%x", ws_optarg, *err);
|
2021-01-11 18:45:24 +00:00
|
|
|
return WTAP_OPEN_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_provider_filters[provider_idx - 1].Level = (UCHAR)convert_level;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g_strfreev(params_array);
|
|
|
|
}
|
2020-12-02 09:05:11 +00:00
|
|
|
|
|
|
|
/* do/while(FALSE) is used to jump out of loop so no complex nested if/else is needed */
|
|
|
|
do
|
|
|
|
{
|
2021-01-11 18:45:24 +00:00
|
|
|
/* Read ETW from an etl file */
|
|
|
|
if (etl_filename)
|
|
|
|
{
|
|
|
|
mbstowcs(w_etl_filename, etl_filename, FILENAME_MAX);
|
2020-12-02 09:05:11 +00:00
|
|
|
|
2021-01-11 18:45:24 +00:00
|
|
|
log_file.LogFileName = w_etl_filename;
|
|
|
|
log_file.ProcessTraceMode = PROCESS_TRACE_MODE_EVENT_RECORD;
|
|
|
|
log_file.EventRecordCallback = event_callback;
|
|
|
|
log_file.Context = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Try the best to stop the leftover session since extcap has no way to cleanup when stop capturing. See issue
|
|
|
|
* https://gitlab.com/wireshark/wireshark/-/issues/17131
|
|
|
|
*/
|
|
|
|
ControlTrace((TRACEHANDLE)NULL, LOGGER_NAME, &super_trace_properties.prop, EVENT_TRACE_CONTROL_STOP);
|
|
|
|
|
|
|
|
g_is_live_session = TRUE;
|
|
|
|
|
|
|
|
log_file.LoggerName = LOGGER_NAME;
|
|
|
|
log_file.LogFileName = NULL;
|
|
|
|
log_file.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD;
|
|
|
|
log_file.EventRecordCallback = event_callback;
|
|
|
|
log_file.BufferCallback = NULL;
|
|
|
|
log_file.Context = NULL;
|
|
|
|
|
|
|
|
*err = StartTrace(
|
|
|
|
&traceControllerHandle,
|
|
|
|
log_file.LoggerName,
|
|
|
|
&super_trace_properties.prop);
|
|
|
|
if (*err != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
*err_info = g_strdup_printf("StartTrace failed with %u", *err);
|
|
|
|
returnVal = WTAP_OPEN_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < ARRAYSIZE(g_provider_filters); i++)
|
|
|
|
{
|
|
|
|
if (IsEqualGUID(&g_provider_filters[i].ProviderId, &ZeroGuid))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
*err = EnableTraceEx(
|
|
|
|
&g_provider_filters[i].ProviderId,
|
|
|
|
NULL,
|
|
|
|
traceControllerHandle,
|
|
|
|
TRUE,
|
|
|
|
g_provider_filters[i].Level,
|
|
|
|
g_provider_filters[i].Keyword,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
NULL);
|
|
|
|
if (*err != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
*err_info = g_strdup_printf("EnableTraceEx failed with %u", *err);
|
|
|
|
returnVal = WTAP_OPEN_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-12-02 09:05:11 +00:00
|
|
|
|
|
|
|
trace_handle = OpenTrace(&log_file);
|
|
|
|
if (trace_handle == INVALID_PROCESSTRACE_HANDLE) {
|
2021-01-11 18:45:24 +00:00
|
|
|
*err = GetLastError();
|
2020-12-02 09:05:11 +00:00
|
|
|
*err_info = g_strdup_printf("OpenTrace failed with %u", err);
|
|
|
|
returnVal = WTAP_OPEN_NOT_MINE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_pdh = etw_dump_open(pcapng_filename, err, err_info);
|
|
|
|
if (g_pdh == NULL)
|
|
|
|
{
|
|
|
|
returnVal = WTAP_OPEN_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-01-11 18:45:24 +00:00
|
|
|
*err = ProcessTrace(&trace_handle, 1, 0, 0);
|
|
|
|
if (*err != ERROR_SUCCESS) {
|
2020-12-02 09:05:11 +00:00
|
|
|
returnVal = WTAP_OPEN_ERROR;
|
2021-01-11 18:45:24 +00:00
|
|
|
*err_info = g_strdup_printf("ProcessTrace failed with %u", err);
|
2020-12-02 09:05:11 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_err != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
*err = g_err;
|
|
|
|
*err_info = g_strdup(g_err_info);
|
|
|
|
returnVal = WTAP_OPEN_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!g_num_events) {
|
2021-01-11 18:45:24 +00:00
|
|
|
*err = ERROR_NO_DATA;
|
2020-12-02 09:05:11 +00:00
|
|
|
*err_info = g_strdup_printf("Didn't find any etw event");
|
|
|
|
returnVal = WTAP_OPEN_NOT_MINE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
|
|
|
|
if (trace_handle != INVALID_PROCESSTRACE_HANDLE)
|
|
|
|
{
|
|
|
|
CloseTrace(trace_handle);
|
|
|
|
}
|
|
|
|
if (g_pdh != NULL)
|
|
|
|
{
|
2021-01-11 18:45:24 +00:00
|
|
|
if (*err == ERROR_SUCCESS)
|
2020-12-02 09:05:11 +00:00
|
|
|
{
|
2021-01-11 18:45:24 +00:00
|
|
|
if (!wtap_dump_close(g_pdh, err, err_info))
|
|
|
|
{
|
|
|
|
returnVal = WTAP_OPEN_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int err_ignore;
|
|
|
|
gchar* err_info_ignore = NULL;
|
|
|
|
if (!wtap_dump_close(g_pdh, &err_ignore, &err_info_ignore))
|
|
|
|
{
|
|
|
|
returnVal = WTAP_OPEN_ERROR;
|
|
|
|
g_free(err_info_ignore);
|
|
|
|
}
|
2020-12-02 09:05:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return returnVal;
|
|
|
|
}
|
|
|
|
|
2021-01-11 18:45:24 +00:00
|
|
|
BOOL is_event_filtered_out(PEVENT_RECORD ev)
|
|
|
|
{
|
|
|
|
if (g_is_live_session)
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IsEqualGUID(&g_provider_filters[0].ProviderId, &ZeroGuid))
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < ARRAYSIZE(g_provider_filters); i++)
|
|
|
|
{
|
|
|
|
if (IsEqualGUID(&g_provider_filters[i].ProviderId, &ev->EventHeader.ProviderId))
|
|
|
|
{
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if (IsEqualGUID(&g_provider_filters[i].ProviderId, &ZeroGuid))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2020-12-02 09:05:11 +00:00
|
|
|
static void WINAPI event_callback(PEVENT_RECORD ev)
|
|
|
|
{
|
|
|
|
ULARGE_INTEGER timestamp;
|
|
|
|
g_num_events++;
|
2021-01-11 18:45:24 +00:00
|
|
|
|
|
|
|
if (is_event_filtered_out(ev))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-12-02 09:05:11 +00:00
|
|
|
/*
|
|
|
|
* 100ns since 1/1/1601 -> usec since 1/1/1970.
|
|
|
|
* The offset of 11644473600 seconds can be calculated with a couple of calls to SystemTimeToFileTime.
|
|
|
|
*/
|
|
|
|
timestamp.QuadPart = (ev->EventHeader.TimeStamp.QuadPart / 10) - 11644473600000000ll;
|
|
|
|
|
|
|
|
/* Write OPN events that needs mbim sub dissector */
|
|
|
|
if (IsEqualGUID(&ev->EventHeader.ProviderId, &mbb_provider))
|
|
|
|
{
|
|
|
|
etw_dump_write_opn_event(ev, timestamp);
|
|
|
|
}
|
|
|
|
/* TODO:: You can write events from other providers that needs specific sub dissector */
|
|
|
|
#if 0
|
|
|
|
else if (IsEqualGUID(&ev->EventHeader.ProviderId, &ndis_packcapture_provider))
|
|
|
|
{
|
|
|
|
etw_dump_write_packet_event(ev, timestamp);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
/* Write any event form other providers other than above */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
etw_dump_write_general_event(ev, timestamp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
wtap_dumper* etw_dump_open(const char* pcapng_filename, int* err, gchar** err_info)
|
|
|
|
{
|
|
|
|
wtap_dump_params params = { 0 };
|
|
|
|
GArray* shb_hdrs = NULL;
|
|
|
|
wtap_block_t shb_hdr;
|
|
|
|
wtapng_iface_descriptions_t* idb_info;
|
|
|
|
GArray* idb_datas;
|
|
|
|
wtap_block_t idb_data;
|
|
|
|
wtapng_if_descr_mandatory_t* descr_mand;
|
|
|
|
|
|
|
|
wtap_dumper* pdh = NULL;
|
|
|
|
|
|
|
|
shb_hdrs = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
|
2021-02-09 21:40:59 +00:00
|
|
|
shb_hdr = wtap_block_create(WTAP_BLOCK_SECTION);
|
2020-12-02 09:05:11 +00:00
|
|
|
g_array_append_val(shb_hdrs, shb_hdr);
|
|
|
|
|
wiretap: have file handlers advertise blocks and options supported.
Instead of a "supports name resolution" Boolean and bitflags for types of
comments supported, provide a list of block types that the file
type/subtype supports, with each block type having a list of options
supported. Indicate whether "supported" means "one instance" or
"multiple instances".
"Supports" doesn't just mean "can be written", it also means "could be
read".
Rename WTAP_BLOCK_IF_DESCRIPTION to WTAP_BLOCK_IF_ID_AND_INFO, to
indicate that it provides, in addition to information about the
interface, an ID (implicitly, in pcapng files, by its ordinal number)
that is associated with every packet in the file. Emphasize that in
comments - just because your capture file format can list the interfaces
on which a capture was done, that doesn't mean it supports this; it
doesn't do so if the file doesn't indicate, for every packet, on which
of those interfaces it was captured (I'm looking at *you*, Microsoft
Network Monitor...).
Use APIs to query that information to do what the "does this file
type/subtype support name resolution information", "does this file
type/subtype support all of these comment types", and "does this file
type/subtype support - and require - interface IDs" APIs did.
Provide backwards compatibility for Lua.
This allows us to eliminate the WTAP_FILE_TYPE_SUBTYPE_ values for IBM's
iptrace; do so.
2021-02-21 22:18:04 +00:00
|
|
|
/* In the future, may create multiple WTAP_BLOCK_IF_ID_AND_INFO separately for IP packet */
|
2020-12-02 09:05:11 +00:00
|
|
|
idb_info = g_new(wtapng_iface_descriptions_t, 1);
|
|
|
|
idb_datas = g_array_new(FALSE, FALSE, sizeof(wtap_block_t));
|
wiretap: have file handlers advertise blocks and options supported.
Instead of a "supports name resolution" Boolean and bitflags for types of
comments supported, provide a list of block types that the file
type/subtype supports, with each block type having a list of options
supported. Indicate whether "supported" means "one instance" or
"multiple instances".
"Supports" doesn't just mean "can be written", it also means "could be
read".
Rename WTAP_BLOCK_IF_DESCRIPTION to WTAP_BLOCK_IF_ID_AND_INFO, to
indicate that it provides, in addition to information about the
interface, an ID (implicitly, in pcapng files, by its ordinal number)
that is associated with every packet in the file. Emphasize that in
comments - just because your capture file format can list the interfaces
on which a capture was done, that doesn't mean it supports this; it
doesn't do so if the file doesn't indicate, for every packet, on which
of those interfaces it was captured (I'm looking at *you*, Microsoft
Network Monitor...).
Use APIs to query that information to do what the "does this file
type/subtype support name resolution information", "does this file
type/subtype support all of these comment types", and "does this file
type/subtype support - and require - interface IDs" APIs did.
Provide backwards compatibility for Lua.
This allows us to eliminate the WTAP_FILE_TYPE_SUBTYPE_ values for IBM's
iptrace; do so.
2021-02-21 22:18:04 +00:00
|
|
|
idb_data = wtap_block_create(WTAP_BLOCK_IF_ID_AND_INFO);
|
2020-12-02 09:05:11 +00:00
|
|
|
descr_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(idb_data);
|
|
|
|
descr_mand->tsprecision = WTAP_TSPREC_USEC;
|
2021-01-27 22:33:09 +00:00
|
|
|
descr_mand->wtap_encap = WTAP_ENCAP_ETW;
|
2020-12-02 09:05:11 +00:00
|
|
|
/* Timestamp for each pcapng packet is usec units, so time_units_per_second need be set to 10^6 */
|
|
|
|
descr_mand->time_units_per_second = G_USEC_PER_SEC;
|
|
|
|
g_array_append_val(idb_datas, idb_data);
|
|
|
|
idb_info->interface_data = idb_datas;
|
|
|
|
|
2021-01-27 22:33:09 +00:00
|
|
|
params.encap = WTAP_ENCAP_ETW;
|
2020-12-02 09:05:11 +00:00
|
|
|
params.snaplen = 0;
|
|
|
|
params.tsprec = WTAP_TSPREC_USEC;
|
|
|
|
params.shb_hdrs = shb_hdrs;
|
|
|
|
params.idb_inf = idb_info;
|
|
|
|
|
2021-02-23 09:18:31 +00:00
|
|
|
pdh = wtap_dump_open(pcapng_filename, wtap_pcapng_file_type_subtype(), WTAP_UNCOMPRESSED, ¶ms, err, err_info);
|
2020-12-02 09:05:11 +00:00
|
|
|
|
|
|
|
if (shb_hdrs)
|
|
|
|
{
|
|
|
|
wtap_block_array_free(shb_hdrs);
|
|
|
|
}
|
|
|
|
if (params.idb_inf)
|
|
|
|
{
|
|
|
|
if (params.idb_inf->interface_data)
|
|
|
|
{
|
|
|
|
wtap_block_array_free(params.idb_inf->interface_data);
|
|
|
|
}
|
|
|
|
g_free(params.idb_inf);
|
|
|
|
params.idb_inf = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pdh;
|
|
|
|
}
|
|
|
|
|
|
|
|
ULONG wtap_etl_record_buffer_init(WTAP_ETL_RECORD** out_etl_record, PEVENT_RECORD ev, BOOLEAN include_user_data, WCHAR* message, WCHAR* provider_name)
|
|
|
|
{
|
|
|
|
ULONG total_packet_length = sizeof(WTAP_ETL_RECORD);
|
|
|
|
WTAP_ETL_RECORD* etl_record = NULL;
|
|
|
|
ULONG user_data_length = 0;
|
|
|
|
ULONG user_data_offset = 0;
|
|
|
|
ULONG message_offset = 0;
|
|
|
|
ULONG provider_name_offset = 0;
|
|
|
|
ULONG message_length = 0;
|
|
|
|
ULONG provider_name_length = 0;
|
|
|
|
|
|
|
|
if (include_user_data)
|
|
|
|
{
|
|
|
|
if (ev->UserDataLength < MAX_PACKET_SIZE)
|
|
|
|
{
|
|
|
|
user_data_length = ev->UserDataLength;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
user_data_length = MAX_PACKET_SIZE;
|
|
|
|
}
|
|
|
|
user_data_offset = sizeof(WTAP_ETL_RECORD);
|
|
|
|
total_packet_length += ROUND_UP_COUNT(user_data_length, sizeof(LONG));
|
|
|
|
}
|
|
|
|
if (message && message[0] != L'\0')
|
|
|
|
{
|
|
|
|
message_offset = total_packet_length;
|
|
|
|
message_length = (ULONG)((wcslen(message) + 1) * sizeof(WCHAR));
|
|
|
|
total_packet_length += ROUND_UP_COUNT(message_length, sizeof(LONG));
|
|
|
|
}
|
|
|
|
if (provider_name && provider_name[0] != L'\0')
|
|
|
|
{
|
|
|
|
provider_name_offset = total_packet_length;
|
|
|
|
provider_name_length = (ULONG)((wcslen(provider_name) + 1) * sizeof(WCHAR));
|
|
|
|
total_packet_length += ROUND_UP_COUNT(provider_name_length, sizeof(LONG));
|
|
|
|
}
|
|
|
|
|
|
|
|
etl_record = g_malloc(total_packet_length);
|
|
|
|
SecureZeroMemory(etl_record, total_packet_length);
|
|
|
|
etl_record->EventHeader = ev->EventHeader;
|
|
|
|
etl_record->BufferContext = ev->BufferContext;
|
|
|
|
etl_record->UserDataLength = user_data_length;
|
|
|
|
etl_record->MessageLength = message_length;
|
|
|
|
etl_record->ProviderLength = provider_name_length;
|
|
|
|
|
|
|
|
if (user_data_offset)
|
|
|
|
{
|
|
|
|
memcpy(ADD_OFFSET_TO_POINTER(etl_record, user_data_offset), ev->UserData, user_data_length);
|
|
|
|
}
|
|
|
|
if (message_offset)
|
|
|
|
{
|
|
|
|
memcpy(ADD_OFFSET_TO_POINTER(etl_record, message_offset), message, message_length);
|
|
|
|
}
|
|
|
|
if (provider_name_offset)
|
|
|
|
{
|
|
|
|
memcpy(ADD_OFFSET_TO_POINTER(etl_record, provider_name_offset), provider_name, provider_name_length);
|
|
|
|
}
|
|
|
|
|
|
|
|
*out_etl_record = etl_record;
|
|
|
|
return total_packet_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
void wtap_etl_rec_dump(ULARGE_INTEGER timestamp, WTAP_ETL_RECORD* etl_record, ULONG total_packet_length, BOOLEAN is_inbound)
|
|
|
|
{
|
|
|
|
gchar* err_info;
|
|
|
|
int err;
|
|
|
|
wtap_rec rec = { 0 };
|
|
|
|
|
|
|
|
wtap_rec_init(&rec);
|
|
|
|
rec.rec_header.packet_header.caplen = total_packet_length;
|
|
|
|
rec.rec_header.packet_header.len = total_packet_length;
|
2021-01-27 22:33:09 +00:00
|
|
|
rec.rec_header.packet_header.pkt_encap = WTAP_ENCAP_ETW;
|
2021-07-12 21:40:21 +00:00
|
|
|
rec.block = wtap_block_create(WTAP_BLOCK_PACKET);
|
|
|
|
wtap_block_add_uint32_option(rec.block, OPT_PKT_FLAGS, is_inbound ? PACK_FLAGS_DIRECTION_INBOUND : PACK_FLAGS_DIRECTION_OUTBOUND);
|
2020-12-02 09:05:11 +00:00
|
|
|
/* Convert usec of the timestamp into nstime_t */
|
|
|
|
rec.ts.secs = (time_t)(timestamp.QuadPart / G_USEC_PER_SEC);
|
|
|
|
rec.ts.nsecs = (int)(((timestamp.QuadPart % G_USEC_PER_SEC) * G_NSEC_PER_SEC) / G_USEC_PER_SEC);
|
|
|
|
|
|
|
|
/* and save the packet */
|
|
|
|
if (!wtap_dump(g_pdh, &rec, (guint8*)etl_record, &err, &err_info)) {
|
|
|
|
g_err = err;
|
|
|
|
sprintf_s(g_err_info, sizeof(g_err_info), "wtap_dump failed, %s", err_info);
|
|
|
|
g_free(err_info);
|
|
|
|
}
|
2021-01-11 18:45:24 +00:00
|
|
|
|
|
|
|
/* Only flush when live session */
|
|
|
|
if (g_is_live_session && !wtap_dump_flush(g_pdh, &err)) {
|
|
|
|
g_err = err;
|
|
|
|
sprintf_s(g_err_info, sizeof(g_err_info), "wtap_dump failed, %d", err);
|
|
|
|
}
|
2020-12-02 09:05:11 +00:00
|
|
|
wtap_rec_cleanup(&rec);
|
|
|
|
}
|
|
|
|
|
|
|
|
void etw_dump_write_opn_event(PEVENT_RECORD ev, ULARGE_INTEGER timestamp)
|
|
|
|
{
|
|
|
|
WTAP_ETL_RECORD* etl_record = NULL;
|
|
|
|
ULONG total_packet_length = 0;
|
|
|
|
BOOLEAN is_inbound = FALSE;
|
|
|
|
/* 0x80000000 mask the function to host message */
|
|
|
|
is_inbound = ((*(INT32*)(ev->UserData)) & 0x80000000) ? TRUE : FALSE;
|
|
|
|
total_packet_length = wtap_etl_record_buffer_init(&etl_record, ev, TRUE, NULL, NULL);
|
|
|
|
wtap_etl_rec_dump(timestamp, etl_record, total_packet_length, is_inbound);
|
|
|
|
g_free(etl_record);
|
|
|
|
}
|
|
|
|
|
|
|
|
void etw_dump_write_event_head_only(PEVENT_RECORD ev, ULARGE_INTEGER timestamp)
|
|
|
|
{
|
|
|
|
WTAP_ETL_RECORD* etl_record = NULL;
|
|
|
|
ULONG total_packet_length = 0;
|
|
|
|
total_packet_length = wtap_etl_record_buffer_init(&etl_record, ev, FALSE, NULL, NULL);
|
|
|
|
wtap_etl_rec_dump(timestamp, etl_record, total_packet_length, FALSE);
|
|
|
|
g_free(etl_record);
|
|
|
|
}
|
|
|
|
|
|
|
|
void etw_dump_write_general_event(PEVENT_RECORD ev, ULARGE_INTEGER timestamp)
|
|
|
|
{
|
|
|
|
PTRACE_EVENT_INFO pInfo = NULL;
|
|
|
|
PBYTE pUserData = NULL;
|
|
|
|
PBYTE pEndOfUserData = NULL;
|
|
|
|
DWORD PointerSize = 0;
|
|
|
|
PROPERTY_KEY_VALUE* prop_arr = NULL;
|
|
|
|
DWORD dwTopLevelPropertyCount = 0;
|
|
|
|
DWORD dwSizeofArray = 0;
|
|
|
|
WCHAR wszMessageBuffer[MAX_LOG_LINE_LENGTH] = { 0 };
|
|
|
|
WCHAR formatMessage[MAX_LOG_LINE_LENGTH] = { 0 };
|
|
|
|
|
|
|
|
WTAP_ETL_RECORD* etl_record = NULL;
|
|
|
|
ULONG total_packet_length = 0;
|
|
|
|
BOOLEAN is_message_dumped = FALSE;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
/* Skip EventTrace events */
|
|
|
|
if (ev->EventHeader.Flags & EVENT_HEADER_FLAG_CLASSIC_HEADER &&
|
|
|
|
IsEqualGUID(&ev->EventHeader.ProviderId, &EventTraceGuid))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* The first event in every ETL file contains the data from the file header.
|
|
|
|
* This is the same data as was returned in the EVENT_TRACE_LOGFILEW by
|
|
|
|
* OpenTrace. Since we've already seen this information, we'll skip this
|
|
|
|
* event.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip events injected by the XPerf tracemerger - they will never be decodable */
|
|
|
|
if (IsEqualGUID(&ev->EventHeader.ProviderId, &ImageIdGuid) ||
|
|
|
|
IsEqualGUID(&ev->EventHeader.ProviderId, &SystemConfigExGuid) ||
|
|
|
|
IsEqualGUID(&ev->EventHeader.ProviderId, &EventMetadataGuid))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!get_event_information(ev, &pInfo))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip those events without format message since most of them need special logic to decode like NDIS-PackCapture */
|
|
|
|
if (pInfo->EventMessageOffset <= 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (EVENT_HEADER_FLAG_32_BIT_HEADER == (ev->EventHeader.Flags & EVENT_HEADER_FLAG_32_BIT_HEADER))
|
|
|
|
{
|
|
|
|
PointerSize = 4;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PointerSize = 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
pUserData = (PBYTE)ev->UserData;
|
|
|
|
pEndOfUserData = (PBYTE)ev->UserData + ev->UserDataLength;
|
|
|
|
|
|
|
|
dwTopLevelPropertyCount = pInfo->TopLevelPropertyCount;
|
|
|
|
if (dwTopLevelPropertyCount > 0)
|
|
|
|
{
|
|
|
|
prop_arr = g_malloc(sizeof(PROPERTY_KEY_VALUE) * dwTopLevelPropertyCount);
|
|
|
|
dwSizeofArray = dwTopLevelPropertyCount * sizeof(PROPERTY_KEY_VALUE);
|
|
|
|
SecureZeroMemory(prop_arr, dwSizeofArray);
|
|
|
|
}
|
|
|
|
|
|
|
|
StringCbCopy(formatMessage, MAX_LOG_LINE_LENGTH, (LPWSTR)ADD_OFFSET_TO_POINTER(pInfo, pInfo->EventMessageOffset));
|
|
|
|
|
|
|
|
for (USHORT i = 0; i < dwTopLevelPropertyCount; i++)
|
|
|
|
{
|
|
|
|
pUserData = extract_properties(ev, pInfo, PointerSize, i, pUserData, pEndOfUserData, &prop_arr[i]);
|
|
|
|
if (NULL == pUserData)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
format_message(formatMessage, prop_arr, dwTopLevelPropertyCount, wszMessageBuffer, sizeof(wszMessageBuffer));
|
|
|
|
|
|
|
|
total_packet_length = wtap_etl_record_buffer_init(&etl_record, ev, FALSE, wszMessageBuffer, (WCHAR*)ADD_OFFSET_TO_POINTER(pInfo, pInfo->ProviderNameOffset));
|
|
|
|
wtap_etl_rec_dump(timestamp, etl_record, total_packet_length, FALSE);
|
|
|
|
g_free(etl_record);
|
|
|
|
|
|
|
|
is_message_dumped = TRUE;
|
|
|
|
} while (FALSE);
|
|
|
|
|
|
|
|
if (NULL != prop_arr)
|
|
|
|
{
|
|
|
|
g_free(prop_arr);
|
|
|
|
prop_arr = NULL;
|
|
|
|
}
|
|
|
|
if (NULL != pInfo)
|
|
|
|
{
|
|
|
|
g_free(pInfo);
|
|
|
|
pInfo = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!is_message_dumped && g_include_undecidable_event)
|
|
|
|
{
|
|
|
|
etw_dump_write_event_head_only(ev, timestamp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Editor modelines - https://www.wireshark.org/tools/modelines.html
|
|
|
|
*
|
|
|
|
* Local variables:
|
|
|
|
* c-basic-offset: 4
|
|
|
|
* tab-width: 8
|
|
|
|
* indent-tabs-mode: nil
|
|
|
|
* End:
|
|
|
|
*
|
|
|
|
* vi: set shiftwidth=4 tabstop=8 expandtab:
|
|
|
|
* :indentSize=4:tabSize=8:noTabs=true:
|
|
|
|
*/
|