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 |