forked from osmocom/wireshark
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.pespin/rlcmac
parent
fe1f947540
commit
2a5b34d8b0
|
@ -1816,6 +1816,7 @@ set(INSTALL_FILES
|
|||
${CMAKE_BINARY_DIR}/doc/mergecap.html
|
||||
${CMAKE_BINARY_DIR}/doc/randpkt.html
|
||||
${CMAKE_BINARY_DIR}/doc/randpktdump.html
|
||||
${CMAKE_BINARY_DIR}/doc/etwdump.html
|
||||
${CMAKE_BINARY_DIR}/doc/rawshark.html
|
||||
${CMAKE_BINARY_DIR}/doc/reordercap.html
|
||||
${CMAKE_BINARY_DIR}/doc/sshdump.html
|
||||
|
@ -3170,6 +3171,7 @@ set(CLEAN_C_FILES
|
|||
${dftest_FILES}
|
||||
${randpkt_FILES}
|
||||
${randpktdump_FILES}
|
||||
${etwdump_FILES}
|
||||
${udpdump_FILES}
|
||||
${text2pcap_FILES}
|
||||
${mergecap_FILES}
|
||||
|
|
|
@ -22,6 +22,12 @@ option(BUILD_sshdump "Build sshdump" ON)
|
|||
option(BUILD_ciscodump "Build ciscodump" ON)
|
||||
option(BUILD_dpauxmon "Build dpauxmon" ON)
|
||||
option(BUILD_randpktdump "Build randpktdump" ON)
|
||||
if(WIN32)
|
||||
option(BUILD_etwdump "Build etwdump" ON)
|
||||
else()
|
||||
option(BUILD_etwdump "Build etwdump" OFF)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
option(BUILD_sdjournal "Build sdjournal" ON)
|
||||
else()
|
||||
|
|
|
@ -65,6 +65,7 @@ pod2manhtml(${CMAKE_CURRENT_SOURCE_DIR}/editcap 1)
|
|||
pod2manhtml(${CMAKE_CURRENT_SOURCE_DIR}/mergecap 1)
|
||||
pod2manhtml(${CMAKE_CURRENT_SOURCE_DIR}/randpkt 1)
|
||||
pod2manhtml(${CMAKE_CURRENT_SOURCE_DIR}/randpktdump 1)
|
||||
pod2manhtml(${CMAKE_CURRENT_SOURCE_DIR}/etwdump 1)
|
||||
pod2manhtml(${CMAKE_CURRENT_SOURCE_DIR}/rawshark 1)
|
||||
pod2manhtml(${CMAKE_CURRENT_SOURCE_DIR}/reordercap 1)
|
||||
pod2manhtml(${CMAKE_CURRENT_SOURCE_DIR}/sshdump 1)
|
||||
|
@ -107,6 +108,7 @@ set(MAN1_INSTALL_FILES
|
|||
${CMAKE_CURRENT_BINARY_DIR}/mergecap.1
|
||||
${CMAKE_CURRENT_BINARY_DIR}/randpkt.1
|
||||
${CMAKE_CURRENT_BINARY_DIR}/randpktdump.1
|
||||
${CMAKE_CURRENT_BINARY_DIR}/etwdump.1
|
||||
${CMAKE_CURRENT_BINARY_DIR}/rawshark.1
|
||||
${CMAKE_CURRENT_BINARY_DIR}/reordercap.1
|
||||
${CMAKE_CURRENT_BINARY_DIR}/sshdump.1
|
||||
|
@ -164,6 +166,7 @@ set(HTML_INSTALL_FILES
|
|||
${CMAKE_CURRENT_BINARY_DIR}/mergecap.html
|
||||
${CMAKE_CURRENT_BINARY_DIR}/randpkt.html
|
||||
${CMAKE_CURRENT_BINARY_DIR}/randpktdump.html
|
||||
${CMAKE_CURRENT_BINARY_DIR}/etwdump.html
|
||||
${CMAKE_CURRENT_BINARY_DIR}/rawshark.html
|
||||
${CMAKE_CURRENT_BINARY_DIR}/reordercap.html
|
||||
${CMAKE_CURRENT_BINARY_DIR}/sshdump.html
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
=begin man
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=end man
|
||||
|
||||
=head1 NAME
|
||||
|
||||
etwdump - Provide an interface to read ETW
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
B<etwdump>
|
||||
S<[ B<--help> ]>
|
||||
S<[ B<--version> ]>
|
||||
S<[ B<--extcap-interfaces> ]>
|
||||
S<[ B<--extcap-dlts> ]>
|
||||
S<[ B<--extcap-interface>=E<lt>interfaceE<gt> ]>
|
||||
S<[ B<--extcap-config> ]>
|
||||
S<[ B<--capture> ]>
|
||||
S<[ B<--fifo>=E<lt>path to file or pipeE<gt> ]>
|
||||
S<[ B<--iue>=E<lt>Should undecidable events be includedE<gt> ]>
|
||||
S<[ B<--etlfile>=E<lt>etl fileE<gt> ]>
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
B<etwdump> is a extcap tool that provides access to a etl file.
|
||||
It is only used to display event trace on Windows.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
=over 4
|
||||
|
||||
=item --help
|
||||
|
||||
Print program arguments.
|
||||
|
||||
=item --version
|
||||
|
||||
Print program version.
|
||||
|
||||
=item --extcap-interfaces
|
||||
|
||||
List available interfaces.
|
||||
|
||||
=item --extcap-interface=E<lt>interfaceE<gt>
|
||||
|
||||
Use specified interfaces.
|
||||
|
||||
=item --extcap-dlts
|
||||
|
||||
List DLTs of specified interface.
|
||||
|
||||
=item --extcap-config
|
||||
|
||||
List configuration options of specified interface.
|
||||
|
||||
=item --capture
|
||||
|
||||
Start capturing from specified interface save saved it in place specified by --fifo.
|
||||
|
||||
=item --fifo=E<lt>path to file or pipeE<gt>
|
||||
|
||||
Save captured packet to file or send it through pipe.
|
||||
|
||||
=item --iue=E<lt>Should undecidable events be includedE<gt>
|
||||
|
||||
Choose if the undecidable event is included.
|
||||
|
||||
=item --etlfile=E<lt>Etl fileE<gt>
|
||||
|
||||
Select etl file to display in Wireshark.
|
||||
|
||||
=back
|
||||
|
||||
=head1 EXAMPLES
|
||||
|
||||
To see program arguments:
|
||||
|
||||
etwdump --help
|
||||
|
||||
To see program version:
|
||||
|
||||
etwdump --version
|
||||
|
||||
To see interfaces:
|
||||
|
||||
etwdump --extcap-interfaces
|
||||
|
||||
Example output:
|
||||
interface {value=etwdump}{display=ETW reader}
|
||||
|
||||
To see interface DLTs:
|
||||
|
||||
etwdump --extcap-interface=etwdump --extcap-dlts
|
||||
|
||||
Example output:
|
||||
dlt {number=1}{name=etwdump}{display=DLT_ETW}
|
||||
|
||||
To see interface configuration options:
|
||||
|
||||
etwdump --extcap-interface=etwdump --extcap-config
|
||||
|
||||
Example output:
|
||||
arg {number=0}{call=--iue}{display=Should undecidable events be included}{type=boolflag}{default=false}{tooltip=Choose if the undecidable event is included}{group=Capture}
|
||||
arg {number=1}{call=--etlfile}{display=etl file}{type=fileselect}{tooltip=Select etl file to display in Wireshark}{required=true}{group=Capture}
|
||||
|
||||
To capture:
|
||||
|
||||
etwdump --extcap-interface=etwdump --fifo=/tmp/etw.pcapng --capture
|
||||
|
||||
NOTE: To stop capturing CTRL+C/kill/terminate application.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
wireshark(1), tshark(1), dumpcap(1), extcap(4)
|
||||
|
||||
=head1 NOTES
|
||||
|
||||
B<etwdump> is part of the B<Wireshark> distribution. The latest version
|
||||
of B<Wireshark> can be found at L<https://www.wireshark.org>.
|
||||
|
||||
HTML versions of the Wireshark project man pages are available at:
|
||||
L<https://www.wireshark.org/docs/man-pages>.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Original Author
|
||||
---------------
|
||||
Odysseus Yang L<wiresharkyyh@outlook.com>
|
|
@ -1027,6 +1027,7 @@ set(DISSECTOR_SRC
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/packet-ethertype.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/packet-etsi_card_app_toolkit.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/packet-etv.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/packet-etw.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/packet-evrc.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/packet-evs.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/packet-exablaze.c
|
||||
|
|
|
@ -0,0 +1,334 @@
|
|||
/* packet-etw.c
|
||||
* Routines for ETW Dissection
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* Dissector based on ETW Trace
|
||||
* https://docs.microsoft.com/en-us/windows/win32/etw/event-tracing-portal
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <epan/packet.h>
|
||||
|
||||
void proto_register_etw(void);
|
||||
void proto_reg_handoff_etw(void);
|
||||
|
||||
static int proto_etw = -1;
|
||||
static int hf_etw_size = -1;
|
||||
static int hf_etw_header_type = -1;
|
||||
static int hf_etw_flags = -1;
|
||||
static int hf_etw_event_property = -1;
|
||||
static int hf_etw_thread_id = -1;
|
||||
static int hf_etw_process_id = -1;
|
||||
static int hf_etw_time_stamp = -1;
|
||||
static int hf_etw_provider_id = -1;
|
||||
static int hf_etw_buffer_context_processor_number = -1;
|
||||
static int hf_etw_buffer_context_alignment = -1;
|
||||
static int hf_etw_buffer_context_logger_id = -1;
|
||||
static int hf_etw_message_length = -1;
|
||||
static int hf_etw_provider_name_length = -1;
|
||||
static int hf_etw_provider_name = -1;
|
||||
static int hf_etw_message = -1;
|
||||
static int hf_etw_user_data_length = -1;
|
||||
static int hf_etw_descriptor_id = -1;
|
||||
static int hf_etw_descriptor_version = -1;
|
||||
static int hf_etw_descriptor_channel = -1;
|
||||
static int hf_etw_descriptor_level = -1;
|
||||
static int hf_etw_descriptor_opcode = -1;
|
||||
static int hf_etw_descriptor_task = -1;
|
||||
static int hf_etw_descriptor_keywords = -1;
|
||||
static int hf_etw_processor_time = -1;
|
||||
static int hf_etw_activity_id = -1;
|
||||
|
||||
static gint ett_etw_header = -1;
|
||||
static gint ett_etw_descriptor = -1;
|
||||
static gint ett_etw_buffer_context = -1;
|
||||
|
||||
static dissector_handle_t mbim_dissector;
|
||||
|
||||
static e_guid_t mbim_net_providerid = { 0xA42FE227, 0xA7BF, 0x4483, {0xA5, 0x02, 0x6B, 0xCD, 0xA4, 0x28, 0xCD, 0x96} };
|
||||
|
||||
#define ROUND_UP_COUNT(Count,Pow2) \
|
||||
( ((Count)+(Pow2)-1) & (~(((int)(Pow2))-1)) )
|
||||
#define ETW_HEADER_SIZE 0x60
|
||||
|
||||
static int etw_counter = 0;
|
||||
|
||||
static int
|
||||
dissect_etw(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree _U_, void* data _U_)
|
||||
{
|
||||
proto_tree* etw_header, * etw_descriptor, * etw_buffer_context;
|
||||
tvbuff_t* mbim_tvb;
|
||||
guint32 message_offset, message_length, provider_name_offset, provider_name_length, user_data_offset, user_data_length;
|
||||
e_guid_t provider_id;
|
||||
gint offset = 0;
|
||||
|
||||
etw_header = proto_tree_add_subtree(tree, tvb, 0, ETW_HEADER_SIZE, ett_etw_header, NULL, "ETW Header");
|
||||
proto_tree_add_item(etw_header, hf_etw_size, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
||||
offset += 2;
|
||||
proto_tree_add_item(etw_header, hf_etw_header_type, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
||||
offset += 2;
|
||||
proto_tree_add_item(etw_header, hf_etw_flags, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
||||
offset += 2;
|
||||
proto_tree_add_item(etw_header, hf_etw_event_property, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
||||
offset += 2;
|
||||
proto_tree_add_item(etw_header, hf_etw_thread_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
|
||||
offset += 4;
|
||||
proto_tree_add_item(etw_header, hf_etw_process_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
|
||||
offset += 4;
|
||||
proto_tree_add_item(etw_header, hf_etw_time_stamp, tvb, offset, 8, ENC_LITTLE_ENDIAN);
|
||||
offset += 8;
|
||||
tvb_get_letohguid(tvb, offset, &provider_id);
|
||||
proto_tree_add_item(etw_header, hf_etw_provider_id, tvb, offset, 16, ENC_LITTLE_ENDIAN);
|
||||
offset += 16;
|
||||
|
||||
etw_descriptor = proto_tree_add_subtree(etw_header, tvb, 40, 16, ett_etw_descriptor, NULL, "Descriptor");
|
||||
proto_tree_add_item(etw_descriptor, hf_etw_descriptor_id, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
||||
offset += 2;
|
||||
proto_tree_add_item(etw_descriptor, hf_etw_descriptor_version, tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
||||
offset += 1;
|
||||
proto_tree_add_item(etw_descriptor, hf_etw_descriptor_channel, tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
||||
offset += 1;
|
||||
proto_tree_add_item(etw_descriptor, hf_etw_descriptor_level, tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
||||
offset += 1;
|
||||
proto_tree_add_item(etw_descriptor, hf_etw_descriptor_opcode, tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
||||
offset += 1;
|
||||
proto_tree_add_item(etw_descriptor, hf_etw_descriptor_task, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
||||
offset += 2;
|
||||
proto_tree_add_item(etw_descriptor, hf_etw_descriptor_keywords, tvb, offset, 8, ENC_LITTLE_ENDIAN);
|
||||
offset += 8;
|
||||
|
||||
proto_tree_add_item(etw_header, hf_etw_processor_time, tvb, offset, 8, ENC_LITTLE_ENDIAN);
|
||||
offset += 8;
|
||||
proto_tree_add_item(etw_header, hf_etw_activity_id, tvb, offset, 16, ENC_LITTLE_ENDIAN);
|
||||
offset += 16;
|
||||
|
||||
etw_buffer_context = proto_tree_add_subtree(etw_header, tvb, 80, 4, ett_etw_descriptor, NULL, "Buffer Context");
|
||||
proto_tree_add_item(etw_buffer_context, hf_etw_buffer_context_processor_number, tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
||||
offset += 1;
|
||||
proto_tree_add_item(etw_buffer_context, hf_etw_buffer_context_alignment, tvb, offset, 1, ENC_LITTLE_ENDIAN);
|
||||
offset += 1;
|
||||
proto_tree_add_item(etw_buffer_context, hf_etw_buffer_context_logger_id, tvb, offset, 2, ENC_LITTLE_ENDIAN);
|
||||
offset += 2;
|
||||
proto_tree_add_item_ret_uint(etw_header, hf_etw_user_data_length, tvb, offset, 4, ENC_LITTLE_ENDIAN, &user_data_length);
|
||||
offset += 4;
|
||||
proto_tree_add_item_ret_uint(etw_header, hf_etw_message_length, tvb, offset, 4, ENC_LITTLE_ENDIAN, &message_length);
|
||||
offset += 4;
|
||||
proto_tree_add_item_ret_uint(etw_header, hf_etw_provider_name_length, tvb, offset, 4, ENC_LITTLE_ENDIAN, &provider_name_length);
|
||||
offset += 4;
|
||||
user_data_offset = offset;
|
||||
message_offset = user_data_offset + ROUND_UP_COUNT(user_data_length, sizeof(gint32));
|
||||
if (message_length) {
|
||||
proto_tree_add_item(etw_header, hf_etw_message, tvb, message_offset, message_length, ENC_LITTLE_ENDIAN | ENC_UTF_16);
|
||||
}
|
||||
provider_name_offset = message_offset + ROUND_UP_COUNT(message_length, sizeof(gint32));
|
||||
if (provider_name_length) {
|
||||
proto_tree_add_item(etw_header, hf_etw_provider_name, tvb, provider_name_offset, provider_name_length, ENC_LITTLE_ENDIAN | ENC_UTF_16);
|
||||
}
|
||||
|
||||
col_set_str(pinfo->cinfo, COL_DEF_SRC, "windows");
|
||||
col_set_str(pinfo->cinfo, COL_DEF_DST, "windows");
|
||||
if (memcmp(&mbim_net_providerid, &provider_id, sizeof(e_guid_t)) == 0) {
|
||||
if (pinfo->rec->presence_flags & WTAP_HAS_PACK_FLAGS) {
|
||||
switch(pinfo->rec->rec_header.packet_header.pack_flags) {
|
||||
case 1:
|
||||
col_set_str(pinfo->cinfo, COL_DEF_SRC, "device");
|
||||
col_set_str(pinfo->cinfo, COL_DEF_DST, "host");
|
||||
break;
|
||||
case 2:
|
||||
col_set_str(pinfo->cinfo, COL_DEF_SRC, "host");
|
||||
col_set_str(pinfo->cinfo, COL_DEF_DST, "device");
|
||||
break;
|
||||
}
|
||||
}
|
||||
mbim_tvb = tvb_new_subset_remaining(tvb, user_data_offset);
|
||||
call_dissector_only(mbim_dissector, mbim_tvb, pinfo, tree, data);
|
||||
}
|
||||
else if (message_length){
|
||||
char* message = (char*)tvb_get_string_enc(wmem_packet_scope(), tvb, message_offset, message_length, ENC_LITTLE_ENDIAN | ENC_UTF_16);
|
||||
col_set_str(pinfo->cinfo, COL_INFO, message);
|
||||
if (provider_name_offset) {
|
||||
char* provider_name = (char*)tvb_get_string_enc(wmem_packet_scope(), tvb, provider_name_offset, provider_name_length, ENC_LITTLE_ENDIAN | ENC_UTF_16);
|
||||
col_set_str(pinfo->cinfo, COL_PROTOCOL, provider_name);
|
||||
}
|
||||
} else {
|
||||
col_set_str(pinfo->cinfo, COL_INFO, guids_resolve_guid_to_str(&provider_id));
|
||||
}
|
||||
|
||||
etw_counter += 1;
|
||||
return tvb_captured_length(tvb);
|
||||
}
|
||||
|
||||
void
|
||||
proto_register_etw(void)
|
||||
{
|
||||
static hf_register_info hf[] = {
|
||||
{ &hf_etw_size,
|
||||
{ "Size", "etw.size",
|
||||
FT_UINT16, BASE_DEC, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_header_type,
|
||||
{ "Header Type", "etw.header_type",
|
||||
FT_UINT16, BASE_DEC, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_flags,
|
||||
{ "Flags", "etw.flags",
|
||||
FT_UINT16, BASE_DEC, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_event_property,
|
||||
{ "Event Property", "etw.event_property",
|
||||
FT_UINT16, BASE_DEC, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_thread_id,
|
||||
{ "Thread ID", "etw.thread_id",
|
||||
FT_UINT32, BASE_DEC, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_process_id,
|
||||
{ "Process ID", "etw.process_id",
|
||||
FT_UINT32, BASE_DEC, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_time_stamp,
|
||||
{ "Time Stamp", "etw.time_stamp",
|
||||
FT_UINT64, BASE_DEC, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_provider_id,
|
||||
{ "Provider ID", "etw.provider_id",
|
||||
FT_GUID, BASE_NONE, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_buffer_context_processor_number,
|
||||
{ "Processor Number", "etw.buffer_context.processor_number",
|
||||
FT_UINT8, BASE_DEC, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_buffer_context_alignment,
|
||||
{ "Alignment", "etw.buffer_context.alignment",
|
||||
FT_UINT8, BASE_DEC, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_buffer_context_logger_id,
|
||||
{ "ID", "etw.buffer_context.logger_id",
|
||||
FT_UINT16, BASE_DEC, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_message_length,
|
||||
{ "Message Length", "etw.message_length",
|
||||
FT_UINT32, BASE_DEC, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_provider_name_length,
|
||||
{ "Provider Name Length", "etw.provider_name_length",
|
||||
FT_UINT32, BASE_DEC, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_provider_name,
|
||||
{ "Provider Name", "etw.provider_name",
|
||||
FT_STRINGZ, BASE_NONE, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_message,
|
||||
{ "Event Message", "etw.message",
|
||||
FT_STRINGZ, BASE_NONE, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_user_data_length,
|
||||
{ "User Data Length", "etw.user_data_length",
|
||||
FT_UINT32, BASE_DEC, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_descriptor_id,
|
||||
{ "ID", "etw.descriptor.id",
|
||||
FT_UINT16, BASE_DEC, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_descriptor_version,
|
||||
{ "Version", "etw.descriptor.version",
|
||||
FT_UINT8, BASE_DEC, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_descriptor_channel,
|
||||
{ "Channel", "etw.descriptor.channel",
|
||||
FT_UINT8, BASE_DEC, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_descriptor_level,
|
||||
{ "Level", "etw.descriptor.level",
|
||||
FT_UINT8, BASE_DEC, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_descriptor_opcode,
|
||||
{ "Opcode", "etw.descriptor.opcode",
|
||||
FT_UINT8, BASE_DEC, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_descriptor_task,
|
||||
{ "Task", "etw.descriptor.task",
|
||||
FT_UINT16, BASE_DEC, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_descriptor_keywords,
|
||||
{ "Keywords", "etw.descriptor.keywords",
|
||||
FT_UINT64, BASE_DEC, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_processor_time,
|
||||
{ "Processor Time", "etw.processor_time",
|
||||
FT_UINT64, BASE_DEC, NULL, 0,
|
||||
NULL, HFILL }
|
||||
},
|
||||
{ &hf_etw_activity_id,
|
||||
{ "Activity ID", "etw.activity_id",
|
||||
FT_GUID, BASE_NONE, NULL, 0,
|
||||
NULL, HFILL }
|
||||
}
|
||||
};
|
||||
|
||||
static gint *ett[] = {
|
||||
&ett_etw_header,
|
||||
&ett_etw_descriptor,
|
||||
&ett_etw_buffer_context
|
||||
};
|
||||
|
||||
proto_etw = proto_register_protocol("Event Trace Windows", "ETW", "etw");
|
||||
proto_register_field_array(proto_etw, hf, array_length(hf));
|
||||
proto_register_subtree_array(ett, array_length(ett));
|
||||
}
|
||||
|
||||
void
|
||||
proto_reg_handoff_etw(void)
|
||||
{
|
||||
static dissector_handle_t etw_handle;
|
||||
|
||||
etw_handle = create_dissector_handle(dissect_etw, proto_etw);
|
||||
dissector_add_uint("wtap_encap", WTAP_ENCAP_ETL, etw_handle);
|
||||
|
||||
mbim_dissector = find_dissector("mbim.control");
|
||||
}
|
||||
|
||||
/*
|
||||
* 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:
|
||||
*/
|
|
@ -2612,14 +2612,20 @@ static const value_string mbim_uicc_file_structure_vals[] = {
|
|||
|
||||
static guint8
|
||||
mbim_dissect_service_id_uuid(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, gint hf,
|
||||
gint *offset, struct mbim_uuid_ext **uuid_ext_info)
|
||||
gint *offset, struct mbim_uuid_ext **uuid_ext_info, gboolean is_net_guid)
|
||||
{
|
||||
e_guid_t uuid;
|
||||
guint i;
|
||||
guint32 uuid_ext[4];
|
||||
|
||||
tvb_get_ntohguid(tvb, *offset, &uuid);
|
||||
|
||||
if (is_net_guid)
|
||||
{
|
||||
tvb_get_ntohguid(tvb, *offset, &uuid);
|
||||
}
|
||||
else
|
||||
{
|
||||
tvb_get_letohguid(tvb, *offset, &uuid);
|
||||
}
|
||||
for (i = 0; i < UUID_MAX; i++) {
|
||||
if (memcmp(&uuid, &(mbim_uuid_service_id_vals[i].uuid), sizeof(e_guid_t)) == 0) {
|
||||
break;
|
||||
|
@ -3483,7 +3489,7 @@ mbim_dissect_device_service_element(tvbuff_t *tvb, packet_info *pinfo, proto_tre
|
|||
guint32 i, cid_count, cid;
|
||||
struct mbim_uuid_ext *uuid_ext_info = NULL;
|
||||
|
||||
uuid_idx = mbim_dissect_service_id_uuid(tvb, pinfo, tree, hf_mbim_device_service_element_device_service_id, &offset, &uuid_ext_info);
|
||||
uuid_idx = mbim_dissect_service_id_uuid(tvb, pinfo, tree, hf_mbim_device_service_element_device_service_id, &offset, &uuid_ext_info, TRUE);
|
||||
proto_tree_add_bitmask(tree, tvb, offset, hf_mbim_device_service_element_dss_payload,
|
||||
ett_mbim_bitmap, mbim_device_service_element_dss_payload_fields, ENC_LITTLE_ENDIAN);
|
||||
offset += 4;
|
||||
|
@ -3555,7 +3561,7 @@ mbim_dissect_event_entry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gi
|
|||
guint32 i, cid_count, cid;
|
||||
struct mbim_uuid_ext *uuid_ext_info = NULL;
|
||||
|
||||
uuid_idx = mbim_dissect_service_id_uuid(tvb, pinfo, tree, hf_mbim_event_entry_device_service_id, &offset, &uuid_ext_info);
|
||||
uuid_idx = mbim_dissect_service_id_uuid(tvb, pinfo, tree, hf_mbim_event_entry_device_service_id, &offset, &uuid_ext_info, TRUE);
|
||||
proto_tree_add_item_ret_uint(tree, hf_mbim_event_entry_cid_count, tvb, offset, 4, ENC_LITTLE_ENDIAN, &cid_count);
|
||||
offset += 4;
|
||||
for (i = 0; i < cid_count; i++) {
|
||||
|
@ -4524,7 +4530,7 @@ mbim_dissect_set_dss_connect(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *
|
|||
guint32 dss_session_id;
|
||||
struct mbim_uuid_ext *uuid_ext_info = NULL;
|
||||
|
||||
mbim_dissect_service_id_uuid(tvb, pinfo, tree, hf_mbim_set_dss_connect_device_service_id, &offset, &uuid_ext_info);
|
||||
mbim_dissect_service_id_uuid(tvb, pinfo, tree, hf_mbim_set_dss_connect_device_service_id, &offset, &uuid_ext_info, TRUE);
|
||||
dss_session_id = tvb_get_letohl(tvb, offset);
|
||||
dissector_delete_uint("mbim.dss_session_id", dss_session_id, NULL);
|
||||
if ((dss_session_id <= 255) && uuid_ext_info && uuid_ext_info->dss_handle) {
|
||||
|
@ -4540,7 +4546,7 @@ mbim_dissect_muticarrier_current_cid_list_req(tvbuff_t *tvb, packet_info *pinfo
|
|||
{
|
||||
guint8 service_idx;
|
||||
|
||||
service_idx = mbim_dissect_service_id_uuid(tvb, pinfo, tree, hf_mbim_multicarrier_current_cid_list_req_uuid, &offset, NULL);
|
||||
service_idx = mbim_dissect_service_id_uuid(tvb, pinfo, tree, hf_mbim_multicarrier_current_cid_list_req_uuid, &offset, NULL, TRUE);
|
||||
if (service_idx != UUID_MULTICARRIER) {
|
||||
expert_add_info_format(pinfo, NULL, &ei_mbim_unexpected_uuid_value,
|
||||
"Unexpected UUID value, should be UUID_MULTICARRIER");
|
||||
|
@ -5574,7 +5580,7 @@ dissect_mbim_control(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *
|
|||
}
|
||||
}
|
||||
|
||||
uuid_idx = mbim_dissect_service_id_uuid(frag_tvb, pinfo, mbim_tree, hf_mbim_device_service_id, &offset, &uuid_ext_info);
|
||||
uuid_idx = mbim_dissect_service_id_uuid(frag_tvb, pinfo, mbim_tree, hf_mbim_device_service_id, &offset, &uuid_ext_info, pinfo->rec->rec_header.packet_header.pkt_encap != WTAP_ENCAP_ETL);
|
||||
cid = mbim_dissect_cid(frag_tvb, pinfo, mbim_tree, &offset, uuid_idx, uuid_ext_info);
|
||||
proto_tree_add_item_ret_uint(mbim_tree, hf_mbim_command_type, frag_tvb, offset, 4, ENC_LITTLE_ENDIAN, &cmd_type);
|
||||
if (mbim_info) {
|
||||
|
@ -6462,7 +6468,7 @@ dissect_mbim_control(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *
|
|||
}
|
||||
}
|
||||
|
||||
uuid_idx = mbim_dissect_service_id_uuid(frag_tvb, pinfo, mbim_tree, hf_mbim_device_service_id, &offset, &uuid_ext_info);
|
||||
uuid_idx = mbim_dissect_service_id_uuid(frag_tvb, pinfo, mbim_tree, hf_mbim_device_service_id, &offset, &uuid_ext_info, pinfo->rec->rec_header.packet_header.pkt_encap != WTAP_ENCAP_ETL);
|
||||
cid = mbim_dissect_cid(frag_tvb, pinfo, mbim_tree, &offset, uuid_idx, uuid_ext_info);
|
||||
if (msg_type == MBIM_COMMAND_DONE) {
|
||||
proto_tree_add_item(mbim_tree, hf_mbim_status, frag_tvb, offset, 4, ENC_LITTLE_ENDIAN);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
*/
|
|
@ -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:
|
||||
*/
|
|
@ -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) |