Wiresharkで独自プロトコルを解析する
いきさつ
WiresharkではLua言語で記述したファイルをプラグインとして扱うことが出来ます。
業務で端末間の通信が怪しかったので、Wiresharkを使うことになりましたが、単にキャプチャーしただけだとバイナリを脳内解析することになります。ちょっと勘弁ということでプラグインを作ることにしました。Lua言語ってなんちゃら?と思うけど、独自プロトコルを解析するくらいであれば簡単に作ることが出来ました。
前準備
Wiresharkのインストールフォルダにある init.lua の設定を変更します。
- disable_luaをfalse に設定
- run_user_scripts_when_superuserをtrueに設定
- ファイルの最終行に「dofile(DATA_DIR.."foo_protcol.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 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で独自プロトコルが表示された
Wiresharkを起動して、目的の通信履歴の詳細を表示すると独自プロトコルの解析が表示されます。(画像はじゃっかん加工しています)
電文サイズが足らない場合や分割パケットの場合
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
一つのパケットに複数の電文がある場合はこのように。
分割パケットの場合はこのようになります。