Wireshark blog post: 2021-01-18 rework (#598)
* Wireshark blog post: 2021-01-18 rework + 01-20 edits * Tweak about preloading of msg defs from .proto Co-authored-by: huangqiangxiong <qiangxiong.huang@qq.com> * More updates following review + new images Co-authored-by: huangqiangxiong <qiangxiong.huang@qq.com>
| 
						 | 
				
			
			@ -1,21 +1,22 @@
 | 
			
		|||
---
 | 
			
		||||
draft: true
 | 
			
		||||
spelling: cSpell:ignore addressbook Huang pcapng Qiangxiong subdissectors tcpdump Wireshark
 | 
			
		||||
spelling: cSpell:ignore addressbook Chalin Huang pcapng Qiangxiong subdissectors tcpdump Wireshark
 | 
			
		||||
title: Analyzing gRPC messages using Wireshark
 | 
			
		||||
date: 2020-12-04
 | 
			
		||||
author:
 | 
			
		||||
  name: Huang Qiangxiong
 | 
			
		||||
date: 2021-01-20
 | 
			
		||||
authors:
 | 
			
		||||
- name: Huang Qiangxiong
 | 
			
		||||
  link: https://github.com/huangqiangxiong
 | 
			
		||||
- name: Patrice Chalin (editor)
 | 
			
		||||
  link: https://github.com/chalin
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
[Wireshark](https://www.wireshark.org) is an open source network protocol
 | 
			
		||||
analyzer, which can be used for protocol development, network troubleshooting,
 | 
			
		||||
and education. Wireshark lets you analyze gRPC messages that are transferred
 | 
			
		||||
over the wire (network), and learn about the binary wire format of these
 | 
			
		||||
messages.
 | 
			
		||||
analyzer that can be used for protocol development, network troubleshooting, and
 | 
			
		||||
education. Wireshark lets you analyze gRPC messages that are transferred over
 | 
			
		||||
the network, and learn about the binary format of these messages.
 | 
			
		||||
 | 
			
		||||
In this post you'll learn how to configure and use the Wireshark [gRPC
 | 
			
		||||
dissector][] and the [Protocol Buffers (Protobuf) dissector][], which are
 | 
			
		||||
In this post, you'll learn how to configure and use the Wireshark [gRPC
 | 
			
		||||
dissector][] and the [Protocol Buffers (Protobuf) dissector][pbd], which are
 | 
			
		||||
protocol-specific components that allow you to analyze gRPC messages with
 | 
			
		||||
Wireshark.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -24,13 +25,13 @@ Wireshark.
 | 
			
		|||
The main features of the gRPC and Protobuf dissectors are the following:
 | 
			
		||||
 | 
			
		||||
- Support dissecting (decoding) gRPC messages serialized in the
 | 
			
		||||
  protocol buffer wire format or as JSON
 | 
			
		||||
  [protocol buffer wire format][] or as JSON
 | 
			
		||||
 | 
			
		||||
- Support dissecting gRPC messages of unary, server streaming, client streaming,
 | 
			
		||||
  and bidirectional streaming RPC calls
 | 
			
		||||
 | 
			
		||||
- Enhanced dissection of serialized protocol buffers data by allowing
 | 
			
		||||
  you do the following:
 | 
			
		||||
  you to do the following:
 | 
			
		||||
  - Load relevant `.proto` files
 | 
			
		||||
  - Register your own subdissectors for protocol buffer fields of type `byte` or
 | 
			
		||||
    `string`
 | 
			
		||||
| 
						 | 
				
			
			@ -38,34 +39,34 @@ The main features of the gRPC and Protobuf dissectors are the following:
 | 
			
		|||
## Capturing gRPC traffic
 | 
			
		||||
 | 
			
		||||
This post focuses on the analysis of captured gRPC messages. To learn how to
 | 
			
		||||
store network traffic in _capture files_, see the [Capturing Live Network
 | 
			
		||||
Data][] section of the [Wireshark User’s Guide][].
 | 
			
		||||
store network traffic in _capture files_, see [Capturing Live Network Data][]
 | 
			
		||||
from the [Wireshark User’s Guide][].
 | 
			
		||||
 | 
			
		||||
{{< alert title="Note" color="info" >}}
 | 
			
		||||
  Currently, only **plain text** gRPC messages can be parsed by Wireshark. While
 | 
			
		||||
  Currently, Wireshark can only parse **plain text** gRPC messages. While
 | 
			
		||||
  [Wireshark supports TLS dissection][], it requires per-session secret keys. As
 | 
			
		||||
  of the time of writing, gRPC libraries do not support the exporting of such
 | 
			
		||||
  keys.
 | 
			
		||||
  of the time of writing, the only [Go gRPC][] supports the exporting such keys.
 | 
			
		||||
  To learn how to export keys using Go gRPC -- and other languages as support
 | 
			
		||||
  becomes available -- see [How to Export TLS Master keys of gRPC][].
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  [Go gRPC]: /docs/languages/go
 | 
			
		||||
  [How to Export TLS Master keys of gRPC]: https://gitlab.com/wireshark/wireshark/-/wikis/How-to-Export-TLS-Master-keys-of-gRPC
 | 
			
		||||
  [languages]: /docs/languages
 | 
			
		||||
  [Wireshark supports TLS dissection]: https://gitlab.com/wireshark/wireshark/-/wikis/tls
 | 
			
		||||
{{</alert>}}
 | 
			
		||||
 | 
			
		||||
## Examples
 | 
			
		||||
## Example
 | 
			
		||||
 | 
			
		||||
There are some examples to show how to use the Wireshark Protobuf and gRPC
 | 
			
		||||
dissectors. You can get more details about these examples from [Wireshark
 | 
			
		||||
Protobuf wiki page](https://gitlab.com/wireshark/wireshark/-/wikis/Protobuf) and
 | 
			
		||||
[Wireshark gRPC wiki page](https://gitlab.com/wireshark/wireshark/-/wikis/gRPC)
 | 
			
		||||
on Wireshark official website.
 | 
			
		||||
Let's walk through the setup necessary to analyze previously-captured messages
 | 
			
		||||
that were generated by a slightly extended version of the _address book_ app
 | 
			
		||||
used in the [Protocol Buffers tutorials][].
 | 
			
		||||
 | 
			
		||||
### Sample .proto files
 | 
			
		||||
### Address book `.proto` files
 | 
			
		||||
 | 
			
		||||
These `.proto` files will be used in the following examples.
 | 
			
		||||
 | 
			
		||||
The content of `addressbook.proto`:
 | 
			
		||||
The app's main protocol file is `addressbook.proto`:
 | 
			
		||||
 | 
			
		||||
```protobuf
 | 
			
		||||
// This file comes from the official Protobuf example with a little modification.
 | 
			
		||||
syntax = "proto3";
 | 
			
		||||
package tutorial;
 | 
			
		||||
import "google/protobuf/timestamp.proto";
 | 
			
		||||
| 
						 | 
				
			
			@ -96,14 +97,17 @@ message AddressBook {
 | 
			
		|||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
According to the line `'import "google/protobuf/timestamp.proto"'`, the file
 | 
			
		||||
`addressbook.proto` depends on one of [Protocol Buffers Well-Known
 | 
			
		||||
Types](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf).
 | 
			
		||||
This file is identical to the [Protocol Buffers tutorial version][pb-ab.proto],
 | 
			
		||||
except for the additional `portrait_image` field.
 | 
			
		||||
 | 
			
		||||
The content of `person_search_service.proto`:
 | 
			
		||||
Note the `import` statement at the top of the file, it is used to import
 | 
			
		||||
`Timestamp`, which is one of many [Protocol Buffers Well-Known Types][].
 | 
			
		||||
 | 
			
		||||
Our variant of the app also defines a _person-search_ service that can be used
 | 
			
		||||
to search for address book entries based on selected `Person` attributes. The
 | 
			
		||||
service is defined in `person_search_service.proto`:
 | 
			
		||||
 | 
			
		||||
```protobuf
 | 
			
		||||
// A gRPC service that searches for persons based on certain attributes.
 | 
			
		||||
syntax = "proto3";
 | 
			
		||||
package tutorial;
 | 
			
		||||
import "addressbook.proto";
 | 
			
		||||
| 
						 | 
				
			
			@ -119,63 +123,94 @@ service PersonSearchService {
 | 
			
		|||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The file `person_search_service.proto` relies on file `addressbook.proto`.
 | 
			
		||||
Because the service uses the `Person` type defined in `addressbook.proto`,
 | 
			
		||||
the address book `.proto` is imported at the start of the file.
 | 
			
		||||
 | 
			
		||||
### Protobuf Search Paths Settings
 | 
			
		||||
### Setting protobuf search paths
 | 
			
		||||
 | 
			
		||||
You can tell Wireshark where to find *.proto files by setting *'Protobuf Search
 | 
			
		||||
Paths'* at the Protobuf protocol preferences. If both the files
 | 
			
		||||
`addressbook.proto` and `person_search_service.proto` are in directory
 | 
			
		||||
`d:/protos/my_proto_files` and the real path of the dependent file
 | 
			
		||||
`"google/protobuf/timestamp.proto"` is
 | 
			
		||||
`d:/protos/protobuf-3.4.1/include/google/protobuf/timestamp.proto` (in Protobuf
 | 
			
		||||
official library directory `d:/protos/protobuf-3.4.1/include`). You should add
 | 
			
		||||
the `d:/protos/protobuf-3.4.1/include/` and `d:/protos/my_proto_files` paths
 | 
			
		||||
into *'Protobuf Search Paths'* table and make the *'Load all files'* option of
 | 
			
		||||
the `'d:/protos/my_proto_files'` record enabled for preloading the message
 | 
			
		||||
definitions from the `addressbook.proto` file:
 | 
			
		||||

 | 
			
		||||
This preferences dialog can be found by menu
 | 
			
		||||
'Edit->Preferences->Protocols->ProtoBuf->Protobuf search paths'.
 | 
			
		||||
Wireshark gives the most meaningful decodings when it knows about the `.proto`
 | 
			
		||||
files used by the apps whose messages you are analyzing.
 | 
			
		||||
 | 
			
		||||
### gRPC Sample Capture files
 | 
			
		||||
You can tell Wireshark where to find `.proto` files by setting the **Protobuf
 | 
			
		||||
Search Paths** in the preferences accessible from the **Edit** menu under
 | 
			
		||||
**Preferences \> Protocols \> Protobuf**.
 | 
			
		||||
 | 
			
		||||
There are two gRPC sample capture files
 | 
			
		||||
[grpc_person_search_protobuf_with_image.pcapng](https://gitlab.com/wireshark/wireshark/-/wikis/uploads/f6fcdceb0248669c0b057bd15d45ab6f/grpc_person_search_protobuf_with_image.pcapng)
 | 
			
		||||
and
 | 
			
		||||
[grpc_person_search_json_with_image.pcapng](https://gitlab.com/wireshark/wireshark/-/wikis/uploads/88c03db83efb2e3253c88f853d40477b/grpc_person_search_json_with_image.pcapng)
 | 
			
		||||
on the [SampleCaptures
 | 
			
		||||
page](https://gitlab.com/wireshark/wireshark/-/wikis/SampleCaptures) of
 | 
			
		||||
Wireshark. The difference between the two captures is that the message of former
 | 
			
		||||
is serialized in Protobuf, and the latter is serialized in JSON.
 | 
			
		||||
If our example app's `.proto` files are in the `d:/protos/my_proto_files` directory,
 | 
			
		||||
and the official Protobuf library directory is
 | 
			
		||||
`d:/protos/protobuf-3.4.1/include`, then add these two paths as _source
 | 
			
		||||
directories_ like this:
 | 
			
		||||
 | 
			
		||||
To decode sample captures as gRPC message, you must decode the traffic on TCP
 | 
			
		||||
port 50051 and 50052 as HTTP2:
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
This is a screenshot of the gRPC service request message of
 | 
			
		||||
[grpc_person_search_protobuf_with_image.pcapng](https://gitlab.com/wireshark/wireshark/-/wikis/uploads/f6fcdceb0248669c0b057bd15d45ab6f/grpc_person_search_protobuf_with_image.pcapng):
 | 
			
		||||

 | 
			
		||||
That shows searching for the Person objects of Jason and Lily based on their
 | 
			
		||||
names.
 | 
			
		||||
By selecting the **Load all files** option for the app's protocol directory you
 | 
			
		||||
enable preloading of message definitions from the `addressbook.proto` and
 | 
			
		||||
`person_search_service.proto` files.
 | 
			
		||||
 | 
			
		||||
Since the `Search` operation is defined as the server streaming RPC mode, the
 | 
			
		||||
Person objects can be return back to client one after another:
 | 
			
		||||

 | 
			
		||||
### Loading a capture file
 | 
			
		||||
 | 
			
		||||
You may have noticed that the `portrait_image` of bytes type is parsed as a
 | 
			
		||||
PNG data. It's because we register the PNG dissector in the `"protobuf_field"`
 | 
			
		||||
dissector table for parsing the value of field `portrait_image` as picture by
 | 
			
		||||
putting following Lua script `'protobuf_portrait_field.lua'` into the 'plugins'
 | 
			
		||||
subdirectory of 'Personal configuration' directory:
 | 
			
		||||
From the Wireshark [SampleCaptures page][], download the following sample gRPC
 | 
			
		||||
capture file created by running the app and issuing a search request:
 | 
			
		||||
[grpc_person_search_protobuf_with_image.pcapng][].
 | 
			
		||||
 | 
			
		||||
```lua
 | 
			
		||||
do
 | 
			
		||||
    local protobuf_field_table = DissectorTable.get("protobuf_field")
 | 
			
		||||
    local png_dissector = Dissector.get("png")
 | 
			
		||||
    protobuf_field_table:add("tutorial.Person.portrait_image", png_dissector)
 | 
			
		||||
end
 | 
			
		||||
```
 | 
			
		||||
Select **Open** from the **File** menu to load the capture file in Wireshark.
 | 
			
		||||
Wireshark displays, in order, all of the network traffic from the capture file
 | 
			
		||||
in the **Packet-list pane** at the top of the window.
 | 
			
		||||
 | 
			
		||||
Select an entry from the packet-list pane and Wireshark will decode it and show
 | 
			
		||||
its details in the lower pane like this:
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Select an entry from the details pane to see the byte sequence corresponding to
 | 
			
		||||
that entry:
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
### Setting port traffic type
 | 
			
		||||
 | 
			
		||||
The app's gRPC traffic was captured on port 50051. Before you can decode the
 | 
			
		||||
port's messages, you need to register the port as carrying HTTP2 traffic; do
 | 
			
		||||
this through the **Decode As** dialog, which you access from the **Analyze**
 | 
			
		||||
menu (or right-click on an entry from the packet-list pane):
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Look at the packet-list pane and you'll see that Wireshark is now decoding HTTP2
 | 
			
		||||
and gRPC messages:
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Decoding the search request message
 | 
			
		||||
 | 
			
		||||
Select the first gRPC message sent to port 50051, it corresponds to the sample's
 | 
			
		||||
service request message. This is how Wireshark dissects the gRPC request:
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
By examining the HTTP2 message header `path` field, you'll see the URL to the
 | 
			
		||||
app's service (`/tutorial.PersonSearchService`), followed by the name of the
 | 
			
		||||
invoked RPC (`Search`).
 | 
			
		||||
 | 
			
		||||
The `content-type`, which is set by the gRPC library, informs Wireshark that the
 | 
			
		||||
HTTP2 message content is a gRPC message. By examining the decoded Protocol
 | 
			
		||||
Buffers message of the sample gRPC request, you can see that the search request
 | 
			
		||||
is for the names "Jason" and "Lily".
 | 
			
		||||
 | 
			
		||||
### Decoding the server-streamed response
 | 
			
		||||
 | 
			
		||||
Since the `Search` RPC response is server-streaming, `Person` objects can be
 | 
			
		||||
returned to the client one after another.
 | 
			
		||||
 | 
			
		||||
The response port, which can differ for each RPC call, is 51035 in the sample
 | 
			
		||||
capture file. Select the second `Person` message returned in the response stream
 | 
			
		||||
to see its details:
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
By registering subdissectors, you can have Wireshark further decode fields of
 | 
			
		||||
type `byte` or `string`. For example, to learn how to register a PNG decoder for
 | 
			
		||||
the `portrait_image` field, see [Protobuf field subdissectors][].
 | 
			
		||||
 | 
			
		||||
## History of gRPC and Protocol Buffers support
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -193,12 +228,20 @@ support of gRPC and Protocol Buffers:
 | 
			
		|||
 | 
			
		||||
## Learn more
 | 
			
		||||
 | 
			
		||||
- [Wireshark User’s Guide][]
 | 
			
		||||
- [gRPC dissector][]
 | 
			
		||||
- [Protocol Buffers (Protobuf) dissector][]
 | 
			
		||||
Interested in learning more? Start with the [Wireshark User’s Guide][]. For more
 | 
			
		||||
details concerning the example used in this post, as well as other sample
 | 
			
		||||
capture files containing gRPC messages, see the [gRPC dissector][] and [Protocol
 | 
			
		||||
Buffers dissector][pbd] wiki pages.
 | 
			
		||||
 | 
			
		||||
[Capturing Live Network Data]: https://www.wireshark.org/docs/wsug_html_chunked/ChapterCapture.html
 | 
			
		||||
[gRPC dissector]: https://gitlab.com/wireshark/wireshark/-/wikis/gRPC
 | 
			
		||||
[Protocol Buffers (Protobuf) dissector]: https://gitlab.com/wireshark/wireshark/-/wikis/Protobuf
 | 
			
		||||
[grpc_person_search_protobuf_with_image.pcapng]: https://gitlab.com/wireshark/wireshark/-/wikis/uploads/f6fcdceb0248669c0b057bd15d45ab6f/grpc_person_search_protobuf_with_image.pcapng
 | 
			
		||||
[pb-ab.proto]: https://github.com/protocolbuffers/protobuf/blob/master/examples/addressbook.proto
 | 
			
		||||
[protocol buffer wire format]: https://developers.google.com/protocol-buffers/docs/encoding
 | 
			
		||||
[pbd]: https://gitlab.com/wireshark/wireshark/-/wikis/Protobuf
 | 
			
		||||
[Protocol Buffers tutorials]: https://developers.google.com/protocol-buffers/docs/tutorials
 | 
			
		||||
[Protocol Buffers Well-Known Types]: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf
 | 
			
		||||
[Protobuf field subdissectors]: https://gitlab.com/wireshark/wireshark/-/wikis/Protobuf#protobuf-field-subdissectors
 | 
			
		||||
[SampleCaptures page]: https://gitlab.com/wireshark/wireshark/-/wikis/SampleCaptures
 | 
			
		||||
[Timestamp]: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.Timestamp
 | 
			
		||||
[Wireshark User’s Guide]: https://www.wireshark.org/docs/wsug_html_chunked/
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
		 After Width: | Height: | Size: 66 KiB  | 
| 
		 Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 21 KiB  | 
| 
		 Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 114 KiB  | 
| 
		 After Width: | Height: | Size: 294 KiB  | 
| 
		 After Width: | Height: | Size: 85 KiB  | 
| 
		 Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 21 KiB  |