LoginSignup
41
51

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-07-13

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

いきさつ

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

前準備

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

  • disable_luaをfalse に設定
  • run_user_scripts_when_superuserをtrueに設定
  • ファイルの最終行に「dofile(DATA_DIR.."foo_protcol.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() はビッグエンディアンで展開します。
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

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

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

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

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

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

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

packet2.png

41
51
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
41
51