forked from osmocom/wireshark
ETW: Extract IP packets from Windows event trace
With this change, Wireshark will be enhanced to display IP packets from an event trace logfile or an event trace live session.pespin/osmux-wip
parent
0f5025eae4
commit
36e834b6b7
|
@ -27,8 +27,8 @@ etwdump - Provide an interface to read Event Tracing for Windows (ETW)
|
|||
|
||||
== DESCRIPTION
|
||||
|
||||
*etwdump* is a extcap tool that provides access to a etl file.
|
||||
It is only used to display event traces on Windows.
|
||||
*etwdump* is a extcap tool that provides access to a event trace log file or an event trace live session.
|
||||
It is only used to display event trace on Windows that includes readable text message and different protocols (like MBIM and IP packets).
|
||||
|
||||
== OPTIONS
|
||||
|
||||
|
@ -134,6 +134,7 @@ To see interface configuration options:
|
|||
To capture:
|
||||
|
||||
etwdump --extcap-interface etwdump --fifo=/tmp/etw.pcapng --capture --params "--p=Microsoft-Windows-Wmbclass-Opn --p=Microsoft-Windows-wmbclass --k=0xff --l=4"
|
||||
etwdump --extcap-interface etwdump --fifo=/tmp/etw.pcapng --capture --params "--p=Microsoft-Windows-Wmbclass-Opn --p=Microsoft-Windows-NDIS-PacketCapture"
|
||||
|
||||
NOTE: To stop capturing CTRL+C/kill/terminate the application.
|
||||
|
||||
|
|
|
@ -114,6 +114,7 @@ They previously shipped with Npcap 1.55.
|
|||
* The interface list on the welcome page sorts active interfaces first and only displays the sparkline for active interfaces.
|
||||
Additionally, the interfaces can now be hidden/unhidden via the context menu in the interface list
|
||||
|
||||
* ETW reader now supports to display IP packets from an event trace logfile or an event trace live session
|
||||
=== Removed Features and Support
|
||||
|
||||
* CMake: The options starting with DISABLE_something were renamed ENABLE_something for consistency.
|
||||
|
|
|
@ -292,6 +292,7 @@ if(BUILD_etwdump AND WIN32)
|
|||
etwdump.c
|
||||
etl.c
|
||||
etw_message.c
|
||||
etw_ndiscap.c
|
||||
)
|
||||
|
||||
set_executable_resources(etwdump "etwdump")
|
||||
|
|
70
extcap/etl.c
70
extcap/etl.c
|
@ -34,7 +34,10 @@
|
|||
|
||||
extern int g_include_undecidable_event;
|
||||
|
||||
//Microsoft-Windows-Wmbclass-Opn
|
||||
const GUID mbb_provider = { 0xA42FE227, 0xA7BF, 0x4483, {0xA5, 0x02, 0x6B, 0xCD, 0xA4, 0x28, 0xCD, 0x96} };
|
||||
// Microsoft-Windows-NDIS-PacketCapture
|
||||
const GUID ndis_capture_provider = { 0x2ed6006e, 0x4729, 0x4609, 0xb4, 0x23, 0x3e, 0xe7, 0xbc, 0xd6, 0x78, 0xef };
|
||||
|
||||
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 } };
|
||||
|
@ -69,8 +72,8 @@ typedef struct _PROVIDER_FILTER {
|
|||
UCHAR Level;
|
||||
} PROVIDER_FILTER;
|
||||
|
||||
static gchar g_err_info[FILENAME_MAX] = { 0 };
|
||||
static int g_err = ERROR_SUCCESS;
|
||||
char g_err_info[FILENAME_MAX] = { 0 };
|
||||
int g_err = ERROR_SUCCESS;
|
||||
static wtap_dumper* g_pdh = NULL;
|
||||
extern ULONGLONG g_num_events;
|
||||
static PROVIDER_FILTER g_provider_filters[32] = { 0 };
|
||||
|
@ -78,9 +81,10 @@ static BOOL g_is_live_session = FALSE;
|
|||
|
||||
static void WINAPI event_callback(PEVENT_RECORD ev);
|
||||
void etw_dump_write_opn_event(PEVENT_RECORD ev, ULARGE_INTEGER timestamp);
|
||||
void etw_dump_write_ndiscap_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);
|
||||
void wtap_etl_rec_dump(char* etl_record, ULONG total_packet_length, ULONG original_packet_length, unsigned int interface_id, BOOLEAN is_inbound, ULARGE_INTEGER timestamp, int pkt_encap, char* comment, unsigned short comment_length);
|
||||
wtap_dumper* etw_dump_open(const char* pcapng_filename, int* err, gchar** err_info);
|
||||
|
||||
DWORD GetPropertyValue(WCHAR* ProviderId, EVT_PUBLISHER_METADATA_PROPERTY_ID PropertyId, PEVT_VARIANT* Value)
|
||||
|
@ -307,7 +311,7 @@ wtap_open_return_val etw_dump(const char* etl_filename, const char* pcapng_filen
|
|||
&super_trace_properties.prop);
|
||||
if (*err != ERROR_SUCCESS)
|
||||
{
|
||||
*err_info = ws_strdup_printf("StartTrace failed with %u", *err);
|
||||
*err_info = ws_strdup_printf("StartTrace failed with 0x%x", *err);
|
||||
returnVal = WTAP_OPEN_ERROR;
|
||||
break;
|
||||
}
|
||||
|
@ -330,7 +334,7 @@ wtap_open_return_val etw_dump(const char* etl_filename, const char* pcapng_filen
|
|||
NULL);
|
||||
if (*err != ERROR_SUCCESS)
|
||||
{
|
||||
*err_info = ws_strdup_printf("EnableTraceEx failed with %u", *err);
|
||||
*err_info = ws_strdup_printf("EnableTraceEx failed with 0x%x", *err);
|
||||
returnVal = WTAP_OPEN_ERROR;
|
||||
break;
|
||||
}
|
||||
|
@ -340,7 +344,7 @@ wtap_open_return_val etw_dump(const char* etl_filename, const char* pcapng_filen
|
|||
trace_handle = OpenTrace(&log_file);
|
||||
if (trace_handle == INVALID_PROCESSTRACE_HANDLE) {
|
||||
*err = GetLastError();
|
||||
*err_info = ws_strdup_printf("OpenTrace failed with %u", err);
|
||||
*err_info = ws_strdup_printf("OpenTrace failed with 0x%x", *err);
|
||||
returnVal = WTAP_OPEN_NOT_MINE;
|
||||
break;
|
||||
}
|
||||
|
@ -355,7 +359,7 @@ wtap_open_return_val etw_dump(const char* etl_filename, const char* pcapng_filen
|
|||
*err = ProcessTrace(&trace_handle, 1, 0, 0);
|
||||
if (*err != ERROR_SUCCESS) {
|
||||
returnVal = WTAP_OPEN_ERROR;
|
||||
*err_info = ws_strdup_printf("ProcessTrace failed with %u", err);
|
||||
*err_info = ws_strdup_printf("ProcessTrace failed with 0x%x", *err);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -450,13 +454,10 @@ static void WINAPI event_callback(PEVENT_RECORD ev)
|
|||
{
|
||||
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))
|
||||
else if (IsEqualGUID(&ev->EventHeader.ProviderId, &ndis_capture_provider))
|
||||
{
|
||||
etw_dump_write_packet_event(ev, timestamp);
|
||||
etw_dump_write_ndiscap_event(ev, timestamp);
|
||||
}
|
||||
#endif
|
||||
/* Write any event form other providers other than above */
|
||||
else
|
||||
{
|
||||
|
@ -579,7 +580,33 @@ ULONG wtap_etl_record_buffer_init(WTAP_ETL_RECORD** out_etl_record, PEVENT_RECOR
|
|||
return total_packet_length;
|
||||
}
|
||||
|
||||
void wtap_etl_rec_dump(ULARGE_INTEGER timestamp, WTAP_ETL_RECORD* etl_record, ULONG total_packet_length, BOOLEAN is_inbound)
|
||||
void wtap_etl_add_interface(int pkt_encap, char* interface_name, unsigned short interface_name_length, char* interface_desc, unsigned short interface_desc_length)
|
||||
{
|
||||
wtap_block_t idb_data;
|
||||
wtapng_if_descr_mandatory_t* descr_mand;
|
||||
gchar* err_info;
|
||||
int err;
|
||||
|
||||
idb_data = wtap_block_create(WTAP_BLOCK_IF_ID_AND_INFO);
|
||||
descr_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(idb_data);
|
||||
descr_mand->wtap_encap = pkt_encap;
|
||||
descr_mand->tsprecision = WTAP_TSPREC_USEC;
|
||||
/* 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;
|
||||
if (interface_name_length) {
|
||||
wtap_block_add_string_option(idb_data, OPT_IDB_NAME, interface_name, interface_name_length);
|
||||
}
|
||||
if (interface_desc_length) {
|
||||
wtap_block_add_string_option(idb_data, OPT_IDB_DESCRIPTION, interface_desc, interface_desc_length);
|
||||
}
|
||||
if(!wtap_dump_add_idb(g_pdh, idb_data, &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);
|
||||
}
|
||||
}
|
||||
|
||||
void wtap_etl_rec_dump(char* etl_record, ULONG total_packet_length, ULONG original_packet_length, unsigned int interface_id, BOOLEAN is_inbound, ULARGE_INTEGER timestamp, int pkt_encap, char* comment, unsigned short comment_length)
|
||||
{
|
||||
gchar* err_info;
|
||||
int err;
|
||||
|
@ -587,10 +614,15 @@ void wtap_etl_rec_dump(ULARGE_INTEGER timestamp, WTAP_ETL_RECORD* etl_record, UL
|
|||
|
||||
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_ETW;
|
||||
rec.rec_header.packet_header.len = original_packet_length;
|
||||
rec.rec_header.packet_header.pkt_encap = pkt_encap;
|
||||
rec.rec_header.packet_header.interface_id = interface_id;
|
||||
rec.presence_flags = WTAP_HAS_INTERFACE_ID;
|
||||
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);
|
||||
if (comment_length) {
|
||||
wtap_block_add_string_option(rec.block, OPT_COMMENT, comment, comment_length);
|
||||
}
|
||||
/* 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);
|
||||
|
@ -605,7 +637,7 @@ void wtap_etl_rec_dump(ULARGE_INTEGER timestamp, WTAP_ETL_RECORD* etl_record, UL
|
|||
/* 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);
|
||||
sprintf_s(g_err_info, sizeof(g_err_info), "wtap_dump failed, 0x%x", err);
|
||||
}
|
||||
wtap_rec_cleanup(&rec);
|
||||
}
|
||||
|
@ -618,7 +650,7 @@ void etw_dump_write_opn_event(PEVENT_RECORD ev, ULARGE_INTEGER timestamp)
|
|||
/* 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);
|
||||
wtap_etl_rec_dump((char*)etl_record, total_packet_length, total_packet_length, 0, is_inbound, timestamp, WTAP_ENCAP_ETW, NULL, 0);
|
||||
g_free(etl_record);
|
||||
}
|
||||
|
||||
|
@ -627,7 +659,7 @@ 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);
|
||||
wtap_etl_rec_dump((char*)etl_record, total_packet_length, total_packet_length, 0, FALSE, timestamp, WTAP_ENCAP_ETW, NULL, 0);
|
||||
g_free(etl_record);
|
||||
}
|
||||
|
||||
|
@ -715,7 +747,7 @@ void etw_dump_write_general_event(PEVENT_RECORD ev, ULARGE_INTEGER timestamp)
|
|||
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);
|
||||
wtap_etl_rec_dump((char*)etl_record, total_packet_length, total_packet_length, 0, FALSE, timestamp, WTAP_ENCAP_ETW, NULL, 0);
|
||||
g_free(etl_record);
|
||||
|
||||
is_message_dumped = TRUE;
|
||||
|
|
|
@ -0,0 +1,705 @@
|
|||
/* etw_ndiscap.c
|
||||
*
|
||||
* Wireshark - Network traffic analyzer
|
||||
* By Gerald Combs <gerald@wireshark.org>
|
||||
* Copyright 1998 Gerald Combs
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
/*
|
||||
* Reads IP packets from an Windows event trace logfile or an Windows event trace live session
|
||||
* and write out a pcap file with LINKTYPE_ETHERNET, LINKTYPE_RAW or LINKTYPE_IEEE802_11.
|
||||
* The major code of this file is from https://github.com/microsoft/etl2pcapng with some changes by Odysseus Yang.
|
||||
* The changes mainly include but not limited
|
||||
* 1. calling pcapng APIs instead of writing the data in the pcapng binary format by its own implementation in etl2pcapng.
|
||||
* 2. Optimize the process of adding pcapng interfaces so it doesn't need process the same Windows event trace logfile twice,
|
||||
that not only impacts the performance, but also breaks Wireshark live capture function.
|
||||
*/
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <evntrace.h>
|
||||
#include <evntcons.h>
|
||||
#include <tdh.h>
|
||||
#include <strsafe.h>
|
||||
#include <winsock2.h>
|
||||
#include <netiodef.h>
|
||||
|
||||
// inet_ipv6.h and netiodef.h define exactly the same stuff, like _IPV6_ROUTING_HEADER and IP6F_OFF_MASK.
|
||||
// So wiretap/wtap.h cannot be directly included in this file. Defines below three WTAP_ENCAP types with the value in wtap.h for compile
|
||||
#define WTAP_ENCAP_ETHERNET 1
|
||||
#define WTAP_ENCAP_RAW_IP 7
|
||||
#define WTAP_ENCAP_IEEE_802_11 20
|
||||
|
||||
#define MAX_PACKET_SIZE 0xFFFF
|
||||
|
||||
// From the ndiscap manifest
|
||||
#define KW_MEDIA_WIRELESS_WAN 0x200
|
||||
#define KW_MEDIA_NATIVE_802_11 0x10000
|
||||
#define KW_PACKET_START 0x40000000
|
||||
#define KW_PACKET_END 0x80000000
|
||||
#define KW_SEND 0x100000000
|
||||
#define KW_RECEIVE 0x200000000
|
||||
|
||||
#define tidPacketFragment 1001
|
||||
#define tidPacketMetadata 1002
|
||||
#define tidVMSwitchPacketFragment 1003
|
||||
|
||||
// From: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/windot11/ns-windot11-dot11_extsta_recv_context
|
||||
#pragma pack(push,8)
|
||||
typedef struct _NDIS_OBJECT_HEADER {
|
||||
unsigned char Type;
|
||||
unsigned char Revision;
|
||||
unsigned short Size;
|
||||
} NDIS_OBJECT_HEADER, * PNDIS_OBJECT_HEADER;
|
||||
|
||||
typedef struct DOT11_EXTSTA_RECV_CONTEXT {
|
||||
NDIS_OBJECT_HEADER Header;
|
||||
unsigned long uReceiveFlags;
|
||||
unsigned long uPhyId;
|
||||
unsigned long uChCenterFrequency;
|
||||
unsigned short usNumberOfMPDUsReceived;
|
||||
long lRSSI;
|
||||
unsigned char ucDataRate;
|
||||
unsigned long uSizeMediaSpecificInfo;
|
||||
void *pvMediaSpecificInfo;
|
||||
unsigned long long ullTimestamp;
|
||||
} DOT11_EXTSTA_RECV_CONTEXT, * PDOT11_EXTSTA_RECV_CONTEXT;
|
||||
#pragma pack(pop)
|
||||
|
||||
// From: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/windot11/ne-windot11-_dot11_phy_type
|
||||
#define DOT11_PHY_TYPE_NAMES_MAX 10
|
||||
static const char* DOT11_PHY_TYPE_NAMES[] = {
|
||||
"Unknown", // dot11_phy_type_unknown = 0
|
||||
"Fhss", // dot11_phy_type_fhss = 1
|
||||
"Dsss", // dot11_phy_type_dsss = 2
|
||||
"IrBaseband", // dot11_phy_type_irbaseband = 3
|
||||
"802.11a", // dot11_phy_type_ofdm = 4
|
||||
"802.11b", // dot11_phy_type_hrdsss = 5
|
||||
"802.11g", // dot11_phy_type_erp = 6
|
||||
"802.11n", // dot11_phy_type_ht = 7
|
||||
"802.11ac", // dot11_phy_type_vht = 8
|
||||
"802.11ad", // dot11_phy_type_dmg = 9
|
||||
"802.11ax" // dot11_phy_type_he = 10
|
||||
};
|
||||
|
||||
unsigned long long NumFramesConverted = 0;
|
||||
char AuxFragBuf[MAX_PACKET_SIZE] = {0};
|
||||
unsigned long AuxFragBufOffset = 0;
|
||||
|
||||
DOT11_EXTSTA_RECV_CONTEXT PacketMetadata;
|
||||
BOOLEAN AddWlanMetadata = FALSE;
|
||||
|
||||
typedef struct _NDIS_NET_BUFFER_LIST_8021Q_INFO {
|
||||
union {
|
||||
struct {
|
||||
UINT32 UserPriority : 3; // 802.1p priority
|
||||
UINT32 CanonicalFormatId : 1; // always 0
|
||||
UINT32 VlanId : 12; // VLAN Identification
|
||||
UINT32 Reserved : 16; // set to 0 for ethernet
|
||||
} TagHeader;
|
||||
|
||||
struct {
|
||||
UINT32 UserPriority : 3; // 802.1p priority
|
||||
UINT32 CanonicalFormatId : 1; // always 0
|
||||
UINT32 VlanId : 12; // VLAN Identification
|
||||
UINT32 WMMInfo : 4;
|
||||
UINT32 Reserved : 12; // set to 0 for Wireless LAN
|
||||
} WLanTagHeader;
|
||||
|
||||
PVOID Value;
|
||||
};
|
||||
} NDIS_NET_BUFFER_LIST_8021Q_INFO, *PNDIS_NET_BUFFER_LIST_8021Q_INFO;
|
||||
|
||||
// The max OOB data size might increase in the future. If it becomes larger than MaxNetBufferListInfo,
|
||||
// this tool will print a warning and the value of MaxNetBufferListInfo in the code should be increased.
|
||||
// From: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/nblinfo/ne-nblinfo-ndis_net_buffer_list_info
|
||||
#define MaxNetBufferListInfo 200
|
||||
#define Ieee8021QNetBufferListInfo 4
|
||||
PBYTE OobData[MaxNetBufferListInfo];
|
||||
|
||||
typedef struct _VMSWITCH_SOURCE_INFO {
|
||||
unsigned long SourcePortId;
|
||||
char* SourcePortName;
|
||||
char* SourceNicName;
|
||||
char* SourceNicType;
|
||||
} VMSWITCH_SOURCE_INFO, *PVMSWITCH_SOURCE_INFO;
|
||||
|
||||
typedef struct _VMSWITCH_PACKET_FRAGMENT {
|
||||
unsigned long SourcePortId;
|
||||
unsigned long DestinationCount;
|
||||
short VlanId;
|
||||
} VMSWITCH_PACKET_FRAGMENT, *PVMSWITCH_PACKET_FRAGMENT;
|
||||
|
||||
BOOLEAN CurrentPacketIsVMSwitchPacketFragment = FALSE;
|
||||
VMSWITCH_PACKET_FRAGMENT VMSwitchPacketFragment;
|
||||
|
||||
struct INTERFACE {
|
||||
struct INTERFACE* Next;
|
||||
unsigned long LowerIfIndex;
|
||||
unsigned long MiniportIfIndex;
|
||||
unsigned long PcapNgIfIndex;
|
||||
int PktEncapType;
|
||||
short VlanId;
|
||||
|
||||
BOOLEAN IsVMNic;
|
||||
VMSWITCH_SOURCE_INFO VMNic;
|
||||
};
|
||||
|
||||
#define IFACE_HT_SIZE 100
|
||||
struct INTERFACE* InterfaceHashTable[IFACE_HT_SIZE] = {0};
|
||||
unsigned long NumInterfaces = 0;
|
||||
|
||||
void wtap_etl_rec_dump(char* etl_record, ULONG total_packet_length, ULONG original_packet_length, unsigned int interface_id, BOOLEAN is_inbound, ULARGE_INTEGER timestamp, int pkt_encap, char* comment, unsigned short comment_length);
|
||||
void wtap_etl_add_interface(int pkt_encap, char* interface_name, unsigned short interface_name_length, char* interface_desc, unsigned short interface_desc_length);
|
||||
|
||||
extern char g_err_info[FILENAME_MAX];
|
||||
extern int g_err;
|
||||
|
||||
unsigned long HashInterface(unsigned long LowerIfIndex)
|
||||
{
|
||||
if (CurrentPacketIsVMSwitchPacketFragment) {
|
||||
return VMSwitchPacketFragment.SourcePortId * (VMSwitchPacketFragment.VlanId + 1);
|
||||
} else {
|
||||
return LowerIfIndex;
|
||||
}
|
||||
}
|
||||
|
||||
struct INTERFACE* GetInterface(unsigned long LowerIfIndex)
|
||||
{
|
||||
struct INTERFACE* Iface = InterfaceHashTable[HashInterface(LowerIfIndex) % IFACE_HT_SIZE];
|
||||
while (Iface != NULL) {
|
||||
if (CurrentPacketIsVMSwitchPacketFragment) {
|
||||
if (Iface->IsVMNic &&
|
||||
Iface->LowerIfIndex == LowerIfIndex &&
|
||||
Iface->VlanId == VMSwitchPacketFragment.VlanId &&
|
||||
Iface->VMNic.SourcePortId == VMSwitchPacketFragment.SourcePortId) {
|
||||
return Iface;
|
||||
}
|
||||
} else {
|
||||
if (!Iface->IsVMNic && Iface->LowerIfIndex == LowerIfIndex && Iface->VlanId == 0) {
|
||||
return Iface;
|
||||
}
|
||||
}
|
||||
Iface = Iface->Next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct INTERFACE* AddInterface(PEVENT_RECORD ev, unsigned long LowerIfIndex, unsigned long MiniportIfIndex, int Type)
|
||||
{
|
||||
struct INTERFACE** Iface = &InterfaceHashTable[HashInterface(LowerIfIndex) % IFACE_HT_SIZE];
|
||||
struct INTERFACE* NewIface = malloc(sizeof(struct INTERFACE));
|
||||
|
||||
#define IF_STRING_MAX_SIZE 128
|
||||
char IfName[IF_STRING_MAX_SIZE];
|
||||
size_t IfNameLength = 0;
|
||||
char IfDesc[IF_STRING_MAX_SIZE];
|
||||
size_t IfDescLength = 0;
|
||||
//etw pcagng interface will be 0 always, network pcagng interface will start with 1
|
||||
static PcapNgIfIndex = 1;
|
||||
|
||||
if (NewIface == NULL) {
|
||||
g_err = ERROR_OUTOFMEMORY;
|
||||
sprintf_s(g_err_info, sizeof(g_err_info), "malloc failed to allocate memory for NewIface");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
NewIface->LowerIfIndex = LowerIfIndex;
|
||||
NewIface->MiniportIfIndex = MiniportIfIndex;
|
||||
NewIface->PktEncapType = Type;
|
||||
NewIface->VlanId = 0;
|
||||
NewIface->IsVMNic = FALSE;
|
||||
|
||||
if (CurrentPacketIsVMSwitchPacketFragment) {
|
||||
|
||||
NewIface->IsVMNic = TRUE;
|
||||
|
||||
wchar_t Buffer[8192];
|
||||
PROPERTY_DATA_DESCRIPTOR Desc;
|
||||
int Err;
|
||||
|
||||
// SourceNicName
|
||||
Desc.PropertyName = (unsigned long long)(L"SourceNicName");
|
||||
Desc.ArrayIndex = ULONG_MAX;
|
||||
ULONG ParamNameSize = 0;
|
||||
(void)TdhGetPropertySize(ev, 0, NULL, 1, &Desc, &ParamNameSize);
|
||||
NewIface->VMNic.SourceNicName = malloc((ParamNameSize / sizeof(wchar_t)) + 1);
|
||||
if (NewIface->VMNic.SourceNicName == NULL) {
|
||||
g_err = ERROR_OUTOFMEMORY;
|
||||
sprintf_s(g_err_info, sizeof(g_err_info), "malloc failed to allocate memory for NewIface->VMNic.SourceNicName");
|
||||
exit(1);
|
||||
}
|
||||
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(Buffer), (PBYTE)Buffer);
|
||||
if (Err != NO_ERROR) {
|
||||
Buffer[0] = L'\0';
|
||||
}
|
||||
Buffer[ParamNameSize / sizeof(wchar_t) + 1] = L'\0';
|
||||
WideCharToMultiByte(CP_ACP,
|
||||
0,
|
||||
Buffer,
|
||||
-1,
|
||||
NewIface->VMNic.SourceNicName,
|
||||
ParamNameSize / sizeof(wchar_t) + 1,
|
||||
NULL,
|
||||
NULL);
|
||||
NewIface->VMNic.SourceNicName[wcslen(Buffer)] = '\0';
|
||||
|
||||
// SourcePortName
|
||||
Desc.PropertyName = (unsigned long long)(L"SourcePortName");
|
||||
Desc.ArrayIndex = ULONG_MAX;
|
||||
(void)TdhGetPropertySize(ev, 0, NULL, 1, &Desc, &ParamNameSize);
|
||||
NewIface->VMNic.SourcePortName = malloc((ParamNameSize / sizeof(wchar_t)) + 1);
|
||||
if (NewIface->VMNic.SourcePortName == NULL) {
|
||||
g_err = ERROR_OUTOFMEMORY;
|
||||
sprintf_s(g_err_info, sizeof(g_err_info), "malloc failed to allocate memory for NewIface->VMNic.SourcePortName");
|
||||
exit(1);
|
||||
}
|
||||
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(Buffer), (PBYTE)Buffer);
|
||||
if (Err != NO_ERROR) {
|
||||
Buffer[0] = L'\0';
|
||||
}
|
||||
Buffer[ParamNameSize / sizeof(wchar_t) + 1] = L'\0';
|
||||
WideCharToMultiByte(CP_ACP,
|
||||
0,
|
||||
Buffer,
|
||||
-1,
|
||||
NewIface->VMNic.SourcePortName,
|
||||
ParamNameSize / sizeof(wchar_t) + 1,
|
||||
NULL,
|
||||
NULL);
|
||||
NewIface->VMNic.SourcePortName[wcslen(Buffer)] = '\0';
|
||||
|
||||
// SourceNicType
|
||||
Desc.PropertyName = (unsigned long long)(L"SourceNicType");
|
||||
Desc.ArrayIndex = ULONG_MAX;
|
||||
(void)TdhGetPropertySize(ev, 0, NULL, 1, &Desc, &ParamNameSize);
|
||||
NewIface->VMNic.SourceNicType = malloc((ParamNameSize / sizeof(wchar_t)) + 1);
|
||||
if (NewIface->VMNic.SourceNicType == NULL) {
|
||||
g_err = ERROR_OUTOFMEMORY;
|
||||
sprintf_s(g_err_info, sizeof(g_err_info), "malloc failed to allocate memory for NewIface->VMNic.SourceNicType");
|
||||
exit(1);
|
||||
}
|
||||
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(Buffer), (PBYTE)Buffer);
|
||||
if (Err != NO_ERROR) {
|
||||
Buffer[0] = L'\0';
|
||||
}
|
||||
Buffer[ParamNameSize / sizeof(wchar_t) + 1] = L'\0';
|
||||
WideCharToMultiByte(CP_ACP,
|
||||
0,
|
||||
Buffer,
|
||||
-1,
|
||||
NewIface->VMNic.SourceNicType,
|
||||
ParamNameSize / sizeof(wchar_t) + 1,
|
||||
NULL,
|
||||
NULL);
|
||||
NewIface->VMNic.SourceNicType[wcslen(Buffer)] = '\0';
|
||||
|
||||
|
||||
NewIface->VMNic.SourcePortId = VMSwitchPacketFragment.SourcePortId;
|
||||
NewIface->VlanId = VMSwitchPacketFragment.VlanId;
|
||||
}
|
||||
|
||||
NewIface->Next = *Iface;
|
||||
|
||||
*Iface = NewIface;
|
||||
NumInterfaces++;
|
||||
|
||||
NewIface->PcapNgIfIndex = PcapNgIfIndex;
|
||||
PcapNgIfIndex++;
|
||||
memset(IfName, 0, sizeof(IfName));
|
||||
memset(IfDesc, 0, sizeof(IfDesc));
|
||||
switch (NewIface->PktEncapType) {
|
||||
case WTAP_ENCAP_ETHERNET:
|
||||
if (NewIface->IsVMNic) {
|
||||
printf("IF: medium=%s\tID=%u\tIfIndex=%u\tVlanID=%i",
|
||||
NewIface->VMNic.SourceNicType,
|
||||
NewIface->PcapNgIfIndex,
|
||||
NewIface->VMNic.SourcePortId,
|
||||
NewIface->VlanId
|
||||
);
|
||||
StringCchPrintfA(
|
||||
IfName,
|
||||
IF_STRING_MAX_SIZE,
|
||||
"%s:%s:%lu:%i",
|
||||
NewIface->VMNic.SourcePortName,
|
||||
NewIface->VMNic.SourceNicType,
|
||||
NewIface->VMNic.SourcePortId,
|
||||
NewIface->VlanId
|
||||
);
|
||||
}
|
||||
else {
|
||||
printf("IF: medium=eth\tID=%u\tIfIndex=%u\tVlanID=%i", NewIface->PcapNgIfIndex, NewIface->LowerIfIndex, NewIface->VlanId);
|
||||
StringCchPrintfA(IfName, IF_STRING_MAX_SIZE, "eth:%lu:%i", NewIface->LowerIfIndex, NewIface->VlanId);
|
||||
}
|
||||
break;
|
||||
case WTAP_ENCAP_IEEE_802_11:
|
||||
printf("IF: medium=wifi ID=%u\tIfIndex=%u", NewIface->PcapNgIfIndex, NewIface->LowerIfIndex);
|
||||
StringCchPrintfA(IfName, IF_STRING_MAX_SIZE, "wifi:%lu", NewIface->LowerIfIndex);
|
||||
break;
|
||||
case WTAP_ENCAP_RAW_IP:
|
||||
printf("IF: medium=mbb ID=%u\tIfIndex=%u", NewIface->PcapNgIfIndex, NewIface->LowerIfIndex);
|
||||
StringCchPrintfA(IfName, IF_STRING_MAX_SIZE, "mbb:%lu", NewIface->LowerIfIndex);
|
||||
break;
|
||||
}
|
||||
StringCchLengthA(IfName, IF_STRING_MAX_SIZE, &IfNameLength);
|
||||
|
||||
if (NewIface->LowerIfIndex != NewIface->MiniportIfIndex) {
|
||||
printf("\t(LWF over IfIndex %u)", NewIface->MiniportIfIndex);
|
||||
StringCchPrintfA(IfDesc, IF_STRING_MAX_SIZE, "LWF over IfIndex %lu", NewIface->MiniportIfIndex);
|
||||
StringCchLengthA(IfDesc, IF_STRING_MAX_SIZE, &IfDescLength);
|
||||
}
|
||||
|
||||
if (NewIface->VlanId != 0) {
|
||||
StringCchPrintfA(IfDesc + IfDescLength, IF_STRING_MAX_SIZE, " VlanID=%i ", NewIface->VlanId);
|
||||
StringCchLengthA(IfDesc, IF_STRING_MAX_SIZE, &IfDescLength);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
wtap_etl_add_interface(NewIface->PktEncapType, IfName, (unsigned short)IfNameLength, IfDesc, (unsigned short)IfDescLength);
|
||||
return NewIface;
|
||||
}
|
||||
|
||||
void ParseVmSwitchPacketFragment(PEVENT_RECORD ev)
|
||||
{
|
||||
// Parse the current VMSwitch packet event for use elsewhere.
|
||||
// NB: Here we only do per-packet parsing. For any event fields that only need to be
|
||||
// parsed once and written into an INTERFACE, we do the parsing in AddInterface.
|
||||
|
||||
PROPERTY_DATA_DESCRIPTOR Desc;
|
||||
int Err;
|
||||
PNDIS_NET_BUFFER_LIST_8021Q_INFO pNblVlanInfo;
|
||||
|
||||
// Get VLAN from OOB
|
||||
unsigned long OobLength;
|
||||
Desc.PropertyName = (unsigned long long)L"OOBDataSize";
|
||||
Desc.ArrayIndex = ULONG_MAX;
|
||||
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(OobLength), (PBYTE)&OobLength);
|
||||
if (Err != NO_ERROR) {
|
||||
g_err = Err;
|
||||
sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty OobLength failed, err is 0x%x", Err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (OobLength > sizeof(OobData)) {
|
||||
g_err = ERROR_INVALID_DATA;
|
||||
sprintf_s(g_err_info, sizeof(g_err_info), "OOB data of %lu bytes too large to fit in hardcoded buffer of size %lu", OobLength, (unsigned long)sizeof(OobData));
|
||||
return;
|
||||
}
|
||||
|
||||
Desc.PropertyName = (unsigned long long)L"OOBData";
|
||||
Desc.ArrayIndex = ULONG_MAX;
|
||||
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, OobLength, (PBYTE)&OobData);
|
||||
if (Err != NO_ERROR) {
|
||||
g_err = Err;
|
||||
sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty OobData failed, err is 0x%x", Err);
|
||||
return;
|
||||
}
|
||||
|
||||
pNblVlanInfo = (PNDIS_NET_BUFFER_LIST_8021Q_INFO)&OobData[Ieee8021QNetBufferListInfo];
|
||||
VMSwitchPacketFragment.VlanId = pNblVlanInfo->TagHeader.VlanId;
|
||||
|
||||
// SourcePortId
|
||||
Desc.PropertyName = (unsigned long long)L"SourcePortId";
|
||||
Desc.ArrayIndex = ULONG_MAX;
|
||||
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(VMSwitchPacketFragment.SourcePortId), (PBYTE)&VMSwitchPacketFragment.SourcePortId);
|
||||
if (Err != NO_ERROR) {
|
||||
g_err = Err;
|
||||
sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty SourcePortId failed, err is 0x%x", Err);
|
||||
return;
|
||||
}
|
||||
|
||||
// DestinationCount
|
||||
Desc.PropertyName = (unsigned long long)L"DestinationCount";
|
||||
Desc.ArrayIndex = ULONG_MAX;
|
||||
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(VMSwitchPacketFragment.DestinationCount), (PBYTE)&VMSwitchPacketFragment.DestinationCount);
|
||||
if (Err != NO_ERROR) {
|
||||
g_err = Err;
|
||||
sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty DestinationCount failed, err is 0x%x", Err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void etw_dump_write_ndiscap_event(PEVENT_RECORD ev, ULARGE_INTEGER timestamp)
|
||||
{
|
||||
int Err;
|
||||
unsigned long LowerIfIndex;
|
||||
|
||||
struct INTERFACE* Iface;
|
||||
unsigned long FragLength;
|
||||
PROPERTY_DATA_DESCRIPTOR Desc;
|
||||
int Type;
|
||||
unsigned long TotalFragmentLength;
|
||||
unsigned long InferredOriginalFragmentLength = 0;
|
||||
PETHERNET_HEADER EthHdr;
|
||||
PIPV4_HEADER Ipv4Hdr;
|
||||
PIPV6_HEADER Ipv6Hdr;
|
||||
|
||||
if ((ev->EventHeader.EventDescriptor.Id != tidPacketFragment &&
|
||||
ev->EventHeader.EventDescriptor.Id != tidPacketMetadata &&
|
||||
ev->EventHeader.EventDescriptor.Id != tidVMSwitchPacketFragment)) {
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentPacketIsVMSwitchPacketFragment = (ev->EventHeader.EventDescriptor.Id == tidVMSwitchPacketFragment);
|
||||
if (CurrentPacketIsVMSwitchPacketFragment) {
|
||||
ParseVmSwitchPacketFragment(ev);
|
||||
}
|
||||
|
||||
Desc.PropertyName = (unsigned long long)L"LowerIfIndex";
|
||||
Desc.ArrayIndex = ULONG_MAX;
|
||||
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(LowerIfIndex), (PBYTE)&LowerIfIndex);
|
||||
if (Err != NO_ERROR) {
|
||||
g_err = Err;
|
||||
sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty LowerIfIndex failed, err is 0x%x", Err);
|
||||
return;
|
||||
}
|
||||
|
||||
Iface = GetInterface(LowerIfIndex);
|
||||
|
||||
if (!!(ev->EventHeader.EventDescriptor.Keyword & KW_MEDIA_NATIVE_802_11)) {
|
||||
Type = WTAP_ENCAP_IEEE_802_11;
|
||||
} else if (!!(ev->EventHeader.EventDescriptor.Keyword & KW_MEDIA_WIRELESS_WAN)) {
|
||||
Type = WTAP_ENCAP_RAW_IP;
|
||||
} else {
|
||||
Type = WTAP_ENCAP_ETHERNET;
|
||||
}
|
||||
|
||||
// Record the IfIndex if it's a new one.
|
||||
if (Iface == NULL) {
|
||||
unsigned long MiniportIfIndex;
|
||||
Desc.PropertyName = (unsigned long long)L"MiniportIfIndex";
|
||||
Desc.ArrayIndex = ULONG_MAX;
|
||||
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(MiniportIfIndex), (PBYTE)&MiniportIfIndex);
|
||||
if (Err != NO_ERROR) {
|
||||
g_err = Err;
|
||||
sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty MiniportIfIndex failed, err is 0x%x", Err);
|
||||
return;
|
||||
}
|
||||
Iface = AddInterface(
|
||||
ev,
|
||||
LowerIfIndex,
|
||||
MiniportIfIndex,
|
||||
Type
|
||||
);
|
||||
} else if (Iface->PktEncapType != Type) {
|
||||
printf("WARNING: inconsistent media type in packet events!\n");
|
||||
}
|
||||
|
||||
if (Iface == NULL) {
|
||||
// We generated the list of interfaces directly from the
|
||||
// packet traces themselves, so there must be a bug.
|
||||
g_err = ERROR_INVALID_DATA;
|
||||
sprintf_s(g_err_info, sizeof(g_err_info), "Packet with unrecognized IfIndex");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Save off Ndis/Wlan metadata to be added to the next packet
|
||||
if (ev->EventHeader.EventDescriptor.Id == tidPacketMetadata) {
|
||||
unsigned long MetadataLength = 0;
|
||||
Desc.PropertyName = (unsigned long long)L"MetadataSize";
|
||||
Desc.ArrayIndex = ULONG_MAX;
|
||||
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(MetadataLength), (PBYTE)&MetadataLength);
|
||||
if (Err != NO_ERROR) {
|
||||
g_err = Err;
|
||||
sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty MetadataSize failed, err is 0x%x", Err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (MetadataLength != sizeof(PacketMetadata)) {
|
||||
g_err = ERROR_INVALID_DATA;
|
||||
sprintf_s(g_err_info, sizeof(g_err_info), "Unknown Metadata length. Expected %lu, got %lu", (unsigned long)sizeof(DOT11_EXTSTA_RECV_CONTEXT), MetadataLength);
|
||||
return;
|
||||
}
|
||||
|
||||
Desc.PropertyName = (unsigned long long)L"Metadata";
|
||||
Desc.ArrayIndex = ULONG_MAX;
|
||||
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, MetadataLength, (PBYTE)&PacketMetadata);
|
||||
if (Err != NO_ERROR) {
|
||||
g_err = Err;
|
||||
sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty Metadata failed, err is 0x%x", Err);
|
||||
return;
|
||||
}
|
||||
|
||||
AddWlanMetadata = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
// N.B.: Here we are querying the FragmentSize property to get the
|
||||
// total size of the packet, and then reading that many bytes from
|
||||
// the Fragment property. This is unorthodox (normally you are
|
||||
// supposed to use TdhGetPropertySize to get the size of a property)
|
||||
// but required due to the way ndiscap puts packet contents in
|
||||
// multiple adjacent properties (which happen to be contiguous in
|
||||
// memory).
|
||||
|
||||
Desc.PropertyName = (unsigned long long)L"FragmentSize";
|
||||
Desc.ArrayIndex = ULONG_MAX;
|
||||
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(FragLength), (PBYTE)&FragLength);
|
||||
if (Err != NO_ERROR) {
|
||||
g_err = Err;
|
||||
sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty FragmentSize failed, err is 0x%x", Err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (FragLength > RTL_NUMBER_OF(AuxFragBuf) - AuxFragBufOffset) {
|
||||
g_err = ERROR_INVALID_DATA;
|
||||
sprintf_s(g_err_info, sizeof(g_err_info), "Packet too large (size = %u) and skipped", AuxFragBufOffset + FragLength);
|
||||
return;
|
||||
}
|
||||
|
||||
Desc.PropertyName = (unsigned long long)L"Fragment";
|
||||
Desc.ArrayIndex = ULONG_MAX;
|
||||
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, FragLength, (PBYTE)(AuxFragBuf + AuxFragBufOffset));
|
||||
if (Err != NO_ERROR) {
|
||||
g_err = Err;
|
||||
sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty Fragment failed, err is 0x%x", Err);
|
||||
return;
|
||||
}
|
||||
|
||||
// The KW_PACKET_START and KW_PACKET_END keywords are used as follows:
|
||||
// -A single-event packet has both KW_PACKET_START and KW_PACKET_END.
|
||||
// -A multi-event packet consists of an event with KW_PACKET_START followed
|
||||
// by an event with KW_PACKET_END, with zero or more events with neither
|
||||
// keyword in between.
|
||||
//
|
||||
// So, we accumulate fragments in AuxFragBuf until KW_PACKET_END is
|
||||
// encountered, then call PcapNgWriteEnhancedPacket and start over. There's
|
||||
// no need for us to even look for KW_PACKET_START.
|
||||
//
|
||||
// NB: Starting with Windows 8.1, only single-event packets are traced.
|
||||
// This logic is here to support packet captures from older systems.
|
||||
|
||||
if (!!(ev->EventHeader.EventDescriptor.Keyword & KW_PACKET_END)) {
|
||||
|
||||
if (ev->EventHeader.EventDescriptor.Keyword & KW_MEDIA_NATIVE_802_11 &&
|
||||
AuxFragBuf[1] & 0x40) {
|
||||
// Clear Protected bit in the case of 802.11
|
||||
// Ndis captures will be decrypted in the etl file
|
||||
|
||||
AuxFragBuf[1] = AuxFragBuf[1] & 0xBF; // _1011_1111_ - Clear "Protected Flag"
|
||||
}
|
||||
|
||||
// COMMENT_MAX_SIZE must be multiple of 4
|
||||
#define COMMENT_MAX_SIZE 256
|
||||
char Comment[COMMENT_MAX_SIZE] = { 0 };
|
||||
size_t CommentLength = 0;
|
||||
|
||||
if (AddWlanMetadata) {
|
||||
if (PacketMetadata.uPhyId > DOT11_PHY_TYPE_NAMES_MAX) {
|
||||
PacketMetadata.uPhyId = 0; // Set to unknown if outside known bounds.
|
||||
}
|
||||
|
||||
Err = StringCchPrintfA(Comment, COMMENT_MAX_SIZE, "PID=%d Packet Metadata: ReceiveFlags:0x%x, PhyType:%s, CenterCh:%u, NumMPDUsReceived:%u, RSSI:%d, DataRate:%u",
|
||||
ev->EventHeader.ProcessId,
|
||||
PacketMetadata.uReceiveFlags,
|
||||
DOT11_PHY_TYPE_NAMES[PacketMetadata.uPhyId],
|
||||
PacketMetadata.uChCenterFrequency,
|
||||
PacketMetadata.usNumberOfMPDUsReceived,
|
||||
PacketMetadata.lRSSI,
|
||||
PacketMetadata.ucDataRate);
|
||||
|
||||
AddWlanMetadata = FALSE;
|
||||
memset(&PacketMetadata, 0, sizeof(DOT11_EXTSTA_RECV_CONTEXT));
|
||||
} else if (CurrentPacketIsVMSwitchPacketFragment) {
|
||||
if (VMSwitchPacketFragment.DestinationCount > 0) {
|
||||
Err = StringCchPrintfA(Comment, COMMENT_MAX_SIZE, "PID=%d VlanId=%d SrcPortId=%d SrcNicType=%s SrcNicName=%s SrcPortName=%s DstNicCount=%d",
|
||||
ev->EventHeader.ProcessId,
|
||||
Iface->VlanId,
|
||||
Iface->VMNic.SourcePortId,
|
||||
Iface->VMNic.SourceNicType,
|
||||
Iface->VMNic.SourceNicName,
|
||||
Iface->VMNic.SourcePortName,
|
||||
VMSwitchPacketFragment.DestinationCount
|
||||
);
|
||||
} else {
|
||||
Err = StringCchPrintfA(Comment, COMMENT_MAX_SIZE, "PID=%d VlanId=%d SrcPortId=%d SrcNicType=%s SrcNicName=%s SrcPortName=%s",
|
||||
ev->EventHeader.ProcessId,
|
||||
Iface->VlanId,
|
||||
Iface->VMNic.SourcePortId,
|
||||
Iface->VMNic.SourceNicType,
|
||||
Iface->VMNic.SourceNicName,
|
||||
Iface->VMNic.SourcePortName
|
||||
);
|
||||
}
|
||||
} else {
|
||||
Err = StringCchPrintfA(Comment, COMMENT_MAX_SIZE, "PID=%d", ev->EventHeader.ProcessId);
|
||||
}
|
||||
|
||||
if (Err != NO_ERROR) {
|
||||
printf("Failed converting comment to string with error: %u\n", Err);
|
||||
} else {
|
||||
Err = StringCchLengthA(Comment, COMMENT_MAX_SIZE, &CommentLength);
|
||||
|
||||
if (Err != NO_ERROR) {
|
||||
printf("Failed getting length of comment string with error: %u\n", Err);
|
||||
CommentLength = 0;
|
||||
memset(Comment, 0, COMMENT_MAX_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
TotalFragmentLength = AuxFragBufOffset + FragLength;
|
||||
|
||||
// Parse the packet to see if it's truncated. If so, try to recover the original length.
|
||||
if (Type == WTAP_ENCAP_ETHERNET) {
|
||||
if (TotalFragmentLength >= sizeof(ETHERNET_HEADER)) {
|
||||
EthHdr = (PETHERNET_HEADER)AuxFragBuf;
|
||||
if (ntohs(EthHdr->Type) == ETHERNET_TYPE_IPV4 &&
|
||||
TotalFragmentLength >= sizeof(IPV4_HEADER) + sizeof(ETHERNET_HEADER)) {
|
||||
Ipv4Hdr = (PIPV4_HEADER)(EthHdr + 1);
|
||||
InferredOriginalFragmentLength = ntohs(Ipv4Hdr->TotalLength) + sizeof(ETHERNET_HEADER);
|
||||
} else if (ntohs(EthHdr->Type) == ETHERNET_TYPE_IPV6 &&
|
||||
TotalFragmentLength >= sizeof(IPV6_HEADER) + sizeof(ETHERNET_HEADER)) {
|
||||
Ipv6Hdr = (PIPV6_HEADER)(EthHdr + 1);
|
||||
InferredOriginalFragmentLength = ntohs(Ipv6Hdr->PayloadLength) + sizeof(IPV6_HEADER) + sizeof(ETHERNET_HEADER);
|
||||
}
|
||||
}
|
||||
} else if (Type == WTAP_ENCAP_RAW_IP) {
|
||||
// Raw frames begins with an IPv4/6 header.
|
||||
if (TotalFragmentLength >= sizeof(IPV4_HEADER)) {
|
||||
Ipv4Hdr = (PIPV4_HEADER)AuxFragBuf;
|
||||
if (Ipv4Hdr->Version == 4) {
|
||||
InferredOriginalFragmentLength = ntohs(Ipv4Hdr->TotalLength) + sizeof(ETHERNET_HEADER);
|
||||
} else if (Ipv4Hdr->Version == 6) {
|
||||
Ipv6Hdr = (PIPV6_HEADER)(AuxFragBuf);
|
||||
InferredOriginalFragmentLength = ntohs(Ipv6Hdr->PayloadLength) + sizeof(IPV6_HEADER) + sizeof(ETHERNET_HEADER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wtap_etl_rec_dump(AuxFragBuf,
|
||||
TotalFragmentLength,
|
||||
// For LSO v2 packets, inferred original fragment length is ignored since length field in IP header is not filled.
|
||||
InferredOriginalFragmentLength <= TotalFragmentLength ? TotalFragmentLength : InferredOriginalFragmentLength,
|
||||
Iface->PcapNgIfIndex,
|
||||
!(ev->EventHeader.EventDescriptor.Keyword & KW_SEND),
|
||||
timestamp,
|
||||
Type,
|
||||
Comment,
|
||||
(unsigned short)CommentLength
|
||||
);
|
||||
|
||||
AuxFragBufOffset = 0;
|
||||
NumFramesConverted++;
|
||||
} else {
|
||||
AuxFragBufOffset += FragLength;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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:
|
||||
*/
|
Loading…
Reference in New Issue