Lua編寫wireshark插件初探——解析Websocket上的MQTT協議


一、背景

最近在做物聯網流量分析時發現, App在使用MQTT協議時往往通過SSL+WebSocket+MQTT這種方式與服務器通信,在使用SSL中間人截獲數據后,Wireshark不能自動解析出MQTT語義,只能解析到WebSocket層,如圖所示。雖然在Data域中顯示了去掉mask的WebSocket數據,但分析起來mqtt仍然很難受。所以打算寫一個插件,利用wireshark自帶的MQTT解析功能來分析Data部分的數據,而不是自己從頭寫一個完全新的解析器。注:很多教程是教如何添加一個新的協議,如設置協議的屬性等,推薦參考【2】,本文主要梳理編寫插件的條理。

二、Lua編寫wireshark插件基礎

 有前輩介紹了用Lua寫wireshark插件的基礎教程,可以參考文末【1】【2】,這里再以自己的理解總結一下,因為實在沒有一個文檔讓我有從入門到精通的感覺。

1. 首先需要知道解析器(Dissector)和post-dissectors的相關概念【3】

1)解析器(Dissector)是用來被wireshark調用解析數據包或部分數據包的,需要以Proto對象的形式注冊后才能被wireshark調用。同時,我們還可以使用wireshark已經自帶的解析器,注冊一個解析器的例子代碼如下所示。

-- trivial protocol example
-- declare our protocol
--trival是協議名字,后面是說明,均需要在wireshark中唯一。
trivial_proto = Proto("trivial","Trivial Protocol") -- create a function to dissect it function trivial_proto.dissector(buffer,pinfo,tree) pinfo.cols.protocol = "TRIVIAL" local subtree = tree:add(trivial_proto,buffer(),"Trivial Protocol Data") subtree:add(buffer(0,2),"The first two bytes: " .. buffer(0,2):uint()) subtree = subtree:add(buffer(2,2),"The next two bytes") subtree:add(buffer(2,1),"The 3rd byte: " .. buffer(2,1):uint()) subtree:add(buffer(3,1),"The 4th byte: " .. buffer(3,1):uint()) end -- load the udp.port table udp_table = DissectorTable.get("udp.port") -- register our protocol to handle udp port 7777 udp_table:add(7777,trivial_proto)

 

2)解析器注冊分為很多種,可以使用函數register_postdissector(trivial_proto)注冊為postdissectors,即在所有解析器執行完后執行;也可以在DissectorTable上注冊,這樣就可以使用wireshark自帶的上一層協議解析后的結果。比如,協議TCP的解析表”tcp.port”包括http,smtp,ftp等。例如,你寫的解析器想解析tcp端口7777上的某個協議,就使用下面的代碼,而不必從tcp或者ip層開始解析。

-- load the udp.port table
udp_table = DissectorTable.get("udp.port")
-- register our protocol to handle udp port 7777
udp_table:add(7777,trivial_proto)

這個功能非常強大。直觀地,如果想解析WebSocket上的mqtt協議,可以這么寫【6】(但是不知什么原因我這么寫一直無法成功解析。):

local mqtt_dissector = Dissector.get("mqtt")
local ws_dissector_table = DissectorTable.get("ws.port")
ws_dissector_table:add(8083, mqtt_dissector)

通過上面這段代碼我們學習到,直接獲得wireshark中解析器的方法Dissector.get,更多的方法可以參考官方文檔11章【7】,比如我們如何獲得已經支持的所有協議呢?mqtt協議的解析器關鍵字是大寫還是小寫?可以這么寫【8】:

local t = Dissector.list()

for _,name in ipairs(t) do
    print(name)
end

--查看所有支持的table
local dt = DissectorTable.list()

for _,name in ipairs(dt) do
    print(name)
end

 

3)被調用時,wireshark會傳遞給解析器三個參數:數據緩沖區(一個Tvb 對象【4】)、包的信息(Pinfo對象【5】)以及顯示在圖形化中的樹形結構(TreeItem 對象 )。注意,理解這三個參數至關重要,同時注意它們不是Lua自身具有的數據類型,經常需要調用對象中的方法轉換。通過這三個參數, 解析器就可以獲得和修改包的相關信息。

Tvb就是包的數據內容,可以像這樣來提取內容。通常,我們需要提取出來包的內容當做字符串處理,或者提供字符串轉換成Tvb來讓解析器處理,這時候需要進行一些轉換,如下代碼所示【10】,詳細可參考【9】。

local b = ByteArray.new(decipheredFrame)
local bufFrame = ByteArray.tvb(b, "My Tvb")

 Pinfo經常被解釋為報文信息,個人理解簡單的說就是給了按照圖中這個條訪問報文的接口,最常見的例子就是修改協議列名稱或者info列顯示的消息,如pinfo.cols.protocol = "MQTT over Websocket" ,更多的屬性從參考文獻【5】中可以獲取。

 

TreeItem 對象表示報文解析樹中的一個樹節點,獲得了這個就可以動態往圖形化界面里添加節點。

 

2.調試與啟用插件

啟動

wireshark在啟動時會加載init.lua腳本, windows平台在wireshark安裝目錄下,linux在etc/wireshark下。想要執行我們寫的插件,只需在該腳本最后加上dofile(".\\plugins\\mqttoverwebsocket.lua")來執行即可。重新加載Lua腳本的快捷鍵是Ctrl+Shift+L

調試

若腳本有語法錯誤,wireshark圖形界面在加載時會彈出提示;若有運行時錯誤,會在圖形化的協議樹中顯示;wireshark還有一個Lua終端來執行編寫的插件腳本、打印錯誤信息,通過“工具——Lua——console”打開,動態執行腳本通過“工具——Lua——evaluate”。注意看到輸出需要使用wireshark提供的內置函數如debug(text)來輸出【14】。

三、實現解析Websocket上的MQTT協議

 由於不明原因將mqtt協議解析器注冊到ws.port或ws.protocol上仍然無法自動解析MQTT,所以我選擇首先獲得已經解析好去掉mask后的WebSocket的data字段,然后再將其轉換成tvb到mqtt解析器中自動解析。獲得包解析后內容的方法主要參考【11】和【12】中的解析樹的例子,使用fieldinfo類與全局函數all_field_infos()來獲得解析樹的各個部分內容。

由於傳入mqtt解析器的tree就是這個包的樹根,所以也會自動添加一個節點。最后取得了不錯的效果。另附github鏈接:https://github.com/a3135134/Wireshark-Plugin-MQTToverWebSocket.git

 

do
    -- calling tostring() on random FieldInfo's can cause an error, so this func handles it
    local function getstring(finfo)
        local ok, val = pcall(tostring, finfo)
        if not ok then val = "(unknown)" end
        return val
    end
    
    -- Create a new dissector
    MQTToverWebsocket = Proto("MQTToverWebsocket", "MQTT over Websocket")
    mqtt_dissector = Dissector.get("mqtt")
    -- The dissector function
    function MQTToverWebsocket.dissector(buffer, pinfo, tree)
        local fields = { all_field_infos() }
        local websocket_flag = false
        for i, finfo in ipairs(fields) do
            if (finfo.name == "websocket") then
                websocket_flag = true
            end
            if (websocket_flag == true and finfo.name == "data") then
                local str1 = getstring(finfo)
                local str2 = string.gsub(str1, ":", "")
                local bufFrame = ByteArray.tvb(ByteArray.new(str2))
                mqtt_dissector = Dissector.get("mqtt")
                --mqtt_dissector:call(finfo.source, pinfo, tree) #9 BUG
                mqtt_dissector:call(bufFrame, pinfo, tree)
                --mqtt_dissector:call(finfo.value, pinfo, tree)
                websocket_flag = false
                pinfo.cols.protocol = "MQTT over Websocket"
            end
    end
        
        --ws_dissector_table = DissectorTable.get("ws.port")
        --ws_dissector_table:add("443",mqtt_dissector)
    end
    -- Register the dissector
    --ws_dissector_table = DissectorTable.get("ws.port")
    --ws_dissector_table:remove(443, mqtt_dissector)
    --ws_dissector_table:add(443, MQTTPROTO)
    --ws_dissector_table:add_for_decode_as(mqtt_dissector)
    register_postdissector(MQTToverWebsocket)
end

 

四、TIPS與BUG

TIP1.如果遇到非知名端口上的多層解析怎么辦?如遇到1885端口上的SSL+Websocket+MQTT如何處理?

首先選擇要解析的包,右鍵點擊“解碼為...”,設置當前1885端口為SSL Port,然后將Current的None修改為HTTP。這樣Wireshark才會將該包解析顯示為Websocket,之后才能使用該插件解析。wireshark默認只解析知名端口如443,所以經常還是要憑借經驗來自己配置。

 

TIP2.手機端使用HTML5編寫控制頁面(國內很多智能家居都是,如蘇寧智能等),Hook后就打不開網頁(似乎是由於使用了系統webview),怎么辦?

想辦法獲得訪問的頁面URL,將該頁面放到瀏覽器中去模擬訪問再抓包。設置系統變量就可以將瀏覽器的SSL Session Key導出,再設置wireshark的“編輯——首選項——協議——SSL”就可以解析了。

 

BUG1.這個插件在wireshark升級或重新安裝后會報錯,提示已經注冊了相同說明的協議,不知如何解決,但不影響使用。

 

參考文獻1

【1】http://www.cnblogs.com/zzqcn/p/4827251.html

【2】https://mika-s.github.io/wireshark/lua/dissector/2017/11/04/creating-a-wireshark-dissector-in-lua-1.html

【3】https://wiki.wireshark.org/Lua/Dissectors#Dissectors

【4】https://wiki.wireshark.org/LuaAPI/Tvb#Tvb

【5】https://wiki.wireshark.org/LuaAPI/Pinfo#Pinfo

【6】https://ask.wireshark.org/question/1480/mqtt-over-websocket/

【7】https://www.wireshark.org/docs/wsdg_html_chunked/lua_module_Proto.html#lua_class_Dissector

【8】https://osqa-ask.wireshark.org/questions/32288/can-over-ethernet-lua-dissector

【9】https://www.wireshark.org/docs/wsdg_html_chunked/lua_module_Tvb.html

【10】https://osqa-ask.wireshark.org/questions/43013/conversion-of-string-into-userdata-type-like-wiresharks-buffer

【11】https://www.wireshark.org/docs/wsdg_html_chunked/lua_module_Field.html#lua_class_Field

【12】https://wiki.wireshark.org/Lua

【13】https://wiki.wireshark.org/Lua/Examples#View_Packet_Tree_of_Fields.2FFieldInfo

【14】https://wiki.wireshark.org/LuaAPI/Utils

 

By ascii0x03, 2018/4/10,轉載請注明出處

我的博客即將搬運同步至騰訊雲+社區,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=16ze50p7mjz0y


免責聲明!

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



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