使用Wireshark分析Redis通信协议 2016-03-17 22:00

简介

使用tcpdump抓取redis报文。

使用Wireshark分析报文,使用Wireshark的redis协议解析插件来解析Redis报文,直观显示Redis的协议报文内容。

抓包

tcpdump port 6380 -i eth0 -w redis6380.cap

备注: 6380是Redis的服务端口,eth0是对应的网卡名称,-w是指定报文输出到文件中,而不是显示在终端上。

解析

安装Redis协议报文解析插件

将如下文本保存为一个.lua文件(文件名任意,比如redis-wireshark.lua),存放到Wireshark的plugin目录下。

Wireshark的plugin目录可通过如下方式查看:菜单"Help"-"About Wireshark"-"Folders"-"Personal plugins"/"Global plugins"。

提醒:需要留意Wireshark软件包中是否已经集成了lua,最新版本的Wireshark已经默认集成lua。可在"Help"-"About Wireshark"中看到"with lua x.x"字样。如果是"without lua",则无法使用此lua插件。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
-- Wireshark packet dissector for Redis
-- Protocol specification: http://redis.io/topics/protocol
-- Written by John Zwinck, 29 November 2011

do -- scope
    local proto = Proto('redis', 'Redis')

    local f = proto.fields
    -- we could make more of these, e.g. to distinguish keys from values
    f.value   = ProtoField.string('redis.value',   'Value')

    function proto.dissector(buffer, pinfo, tree)
        pinfo.cols.protocol = 'Redis'

        mtypes = {
            ['+'] = 'Status',
            ['-'] = 'Error',
            [':'] = 'Integer',
            ['$'] = 'Bulk',
            ['*'] = 'Multi-Bulk',
        }

        local CRLF = 2 -- constant length of \r\n

        -- recursively parse and generate a tree of data from messages in a packet
        -- parent: the tree root to populate under
        -- buffer: the entire packet buffer
        -- offset: the current offset in the buffer
        -- matches: a one-pass generator function which yields parsed lines from the packet
        -- returns: the new offset (i.e. the input offset plus the number of bytes consumed)
        local function recurse(parent, buffer, offset, matches)
            local line = matches() -- get next line
            local length = line:len()
            local prefix, text = line:match('([-+:$*])(.+)')
            local mtype = mtypes[prefix]

            assert(prefix and text, 'unrecognized line: '..line)
            assert(mtype, 'unrecognized message type: '..prefix)

            if prefix == '*' then -- multi-bulk, contains multiple sub-messages
                local replies = tonumber(text)

                -- this is a bit gross: we parse (part of) the buffer again to
                -- calculate the length of the entire multi-bulk message
                -- if we don't do this, Wireshark will highlight only our prologue
                local bytes = 0
                local remainder = buffer():string():sub(offset + length + CRLF)
                local submatches = remainder:gmatch('[^\r\n]+')
                local replies_left = replies
                while replies_left > 0 do
                    local submatch = submatches()
                    if submatch:sub(1,1) ~= '$' then -- bulk messages contain an extra CRLF
                        replies_left = replies_left - 1
                    end
                    bytes = bytes + submatch:len() + CRLF
                end

                local child = parent:add(proto, buffer(offset, length + CRLF + bytes),
                                         'Redis '..mtype..' Reply')
                offset = offset + length + CRLF

                -- recurse down for each message contained in this multi-bulk message
                for ii = 1, replies do
                    offset = recurse(child, buffer, offset, matches)
                end

            elseif prefix == '$' then -- bulk, contains one binary string
                local bytes = tonumber(text)

                if bytes == -1 then
                    local child = parent:add(proto, buffer(offset, length + CRLF),
                                             'Redis '..mtype..' Reply')

                    offset = offset + length + CRLF

                    child:add(f.value, '<null>')
                else
                    local child = parent:add(proto, buffer(offset, length + CRLF + bytes + CRLF),
                                             'Redis '..mtype..' Reply')

                    offset = offset + length + CRLF

                    -- get the string contained within this bulk message
                    local line = matches()
                    local length = line:len()
                    child:add(f.value, buffer(offset, length))
                    offset = offset + length + CRLF
                end
            else -- integer, status or error
                local child = parent:add(proto, buffer(offset, length + CRLF),
                                         'Redis '..mtype..' Reply')
                child:add(f.value, buffer(offset + prefix:len(), length - prefix:len()))
                offset = offset + length + CRLF

            end

            return offset
        end

        -- parse top-level messages until the buffer is exhausted
        local matches = buffer():string():gmatch('[^\r\n]+')
        local offset = 0
        while offset < buffer():len() do
            offset = recurse(tree, buffer, offset, matches)
        end

        -- check that we consumed exactly the right number of bytes
        assert(offset == buffer():len(), 'consumed '..offset..' bytes of '..buffer():len())
    end

    -- register this dissector for the standard Redis ports
    local dissectors = DissectorTable.get('tcp.port')
    for _, port in ipairs{ 6379, } do
        dissectors:add(port, proto)
    end
end

推荐:Wireshark推荐使用1.12.10版本,而不是最新的2.X版本。

解析Redis报文

Redis采用的是默认端口6379,则Wireshark可直接解析出Redis报文,并显示Redis报文的内容。

如果不是采用默认的端口,则选中一个Redis报文,右键"Decode As..."-"Transport"-"TCP"-选择相关端口的过滤条件,右边选择Redis协议,应用之后,以后所有此端口的报文都会以Redis报文进行解析。可通过"Analyze"-"Decode As..."-"Show current"查看配置。

如下图:

参考文档

  1. redis-wireshark
Tags: #Redis    Post on Linux