
Read a pcap without Wireshark: headers, flows and TLS
How pcap and pcapng are laid out, where snaplen truncation bites, how to turn packets into flows and endpoints, and what TLS still leaks in a capture.

A Windows executable is a documented container. Every field the loader reads to map the file into memory is one you can read too, long before you decide whether the thing should ever run.
Someone hands you setup_v3_final.exe from a mirror you have never heard of, or a build lands in your release folder and you want to know it is the artifact you think it is. Double-clicking is the one option that cannot be undone. Everything else can: a Windows executable is a documented container called Portable Executable, and every field the loader reads to map the file into memory is a field you can read too.
Be clear about the limit. Static inspection is not a verdict on safety. A clean-looking PE can be malicious and an odd-looking PE is often just an old compiler. Reading headers produces questions, not answers. If the questions do not resolve, the file goes to an isolated sandbox or a multi-engine scanning service, not onto your work machine.
Every PE starts with the two bytes MZ. The 64-byte DOS header that follows ends in e_lfanew, the file offset of the real PE header. Between them sits the DOS stub, a tiny program whose only job is to print "This program cannot be run in DOS mode" if the file is ever launched under DOS. The stub itself is boring. What is occasionally interesting is the gap: some toolchains leave extra data between the stub and the PE signature, and an unexpectedly large gap is a hint that the file was touched after it was linked.
At e_lfanew you find four bytes, PE\0\0, then the 20-byte COFF file header. Four fields there are worth your attention. Machine gives the target architecture: 0x014c for x86, 0x8664 for x64, 0xAA64 for ARM64. A 32-bit binary in 2026 is not suspicious, but it is a fact worth having before you wonder why a driver will not load. NumberOfSections tells you how many rows the section table has, typically four to eight for ordinary compiler output. TimeDateStamp holds the link time as a Unix epoch value. The Characteristics flags say whether the image is an executable or a DLL and whether it handles addresses above 2 GB.
For an image file the optional header is mandatory. Its magic value is 0x10b for PE32 and 0x20b for PE32+. The difference is more than bit width: PE32+ drops the BaseOfData field and widens ImageBase and the stack and heap reserve fields to eight bytes, which is why naive parsers written for 32-bit binaries silently misread 64-bit ones. ImageBase is the module's preferred load address. AddressOfEntryPoint is the RVA of the first instruction to run. Subsystem says whether this is a console program (3), a windowed GUI program (2) or a driver. A file presented as a silent installer but marked as a console subsystem deserves a second look.
After the headers comes one 40-byte record per section: name, virtual size, virtual address, raw size, raw offset and characteristics. The familiar names are .text, .rdata, .data, .rsrc and .reloc. Two things stand out when they go wrong. A section marked writable and executable at the same time is the classic footprint of code that rewrites or unpacks itself. A virtual size far larger than the raw size means the image reserves room at load that is not in the file, which is exactly what a packer needs.
The densest information in a binary is the import table. A PE has to name every DLL and every function it wants resolved, so the loader can wire them up. Within a minute you can tell whether the program speaks to the network, writes to the registry, or spawns other processes.
RegSetValueEx and friends: persistence or configuration writes.That last line matters more than the others. A short import table is rarely a sign of a lean program; it is usually a sign of packing. Opening the file in the EXE and PE viewer puts the headers, section table, imports, resources and per-section entropy on one screen. The parsing happens in your browser from the file bytes, and nothing about the file is executed.
Per-section Shannon entropy is a number between 0 and 8 that measures how unpredictable the bytes are. Compiled x86 code usually sits somewhere around 6.0 to 6.8. Compressed or encrypted data pushes past 7.5. A .text section reading 7.9 is not machine code; it is a blob waiting to be unpacked.
High entropy on its own proves nothing. Embedded PNGs, compressed resources and the digital signature blob are all high entropy. Read it in context: section names such as UPX0 and UPX1, a two-entry import table and a 7.9 code section together tell a consistent story, while any one of them alone does not. Packing is not inherently hostile either, since commercial licensing and anti-tamper products compress and encrypt code for their own reasons.
TimeDateStamp is a plain 32-bit integer sitting in the file. Changing it takes a hex editor and ten seconds. When you see a 1992 date, the conclusion is not "old program" but "this field was zeroed or written by hand". Some toolchains deliberately pin it to a fixed value so that builds are reproducible, which is a good practice that also destroys the field's forensic value. It is still worth reading, because a timestamp that contradicts the resource version or the file's claimed release date is a thread to pull. The timestamp converter turns the raw epoch value into a date you can compare.
One of the optional header's data directories points at the certificate table. A non-empty entry means the file carries a signature blob. It does not mean the signature checks out. Validation requires recomputing the authenticated hash, walking the certificate chain to a trusted root, checking revocation and weighing the countersigned timestamp, and the operating system is the component that does all of that. When an inspection tool says "signed", read it as "a signature block exists here". Even a signature that validates only proves who signed the file; stolen and abused code-signing certificates are a recurring problem, and a valid signature says nothing about what the code does.
Pulling printable text out of a binary is easy and habit-forming. The trouble is that most of those strings are not addressed to you. Compiler runtime messages, assert text, licence blocks in embedded resources and accidental runs of ASCII inside compressed data all pad the list. Seeing a domain name does not mean the program contacts it; the string may sit in a data section no code path ever reaches. The inverse is worse: a program that builds its destination at runtime from fragments has perfectly innocent strings.
Treat strings as a hypothesis generator. If a URL or an address looks interesting, check it separately with whois and DNS lookup, and remember that the only way to learn where a program actually connects is to run it under observation and read the resulting capture.
Most of the time this ends the question without the file ever running. If you cannot explain why a PDF converter needs process creation and raw sockets, do not look for the answer on your laptop. The same discipline shows up elsewhere in this toolkit: decoding a JWT is not verifying it, and reading a PE header is not clearing a binary.
No. Parsing a file means reading its bytes; no code from the binary executes. The realistic risks are accidentally double-clicking it while you work and archives that contain auto-running components. Keep the sample in its own folder and consider renaming the extension while you study it.
It does not. Compressed resources, embedded images, signature blobs and commercial protection layers are all high entropy. Entropy only becomes meaningful alongside a very short import table, unusual section names and sections that are both writable and executable.
Not as evidence. TimeDateStamp is a plain integer in the file and trivial to forge, and some toolchains pin it to a constant so that builds are reproducible. Record it as a hint and corroborate it against version resources and the claimed release date.
A present signature block is not a validated signature. Validation means recomputing the hash, chaining the certificate to a trusted root and checking revocation, which is the operating system's job. Even a valid signature identifies the signer, not the behaviour of the code.
Stop when the imports and resources do not match the job the file claims to do, or when packing hides the interior. The next step is detonation in an isolated sandbox or submission to a multi-engine scanner, never a trial run on a working machine.

How pcap and pcapng are laid out, where snaplen truncation bites, how to turn packets into flows and endpoints, and what TLS still leaks in a capture.

JWT structure and base64url, the alg and kid headers, the none and algorithm-confusion attacks, expiry and clock skew, and how to debug a token without leaking it.