Every tool this course has built so far acted in the moment: a scanner knocking on a live door, an SSH client running a command right now. This unit works differently. It reads traffic that has already happened, recorded to a file, and asks what it shows.
Analogy: The aircraft black box
When an aircraft is in trouble, investigators do not rely on memory; they read the black box, which recorded everything up to the moment of the incident. A packet capture is a network's black box. Tools such as tcpdump and Wireshark record every frame that crossed an interface during a window of time, writing it to a .pcap file. Long after the attack has ended, an analyst can open that file and replay the whole event, asking a different question of it each time.
Why it carries weight: Why it carries weight
This unit asks more of you than the earlier ones: not just to build a tool or analyse a system, but to evaluate live evidence and judge whether it shows an attack. Being able to read a capture packet by packet is what lets an analyst confirm or refute an automated alert rather than trusting it blindly, which is among the most valued skills a working defender can have.
The tool for the job
Tool in focus: dpkt
dpkt is a fast, lightweight library for reading packet captures and pulling apart each packet layer by layer. dpkt.pcap.Reader reads a .pcap file and hands back each packet's timestamp and raw bytes; dpkt.ethernet.Ethernet, dpkt.ip.IP and dpkt.tcp.TCP then decode each layer. It does no live capture itself, which keeps it small and well suited to offline analysis.
open_capture.py
# open_capture.py: the pattern every script in this unit reuses
import dpkt
def read_packets(pcap_path):
with open(pcap_path, "rb") as f:
for timestamp, raw_bytes in dpkt.pcap.Reader(f):
yield timestamp, raw_bytes
for ts, raw in read_packets("capture.pcap"):
eth = dpkt.ethernet.Ethernet(raw)
print(f"{ts}: frame of {len(raw)} bytes, ethertype {eth.type}")
Sample output, against a small three-packet capture
1792294810.994522: frame of 571 bytes, ethertype 2048
1792294811.004522: frame of 1514 bytes, ethertype 2048
1792294811.014522: frame of 54 bytes, ethertype 2048
ethertype 2048 is 0x0800, the code that means "an IPv4 packet follows". You will see this number again the moment a capture contains something else, such as ARP's 0x0806, which prints as 2054.
Python concept: Generators and yield
read_packets is a generator: instead of reading the whole capture into a list, yield hands back one packet at a time and pauses until the next is asked for. A capture can hold millions of packets, so this keeps memory use flat no matter how large the file is. Every later script in this unit reuses this one generator.
Check your understanding
Question 1: What does dpkt.pcap.Reader hand back for each packet in a capture?
A timestamp and the packet's raw bytes, as a pair. Nothing is decoded yet at this point; the raw bytes still need to be unwrapped layer by layer, which the next article covers.
Question 2: Why is read_packets written as a generator rather than a function that returns a list of every packet?
A capture can hold millions of packets, and building a list of all of them at once would need to hold the entire capture in memory simultaneously. A generator yields one packet at a time and pauses in between, so memory use stays flat no matter how large the file is.
Question 3: This unit asks you to "evaluate" evidence rather than just build a tool. What does that distinction mean in practice?
Building a tool means writing code that performs an action. Evaluating evidence means looking at what a script reports and judging, using your own understanding, whether it genuinely shows an attack, rather than trusting an alert automatically. The unit's later articles build detectors precisely so you can practise forming that judgement yourself.
Key takeaway: Key takeaway
A packet capture is a replayable black box, and dpkt.pcap.Reader is the door into it. Every script in this unit starts from the same generator, read_packets(), which hands back one packet at a time and keeps memory flat regardless of how large the capture is.

