使用 lua 編寫 wireshark 協議解析插件


一、平台

操作系統:windows 7

wireshark:1.10.3

lua:5.1

 

二、准備

lua 語言基本語法,特別是關於表操作和循環

wireshark 文檔,包括用戶使用文檔和開發者文檔,這些在 wireshark 官方網站都能找到

 

三、開始

我們首先定義一個簡單的協議,我們使用 C 語言的語法描述,

1 struct foo
2 {
3     char protocol_type[16];     /* request response notify */
4     char service_type[16];      /* 我們定義的各種服務類型 */
5     unsigned int msg_len;       /* 消息體的長度 */
6     char msg_content[0];        /* 消息體的內容,由於是變長的所以采用此方法定義 */
7 };

現在可以讓我們使用 lua 定義一個最基本的框架

 1 do
 2     --[[
 3         創建一個新的協議結構 foo_proto
 4         第一個參數是協議名稱會體現在過濾器中
 5         第二個參數是協議的描述信息,無關緊要
 6     --]]
 7     local foo_proto = Proto("foo", "Foo Protolcol")
 8     
 9     --[[
10         下面定義字段
11     --]]
12     local foo_protocol_type = ProtoField.string("foo.prototype", "Protocol Type", base.NONE)
13     local foo_service_type = ProtoField.string("foo.servicetype", "Service Type", base.NONE)
14     local foo_msg_len = ProtoField.uint32("foo.msglen", "Message Length", base.DEC)
15     local foo_msg_content = ProtoField.string("foo.msgcontent", "Message Content", base.NONE)
16     
17     -- 將字段添加都協議中
18     foo_proto.fields = {
19         foo_protocol_type,
20         foo_service_type,
21         foo_msg_len,
22         foo_msg_content
23     }
24     
25     --[[
26         下面定義 foo 解析器的主函數,這個函數由 wireshark調用
27         第一個參數是 Tvb 類型,表示的是需要此解析器解析的數據
28         第二個參數是 Pinfo 類型,是協議解析樹上的信息,包括 UI 上的顯示
29         第三個參數是 TreeItem 類型,表示上一級解析樹
30     --]]
31     function foo_proto.dissector(tvb, pinfo, treeitem)
32         
33         -- 設置一些 UI 上面的信息
34         pinfo.cols.protocol:set("FOO")
35         pinfo.cols.info:set("Foo Protocol")
36         
37         local offset = 0
38         local tvb_len = tvb:len()
39         
40         -- 在上一級解析樹上創建 foo 的根節點
41         local foo_tree = treeitem:add(foo_proto, tvb:range(offset))
42         
43         -- 下面是想該根節點上添加子節點,也就是自定義協議的各個字段
44         -- 注意 range 這個方法的兩個參數的意義,第一個表示此時的偏移量
45         -- 第二個參數代表的是字段占用數據的長度
46         foo_tree:add(foo_protocol_type, tvb:range(offset, 16))
47         offset = offset+16        
48         foo_tree:add(foo_service_type, tvb:range(offset, 16))
49         offset = offset+16
50         foo_tree:add(foo_msg_len, tvb:range(offset, 4))
51         offset = offset+4
52         
53         -- 計算消息內容的長度
54         local foo_content_len = tvb_len-offset
55         foo_tree:add(foo_msg_content, tvb:range(offset, foo_content_len))
56         offset = offset+foo_content_len
57         
58     end
59     
60     -- 向 wireshark 注冊協議插件被調用的條件
61     local tcp_port_table = DissectorTable.get("tcp.port")
62     tcp_port_table:add(12345, foo_proto)
63 end

 

四、擴展

(1)單 TCP 分節多應用 PDU

這種情況可以在我們的協議解析函數中使用循環解決,但是應用層 PDU 一般都需要有我們自定義的分割標識符,不然無法區分,這里我們使用協議尾端標記的方式,采用0xFF分割,這里我們假設我們的協議體中不會出現這個值,也就是我們的消息體長度字段的值不會出現這個值。

(2)單應用 PDU 多 TCP 分節

這種情況就需要 wireshark 為我們重新拼接被 TCP 協議棧拆分的數據,不過這已經有接口可用了,下面是處理兩種情況的框架

 1 do
 2     -- 這里便於運算需要將數據類型進行轉換
 3     local tvbrange = tvb:range(0)
 4     local bytearray = tvbrange:bytes()
 5     local offset = pinfo.desegment_offset or 0
 6     
 7     while true do
 8         local pdu_flg = false
 9         local pdu_end_index = 0
10         
11         -- ByteArray 類型的索引與 C 類似
12         -- 這與 lua 本身處理表的方式有所區別
13         for i = offset, tvb_len do
14             if 0xFF == bytearray:get_index(i) then
15                 pdu_flg = true
16                 pdu_end_index = i
17             end
18         end
19         
20         -- 如果沒有找到 PDU 的結束符,進行合包延遲解析
21         if false == frame_end_flg then
22             -- 如果明確知道這個應用協議還需要多少字節
23             -- 可以明確地將需要的值填上,而不是使用下面的值
24             -- 只是表示需要更多的分節
25             pinfo.desegment_len = DESEGMENT_ONE_MORE_SEGMENT
26             return
27         end
28         
29         -- 下面是正常的處理流程,在上面的代碼中已經有體現了
30         -- 不過此時協議最大邊界為 pdu_end_index
31         
32     end
33 end

 

五、完善

(1)談到網絡協議,如果涉及到整數必然會有字節序的問題,如果 wireshark 默認使用的是大端字節序,如果協議中的整數采用的是小端字節序,那么請考慮使用 TreeItem 類型的 le_add() 方法替代 add() 方法。

(2)在 add 和 le_add 方法中,我們可以顯式設定我們自己的值,可以給 add 方法傳遞第三個值,例如,

1         local protocol_type = tvb:range(offset, 16):string()
2         foo_tree:add(foo_protocol_type, tvb:range(offset, 4), protocol_type)

這樣顯示的就是 protocol_type 的內容,另外這里的 protocol_type 類型一定要與上面定義 foo_protocol_type 讓 wireshark 解析的一致,不然這個值是無效的,也就是說這個地方 protocol_type 如果是整數,就是無效,反過來也是一樣。

(3)在協議解析樹上增加節點不一定要提前定義字段,也就是字段的定義不是必須的,例如,

1 local protocol_type = tvb:range(offset, 16):string()
2 foo_tree:add(tvb:range(offset, 16), "Protocol Type: " .. protocol_type)

效果是一樣的,但是在過濾器中無法使用此字段。

(4)pinfo.cols.info:set 方法可以延遲調用,也就說可以在確定了消息的各種屬性之后在調用,這樣可以更清晰的顯示協議的摘要。

 

六、參考

(1)wireshark 用戶手冊,在 wireshark 的安裝目錄下有 chm 的版本,最值得參考的是第11章的數據類型和各種方法的解釋。

(2)重組被拆分的 TCP 分節,http://stackoverflow.com/questions/13138088/how-do-i-reassemble-tcp-packet-in-lua-dissector

(3)處理一個分節多應用 PDU,http://stackoverflow.com/questions/14387426/reassembling-packets-in-a-lua-wireshark-dissector

(4)調用子協議的方法,http://blog.csdn.net/phunxm/article/details/5972904

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM