Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

From the Wireshark wiki - Protobuf#protocol-dependencies:

Protobuf content is normally dissected by Wireshark from some higher layer dissectors including gRPC or other UDP/TCP based dissectors

There are two TCP streams in the provided pcap file:

image description

Add a Decode As... entry for a Stream ID 1 TCP port (34727):

# "Decode As" entries file for Wireshark 4.2.3.
#
# This file is regenerated each time "Decode As" preferences
# are saved within Wireshark. Making manual changes should be safe,
# however.
decode_as_entry: tcp.port,34727,(none),HTTP2

The out of the box protobuf decode:

Frame 46: 127 bytes on wire (1016 bits), 127 bytes captured (1016 bits) on interface lo, id 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 34727, Dst Port: 50051, Seq: 602, Ack: 231, Len: 61
HyperText Transfer Protocol 2
GRPC Message: /export_data.streamDataOut/streamData, Request
Protocol Buffers: /export_data.streamDataOut/streamData,request
    Message: export_data.streamDataArgs
        [Message Name: export_data.streamDataArgs]
        Field(1): Id = 1234 (int64)
            [Field Name: Id]
            [Field Type: int64 (3)]
            .000 1... = Field Number: 1
            .... .000 = Wire Type: varint (0)
            Value: d209
                Int64: 1234
            Id: 1234
        Field(2): data  (bytes)
            [Field Name: data]
            [Field Type: bytes (12)]
            .001 0... = Field Number: 2
            .... .010 = Wire Type: Length-delimited (2)
            Value Length: 42
            Value: 0a0d6d6163446174612e70726f746f12190a1130303a30303a30303a30303a31323a464610eb86b5af06
            data: (42 bytes)
                Data (42 bytes)
                    Data: 0a0d6d6163446174612e70726f746f12190a1130303a30303a30303a30303a31323a464610eb86b5af06
                    Text: \n\rmacData.proto\x12\x19\n\x1100:00:00:00:12:FF\x10놵�\x06
                    [Length: 42]


Wireshark wiki - Protobuf field subdissectors:

A subdissector can register itself in "protobuf_field" dissector table for parsing the value of the field. The key of record in "protobuf_field" table is the full name of the field.

    local protobuf_field_table = DissectorTable.get("protobuf_field")
    local png_dissector = Dissector.get("png")
    protobuf_field_table:add("tutorial.Person.portrait_image", png_dissector)

In their example, it's to decode a png. Our goal is to decode an embedded protobuf:

local protobuf_dissector = Dissector.get("protobuf")
...
pinfo.private["pb_msg_type"] = "message,tutorial.AddressBook"
pcall(Dissector.call, protobuf_dissector, tvb, pinfo, tree)


-- 240313_33954_nested_pb.lua
-- "Am looking for working example for below case where data is serialized as bytes to another protobuf."
-- https://ask.wireshark.org/question/33954/lua-example-for-protobuf-dissector-which-has-protobuf-encoded-within-protobuf/

-- Step 1 - document as you go. See header above and set_plugin_info().
local nested_pb_info =
{
    version = "1.0.0",
    author = "Good Coder",
    description = "Decode nested protobufs",
    repository = "Floppy in top drawer"
}

set_plugin_info(nested_pb_info)

do
    local protobuf_field_table = DissectorTable.get("protobuf_field") 
    local protobuf_dissector = Dissector.get("protobuf")

    protobuf_message_name_f = Field.new("protobuf.message.name")

    encoded_pb_p = Proto("encoded_pb_dissector", "Encoded Protobuf")
    encoded_pb_p.dissector = function(tvb, pinfo, subtree)

        -- What is the message type for the nested protobufs
        local message_type = {
            ["export_data.streamDataArgs"] = "message,encoded_data.encodeDataInfo",
            ["encoded_data.encodeDataInfo"] = "message,mac_data.macDataInfo"
        }

        -- Prevent recursion by only calling on last message name in tree
        local message_name = {
            ["message,encoded_data.encodeDataInfo"] = "encoded_data.encodeDataInfo",
            ["message,mac_data.macDataInfo"] = "mac_data.macDataInfo"
        }

        finfo_messages = { protobuf_message_name_f() }

        if (#finfo_messages > 0) then
            local call_dissector

            -- First time we're called there will be no pb_msg_type
            if pinfo.private["pb_msg_type"] == nil then
                call_dissector = true
            end

            v_string = string.format("%s", finfo_messages[#finfo_messages])

            -- Only call if last message is type we're asked to decode
            if v_string == message_name[pinfo.private["pb_msg_type"]]  then
                call_dissector = true
            end

            if call_dissector == true then
                pinfo.private["pb_msg_type"] = message_type[v_string]
                local subsubtree = subtree:add(encoded_pb_p, tvb())
                pcall(Dissector.call, protobuf_dissector, tvb, pinfo, subsubtree)
            end
        end
    end 
    -- What fields are the encoded protobufs in
    protobuf_field_table:add("export_data.streamDataArgs.data", encoded_pb_p) 
    protobuf_field_table:add("encoded_data.encodeDataInfo.val", encoded_pb_p) 
end


Frame 46: 127 bytes on wire (1016 bits), 127 bytes captured (1016 bits) on interface lo, id 0
Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: 00:00:00_00:00:00 (00:00:00:00:00:00)
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 34727, Dst Port: 50051, Seq: 602, Ack: 231, Len: 61
HyperText Transfer Protocol 2
GRPC Message: /export_data.streamDataOut/streamData, Request
Protocol Buffers: /export_data.streamDataOut/streamData,request
    Message: export_data.streamDataArgs
        [Message Name: export_data.streamDataArgs]
        Field(1): Id = 1234 (int64)
            [Field Name: Id]
            [Field Type: int64 (3)]
            .000 1... = Field Number: 1
            .... .000 = Wire Type: varint (0)
            Value: d209
                Int64: 1234
            Id: 1234
        Field(2): data  (bytes)
            [Field Name: data]
            [Field Type: bytes (12)]
            .001 0... = Field Number: 2
            .... .010 = Wire Type: Length-delimited (2)
            Value Length: 42
            Value: 0a0d6d6163446174612e70726f746f12190a1130303a30303a30303a30303a31323a464610eb86b5af06
            data: (42 bytes)
                Encoded Protobuf
                    Protocol Buffers: encoded_data.encodeDataInfo
                        Message: encoded_data.encodeDataInfo
                            [Message Name: encoded_data.encodeDataInfo]
                            Field(1): name = macData.proto (string)
                                [Field Name: name]
                                [Field Type: string (9)]
                                .000 1... = Field Number: 1
                                .... .010 = Wire Type: Length-delimited (2)
                                Value Length: 13
                                Value: 6d6163446174612e70726f746f
                                    String: macData.proto
                                name: macData.proto
                            Field(2): val  (bytes)
                                [Field Name: val]
                                [Field Type: bytes (12)]
                                .001 0... = Field Number: 2
                                .... .010 = Wire Type: Length-delimited (2)
                                Value Length: 25
                                Value: 0a1130303a30303a30303a30303a31323a464610eb86b5af06
                                val: (25 bytes)
                                    Encoded Protobuf
                                        Protocol Buffers: mac_data.macDataInfo
                                            Message: mac_data.macDataInfo
                                                [Message Name: mac_data.macDataInfo]
                                                Field(1): mac_addr = 00:00:00:00:12:FF (string)
                                                    [Field Name: mac_addr]
                                                    [Field Type: string (9)]
                                                    .000 1... = Field Number: 1
                                                    .... .010 = Wire Type: Length-delimited (2)
                                                    Value Length: 17
                                                    Value: 30303a30303a30303a30303a31323a4646
                                                        String: 00:00:00:00:12:FF
                                                    mac_addr: 00:00:00:00:12:FF
                                                Field(2): time = 1710048107 (uint64)
                                                    [Field Name: time]
                                                    [Field Type: uint64 (4)]
                                                    .001 0... = Field Number: 2
                                                    .... .000 = Wire Type: varint (0)
                                                    Value: eb86b5af06
                                                        Uint64: 1710048107
                                                    time: 1710048107