dpkt and impacket
dpkt is a lightweight, fast Python library for low-level pcap parsing with no external dependencies — ideal for high-performance iteration through millions of packets. impacket is a collection of Python classes for working with network protocols at the application layer, specifically SMB, NTLM, and Kerberos — the same library that powers tools like psexec.py and secretsdump.py. Together they give you both speed and depth for forensic network analysis.
You need two things from a large capture: (1) fast extraction of every IP/port pair to build a network map — dpkt handles this in seconds with minimal overhead, (2) extraction of every NTLMv2 hash from NTLM authentication exchanges in SMB traffic — impacket reconstructs the full NTLM exchange at the application layer and gives you the hash in hashcat-ready format. Different tools for different depths.
dpkt — Fast Low-Level Parsing
import dpkt
import socket
import struct
from collections import defaultdict
def ip_to_str(addr: bytes) -> str:
return socket.inet_ntoa(addr)
# Open and iterate pcap — streaming, memory-efficient
connections = defaultdict(int) # (src, dst, dstport) → count
dns_names = set()
with open("capture.pcap", "rb") as f:
pcap = dpkt.pcap.Reader(f)
for ts, buf in pcap:
try:
# Parse Ethernet frame
eth = dpkt.ethernet.Ethernet(buf)
# Skip non-IP frames
if not isinstance(eth.data, dpkt.ip.IP):
continue
ip = eth.data
src = ip_to_str(ip.src)
dst = ip_to_str(ip.dst)
# TCP analysis
if isinstance(ip.data, dpkt.tcp.TCP):
tcp = ip.data
dport = tcp.dport
sport = tcp.sport
flags = tcp.flags
# SYN flag = new connection
if flags & dpkt.tcp.TH_SYN and not (flags & dpkt.tcp.TH_ACK):
connections[(src, dst, dport)] += 1
# UDP + DNS analysis
if isinstance(ip.data, dpkt.udp.UDP):
udp = ip.data
if udp.dport == 53 or udp.sport == 53:
try:
dns = dpkt.dns.DNS(udp.data)
if dns.qr == dpkt.dns.DNS_Q: # query, not response
for q in dns.qd:
dns_names.add(q.name)
except Exception:
pass
except Exception:
pass
# Top destinations
print("Top 20 connection destinations:")
for (src, dst, port), count in sorted(connections.items(), key=lambda x: -x[1])[:20]:
print(f" {src} → {dst}:{port} ({count} SYN packets)")
print(f"\nTotal unique DNS names: {len(dns_names)}")
for name in sorted(dns_names)[:20]:
print(f" {name}")
impacket — NTLM Hash Extraction
#!/usr/bin/env python3
"""
Extract NTLMv2 hashes from PCAP for offline cracking.
Output format: hashcat mode -m 5600 (NTLMv2)
Requires: pip install impacket dpkt
"""
import dpkt
import socket
import struct
from impacket.ntlm import NTLMAuthNegotiate, NTLMAuthChallenge, NTLMAuthAuthenticate
NTLMSSP_MAGIC = b"NTLMSSP\x00"
def ip_to_str(addr):
return socket.inet_ntoa(addr)
def find_ntlmssp(data: bytes) -> list[int]:
"""Find all NTLMSSP magic bytes offsets in data."""
positions = []
offset = 0
while True:
pos = data.find(NTLMSSP_MAGIC, offset)
if pos == -1:
break
positions.append(pos)
offset = pos + 1
return positions
# Track NTLM state: (src, dst) → {challenge: bytes, negotiate: bytes}
ntlm_state = {}
hashes = []
with open("smb_traffic.pcap", "rb") as f:
pcap = dpkt.pcap.Reader(f)
for ts, buf in pcap:
try:
eth = dpkt.ethernet.Ethernet(buf)
if not isinstance(eth.data, dpkt.ip.IP):
continue
ip = eth.data
if not isinstance(ip.data, dpkt.tcp.TCP):
continue
tcp = ip.data
payload = bytes(tcp.data)
if not payload:
continue
src = ip_to_str(ip.src)
dst = ip_to_str(ip.dst)
# Look for NTLMSSP in TCP payload
offsets = find_ntlmssp(payload)
for offset in offsets:
ntlm_data = payload[offset:]
msg_type = struct.unpack_from(" 24:
# NTLMv2 response (> 24 bytes)
key = (src, dst)
if key in ntlm_state and "challenge" in ntlm_state[key]:
server_challenge = ntlm_state[key]["challenge"]
ntproofstr = nt_response[:16].hex()
blob = nt_response[16:].hex()
sc_hex = server_challenge.hex()
# hashcat NTLMv2 format
hashcat_hash = (
f"{username}::{domain}:{sc_hex}:{ntproofstr}:{blob}"
)
hashes.append(hashcat_hash)
print(f"[+] NTLMv2 hash: {username}@{domain} ({src}→{dst})")
except Exception:
pass
except Exception:
pass
# Write hashes for hashcat
if hashes:
with open("ntlm_hashes.txt", "w") as f:
f.write("\n".join(set(hashes)) + "\n")
print(f"\n[+] Wrote {len(set(hashes))} unique NTLMv2 hashes to ntlm_hashes.txt")
print("Crack with: hashcat -m 5600 ntlm_hashes.txt wordlist.txt")
dpkt gives you the fastest Python path from PCAP to raw packet fields — it parses Ethernet/IP/TCP/UDP headers with minimal overhead and gives you raw payload bytes. For extracting 10 million IP addresses from a large capture, dpkt finishes in under a minute. impacket, by contrast, implements full application-layer protocol state machines — the NTLM authentication state machine requires tracking the negotiate, challenge, and authenticate messages across three separate packets and matching them by conversation. You can't do that with dpkt alone without building the state machine yourself. impacket already has the state machine. Use dpkt for anything that fits in a per-packet analysis. Use impacket when the analysis requires multi-packet protocol state.
Q & A
Q: The dpkt loop is throwing exceptions on many packets. Should I catch them all or investigate each one?
In a production forensics script, catch all exceptions at the packet level with a broad except Exception: pass after logging to a counter. Reasons packets fail dpkt parsing: (1) Corrupted packets — PCAP has checksum errors or truncated captures, (2) Non-Ethernet encapsulation — VLAN tags (802.1Q) require dpkt.ethernet.Ethernet to be configured for VLAN parsing, (3) Tunneled traffic — GRE, L2TP, or VPN encapsulation adds headers dpkt doesn't strip automatically, (4) Jumbo frames — frames larger than standard MTU that require specific handling. Track the exception count: if 0.1% of packets fail, that's noise. If 20% fail, your code has a structural issue (wrong link layer type — check the pcap global header's network field: 1=Ethernet, but some captures use 228=IPv4-only and need different parsing). Use pcap.datalink() to check the link type and adjust your parsing accordingly.