Wiresharkで独自プロトコルの解析

# Wiresharkで独自プロトコルを解析する

## いきさつ
WiresharkではLua言語で記述したファイルをプラグインとして扱うことが出来ます。
業務で端末間の通信が怪しかったので、Wiresharkを使うことになりましたが、単にキャプチャーしただけだとバイナリを脳内解析することになります。ちょっと勘弁ということでプラグインを作ることにしました。Lua言語ってなんちゃら？と思うけど、独自プロトコルを解析するくらいであれば簡単に作ることが出来ました。

## 前準備
Wiresharkのインストールフォルダにある init.lua の設定を変更します。

* disable_luaをfalse に設定
* run_user_scripts_when_superuserをtrueに設定
* ファイルの最終行に「dofile(DATA_DIR.."foo_protcol.lua")」を追記

```lua:init.lua
-- Set disable_lua to true to disable Lua support.
disable_lua = false

if disable_lua then
    return
end

-- If set and we are running with special privileges this setting
-- tells whether scripts other than this one are to be run.
run_user_scripts_when_superuser = true

 ：
 ：
 ：

dofile(DATA_DIR.."console.lua")
--dofile(DATA_DIR.."dtd_gen.lua")
dofile(DATA_DIR.."foo_protcol.lua")
```

## luaファイルの作成
Wiresharkのインストールフォルダにて「foo_protcol.lua」を作成します。
通信電文とかポート番号はフィクションです。

* le_uint() はリトルエンディアンで展開します。
* uint() はビッグエンディアンで展開します。

```lua:foo_protcol.lua
-- foo protocol example
-- プロトコルの定義
foo_proto = Proto("foo","Foo","Foo protocol")
-- パース用の関数定義
function foo_proto.dissector(buffer,pinfo,tree)
    pinfo.cols.protocol = "Foo"
    local subtree = tree:add(foo_proto,buffer(),"Foo Protocol Data")
    subtree:add(buffer(0,1),"cmd_id: " .. buffer(0,1):le_uint())
    subtree:add(buffer(1,1),"req_id: " .. buffer(1,1):le_uint())
    subtree:add(buffer(2,6),"date: " .. buffer(2,1):le_uint() .. "/" .. buffer(3,1):le_uint() .. "/"  .. buffer(4,1):le_uint() .. " "  .. buffer(5,1):le_uint() .. ":"  .. buffer(6,1):le_uint() .. ":"  .. buffer(7,1):le_uint())
    subtree:add(buffer(8,2),"seq_no: " .. buffer(8,2):le_uint())
    subtree:add(buffer(10,1),"rty_cnt: " .. buffer(10,1):le_uint())
end
-- tcp.portテーブルのロード
tcp_table = DissectorTable.get("tcp.port")
-- ポート9999番とプロトコルの紐付けをする
tcp_table:add(9999,foo_proto)
```

## Wiresharkで独自プロトコルが表示された :blush:

Wiresharkを起動して、目的の通信履歴の詳細を表示すると独自プロトコルの解析が表示されます。（画像はじゃっかん加工しています）

![foo_protocol.png](https://qiita-image-store.s3.amazonaws.com/0/114529/c106e070-087a-f9d5-a125-9f5cb4febda0.png)

## 電文サイズが足らない場合や分割パケットの場合

TCPのパケットは、一つのパケットで複数の電文が入っていたり、一回で全ての電文が到達しなかったりします。上記の foo_proto.dissector では、そのあたりの考慮を加えます。

まずは電文解析自体のロジックを分けます。

```lua
function disp_foo(buffer,pinfo,tree)
    pinfo.cols.protocol = "Foo"
    local subtree = tree:add(foo_proto,buffer(),"Foo Protocol Data")
    subtree:add(buffer(0,1),"cmd_id: " .. buffer(0,1):le_uint())
    subtree:add(buffer(1,1),"req_id: " .. buffer(1,1):le_uint())
    subtree:add(buffer(2,6),"date: " .. buffer(2,1):le_uint() .. "/" .. buffer(3,1):le_uint() .. "/"  .. buffer(4,1):le_uint() .. " "  .. buffer(5,1):le_uint() .. ":"  .. buffer(6,1):le_uint() .. ":"  .. buffer(7,1):le_uint())
    subtree:add(buffer(8,2),"seq_no: " .. buffer(8,2):le_uint())
    subtree:add(buffer(10,1),"rty_cnt: " .. buffer(10,1):le_uint())
end
```

dissector自体では、受け取ったバッファーのサイズを見て、後続のパケットとアペンドしたり、複数電文を処理するためのループを行うようにします。

```lua
function foo_proto.dissector(buffer,pinfo,tree)
    local HEADDER_LENGTH = 17
    if buffer:len() < HEADDER_LENGTH then
        -- ヘッダ部に必要なデータ長が無いので、後続のパケットにアペンドする.
        pinfo.desegment_len = DESEGMENT_ONE_MORE_SEGMENT
        return buffer:len() - HEADDER_LENGTH
    end

    while buffer:len() > 0 do
        local length = buffer(13,2):le_uint()
        if (buffer:len() - HEADDER_LENGTH) < length then
            -- データ部に必要なデータ長が無いので、後続のパケットにアペンドする.
            pinfo.desegment_len = DESEGMENT_ONE_MORE_SEGMENT
            return buffer:len() - length + HEADDER_LENGTH
        end
        
        disp_foo(buffer,pinfo,tree)
        if (buffer:len() - (HEADDER_LENGTH + length)) == 0 then
            break
        end
        buffer = buffer:range(HEADDER_LENGTH + length)
    end
end
```

一つのパケットに複数の電文がある場合はこのように。

![packet1.png](https://qiita-image-store.s3.amazonaws.com/0/114529/844c1725-0a0e-9bfc-6798-aded2753c47e.png)

分割パケットの場合はこのようになります。

![packet2.png](https://qiita-image-store.s3.amazonaws.com/0/114529/45e5821a-d7c8-2abd-bf1e-68e0ffccb223.png)