skip to content

Protocol Dissectors

Dissectors turn bytes into named fields layer by layer, picked by port tables or heuristics, and Decode As or a Lua dissector fixes the rest. Interviewers ask why traffic on 8443 shows as bare TCP.

on this pageshow

explore

questions

5

In Wireshark 4.6, a service on TCP 8443 shows as TCP carrying Data; how is a payload dissector picked, and how do you fix it?

level: middleimportance: must knowfreq 30%

answer

  1. tables first, guesses second
  2. a table keyed by port number
  3. Data means nobody claimed it
  4. Analyze > Decode As, then Save
  5. tshark -d tcp.port==8443,<proto>

basics

~20 s

Wireshark offers the TCP payload to the dissector the tcp.port table maps to that port, then to heuristic dissectors; if none claims it, the bytes fall to Data. Analyze > Decode As maps port 8443 to the right dissector.

solid answer

~50 s

Most dissectors register either in a **dissector table** keyed by a lower-layer value (`ethertype`, `ip.proto`, `tcp.port`, `udp.port`, `tls.port`) or as a **heuristic** that inspects the payload's first bytes. For a TCP payload, Wireshark 4.6 tries the dissector already pinned to the conversation, then any port changed with Decode As, then the default `tcp.port` entries (the server port seen in the SYN, then the lower port, then the higher), then the TCP heuristics, and finally `Data`. So `Data` on 8443 means no table entry and no heuristic recognised the bytes. The fix is **Analyze > Decode As…**: add an entry for TCP port 8443, set *Current* to the right protocol and press **Save**, which writes it to the profile's `decode_as_entries` file. For a one-off tshark run, `-d tcp.port==8443,http2` does the same. Once it decodes, that protocol's fields exist and can be filtered.

go deeper

for a junior

Recall that Data means no dissector was chosen, and that Analyze > Decode As maps a port to a protocol. Know to press Save if the mapping must survive a restart.

for a middle

Explain the selection order for a TCP payload: conversation dissector, Decode As ports, default port entries, heuristics, then Data. Show where the saved entry lives and the tshark -d equivalent.

for a senior

Diagnose why a mapping only half-works: pinned conversations, segments TCP never passes up, TLS hiding the inner protocol. Make the setting reproducible for the team through a shared profile or a scripted tshark -d.

for a principal

Weigh standardising service ports against shipping analysis profiles with Decode As entries, so incident responders are not decoding in-house services by hand under time pressure.

## What a dissector is A **dissector** is the piece of Wireshark that understands one protocol. It reads that protocol's header, adds named fields to the packet-details tree, and hands the remaining bytes to the next dissector up. A TCP packet over Ethernet is dissected as a chain: the Ethernet dissector reads its header and uses the type value 0x0800 to call IP; IP uses its protocol number to call TCP; TCP must then decide who gets the payload. When nobody is chosen, Wireshark shows the remainder as a **Data** layer of raw bytes. Seeing `Data` on a known service is therefore not a statement about the traffic. It is a statement that Wireshark did not know which dissector to call. ## The two ways a dissector registers | Mechanism | How it works | Typical keys | |---|---|---| | **Dissector table** (static) | A dissector registers under a value of a lower-layer field; the lower layer looks the value up | `ethertype`, `ip.proto`, `tcp.port`, `udp.port`, `tls.port`, `tls.alpn` | | **Heuristic list** | A dissector registers a test function; the lower layer asks each one whether the bytes look like its protocol | lists named after the carrier, such as `tcp` and `udp` | A dissector can also be called directly by name from another dissector, or attached to a **conversation** so that every later packet of one connection goes straight to it; both show up in the order below. Tables are fast and certain when the convention holds, and wrong when it does not: HTTP on a port HTTP never registered is invisible to the table. Heuristics cover that gap, but only for protocols that ship one and only when their test matches. `tshark -G dissector-tables` lists every table and whether it supports Decode As; `tshark -G decodes` lists the current port-to-protocol associations. ## The order for a TCP payload in Wireshark 4.6 The TCP dissector's selection routine tries, in this order: 1. A dissector already attached to the **conversation**, for example one a heuristic pinned earlier in the same connection. 2. Any port whose table entry was **changed with Decode As**: the server port seen in the SYN or SYN/ACK, then the lower port, then the higher. 3. The **heuristic** list, only if the TCP preference *Try heuristic sub-dissectors first* is on (it is off by default). 4. The **default** `tcp.port` entries: server port, then the lower port, then the higher. Preferring the lower port makes both directions pick the same dissector and favours well-known ports over ephemeral ones. 5. The **heuristic** list, in its normal position. 6. `Data`. Two practical consequences follow. TLS or plain HTTP/1.x on an odd port are usually still recognised, because the TLS and HTTP heuristics over TCP are enabled by default and match record headers or an `HTTP/1.` request or status line. What lands in `Data` is a protocol with no heuristic, a heuristic that is switched off, or one whose signature the capture never contained. ## Fixing it with Decode As - Open **Analyze > Decode As…**, or right-click a packet and choose Decode As so the entry is pre-filled from that packet. - The entry has a **Field** (here TCP port), a **Value** (8443), a read-only **Default** column showing what would normally be called, and **Current**, the dissector you want instead. - *Current* only offers protocols the carrier can carry directly: you cannot hand a TCP payload to the Ethernet dissector. - **OK** applies the mapping for this session; **Save** writes it to the `decode_as_entries` file of the current configuration profile. Unsaved entries are lost on exit or profile change. - *Current* may also be `(none)`, which stops a wrong dissector claiming a port and leaves the bytes as `Data`. - On the command line, `tshark -d tcp.port==8443,http2` applies the same override for one run; a range such as `tcp.port==8888-8890,http` also works, and an invalid selector makes tshark print the valid ones. A saved Decode As entry for a port is also applied to that protocol's own port-range preference where it has one. HTTP's default TCP range, for instance, is `80,3128,3132,5985,8080,8088,11371,1900,2869,2710`, so adding 8443 there is the other persistent route. ## What changes once it decodes The chosen dissector creates that protocol's **fields**, such as `http.request.method` or `http2.streamid`. Those names exist in a packet only when the dissector ran on it, so a filter on them silently matches nothing while the traffic is still `Data`. Decode As is an analysis setting, not an edit to the capture: the pcapng file is unchanged, and a colleague opening it with a different profile sees `Data` again. If the payload is TLS without keys, Decode As cannot reveal the inner protocol; decryption is a separate step.

  • Decode As on port 8443 is set, yet some packets in that connection still show as Data. Why?
    Not every segment reaches a payload dissector. TCP hands keep-alive payload straight to `Data`, and with the TCP preference *Do not call subdissectors for error packets* (on by default) retransmitted or out-of-order segments are not passed up either. And if something attached a dissector to the conversation itself, such as a heuristic that matched or another protocol that set the connection up, that dissector is tried before any Decode As port.
  • Should you use Decode As or edit the protocol's port preference?
    They feed the same `tcp.port` table. Decode As works for any protocol that supports it, offers `(none)` to suppress a wrong dissector, and is pre-filled from a selected packet. A protocol's port-range preference is convenient when it listens on several ports, and a saved Decode As entry for a port is applied to that preference anyway. Both live in the current profile, not in the capture file.

saying these in an interview costs you the question

  • Wireshark identifies every protocol from content, so the port number never matters.
  • Data in the packet list means the payload is encrypted.
  • Pressing OK in Decode As keeps the mapping for the next session.
  • Decode As rewrites the capture file, so colleagues see the same decoding.
  • Any dissector can be chosen for any layer in Decode As.
open as a page

What does a [Malformed Packet] entry in Wireshark's packet list mean, and how do you work out what caused it?

level: juniorimportance: should knowfreq 18%

basics

~20 s

A dissector tried to read past the data it was given and threw, so Wireshark stopped decoding that protocol. The cause can be the wrong dissector, an unreassembled message, genuinely broken bytes, or a dissector bug.

open as a page

What is a heuristic dissector in Wireshark 4.6, and why might one miss traffic it should recognise or claim traffic it should not?

level: middleimportance: should knowfreq 14%

basics

~20 s

A heuristic dissector recognises its protocol from the payload's first bytes rather than a port. It misses when it is disabled, a registered port is tried first, or its signature was never captured; it misclaims when unrelated bytes happen to fit.

open as a page

In Wireshark 4.6, what does disabling a protocol in Analyze > Enabled Protocols change, and when is it the right tool rather than Decode As?

level: middleimportance: nice to knowfreq 8%

basics

~20 s

Disabling a protocol stops Wireshark calling its dissector and its heuristics anywhere, so that layer and the layers it would have decoded disappear. Use it when a dissector misclaims or misbehaves across many ports; use Decode As to retarget one port.

open as a page

For an in-house binary protocol on TCP port 9410, what does a minimal Wireshark 4.6 Lua dissector need, and how do you load it?

level: seniorimportance: nice to knowfreq 6%

basics

~20 s

A Proto object, ProtoField definitions assigned to its fields table, a dissector function that adds them to the tree, and DissectorTable.get("tcp.port"):add(9410, proto). The .lua file goes in the personal plugins folder and loads at startup.

open as a page