diff options
author | Odysseus Yang <wiresharkyyh@outlook.com> | 2020-12-02 09:05:11 +0000 |
---|---|---|
committer | AndersBroman <a.broman58@gmail.com> | 2020-12-02 09:05:11 +0000 |
commit | 2a5b34d8b02b1663237490eec5dfd8652a9bdb51 (patch) | |
tree | 5acf1640ab708e32a172a00763145e41e19c5089 /extcap | |
parent | fe1f9475409f252b6ca2dccc71187868e168c74b (diff) |
MBIM: Update dissector to support DLT_ETW
New link type DLT_ETW is added for write and read Event Trace on Windows.
This change updates MBIM dissector to decode a MBIM message from
a DLT_ETW packet.
Diffstat (limited to 'extcap')
-rw-r--r-- | extcap/CMakeLists.txt | 27 | ||||
-rw-r--r-- | extcap/etl.c | 438 | ||||
-rw-r--r-- | extcap/etl.h | 42 | ||||
-rw-r--r-- | extcap/etw_message.c | 416 | ||||
-rw-r--r-- | extcap/etw_message.h | 60 | ||||
-rw-r--r-- | extcap/etwdump.c | 262 |
6 files changed, 1245 insertions, 0 deletions
diff --git a/extcap/CMakeLists.txt b/extcap/CMakeLists.txt index 39389aced1..f59fd55838 100644 --- a/extcap/CMakeLists.txt +++ b/extcap/CMakeLists.txt @@ -246,6 +246,33 @@ if(BUILD_randpktdump) add_dependencies(extcaps randpktdump) endif() + +if(BUILD_etwdump AND WIN32) + set(etwdump_LIBS + wiretap + wsutil + tdh + ${GLIB2_LIBRARIES} + ${ZLIB_LIBRARIES} + ${CMAKE_DL_LIBS} + ${WIN_WS2_32_LIBRARY} + ) + set(etwdump_FILES + $<TARGET_OBJECTS:cli_main> + $<TARGET_OBJECTS:extcap-base> + etwdump.c + etl.c + etw_message.c + ) + + set_executable_resources(etwdump "etwdump") + add_executable(etwdump ${etwdump_FILES}) + set_extcap_executable_properties(etwdump) + target_link_libraries(etwdump ${etwdump_LIBS}) + install(TARGETS etwdump RUNTIME DESTINATION ${EXTCAP_INSTALL_LIBDIR}) + add_dependencies(extcaps etwdump) +endif() + if(BUILD_sdjournal AND SYSTEMD_FOUND) set(sdjournal_LIBS wiretap diff --git a/extcap/etl.c b/extcap/etl.c new file mode 100644 index 0000000000..0aac40751b --- /dev/null +++ b/extcap/etl.c @@ -0,0 +1,438 @@ +/* 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 + */ + + /* + https://docs.microsoft.com/en-us/windows/win32/etw/event-tracing-portal + */ + +#include "config.h" +#include "etl.h" + +#include "etw_message.h" + +#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 } }; + +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; + +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; + +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); + +wtap_open_return_val etw_dump(const char* etl_filename, const char* pcapng_filename, int* err, gchar** err_info) +{ + EVENT_TRACE_LOGFILE log_file; + TRACEHANDLE trace_handle = INVALID_PROCESSTRACE_HANDLE; + WCHAR w_etl_filename[FILENAME_MAX] = { 0 }; + WCHAR w_pcapng_filename[FILENAME_MAX] = { 0 }; + ULONG trace_error; + wtap_open_return_val returnVal = WTAP_OPEN_MINE; + + SecureZeroMemory(g_err_info, FILENAME_MAX); + g_err = ERROR_SUCCESS; + g_num_events = 0; + + /* do/while(FALSE) is used to jump out of loop so no complex nested if/else is needed */ + do + { + mbstowcs(w_etl_filename, etl_filename, FILENAME_MAX); + mbstowcs(w_pcapng_filename, pcapng_filename, FILENAME_MAX); + + SecureZeroMemory(&log_file, sizeof(log_file)); + log_file.LogFileName = w_etl_filename; + log_file.ProcessTraceMode = PROCESS_TRACE_MODE_EVENT_RECORD; + log_file.EventRecordCallback = event_callback; + log_file.Context = NULL; + + trace_handle = OpenTrace(&log_file); + if (trace_handle == INVALID_PROCESSTRACE_HANDLE) { + *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; + } + + trace_error = ProcessTrace(&trace_handle, 1, 0, 0); + if (trace_error != ERROR_SUCCESS) { + returnVal = WTAP_OPEN_ERROR; + 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) { + *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) + { + if (!wtap_dump_close(g_pdh, err, err_info)) + { + returnVal = WTAP_OPEN_ERROR; + } + } + return returnVal; +} + +static void WINAPI event_callback(PEVENT_RECORD ev) +{ + ULARGE_INTEGER timestamp; + g_num_events++; + /* + * 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)); + shb_hdr = wtap_block_create(WTAP_BLOCK_NG_SECTION); + g_array_append_val(shb_hdrs, shb_hdr); + + /* In the future, may create multiple WTAP_BLOCK_IF_DESCR separately for IP packet */ + idb_info = g_new(wtapng_iface_descriptions_t, 1); + idb_datas = g_array_new(FALSE, FALSE, sizeof(wtap_block_t)); + idb_data = wtap_block_create(WTAP_BLOCK_IF_DESCR); + descr_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(idb_data); + descr_mand->tsprecision = WTAP_TSPREC_USEC; + descr_mand->wtap_encap = WTAP_ENCAP_ETL; + /* 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; + + params.encap = WTAP_ENCAP_ETL; + params.snaplen = 0; + params.tsprec = WTAP_TSPREC_USEC; + params.shb_hdrs = shb_hdrs; + params.idb_inf = idb_info; + + pdh = wtap_dump_open(pcapng_filename, WTAP_FILE_TYPE_SUBTYPE_PCAPNG, WTAP_UNCOMPRESSED, ¶ms, err, err_info); + + 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; + rec.rec_header.packet_header.pkt_encap = WTAP_ENCAP_ETL; + rec.presence_flags = rec.presence_flags | WTAP_HAS_PACK_FLAGS; + rec.rec_header.packet_header.pack_flags = is_inbound ? 1 : 2; + /* 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); + } + 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: + */ diff --git a/extcap/etl.h b/extcap/etl.h new file mode 100644 index 0000000000..7410de3a85 --- /dev/null +++ b/extcap/etl.h @@ -0,0 +1,42 @@ +/* etl.h +* + * 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 + */ + +#ifndef __W_ETL_H__ +#define __W_ETL_H__ + +#include "wiretap/wtap.h" +#include "ws_symbol_export.h" +#include "wiretap/wtap-int.h" + +#include <glib.h> +#include <stdlib.h> +#include <windows.h> +#include <strsafe.h> +#include <tdh.h> +#include <guiddef.h> + +wtap_open_return_val etw_dump(const char* etl_filename, const char* pcapng_filename, int* err, gchar** err_info); + +#endif + + +/* + * 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: + */ diff --git a/extcap/etw_message.c b/extcap/etw_message.c new file mode 100644 index 0000000000..fa74d8b637 --- /dev/null +++ b/extcap/etw_message.c @@ -0,0 +1,416 @@ +/* etw_message.h + * + * 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 + */ + +#include "etw_message.h" +ULONGLONG g_num_events = 0; + +VOID format_message(WCHAR* lpszMessage, PROPERTY_KEY_VALUE* propArray, DWORD dwPropertyCount, WCHAR* lpszOutBuffer, DWORD dwOutBufferCount) +{ + DWORD startLoc = 0; + int percent_loc = 0; + + for (int i = 0; lpszMessage[i] != L'\0';) + { + if (lpszMessage[i] != L'%') + { + i++; + continue; + } + if (lpszMessage[i + 1] == '%') + { + i += 2; + continue; + } + + percent_loc = i; + i++; + + if (iswdigit(lpszMessage[i])) + { + DWORD dwDigitalCount = 0; + WCHAR smallBuffer[MAX_SMALL_BUFFER] = { 0 }; + while (iswdigit(lpszMessage[i])) + { + if (dwDigitalCount < (MAX_SMALL_BUFFER - 1)) + { + smallBuffer[dwDigitalCount] = lpszMessage[i]; + } + dwDigitalCount++; + i++; + } + + /* We are not parsing this */ + if (dwDigitalCount >= (MAX_SMALL_BUFFER - 1)) + { + continue; + } + DWORD num = _wtoi(smallBuffer); + /* We are not parsing this */ + if (num == 0 || num > dwPropertyCount || propArray[num - 1].value[0] == L'\0') + { + continue; + } + + if (lpszMessage[i] == L'!' && lpszMessage[i + 1] == L'S' && lpszMessage[i + 2] == L'!') + { + i += 3; + } + + /* We have everything */ + lpszMessage[percent_loc] = L'\0'; + StringCbCat(lpszOutBuffer, dwOutBufferCount, lpszMessage + startLoc); + StringCbCat(lpszOutBuffer, dwOutBufferCount, propArray[num - 1].value); + startLoc = i; + continue; // for + } + } + StringCbCat(lpszOutBuffer, dwOutBufferCount, lpszMessage + startLoc); +} + +/* +* Get the length of the property data. For MOF-based events, the size is inferred from the data type +* of the property. For manifest-based events, the property can specify the size of the property value +* using the length attribute. The length attribue can specify the size directly or specify the name +* of another property in the event data that contains the size. If the property does not include the +* length attribute, the size is inferred from the data type. The length will be zero for variable +* length, null-terminated strings and structures. +*/ +DWORD GetPropertyLength(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, USHORT i, PUSHORT PropertyLength) +{ + DWORD status = ERROR_SUCCESS; + PROPERTY_DATA_DESCRIPTOR DataDescriptor = { 0 }; + DWORD PropertySize = 0; + + /* + * If the property is a binary blob and is defined in a manifest, the property can + * specify the blob's size or it can point to another property that defines the + * blob's size. The PropertyParamLength flag tells you where the blob's size is defined. + */ + if ((pInfo->EventPropertyInfoArray[i].Flags & PropertyParamLength) == PropertyParamLength) + { + DWORD Length = 0; // Expects the length to be defined by a UINT16 or UINT32 + DWORD j = pInfo->EventPropertyInfoArray[i].lengthPropertyIndex; + DataDescriptor.PropertyName = ((ULONGLONG)(pInfo)+(ULONGLONG)pInfo->EventPropertyInfoArray[j].NameOffset); + DataDescriptor.ArrayIndex = ULONG_MAX; + status = TdhGetPropertySize(pEvent, 0, NULL, 1, &DataDescriptor, &PropertySize); + status = TdhGetProperty(pEvent, 0, NULL, 1, &DataDescriptor, PropertySize, (PBYTE)&Length); + *PropertyLength = (USHORT)Length; + } + else + { + if (pInfo->EventPropertyInfoArray[i].length > 0) + { + *PropertyLength = pInfo->EventPropertyInfoArray[i].length; + } + else + { + /* + * If the property is a binary blob and is defined in a MOF class, the extension + * qualifier is used to determine the size of the blob. However, if the extension + * is IPAddrV6, you must set the PropertyLength variable yourself because the + * EVENT_PROPERTY_INFO.length field will be zero. + */ + if (TDH_INTYPE_BINARY == pInfo->EventPropertyInfoArray[i].nonStructType.InType && + TDH_OUTTYPE_IPV6 == pInfo->EventPropertyInfoArray[i].nonStructType.OutType) + { + *PropertyLength = (USHORT)sizeof(IN6_ADDR); + } + else if (TDH_INTYPE_UNICODESTRING == pInfo->EventPropertyInfoArray[i].nonStructType.InType || + TDH_INTYPE_ANSISTRING == pInfo->EventPropertyInfoArray[i].nonStructType.InType || + (pInfo->EventPropertyInfoArray[i].Flags & PropertyStruct) == PropertyStruct) + { + *PropertyLength = pInfo->EventPropertyInfoArray[i].length; + } + else + { + g_debug("Event %d Unexpected length of 0 for intype %d and outtype %d", g_num_events, + pInfo->EventPropertyInfoArray[i].nonStructType.InType, + pInfo->EventPropertyInfoArray[i].nonStructType.OutType); + + status = ERROR_EVT_INVALID_EVENT_DATA; + goto cleanup; + } + } + } +cleanup: + return status; +} + +DWORD GetArraySize(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, USHORT i, PUSHORT ArraySize) +{ + DWORD status = ERROR_SUCCESS; + PROPERTY_DATA_DESCRIPTOR DataDescriptor = { 0 }; + DWORD PropertySize = 0; + + if ((pInfo->EventPropertyInfoArray[i].Flags & PropertyParamCount) == PropertyParamCount) + { + /* Expects the count to be defined by a UINT16 or UINT32 */ + DWORD Count = 0; + DWORD j = pInfo->EventPropertyInfoArray[i].countPropertyIndex; + DataDescriptor.PropertyName = ((ULONGLONG)(pInfo)+(ULONGLONG)(pInfo->EventPropertyInfoArray[j].NameOffset)); + DataDescriptor.ArrayIndex = ULONG_MAX; + status = TdhGetPropertySize(pEvent, 0, NULL, 1, &DataDescriptor, &PropertySize); + status = TdhGetProperty(pEvent, 0, NULL, 1, &DataDescriptor, PropertySize, (PBYTE)&Count); + *ArraySize = (USHORT)Count; + } + else + { + *ArraySize = pInfo->EventPropertyInfoArray[i].count; + } + return status; +} + +DWORD GetMapInfo(PEVENT_RECORD pEvent, LPWSTR pMapName, PEVENT_MAP_INFO* pMapInfo) +{ + DWORD status = ERROR_SUCCESS; + DWORD MapSize = 0; + + /* Retrieve the required buffer size for the map info. */ + status = TdhGetEventMapInformation(pEvent, pMapName, *pMapInfo, &MapSize); + if (ERROR_INSUFFICIENT_BUFFER == status) + { + *pMapInfo = (PEVENT_MAP_INFO)g_malloc(MapSize); + if (*pMapInfo == NULL) + { + status = ERROR_OUTOFMEMORY; + goto cleanup; + } + /* Retrieve the map info. */ + status = TdhGetEventMapInformation(pEvent, pMapName, *pMapInfo, &MapSize); + } + + if (ERROR_NOT_FOUND == status) + { + /* This case is okay. */ + status = ERROR_SUCCESS; + } + +cleanup: + + return status; +} + + +PBYTE extract_properties(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, DWORD PointerSize, USHORT i, PBYTE pUserData, PBYTE pEndOfUserData, PROPERTY_KEY_VALUE* pExtract) +{ + TDHSTATUS status = ERROR_SUCCESS; + USHORT PropertyLength = 0; + USHORT UserDataConsumed = 0; + /* Last member of a structure */ + DWORD LastMember = 0; + USHORT ArraySize = 0; + PEVENT_MAP_INFO pMapInfo = NULL; + WCHAR formatted_data[MAX_LOG_LINE_LENGTH]; + DWORD formatted_data_size = sizeof(formatted_data); + LPWSTR oversize_formatted_data = NULL; + + do + { + StringCbCopy(pExtract->key, sizeof(pExtract->key), (PWCHAR)((PBYTE)(pInfo)+pInfo->EventPropertyInfoArray[i].NameOffset)); + /* Get the length of the property. */ + status = GetPropertyLength(pEvent, pInfo, i, &PropertyLength); + if (ERROR_SUCCESS != status) + { + StringCbPrintf(pExtract->value, sizeof(pExtract->value), L"%s: GetPropertyLength failed 0x%x", pExtract->key, status); + break; + } + + /* Get the size of the array if the property is an array. */ + status = GetArraySize(pEvent, pInfo, i, &ArraySize); + if (ERROR_SUCCESS != status) + { + StringCbPrintf(pExtract->value, sizeof(pExtract->value), L"%s: GetArraySize failed 0x%x", pExtract->key, status); + break; + } + + /* Add [] for an array property */ + if (ArraySize > 1) + { + StringCbCat(pExtract->value, sizeof(pExtract->value), L"["); + } + + for (USHORT k = 0; k < ArraySize; k++) + { + /* Add array item separator "," */ + if (k > 0) + { + StringCbCat(pExtract->value, sizeof(pExtract->value), L","); + } + /* If the property is a structure, print the members of the structure. */ + if ((pInfo->EventPropertyInfoArray[i].Flags & PropertyStruct) == PropertyStruct) + { + /* Add {} for an array property */ + StringCbCat(pExtract->value, sizeof(pExtract->value), L"{"); + /* Add struct member separator ";" */ + if (k > 0) + { + StringCbCat(pExtract->value, sizeof(pExtract->value), L";"); + } + LastMember = pInfo->EventPropertyInfoArray[i].structType.StructStartIndex + + pInfo->EventPropertyInfoArray[i].structType.NumOfStructMembers; + + for (USHORT j = pInfo->EventPropertyInfoArray[i].structType.StructStartIndex; j < LastMember; j++) + { + pUserData = extract_properties(pEvent, pInfo, PointerSize, j, pUserData, pEndOfUserData, pExtract); + if (NULL == pUserData) + { + StringCbPrintf(pExtract->value, sizeof(pExtract->value), L"%s: extract_properties of member %d failed 0x%x", pExtract->key, j, status); + break; + } + } + StringCbCat(pExtract->value, sizeof(pExtract->value), L"}"); + } + else + { + /* Get the name/value mapping only at the first time if the property specifies a value map. */ + if (pMapInfo == NULL) + { + status = GetMapInfo(pEvent, + (PWCHAR)((PBYTE)(pInfo)+pInfo->EventPropertyInfoArray[i].nonStructType.MapNameOffset), + &pMapInfo); + + if (ERROR_SUCCESS != status) + { + StringCbPrintf(pExtract->value, sizeof(pExtract->value), L"%s: GetMapInfo failed 0x%x", pExtract->key, status); + break; + } + } + + /* Get the size of the buffer required for the formatted data. */ + + status = TdhFormatProperty( + pInfo, + pMapInfo, + PointerSize, + pInfo->EventPropertyInfoArray[i].nonStructType.InType, + pInfo->EventPropertyInfoArray[i].nonStructType.OutType, + PropertyLength, + (USHORT)(pEndOfUserData - pUserData), + pUserData, + &formatted_data_size, + formatted_data, + &UserDataConsumed); + + if (ERROR_INSUFFICIENT_BUFFER == status) + { + if (oversize_formatted_data) + { + g_free(oversize_formatted_data); + oversize_formatted_data = NULL; + } + + oversize_formatted_data = (LPWSTR)g_malloc(formatted_data_size); + if (oversize_formatted_data == NULL) + { + status = ERROR_OUTOFMEMORY; + StringCbPrintf(pExtract->value, sizeof(pExtract->value), L"%s: Allocate FormattedData memory (size %d) for array item %d failed 0x%x", pExtract->key, formatted_data_size, k, status); + break; + } + + /* Retrieve the formatted data. */ + status = TdhFormatProperty( + pInfo, + pMapInfo, + PointerSize, + pInfo->EventPropertyInfoArray[i].nonStructType.InType, + pInfo->EventPropertyInfoArray[i].nonStructType.OutType, + PropertyLength, + (USHORT)(pEndOfUserData - pUserData), + pUserData, + &formatted_data_size, + oversize_formatted_data, + &UserDataConsumed); + } + + if (ERROR_SUCCESS == status) + { + if (formatted_data_size > sizeof(formatted_data) && oversize_formatted_data != NULL) + { + /* Any oversize FormattedData will be truncated */ + StringCbCat(pExtract->value, sizeof(pExtract->value), oversize_formatted_data); + } + else + { + StringCbCat(pExtract->value, sizeof(pExtract->value), formatted_data); + } + pUserData += UserDataConsumed; + } + else + { + StringCbPrintf(pExtract->value, sizeof(pExtract->value), L"%s: TdhFormatProperty for array item %d failed 0x%x", pExtract->key, k, status); + break; + } + } + } + /* Add [] for an array property */ + if (ArraySize > 1) + { + StringCbCat(pExtract->value, sizeof(pExtract->value), L"]"); + } + } while (FALSE); + + if (oversize_formatted_data) + { + g_free(oversize_formatted_data); + oversize_formatted_data = NULL; + } + if (pMapInfo) + { + g_free(pMapInfo); + pMapInfo = NULL; + } + + return (ERROR_SUCCESS == status) ? pUserData : NULL; +} + + +BOOL get_event_information(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO* pInfo) +{ + BOOL bReturn = FALSE; + DWORD status; + DWORD BufferSize = 0; + + /* Retrieve the required buffer size for the event metadata. */ + status = TdhGetEventInformation(pEvent, 0, NULL, *pInfo, &BufferSize); + if (ERROR_INSUFFICIENT_BUFFER == status) + { + *pInfo = (TRACE_EVENT_INFO*)g_malloc(BufferSize); + if (*pInfo == NULL) + { + g_debug("Event %d GetEventInformation Failed to allocate memory for event info (size=%lu).", g_num_events, BufferSize); + goto Exit; + } + /* Retrieve the event metadata. */ + status = TdhGetEventInformation(pEvent, 0, NULL, *pInfo, &BufferSize); + } + + if (ERROR_SUCCESS != status) + { + goto Exit; + } + bReturn = TRUE; +Exit: + + return bReturn; +} + +/* + * 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: + */ diff --git a/extcap/etw_message.h b/extcap/etw_message.h new file mode 100644 index 0000000000..b88e320233 --- /dev/null +++ b/extcap/etw_message.h @@ -0,0 +1,60 @@ +/* etl.h +* + * 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 + */ + +#ifndef __W_ETW_MESSAGE_H__ +#define __W_ETW_MESSAGE_H__ + +#include <glib.h> +#include "config.h" + +#include <windows.h> +#include <SDKDDKVer.h> +#include <strsafe.h> +#include <evntcons.h> +#include <tdh.h> +#include <stdlib.h> + +#define MAX_SMALL_BUFFER 4 +#define MAX_LOG_LINE_LENGTH 1024 +#define MAX_KEY_LENGTH 64 + +typedef struct Property_Key_Value +{ + WCHAR key[MAX_KEY_LENGTH]; + WCHAR value[MAX_LOG_LINE_LENGTH]; +} PROPERTY_KEY_VALUE; + +typedef struct in6_addr { + union { + UCHAR Byte[16]; + USHORT Word[8]; + } u; +} IN6_ADDR, * PIN6_ADDR, FAR* LPIN6_ADDR; + +VOID format_message(WCHAR* lpszMessage, PROPERTY_KEY_VALUE* propArray, DWORD dwPropertyCount, WCHAR* lpszOutBuffer, DWORD dwOutBufferCount); +BOOL get_event_information(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO* pInfo); +PBYTE extract_properties(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, DWORD PointerSize, USHORT i, PBYTE pUserData, PBYTE pEndOfUserData, PROPERTY_KEY_VALUE* pExtract); + +#endif + + +/* + * 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: + */ diff --git a/extcap/etwdump.c b/extcap/etwdump.c new file mode 100644 index 0000000000..dcc0e293cf --- /dev/null +++ b/extcap/etwdump.c @@ -0,0 +1,262 @@ +/* etwdump.c + * etwdump is an extcap tool used to dump etw to pcapng + * + * 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 + */ + +#include "config.h" + +#include "extcap-base.h" + +#include <wsutil/strtoi.h> +#include <wsutil/filesystem.h> +#include <wsutil/privileges.h> +#include <wsutil/please_report_bug.h> + +#include <cli_main.h> +#include <ui/cmdarg_err.h> +#include "etl.h" + +/* extcap-interface has to be unique, or it may use wrong option output by a different extcapbin */ +#define ETW_EXTCAP_INTERFACE "etwdump" +#define ETWDUMP_VERSION_MAJOR "1" +#define ETWDUMP_VERSION_MINOR "0" +#define ETWDUMP_VERSION_RELEASE "0" + +enum { + EXTCAP_BASE_OPTIONS_ENUM, + OPT_HELP, + OPT_VERSION, + OPT_INCLUDE_UNDECIDABLE_EVENT, + OPT_ETLFILE +}; + +static struct option longopts[] = { + EXTCAP_BASE_OPTIONS, + { "help", no_argument, NULL, OPT_HELP}, + { "version", no_argument, NULL, OPT_VERSION}, + { "iue", optional_argument, NULL, OPT_INCLUDE_UNDECIDABLE_EVENT}, + { "etlfile", required_argument, NULL, OPT_ETLFILE}, + { 0, 0, 0, 0 } +}; + +int g_include_undecidable_event = FALSE; + +static void help(extcap_parameters* extcap_conf) +{ + extcap_help_print(extcap_conf); +} + +static int list_config(char* interface) +{ + unsigned inc = 0; + + if (!interface) { + g_warning("No interface specified."); + return EXIT_FAILURE; + } + + if (g_strcmp0(interface, ETW_EXTCAP_INTERFACE)) { + g_warning("Interface must be %s", ETW_EXTCAP_INTERFACE); + return EXIT_FAILURE; + } + /* Saved for later live capture support */ +#if 0 + printf("arg {number=%u}{call=--type}{display=Capture type}" + "{type=selector}{tooltip=Choose the type of capture}{group=Capture}\n", + inc); + printf("value {arg=%u}{value=etl}{display=From a etl file}\n", inc); + printf("value {arg=%u}{value=live}{display=From a live session}\n", inc); + inc++; +#endif + /* + * The undecidable events are those that either don't have sub-dissector or don't have anthing meaningful to display except for the EVENT_HEADER. + */ + printf("arg {number=%u}{call=--iue}{display=Should undecidable events be included}" + "{type=boolflag}{default=false}{tooltip=Choose if the undecidable event is included}{group=Capture}\n", + inc++); + printf("arg {number=%u}{call=--etlfile}{display=etl file}" + "{type=fileselect}{tooltip=Select etl file to display in Wireshark}{required=true}{group=Capture}\n", + inc++); + /* Saved for later live capture support */ +#if 0 + printf("arg {number=%u}{call=--session-params}{display=Live session parameters}" + "{type=string}{tooltip=providers, keyword and level}{group=Capture}\n", + inc++); +#endif + + extcap_config_debug(&inc); + return EXIT_SUCCESS; +} + +int main(int argc, char* argv[]) +{ + char* err_msg; + int option_idx = 0; + int result; + int ret = EXIT_FAILURE; + + char* etlfile = NULL; + + extcap_parameters* extcap_conf = g_new0(extcap_parameters, 1); + char* help_url; + char* help_header = NULL; + + /* + * Get credential information for later use. + */ + init_process_policies(); + + /* + * Attempt to get the pathname of the directory containing the + * executable file. + */ + err_msg = init_progfile_dir(argv[0]); + if (err_msg != NULL) { + g_warning("Can't get pathname of directory containing the captype program: %s.", + err_msg); + g_free(err_msg); + } + + help_url = data_file_url("etwdump.html"); + extcap_base_set_util_info(extcap_conf, argv[0], ETWDUMP_VERSION_MAJOR, ETWDUMP_VERSION_MINOR, + ETWDUMP_VERSION_RELEASE, help_url); + g_free(help_url); + extcap_base_register_interface(extcap_conf, ETW_EXTCAP_INTERFACE, "ETW reader", 290, "DLT_ETW"); + + help_header = g_strdup_printf( + " %s --extcap-interfaces\n" + " %s --extcap-interface=%s --extcap-dlts\n" + " %s --extcap-interface=%s --extcap-config\n" + " %s --extcap-interface=%s --etlfile c:\\wwansvc.etl \n" + "--fifo=FILENAME --capture\n", argv[0], argv[0], ETW_EXTCAP_INTERFACE, argv[0], ETW_EXTCAP_INTERFACE, + argv[0], ETW_EXTCAP_INTERFACE); + extcap_help_add_header(extcap_conf, help_header); + g_free(help_header); + + extcap_help_add_option(extcap_conf, "--help", "print this help"); + extcap_help_add_option(extcap_conf, "--version", "print the version"); + extcap_help_add_option(extcap_conf, "--etlfile <filename>", "A etl filename"); + extcap_help_add_option(extcap_conf, "--iue", "Choose if undecidable event is included"); + + if (argc == 1) { + help(extcap_conf); + goto end; + } + + while ((result = getopt_long(argc, argv, ":", longopts, &option_idx)) != -1) { + switch (result) { + case OPT_VERSION: + extcap_version_print(extcap_conf); + ret = EXIT_SUCCESS; + goto end; + + case OPT_HELP: + help(extcap_conf); + ret = EXIT_SUCCESS; + goto end; + + case OPT_ETLFILE: + etlfile = g_strdup(optarg); + break; + + case OPT_INCLUDE_UNDECIDABLE_EVENT: + g_include_undecidable_event = TRUE; + break; + + case ':': + /* missing option argument */ + g_warning("Option '%s' requires an argument", argv[optind - 1]); + break; + + default: + /* Handle extcap specific options */ + if (!extcap_base_parse_options(extcap_conf, result - EXTCAP_OPT_LIST_INTERFACES, optarg)) + { + g_warning("Invalid option: %s", argv[optind - 1]); + goto end; + } + } + } + + extcap_cmdline_debug(argv, argc); + + if (extcap_base_handle_interface(extcap_conf)) { + ret = EXIT_SUCCESS; + goto end; + } + + if (extcap_conf->show_config) { + ret = list_config(extcap_conf->interface); + goto end; + } + + if (extcap_conf->capture) { + + if (g_strcmp0(extcap_conf->interface, ETW_EXTCAP_INTERFACE)) { + g_warning("ERROR: invalid interface"); + goto end; + } + + wtap_init(FALSE); + + switch(etw_dump(etlfile, extcap_conf->fifo, &ret, &err_msg)) + { + case WTAP_OPEN_ERROR: + if (err_msg != NULL) { + g_warning("etw_dump failed: %s.", + err_msg); + g_free(err_msg); + } + else + { + g_warning("etw_dump failed"); + } + break; + case WTAP_OPEN_NOT_MINE: + if (err_msg != NULL) { + g_warning("The file %s is not etl format. Error message: %s.", + etlfile, err_msg); + g_free(err_msg); + } + else + { + g_warning("The file %s is not etl format"); + } + break; + case WTAP_OPEN_MINE: + ret = EXIT_SUCCESS; + break; + } + } + +end: + /* clean up stuff */ + extcap_base_cleanup(&extcap_conf); + + if (etlfile != NULL) + { + g_free(etlfile); + } + + return ret; +} + +/* + * 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: + */ |