diff --git a/src/main.c b/src/main.c index ebf9676..8a5e11b 100644 --- a/src/main.c +++ b/src/main.c @@ -41,12 +41,57 @@ in Windows that produces packet capture events) to pcapng format #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 +}; + HANDLE OutFile = INVALID_HANDLE_VALUE; unsigned long long NumFramesConverted = 0; BOOLEAN Pass2 = FALSE; char AuxFragBuf[MAX_PACKET_SIZE] = {0}; unsigned long AuxFragBufOffset = 0; +DOT11_EXTSTA_RECV_CONTEXT PacketMetadata; +BOOLEAN AddMetadata = FALSE; + const GUID NdisCapId = { // Microsoft-Windows-NDIS-PacketCapture {2ED6006E-4729-4609-B423-3EE7BCD678EF} 0x2ed6006e, 0x4729, 0x4609, 0xb4, 0x23, 0x3e, 0xe7, 0xbc, 0xd6, 0x78, 0xef}; @@ -183,16 +228,17 @@ void WINAPI EventCallback(PEVENT_RECORD ev) ULARGE_INTEGER TimeStamp; if (!IsEqualGUID(&ev->EventHeader.ProviderId, &NdisCapId) || - (ev->EventHeader.EventDescriptor.Id != 1001 && // tidPacketFragment - ev->EventHeader.EventDescriptor.Id != 1003)) { // tidVMSwitchPacketFragment + (ev->EventHeader.EventDescriptor.Id != tidPacketFragment && + ev->EventHeader.EventDescriptor.Id != tidPacketMetadata && + ev->EventHeader.EventDescriptor.Id != tidVMSwitchPacketFragment)) { return; } - Desc.PropertyName = (ULONGLONG)L"LowerIfIndex"; + 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) { - printf("TdhGetPropertySize failed with %u\n", Err); + printf("TdhGetProperty LowerIfIndex failed with %u\n", Err); return; } @@ -210,11 +256,11 @@ void WINAPI EventCallback(PEVENT_RECORD ev) // Record the IfIndex if it's a new one. if (Iface == NULL) { unsigned long MiniportIfIndex; - Desc.PropertyName = (ULONGLONG)L"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) { - printf("TdhGetPropertySize failed with %u\n", Err); + printf("TdhGetProperty MiniportIfIndex failed with %u\n", Err); return; } AddInterface(LowerIfIndex, MiniportIfIndex, Type); @@ -231,6 +277,34 @@ void WINAPI EventCallback(PEVENT_RECORD ev) 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) { + printf("TdhGetProperty MetadataSize failed with %u\n", Err); + return; + } + + if (MetadataLength != sizeof(PacketMetadata)) { + printf("Unknown Metadata length. Expected %u, got %u\n", 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) { + printf("TdhGetProperty Metadata failed with %u\n", Err); + return; + } + + AddMetadata = 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 @@ -239,11 +313,11 @@ void WINAPI EventCallback(PEVENT_RECORD ev) // multiple adjacent properties (which happen to be contiguous in // memory). - Desc.PropertyName = (ULONGLONG)L"FragmentSize"; + 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) { - printf("TdhGetPropertySize failed with %u\n", Err); + printf("TdhGetProperty FragmentSize failed with %u\n", Err); return; } @@ -252,11 +326,11 @@ void WINAPI EventCallback(PEVENT_RECORD ev) return; } - Desc.PropertyName = (ULONGLONG)L"Fragment"; + 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) { - printf("TdhGetPropertySize failed with %u\n", Err); + printf("TdhGetProperty Fragment failed with %u\n", Err); return; } @@ -279,6 +353,52 @@ void WINAPI EventCallback(PEVENT_RECORD ev) // 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 (AddMetadata) { + 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); + + AddMetadata = FALSE; + memset(&PacketMetadata, 0, sizeof(DOT11_EXTSTA_RECV_CONTEXT)); + } 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); + } + } + PcapNgWriteEnhancedPacket( OutFile, AuxFragBuf, @@ -287,7 +407,9 @@ void WINAPI EventCallback(PEVENT_RECORD ev) !!(ev->EventHeader.EventDescriptor.Keyword & KW_SEND), TimeStamp.HighPart, TimeStamp.LowPart, - ev->EventHeader.ProcessId); + CommentLength > 0 ? (char*)&Comment : NULL, + (unsigned short)CommentLength); + AuxFragBufOffset = 0; NumFramesConverted++; } else { @@ -306,7 +428,7 @@ int __cdecl wmain(int argc, wchar_t** argv) if (argc == 2 && (!wcscmp(argv[1], L"-v") || !wcscmp(argv[1], L"--version"))) { - printf("etl2pcapng version 1.3.0\n"); + printf("etl2pcapng version 1.4.0\n"); return 0; } diff --git a/src/pcapng.h b/src/pcapng.h index 6a32c9c..ac12ae6 100644 --- a/src/pcapng.h +++ b/src/pcapng.h @@ -27,43 +27,44 @@ Helpers for working with .pcapng files. #include struct PCAPNG_BLOCK_HEAD { - long Type; - long Length; + unsigned long Type; + unsigned long Length; }; struct PCAPNG_SECTION_HEADER_BODY { - long Magic; // endian detection (set this to PCAPNG_SECTION_HEADER_MAGIC) - short MajorVersion; - short MinorVersion; - long long Length; + unsigned long Magic; // endian detection (set this to PCAPNG_SECTION_HEADER_MAGIC) + unsigned short MajorVersion; + unsigned short MinorVersion; + long long Length; }; struct PCAPNG_INTERFACE_DESC_BODY { - short LinkType; - short Reserved; - long SnapLen; + unsigned short LinkType; + unsigned short Reserved; + unsigned long SnapLen; }; struct PCAPNG_ENHANCED_PACKET_BODY { - long InterfaceId; - long TimeStampHigh; - long TimeStampLow; - long CapturedLength; // excludes padding - long PacketLength; // excludes padding - char PacketData[0]; // padded to 4 bytes + unsigned long InterfaceId; + unsigned long TimeStampHigh; + unsigned long TimeStampLow; + unsigned long CapturedLength; // excludes padding + unsigned long PacketLength; // excludes padding + unsigned char PacketData[0]; // padded to 4 bytes }; struct PCAPNG_BLOCK_OPTION_ENDOFOPT { - short Code; // PCAPNG_OPTIONCODE_ENDOFOPT - short Length; // 0 + unsigned short Code; // PCAPNG_OPTIONCODE_ENDOFOPT + unsigned short Length; // 0 }; struct PCAPNG_BLOCK_OPTION_EPB_FLAGS { - short Code; // PCAPNG_OPTIONCODE_EPB_FLAGS - short Length; // 4 - long Value; + unsigned short Code; // PCAPNG_OPTIONCODE_EPB_FLAGS + unsigned short Length; // 4 + unsigned long Value; }; struct PCAPNG_BLOCK_OPTION_COMMENT { - unsigned short Code; // PCAPNG_OPTIONCODE_COMMENT + unsigned short Code; // PCAPNG_OPTIONCODE_COMMENT unsigned short Length; + char Comment[0]; // padded to 4 bytes }; struct PCAPNG_BLOCK_TAIL { - long Length; // Same as PCAPNG_BLOCK_HEAD.Length, for easier backward processing. + unsigned long Length; // Same as PCAPNG_BLOCK_HEAD.Length, for easier backward processing. }; #include @@ -150,6 +151,44 @@ PcapNgWriteInterfaceDesc( return Err; } +inline int +PcapNgWriteCommentOption( + __in HANDLE File, + __in PCHAR CommentBuffer, + __in unsigned short CommentLength + ) +{ + int Err = NO_ERROR; + int CommentPadLength = 4 - (CommentLength % 4 == 0 ? 4 : CommentLength % 4); + struct PCAPNG_BLOCK_OPTION_COMMENT Comment; + char Pad[4] = { 0 }; + + Comment.Code = PCAPNG_OPTIONCODE_COMMENT; + Comment.Length = CommentLength; + + if (!WriteFile(File, &Comment, sizeof(Comment), NULL, NULL)) { + Err = GetLastError(); + printf("WriteFile failed with %u\n", Err); + goto Done; + } + if (!WriteFile(File, CommentBuffer, CommentLength, NULL, NULL)) { + Err = GetLastError(); + printf("WriteFile failed with %u\n", Err); + goto Done; + } + if (CommentPadLength > 0) { + if (!WriteFile(File, Pad, CommentPadLength, NULL, NULL)) { + Err = GetLastError(); + printf("WriteFile failed with %u\n", Err); + goto Done; + } + } + +Done: + + return Err; +} + inline int PcapNgWriteEnhancedPacket( HANDLE File, @@ -159,7 +198,8 @@ PcapNgWriteEnhancedPacket( long IsSend, long TimeStampHigh, // usec (unless if_tsresol is used) long TimeStampLow, - unsigned long ProcessId + char* Comment, + unsigned short CommentLength ) { int Err = NO_ERROR; @@ -167,31 +207,17 @@ PcapNgWriteEnhancedPacket( struct PCAPNG_ENHANCED_PACKET_BODY Body; struct PCAPNG_BLOCK_OPTION_ENDOFOPT EndOption; struct PCAPNG_BLOCK_OPTION_EPB_FLAGS EpbFlagsOption; - struct PCAPNG_BLOCK_OPTION_COMMENT CommentOption; struct PCAPNG_BLOCK_TAIL Tail; char Pad[4] = {0}; -// COMMENT_MAX_SIZE must be multiple of 4 -#define COMMENT_MAX_SIZE 16 - char Comment[COMMENT_MAX_SIZE]; - size_t CommentLength = 0; + BOOLEAN CommentProvided = (CommentLength > 0 && Comment != NULL); int FragPadLength = (4 - ((sizeof(Body) + FragLength) & 3)) & 3; // pad to 4 bytes per the spec. - int TotalLength; - - memset(Comment, 0, COMMENT_MAX_SIZE); - if SUCCEEDED(StringCchPrintfA(Comment, COMMENT_MAX_SIZE, "PID=%d", ProcessId)) { - if FAILED(StringCchLengthA(Comment, COMMENT_MAX_SIZE, &CommentLength)) { - CommentLength = 0; - } - } else { - memset(Comment, 0, COMMENT_MAX_SIZE); - } - CommentOption.Code = PCAPNG_OPTIONCODE_COMMENT; - CommentOption.Length = (unsigned short) CommentLength; - if (CommentOption.Length % 4 != 0) - CommentOption.Length += (4 - CommentOption.Length % 4); - TotalLength = + int TotalLength = sizeof(Head) + sizeof(Body) + FragLength + FragPadLength + - sizeof(EpbFlagsOption) + sizeof(CommentOption) + CommentOption.Length + sizeof(EndOption) + sizeof(Tail); + sizeof(EpbFlagsOption) + sizeof(EndOption) + sizeof(Tail) + + (CommentProvided ? + sizeof(struct PCAPNG_BLOCK_OPTION_COMMENT) + sizeof(EndOption) + CommentLength + + (4 - (CommentLength % 4 == 0 ? 4 : CommentLength % 4)) //Comment Padding + : 0); Head.Type = PCAPNG_BLOCKTYPE_ENHANCED_PACKET; Head.Length = TotalLength; @@ -233,15 +259,15 @@ PcapNgWriteEnhancedPacket( goto Done; } - if (!WriteFile(File, &CommentOption, sizeof(CommentOption), NULL, NULL)) { - Err = GetLastError(); - printf("WriteFile failed with %u\n", Err); - goto Done; - } - if (!WriteFile(File, &Comment, CommentOption.Length, NULL, NULL)) { - Err = GetLastError(); - printf("WriteFile failed with %u\n", Err); - goto Done; + if (CommentProvided) { + Err = PcapNgWriteCommentOption( + File, + Comment, + CommentLength); + if (Err != NO_ERROR) { + printf("WriteFile failed with %u\n", Err); + goto Done; + } } EndOption.Code = PCAPNG_OPTIONCODE_ENDOFOPT;