patterns/PcapNG: Rewrite hexpat for bugfixing (#462)

PcapNG: Rewrite hexpat.

Co-authored-by: Nik <werwolv98@gmail.com>
This commit is contained in:
Antonio Vazquez
2025-12-05 22:15:28 +01:00
committed by GitHub
parent 681b208aab
commit 998655f74a

View File

@@ -1,39 +1,27 @@
#pragma description PcapNG
#pragma MIME application/vnd.tcpdump.pcapng
#pragma author 5h4rrK
import std.mem;
import std.core;
import std.math;
import std.io;
#pragma author Antonio Vázquez Blanco
#define MAX_VALUE_U64 0xffffffffffffffff
fn format_version(auto version){
return std::format(
"{}.{}", version.major, version.minor
);
};
fn is_valid_len(auto val){
if (val == MAX_VALUE_U64){
return std::format("[Not Specified]");
}
else{
return val;
}
};
import std.mem;
import std.core;
import std.io;
enum PcapOrder : u32{
Little = 0x1a2b3c4d,
Big = 0x4d3c2b1a,
};
struct Version {
u16 major;
u16 minor;
};
// Perform a read to get the endianness of the file and set
// the default endianness acordingly.
u32 order = std::mem::read_unsigned(0x8, 0x4);
if (order == PcapOrder::Little) {
std::core::set_endian(std::mem::Endian::Little);
} else {
std::core::set_endian(std::mem::Endian::Big);
}
enum BlockType : u32{
// Mandatory Blocks
@@ -79,6 +67,119 @@ enum BlockType : u32{
};
enum SHBType : u16 {
EndOfOpt = 0x0,
Hardware = 0x2,
OS = 0x3,
Application = 0x4,
EndOfOpt = 0x0
};
enum IDHType : u16 {
EndOfOpt = 0,
Name = 2, // UTF-8 device name (e.g., "eth0")
Description = 3, // UTF-8 device description
IPv4Addr = 4, // IPv4 address + netmask (8 bytes)
IPv6Addr = 5, // IPv6 address + prefix len (17 bytes)
MACAddr = 6, // MAC address (6 bytes)
EUIAddr = 7, // EUI-64 address (8 bytes)
Speed = 8, // Interface speed (bps, 8 bytes)
TimestampRes = 9, // Timestamp resolution (1 byte)
TimeZone = 10, // Time zone (4 bytes)
Filter = 11, // Capture filter string
OS = 12, // OS name (UTF-8 string)
FCSLength = 13, // Frame Check Sequence length (1 byte)
TimestampOffset = 14, // Timestamp offset (8 bytes)
Hardware = 15, // Variable length
TxSpeed = 16, // 8bytes
RxSpeed = 17 // 8bytes
};
enum NameResolutionType: u16 {
EndOfRecord = 0x00,
IPv4 = 0x01,
IPv6 = 0x02
};
enum InterfaceStatsType : u16 {
EndOfOpt = 0,
Comment = 1, // isb_comment
StartTime = 2, // isb_starttime
EndTime = 3, // isb_endtime
IfRecv = 4, // isb_ifrecv
IfDrop = 5, // isb_ifdrop
FilterAccept = 6, // isb_filteraccept
OSdrop = 7, // isb_osdrop
UserDeliver = 8, // isb_usrdeliv
};
enum EnhancedPacketType : u16 {
EndOfOpt = 0x0,
Flags = 0x2,
Hash = 0x3,
DropCount = 0x4,
PacketId = 0x5,
Queue = 0x6,
Verdict = 0x7,
};
enum PacketBlockType : u16 {
Flags = 0x2,
Hash = 0x3,
EndOfOpt = 0x0,
};
struct Option {
if (this.parent.block_type == BlockType::SectionHeader) {
SHBType option_type;
} else if(this.parent.block_type == BlockType::InterfaceDesc) {
IDHType option_type;
} else if (this.parent.block_type == BlockType::NameResolution) {
NameResolutionType record_type[[name("record")]];
} else if (this.parent.block_type == BlockType::InterfaceStats){
InterfaceStatsType option_type;
} else if (this.parent.block_type == BlockType::EnhancedPacket) {
EnhancedPacketType option_type;
} else if (this.parent.block_type == BlockType::Packet){
PacketBlockType option_type;
}
u16 option_len;
if (option_len > 0){
char data[option_len];
} else{
return;
}
u8 pad_size = (4 - ( $ % 4 )) % 4;
$ = $ + pad_size;
};
struct Version {
u16 major;
u16 minor;
};
fn format_version(auto version){
return std::format(
"{}.{}", version.major, version.minor
);
};
fn is_valid_len(auto val){
if (val == MAX_VALUE_U64){
return std::format("[Not Specified]");
}
else{
return val;
}
};
struct SectionHeaderBlockBody {
PcapOrder sectionbyteorder;
Version version [[name("Version"), format("format_version")]];
u64 section_len [[name("SectionLen"), format("is_valid_len")]];
};
enum LinkType : u16 {
LINKTYPE_NULL = 0,
LINKTYPE_ETHERNET = 1,
@@ -196,335 +297,104 @@ enum LinkType : u16 {
LINKTYPE_ETW = 290
};
enum SHBType : u16{
EndOfOpt = 0x0,
Hardware = 0x2,
OS = 0x3,
Application = 0x4,
EndOfOpt = 0x0
};
enum IDHType : u16 {
EndOfOpt = 0,
Name = 2, // UTF-8 device name (e.g., "eth0")
Description = 3, // UTF-8 device description
IPv4Addr = 4, // IPv4 address + netmask (8 bytes)
IPv6Addr = 5, // IPv6 address + prefix len (17 bytes)
MACAddr = 6, // MAC address (6 bytes)
EUIAddr = 7, // EUI-64 address (8 bytes)
Speed = 8, // Interface speed (bps, 8 bytes)
TimestampRes = 9, // Timestamp resolution (1 byte)
TimeZone = 10, // Time zone (4 bytes)
Filter = 11, // Capture filter string
OS = 12, // OS name (UTF-8 string)
FCSLength = 13, // Frame Check Sequence length (1 byte)
TimestampOffset = 14, // Timestamp offset (8 bytes)
Hardware = 15, // Variable length
TxSpeed = 16, // 8bytes
RxSpeed = 17 // 8bytes
};
enum NameResolutionType: u16 {
EndOfRecord = 0x00,
IPv4 = 0x01,
IPv6 = 0x02
};
enum InterfaceStatsType : u16 {
EndOfOpt = 0,
Comment = 1, // isb_comment
StartTime = 2, // isb_starttime
EndTime = 3, // isb_endtime
IfRecv = 4, // isb_ifrecv
IfDrop = 5, // isb_ifdrop
FilterAccept = 6, // isb_filteraccept
OSdrop = 7, // isb_osdrop
UserDeliver = 8, // isb_usrdeliv
};
enum EnhancedPacketType : u16 {
EndOfOpt = 0x0,
Flags = 0x2,
Hash = 0x3,
DropCount = 0x4,
PacketId = 0x5,
Queue = 0x6,
Verdict = 0x7,
};
enum PacketBlockType : u16 {
Flags = 0x2,
Hash = 0x3,
EndOfOpt = 0x0,
};
struct Option {
if (this.parent.block_type == BlockType::SectionHeader) {
SHBType option_type;
} else if(this.parent.block_type == BlockType::InterfaceDesc) {
IDHType option_type;
} else if (this.parent.block_type == BlockType::NameResolution) {
NameResolutionType record_type[[name("record")]];
} else if (this.parent.block_type == BlockType::InterfaceStats){
InterfaceStatsType option_type;
} else if (this.parent.block_type == BlockType::EnhancedPacket) {
EnhancedPacketType option_type;
} else if (this.parent.block_type == BlockType::Packet){
PacketBlockType option_type;
}
u16 option_len;
if (option_len > 0){
char data[option_len];
} else{
return;
}
// u8 pad_size = (4 - ( $ % 4 ));
u8 pad_size = (4 - ( $ % 4 )) % 4;
// if(this.parent.block_type == BlockType::InterfaceStats) {
// std::print("Current Pos {:#x} {:#x}",$, pad_size);
// }
$ = $ + pad_size;
};
struct SectionHeaderBlock {
BlockType block_type;
u32 length;
PcapOrder sectionbyteorder;
Version version [[name("Version"), format("format_version")]];
u64 section_len [[name("SectionLen"), format("is_valid_len")]];
u64 prev_pos = $;
Option options [ while( $ < (prev_pos + length - 28))];
u32 block_length [[name("BlockLen")]];
};
struct NameResolutionBlock {
BlockType block_type[[name("BlockType")]];
u32 block_len1[[name("BlockLen1")]];
u64 prev_pos = $;
Option records [ while($ < (prev_pos + block_len1 - 12)) ];
u32 block_len2[[name("BlockLen2")]];
};
struct SystemJournalExportBlock{
BlockType block_type[[name("BlockType")]];
u32 block_len1[[name("BlockLen1")]];
char data[block_len1];
$ = $ + (4 - ($ % 4 ) ); // Padding
u32 block_len2[[name("BlockLen2")]];
};
struct CustomBlock{
BlockType block_type[[name("BlockType")]];
u32 block_len1[[name("BlockLen1")]];
u32 pen[[name("PrivateEnterpriseNumber")]];
char data[block_len1];
u64 prev_pos = $;
$ = $ + (4 - ($ % 4 ) ); // Padding
Option options [while($ < (prev_pos + block_len1 - 16))] [[name("Options")]];
u32 block_len2[[name("BlockLen2")]];
};
struct InterfaceBlock{
BlockType block_type[[name("BlockType")]];
u32 block_len1[[name("BlockLen1")]];
struct InterfaceDescBlockBody {
LinkType link_type [[name("LinkType")]];
u16 reserved [[name("Reserved")]];
u32 snap_len [[name("SnapLen")]];
u64 prev_pos = $;
Option options [ while( $ < (prev_pos + block_len1 - 20))];
u32 block_len2[[name("BlockLen2")]];
};
struct EnhancedPacketBlock{
BlockType block_type[[name("BlockType")]];
u32 block_len1[[name("BlockLen1")]];
struct EnhancedPacketBlockBody {
u32 interface_id[[name("InterfaceID")]];
u64 timestamp[[name("Timestamp")]];
u32 captured_len[[name("CapturedLen")]];
u32 pkt_len[[name("PacketLen")]];
char data[captured_len][[name("Data")]];
// 32 (BlockType + BlockLen1 + InterfaceID + Timestamp + CapturedLen + PacketLen + BlockLen2)
$ = $ + (block_len1 - captured_len - 32);
u64 prev_pos = $;
// Current Pos - Prev Pos - 4 (BlockLen2)
// std::print("Option Size :: {:#x}", ($ - prev_pos));
if (($ - prev_pos ) > 0) {
Option options [ while( $ < ( prev_pos + ($ - prev_pos ) ) ) ];
}
u32 block_len2[[name("BlockLen2")]];
};
struct SimplePacketBlock {
BlockType block_type[[name("BlockType")]];
u32 block_len1[[name("BlockLen1")]];
struct NameResolutionBlockBody {
// This block is empty, only contains options.
};
struct InterfaceStatsBlock {
u32 interface_id[[name("InterfaceID")]];
u64 timestamp[[name("Timestamp")]];
};
struct SimplePacketBlockBody {
u32 pkt_len[[name("PacketLen")]];
char data[[name("Data")]];
u8 pad_size = (4 - ( $ % 4 )) % 4;
$ = $ + pad_size;
u32 block_len2[[name("BlockLen2")]];
};
// Obsolete!
struct PacketBlock {
BlockType block_type[[name("BlockType")]];
u32 block_len1[[name("BlockLen1")]];
struct PacketBlockBody {
std::print("Obsolete Packet Block Body at offset {:#x}\n", $);
u16 interface_id[[name("InterfaceID")]];
u16 drop_count[[name("DropCount")]];
u64 timestamp[[name("Timestamp")]];
u32 captured_len[[name("CapturedLen")]];
u32 pkt_len[[name("PacketLen")]];
if (block_len1 > 32){
if (parent.block_body_len > 20){
char data[captured_len];
}
u32 block_len2[[name("BlockLen2")]];
};
enum CompressionType: u8 {
Uncompressed = 0x0,
LempelZiv = 0x1,
Gzip = 0x2
struct CustomBlockBody {
u32 pen[[name("PrivateEnterpriseNumber")]];
char data[parent.block_body_len-4];
};
enum EncryptionType : u32 {
TLS = 0x544c534b, /* TLS Key Log */
SSH = 0x5353484b, /* SSH Key Log */
WIREGUARD = 0x57474b4c, /* WireGuard Key Log */
ZIGBEE_NWK_KEY = 0x5a4e574b, /* Zigbee NWK Key */
ZIGBEE_APS_KEY = 0x5a415053 /* Zigbee APS Key */
TLS = 0x544c534b, // TLS Key Log
SSH = 0x5353484b, // SSH Key Log
WIREGUARD = 0x57474b4c, // WireGuard Key Log
ZIGBEE_NWK_KEY = 0x5a4e574b, // Zigbee NWK Key
ZIGBEE_APS_KEY = 0x5a415053 // Zigbee APS Key
};
enum FixedLengthType : u8 {
// Experimental
};
// Experimental
struct CompressionBlock{
BlockType block_type[[name("BlockType")]];
u32 block_len1[[name("BlockLen1")]];
CompressionType comp_type [[name("Compresssion")]];
char data[block_len1];
u32 block_len2[[name("BlockLen2")]];
};
struct EncryptionBlock{
BlockType block_type[[name("BlockType")]];
u32 block_len1[[name("BlockLen1")]];
struct EncryptionBlockBody {
EncryptionType comp_type [[name("Encryption")]];
u32 secrets_len[[name("SecretsLength")]];
char data[secrets_len];
padding[-$ & 3];
// https://ietf-opsawg-wg.github.io/draft-ietf-opsawg-pcap/draft-ietf-opsawg-pcapng.html#section-4.7-6.6.1
// No DSB-specific options are currently defined
// Option options;
u32 block_len2[[name("BlockLen2")]];
};
// Experimental
struct FixedLengthBlock {
struct Block {
BlockType block_type[[name("BlockType")]];
u32 block_len1[[name("BlockLen1")]];
FixedLengthType comp_type [[name("FixedLength")]];
char data[block_len1];
u32 block_len2[[name("BlockLen2")]];
};
struct InterfaceStatsBlock {
BlockType block_type[[name("BlockType")]];
u32 block_len1[[name("BlockLen1")]];
u32 interface_id[[name("InterfaceID")]];
u64 timestamp[[name("Timestamp")]];
if(block_len1 > 24) {
u64 prev_pos = $;
Option options [ while( $ < (prev_pos + block_len1 - 24))];
}
u32 block_len2[[name("BlockLen2")]];
};
struct PCAPng{
if (std::mem::eof()){
break;
}
if (order == PcapOrder::Little){
le u32 block_type = std::mem::read_unsigned($, 0x4, std::mem::Endian::Little);
} else {
be u32 block_type = std::mem::read_unsigned($, 0x4, std::mem::Endian::Big);
}
// std::print("{} , {:#x}, {:#x}", "Parsing", $, block_type);
// std::print("Parsing : Offset({:#x}), Order({:#x}), BlockType({:#x})", $, order, block_type);
// Block body
u32 block_body_len = block_len1-12;
u64 block_body_end = $ + block_body_len;
if (block_type == BlockType::SectionHeader) {
if (order == PcapOrder::Little) {
le SectionHeaderBlock SHB;
} else {
be SectionHeaderBlock SHB;
}
SectionHeaderBlockBody SHB;
} else if (block_type == BlockType::InterfaceDesc) {
if (order == PcapOrder::Little) {
le InterfaceBlock IDH;
} else {
be InterfaceBlock IDH;
}
InterfaceDescBlockBody IDH;
} else if (block_type == BlockType::EnhancedPacket) {
if (order == PcapOrder::Little) {
le EnhancedPacketBlock EBP;
} else {
be EnhancedPacketBlock EBP;
}
EnhancedPacketBlockBody EBP;
} else if (block_type == BlockType::NameResolution) {
if (order == PcapOrder::Little) {
le NameResolutionBlock NRB;
} else {
be NameResolutionBlock NRB;
}
NameResolutionBlockBody NRB;
} else if (block_type == BlockType::InterfaceStats) {
if (order == PcapOrder::Little) {
le InterfaceStatsBlock ISB;
} else {
be InterfaceStatsBlock ISB;
}
InterfaceStatsBlock ISB;
} else if (block_type == BlockType::SimplePacket) {
if (order == PcapOrder::Little) {
le SimplePacketBlock SPB;
} else {
be SimplePacketBlock SPB;
}
SimplePacketBlockBody SPB;
} else if (block_type == BlockType::Packet) {
if (order == PcapOrder::Little) {
le PacketBlock PB;
} else {
be PacketBlock PB;
}
PacketBlockBody PB;
} else if (block_type == BlockType::Custom) {
if (order == PcapOrder::Little) {
le CustomBlock CB;
} else {
be CustomBlock CB;
}
CustomBlockBody CB;
} else if (block_type == BlockType::CustomNoCopy) {
if (order == PcapOrder::Little) {
le CustomBlock CBN;
} else {
be CustomBlock CBN;
}
CustomBlockBody CBN;
} else if (block_type == BlockType::Decryption) {
if (order == PcapOrder::Little) {
le EncryptionBlock DSB;
} else {
be EncryptionBlock DSB;
}
EncryptionBlockBody DSB;
} else {
std::print("Unknown BlockType at offset {:#x}\n", $);
break;
char block[block_body_len][[name("Block Body Data")]];
std::print("Unknown Block type at offset {:#x}\n", $);
}
} [[inline]];
// Block body must be aligned to 32 bit (4 byte)
std::mem::AlignTo<4>;
// Block options
Option options [ while( $ < block_body_end)];
u32 block_len2[[name("BlockLen2")]];
if (block_len1 != block_len2) {
std::print("Inconsistent Block size at offset {:#x}\n", $);
}
};
u32 order = std::mem::read_unsigned(0x8, 0x4);
PCAPng PcapNG [while(!std::mem::eof())] @0x00;
Block PcapNG [while(!std::mem::eof())] @0x00;