在分析工控設備流量時,經過Wireshark內置的協議解碼插件能夠解析一些開放協議的數據格式,可是不少廠家考慮到安全性和產品獨特性並不會公開私有的報文格式。這就須要經過逆向工程或者查閱相關文檔來了解通信協議的數據格式,從而編寫Wireshark協議解碼插件來解析未知的工業網絡通信數據報文格式。安全
本文中,咱們以一個國產某工控設備的上位機流量做爲例子,以下:微信
使用Wireshark抓取到的流量以下,能夠看到該數據報文是基於TCP協議的,下位機的端口爲500。網絡
因爲上位機是C#寫的,能夠用dnSpy快速定位到其協議解析的邏輯代碼,經過靜態分析和動態調試的手段來分析出上位機和下位設備之間通訊的報文格式。tcp
該報文簡單格式以下表格,這裏只分析到報文格式的頭部,不過對於工業網絡通信數據分析來講,識別出該報文對應的業務操做已經足夠了。ide
目前對於Wireshark來講,C/C++語言和Lua腳本是編寫插件的主流語言,對於C/C++語言這類語言來講,不能否認它具備很是高的性能,可是其編譯配置較爲麻煩,每次修改都要從新編譯,而Lua腳本雖然解析效率沒前者那麼高,可是它的語法和修改都很是簡單,能夠提升了開發效率。因此,這裏選擇了Lua做爲編寫插件的語言。函數
Wireshark軟件是否支持Lua插件腳本的檢查方法:啓動Wireshark,依次點擊」幫助」,」關於 Wireshark「菜單,在打開的對話框中的」Wireshark」標籤頁上觀察版本信息,若是以下圖同樣顯示With Lua,說明此版本支持Lua插件。性能
在這裏,能夠將不一樣的命令解釋顯示出來,從而增長可讀性,以下:ui
--Etrol plugin local subcmd_desc={ [1]="SYSCOM", [2]="SCANCOM", [3]="EVENCOM", [4]="PIDCOM", [5]="PIDPARA", [6]="SYSMSG", [7]="HARTCOM", [8]="SYSRESET", [17]="DNP3DBCOM", [18]="DNP3CHN0COM", [19]="ZIGBEECOM", [20]="DNP3OTHER", } local operate_desc={ [0]="CLRMOD", [1]="READCOM", [2]="WRITECOM", [4]="WRITEFLASH", } local cmd_desc={ [137]="read/write", [144]="PROG_update", } Echo_protocol=Proto("Echo_500","Echo_500 protocol") pktid = ProtoField.uint32("Echo_500.ID", "ID", base.DEC) pktlen = ProtoField.uint32("Echo_500.length", "length", base.DEC) station=ProtoField.uint32("Echo_500.station", "station", base.DEC) cmd = ProtoField.uint32("Echo_500.cmd", "cmd", base.DEC,cmd_desc) address = ProtoField.uint32("Echo_500.address", "address", base.DEC) subcmd = ProtoField.uint32("Echo_500.subcmd", "subcmd", base.DEC,subcmd_desc) operate = ProtoField.uint32("Echo_500.operate", "operate", base.DEC,operate_desc) subaddress = ProtoField.uint32("Echo_500.subaddress", "subaddress", base.DEC) datalen = ProtoField.uint32("Echo_500.datalen", "datalen", base.DEC) data = ProtoField.bytes("Echo_500.Data", "Data") hex=function(num) return string.format("%#x",num) end Echo_protocol.fields={pktid,pktlen,station,cmd,address,subcmd,operate,subaddress,datalen,data} function Echo_protocol.dissector(buffer,pinfo,tree) local length=buffer:len() if length<14 then return end pinfo.cols.protocol=Echo_protocol.name local subtree=tree:add(Echo_protocol,buffer(),"Echo Protocol Data") subtree:add(pktid,buffer(0,4)) subtree:add(pktlen,buffer(4,2)) subtree:add(station,buffer(6,1)) subtree:add(cmd,buffer(7,1)) subtree:add(address,buffer(8,1)) subtree:add(subcmd,buffer(9,1)) sub_cmd=buffer(9,1):uint() subtree:add(operate,buffer(10,1)) Operate=buffer(10,1):uint() subtree:add(subaddress,buffer(11,2)) subtree:add(datalen,buffer(13,1)) if length>14 then local databuf=buffer(14,length-14) local data_subtree=subtree:add(data,databuf) end end local tcp_port=DissectorTable.get("tcp.port") tcp_port:add(500, Echo_protocol)
爲了Wireshark加載編寫好的插件,須要打開Wireshark主目錄下的init.lua文件,確保disable_lua的值爲false,即開啓了lua。同時將編寫好的插件保存爲echo_500.lua放到Wireshark主目錄,在init.lua文件最後一行添加dofile(DATA_DIR.."echo_500.lua"),經過這樣的方式加載自定義的插件。加密
重啓Wireshark,從新抓取數據包,能夠看到協議已經解析成功。這裏識別出來這是一個SYSMSG命令,用於讀取設備狀態。lua
以後每次修改腳本後,能夠經過快捷鍵「Ctrl+Shift+L」從新加載腳本,不須要每次重啓Wireshark軟件。
本文簡單介紹了Wireshark插件的編寫方法,經過編寫插件來解析第三方的私有協議,幫助咱們更好理解工控上位機和下位機的交互。固然,有些工控流量可能比本文的例子相對複雜一些,可能包含加密和簽名等字段,這就須要更長的逆向分析時間,只要把協議數據的報文格式瞭解清楚了,那麼編寫插件也是水到渠成的事情。
轉載請註明來自:工業互聯網安全應急響應中心(微信號ICSCERT)