無線通信エンジニアの備忘録

無線通信だったり、ITだったり、仕事で覚えた専門知識の備忘録

Wiresharkで独自プロトコルのメッセージを統計解析し、結果をコンソールに表示する(その2)

前回の記事で、「Wireshark Developer’s Guide」の「Chapter 11. Wireshark’s Lua API Reference Manual」で規定されている

・Field
・FieldInfo
・TextWindow
・Listener

の4つのクラスの使用方法について、サンプルプロトコルのメッセージ内容を統計表示するコンソールアプリケーションの作成を例に紹介しました。
taekwongineer.hatenablog.jp

また、前回の記事のサンプルコードの手法では、1つのIPパケットに複数のサンプルプロトコルのメッセージが含まれていた場合に、先頭のメッセージしかカウントすることができないという欠点についても説明しました。

今回の記事では、前回の記事のサンプルコードに改良を加え、上記の欠点を解消する方法を紹介したいと思います。

この記事の目次

1.改良後のサンプルコード

では、早速改良後のサンプルコードを以下に示します。
前回の記事のサンプルコードからの変更箇所に★印を付けています。

-- ★(1)Fieldクラスのオブジェクトを定義
SampleMsg_f = Field.new("sample")

-- (2)サンプルプロトコルのメッセージ統計解析関数の定義
function MsgCount()

    -- メッセージの統計計算に使用する変数の定義
    local MsgCountTable = {}    -- 統計情報を格納するテーブル

    -- (3)解析結果を表示するコンソールの定義
    local tw = TextWindow.new("Message Counter")

    -- (4)リセットボタン追加
    tw:add_button("Reset", function()
        -- コンソールのテキストを削除
        tw:clear()
        -- メッセージ統計テーブルを初期化
        MsgCountTable = {}
    end)

    -- (5)Listenerオブジェクトを定義
    local listener = Listener.new(nil, "sample")

    -- (6)TextWindowのクローズ時の処理
    tw:set_atclose(function()
        -- Listenerを停止
        listener:remove()
    end)

    -- ★(7)サンプルプロトコルメッセージ受信時の解析処理の定義
    function listener.packet(pinfo, buffer)

        local finfo = SampleMsg_f()  -- サンプルプロトコルのFieldInfo
        local Offset = finfo.offset  -- サンプルプロトコルメッセージの開始位置
        local MsgSize    -- サンプルプロトコルのMessage Size
        local Msg    -- サンプルプロトコルのMessage
        local PktLen = buffer:len()    -- bufferのデータ長
        local Count

        while Offset <= (PktLen - 3) do
            -- MessageSizeを読込み
            MsgSize = buffer(Offset, 2):uint()
            -- Messageを読込み
            Msg = buffer(Offset + 2, MsgSize):string(ENC_UTF_8)
            -- サンプルプロトコルメッセージの読出し位置を更新
            Offset = Offset + 2 + MsgSize

            Count = MsgCountTable[Msg] or 0
            MsgCountTable[Msg] = Count + 1
        end
    end

    -- (8)メッセージ統計テーブルの内容をコンソールに書き込み
    function listener.draw()
        tw:clear()
        for msg, count in pairs(MsgCountTable) do
            tw:append(msg .. " : " .. count .. "\n")
        end
    end

    -- (9)キャプチャファイルの読込時に呼ばれる関数
    function listener.reset()
        tw:clear()
        MsgCountTable = {}
    end
end

-- (10)メニュー登録
register_menu("Test/Message Counter", MsgCount, MENU_TOOLS_UNSORTED)

以降、★を付けた変更箇所について内容を説明していきます。

(1)Fieldクラスのオブジェクトを定義

-- ★(1)Fieldクラスのオブジェクトを定義
SampleMsg_f = Field.new("sample")

前回の記事のサンプルコードでは、

SampleMsg_f = Field.new("sample.msg")

のように、Field.new()の引数にサンプルプロトコルのMessageフィールドを指定していましたが、今回はサンプルプロトコルメッセージ全体を指定します。

(7)サンプルプロトコルメッセージ受信時の解析処理の定義

    -- ★(7)サンプルプロトコルメッセージ受信時の解析処理の定義
    function listener.packet(pinfo, buffer)

        local finfo = SampleMsg_f()  -- サンプルプロトコルのFieldInfoの定義
        local Offset = finfo.offset  -- サンプルプロトコルメッセージの開始位置
        local MsgSize    -- サンプルプロトコルのMessage Size
        local Msg    -- サンプルプロトコルのMessage
        local PktLen = buffer:len()    -- bufferのデータ長
        local Count

        while Offset <= (PktLen - 3) do
            -- MessageSizeを読込み
            MsgSize = buffer(Offset, 2):uint()
            -- Messageを読込み
            Msg = buffer(Offset + 2, MsgSize):string(ENC_UTF_8)
            -- サンプルプロトコルメッセージの読出し位置を更新
            Offset = Offset + 2 + MsgSize

            Count = MsgCountTable[Msg] or 0
            MsgCountTable[Msg] = Count + 1
        end
    end

前回の記事のサンプルコードでは、

Msg = tostring(SampleMsg_f())

のように、SampleMsg_f()で取得したFieldInfoオブジェクトに対し、tostring()でMessageの値を取得していました。

一方で今回のサンプルコードでは、FieldInfoオブジェクトfinfoに対し、finfo.offsetで、buffer内のサンプルプロトコルメッセージの開始位置を取得しています。

その後は、サンプルプロトコルの解析プラグインの作成手順と同様、bufferからMessageSize、Messageの順にデータを読み出していき、Offsetがbufferの最後尾に到達するまでこれを繰り返していきます。

2.まとめ

今回の記事では、FieldInfoクラスのオブジェクトfinfoに対し、finfo.offsetによりbuffer内のサンプルプロトコルメッセージの開始位置を取得することで、1つのIPパケット内に複数のサンプルプロトコルメッセージが含まれている場合においても、Messageデータの統計を漏れなく実施する方法について紹介しました。

これまで4回に渡って、Wiresharkの独自プロトコル解析プラグインの作成方法について紹介してきましたが、Wiresharkに関する記事はここで一度一区切りつけようと思います。

また、何かこれは忘れずに覚えておきたい!という使い方が出てきたら記事にしようと思います。