Wireshark是一款著名的网络协议分析工具,基于GPLv2协议开源,支持Linux、Windows、MacOS等多个操作系统。
Wireshark is the world's foremost network protocol analyzer, and is the de facto (and often de jure) standard across many industries and educational institutions.
Wireshark development thrives thanks to the contributions of networking experts across the globe. It is the continuation of a project that started in 1998.[via]
Wireshark已经支持数千种协议,对新协议的支持还在不断增加。今天,你发明了一个新的网络协议,也想让Wireshark识别,你该怎么办呢?你有两个选择:
如果你选择了前者,请按下CTRL D,然后在你改变主意的时候再回来。
如果你还没有安装Wireshark,请下载并安装Wireshark。
从功能看,Wireshark可以分为以下几个模块:
要让Wireshark识别你发明的协议,应该从“协议分析”部分入手,也就是编写一个Wireshark dissector。
Wireshark本身是用C语言编写的,用C语言编写dissector是很自然的选择。但是,C语言并不简单,在Windows下编译C语言的代码也并不容易。浏览一遍Wireshark User Guide的目录,你会看到一章Lua Support in Wireshark,这就是编写Wireshark dissector插件的另一种选择。
Lua是一种功能强大的、快速的、轻量的、嵌入式的脚本语言。Lua在葡萄牙语里是“月亮”的意思,它不是一个缩写因此不能写成“LUA”。
Lua is a powerful, fast, light-weight, embeddable scripting language.
Lua combines simple procedural syntax with powerful data description constructs based on associative arrays and extensible semantics. Lua is dynamically typed, runs by interpreting bytecode for a register-based virtual machine, and has automatic memory management with incremental garbage collection, making it ideal for configuration, scripting, and rapid prototyping.[via]
Lua是一种嵌入式脚本语言,Wireshark嵌入了Lua脚本引擎,因此我们可以使用Lua脚本语言扩展Wireshark。
Wireshark User Guide, Example of Dissector written in Lua已经给出了一段Wireshark插件代码,先解读一下:
--这个dissector只是把几个协议组合起来而已,并不是识别一种新的协议do --do...end是Lua语言的语句块关键字,相当于C#语言的{..} --创建一个Proto类的对象,表示一种协议 local p_multi = Proto("multi","MultiProto"); local vs_protos = { [2] = "mtp2", [3] = "mtp3", [4] = "alcap", [5] = "h248", [6] = "ranap", [7] = "rnsap", [8] = "nbap" } --创建几个ProtoField对象,就是主界面中部Packet Details窗格中能显示的那些属性 local f_proto = ProtoField.uint8("multi.protocol","Protocol",base.DEC,vs_protos) local f_dir = ProtoField.uint8("multi.direction","Direction",base.DEC,{ [1] = "incoming", [0] = "outgoing"}) local f_text = ProtoField.string("multi.text","Text") --把ProtoField对象加到Proto对象上 p_multi.fields = { f_proto, f_dir, f_text } --用Dissector.get函数可以获得另外一个协议的解析组件 local data_dis = Dissector.get("data") local protos = { [2] = Dissector.get("mtp2"), [3] = Dissector.get("mtp3"), [4] = Dissector.get("alcap"), [5] = Dissector.get("h248"), [6] = Dissector.get("ranap"), [7] = Dissector.get("rnsap"), [8] = Dissector.get("nbap"), [9] = Dissector.get("rrc"), [10] = DissectorTable.get("sctp.ppi"):get_dissector(3), -- m3ua [11] = DissectorTable.get("ip.proto"):get_dissector(132), -- sctp } --为Proto对象添加一个名为dissector的函数, --Wireshark会对每个“相关”数据包调用这个函数 function p_multi.dissector(buf,pkt,root) --root:add会在Packet Details窗格中增加一行协议 local t = root:add(p_multi,buf(0,2)) --t:add,在Packet Details窗格中增加一行属性, --并指定要鼠标点击该属性时Packet Bytes窗格中会选中哪些字节 t:add(f_proto,buf(0,1)) t:add(f_dir,buf(1,1)) --这句是将数据的第一个字节转换成无符号整数 local proto_id = buf(0,1):uint() local dissector = protos[proto_id] if dissector ~= nil then dissector:call(buf(2):tvb(),pkt,root) elseif proto_id < 2 then t:add(f_text,buf(2)) -- pkt.cols.info:set(buf(2,buf:len() - 3):string()) else --调用另外一个dissector data_dis:call(buf(2):tvb(),pkt,root) end end --所有的dissector都是以“table”的形式组织的,table表示上级协议 local wtap_encap_table = DissectorTable.get("wtap_encap") --这个是获得udp协议的DissectorTable,并且以端口号排列 local udp_encap_table = DissectorTable.get("udp.port") wtap_encap_table:add(wtap.USER15,p_multi) wtap_encap_table:add(wtap.USER12,p_multi) --为UDP的7555端口注册这个Proto对象, --当遇到源或目的为UDP7555的数据包,就会调用上面的p_multi.dissector函数 udp_encap_table:add(7555,p_multi)end
Wireshark虽然嵌入了Lua解释器,但是默认情况下并没有打开。安装Lua插件的方法是:
disable_lua = true; do return end;
),在这一行的开头添加--
符号。dofile('文件名.lua');
为了演示如何用Lua脚本语言编写一个实用的Wireshark dissector插件,我设计了一个简单的网络协议——ScoreBoard。
ScoreBoard协议用于更新比分牌的数值和背景颜色。服务端监听UDP1127端口,客户端端口任意。
每个报文的前16字节是固定的识别符identifier:e2 cb b5 80 cb 09 4e ba a3 6b f6 07 ce 95 3f 2b
第17字节表示报文类型operator:
数据部分:
do --协议名称为ScoreBoard,在Packet Details窗格显示为yoursunny.P2008.IS409 ScoreBoard local p_ScoreBoard = Proto("ScoreBoard","yoursunny.P2008.IS409 ScoreBoard") --协议的各个字段 local f_identifier = ProtoField.bytes("ScoreBoard.identifier","Identifier") local f_operator = ProtoField.uint8("ScoreBoard.operator","Operator",base.HEX, --这个字段的数字值都有相应的含义,可以自动对应成字符串 { [0] = "get-value", [1] = "set-value", [128] = "resp-value", [16] = "get-color", [17] = "set-color", [144] = "resp-color"}) --所有可能的字段都要定义,到时没有t:add就不会显示 local f_left = ProtoField.uint32("ScoreBoard.left","Value Left",base.DEC) local f_right = ProtoField.uint32("ScoreBoard.right","Value Right",base.DEC) local f_red = ProtoField.uint8("ScoreBoard.red","Color Red",base.DEC) local f_green = ProtoField.uint8("ScoreBoard.green","Color Green",base.DEC) local f_blue = ProtoField.uint8("ScoreBoard.blue","Color Blue",base.DEC) p_ScoreBoard.fields = { f_identifier, f_operator, f_left, f_right, f_red, f_green, f_blue } local data_dis = Dissector.get("data") local function ScoreBoard_dissector(buf,pkt,root) local buf_len = buf:len(); --先检查报文长度,太短的不是我的协议 if buf_len < 17 then return false end --取得前16字节identifier字段的值 local v_identifier = buf(0,16) --验证identifier是否正确 if ((buf(0,1):uint()~=226) or (buf(1,1):uint()~=203) or (buf(2,1):uint()~=181) or (buf(3,1):uint()~=128) or (buf(4,1):uint()~=203) or (buf(5,1):uint()~=9) or (buf(6,1):uint()~=78) or (buf(7,1):uint()~=186) or (buf(8,1):uint()~=163) or (buf(9,1):uint()~=107) or (buf(10,1):uint()~=246) or (buf(11,1):uint()~=7) or (buf(12,1):uint()~=206) or (buf(13,1):uint()~=149) or (buf(14,1):uint()~=63) or (buf(15,1):uint()~=43)) --不正确就不是我的协议 then return false end --取得operator的值 local v_operator = buf(16,1) local i_operator = v_operator:uint() --现在知道是我的协议了,放心大胆添加Packet Details local t = root:add(p_ScoreBoard,buf) --在Packet List窗格的Protocol列也可以“做个小广告” pkt.cols.protocol = "ScoreBoard" t:add(f_identifier,v_identifier) t:add(f_operator,v_operator) if ((i_operator == 1) or (i_operator == 128)) and (buf_len >= 25) then --把存在的字段逐个添加进去 t:add(f_left,buf(17,4)) t:add(f_right,buf(21,4)) elseif ((i_operator == 17) or (i_operator == 144)) and (buf_len >= 20) then t:add(f_red,buf(17,1)) t:add(f_green,buf(18,1)) t:add(f_blue,buf(19,1)) end return true end function p_ScoreBoard.dissector(buf,pkt,root) if ScoreBoard_dissector(buf,pkt,root) then --valid ScoreBoard diagram else --data这个dissector几乎是必不可少的;当发现不是我的协议时,就应该调用data data_dis:call(buf,pkt,root) end end local udp_encap_table = DissectorTable.get("udp.port") --只需要处理UDP1127端口就可以了 udp_encap_table:add(1127,p_ScoreBoard)end
查看ScoreBoard协议的Wireshark插件效果演示,下载ScoreBoard协议的样例客户端脚本、服务端程序、pcap抓包、Lua插件源码
我没有找到非常有效的插件调试方法。当Lua脚本中有语法错误时,Wireshark会在启动时弹出提示框;有调用错误时,会在相关数据包的Packet Details窗格中以红色显示。
你可以把捕获的数据包保存为.pcap文件,每次修改Lua脚本后双击.pcap文件打开Wireshark即可。不必每次都进行Live Capture。
Wireshark有了Lua支持后,如虎添翼,大大方便了插件的开发。当你发明了一个网络协议,要让Wireshark支持它的最佳办法就是动手用Lua语言写一个dissector。只要你充分理解自己设计的网络协议,结合Lua语言参考和Wireshark文档(不过有些函数名不够准确、请参考Wireshark源码),写出一个Wireshark插件还是挺容易的。
联系客服