Search code examples
luazigbeewireshark-dissector

How to only dissect one attribute of a Zigbee Cluster Library frame in a Wireshark Lua dissector


this is my first time asking and I am new to Lua, so please correct me if I am doing something wrong

General Problem:

I want to write a Lua dissector for wireshark that only dissects one attribute of a given Zigbee Cluster Library frame (see screenshot).

Wireshark example frame with attribute

Now I have got many problems and solutions for them that don't work.

Problem 1 - Adding the dissector (at the right spot?):

for testing purposes I added my dissector to the DissectorTable with the name "wpan.panid" for my given PANID with the value of "0x9dbd". This works, but only for the given PANID and way too "low level", because I want to only change one attribute in a deeper layer.

Question 1: So how do I get the dissector to always get called, no matter which PANID and how do I "anchor" (for lack of better wording) it only for my attribute "zbee_zcl.attr.id"

Problem 2 - Letting the "official" wireshark dissectors do its work and then just alter their result

My thought was, that I could call the wireshark dissectors "zbee_nwk", "zbee_aps" and "zbee_zcl" for the three layers and just alter the one attribute I want to dissect. But when I get the "zbee_nwk" dissector and call the call method on it with the exact same information that my dissector got, nothing happens and the tree just stays the same (see next screenshot).

My Wireshark Console. For code, see below

alreadyCalled = false

networkDissector = Dissector.get("zbee_nwk")
applicationSupportDissector = Dissector.get("zbee_aps")
clusterLibraryDissector = Dissector.get("zbee_zcl")

local myProtoField = ProtoField.new("zbee_zcl.attr.id", "zzbee_zcl.attr.id", ftypes.UINT16)
function myFunction(treeViewBuffer, packetInfo, tree)
    if(alreadyCalled == false) then
        print(tostring(treeViewBuffer) .. " / " .. tostring(packetInfo) .. " / " .. tostring(tree))
        print("Nr. of changed stuff: " .. networkDissector:call(treeViewBuffer, packetInfo, tree))
        print(tostring(treeViewBuffer) .. " / " .. tostring(packetInfo) .. " / " .. tostring(tree))
        alreadyCalled = true
    end
end

myProtocol = Proto("MYZIGBEE", "myZigbee")
myProtocol.dissector = myFunction
DissectorTable.get("wpan.panid"):add(0x9dbd, myProtocol)

Questions 2:

  • Why doesn't the called dissector alter the tree it is given?
  • Am I calling the call method wrong?
  • could it be, because the payload is encrypted and I just added the key in the Wireshark application?

TLDR: How would someone with knowledge and experience dissect the attribute and only the attribute shown in the first screenshot and let the "official" wireshark dissectors do the rest.

Example for dissection: Attribute: 0x0400 -> Attribute: Temperature (0x0400)

FROM ACCEPTED ANSWER: (By no means perfect. Loop could probably be made much more efficient)

function zbee_postDissector.dissector(tvbuf, pinfo, tree)

local attr_id_ex = {attr_id()}
i = 1
myValue = attr_id_ex[i]

if myValue == nil then
    return
end

pinfo.cols.protocol:set("zbee_post")

local attr_id_tvbr = attr_id_ex.range
local zbee_post_tree = tree:add(zbee_postDissector, attr_id_tvbr)

while myValue ~= nil do
    zbee_post_tree:add(pf.attr_id, myValue.range, myValue.value)
    
    i = i + 1
    myValue = attr_id_ex[i]
end
end

Solution

  • As far as I'm aware, it's not possible to modify an existing tree item. But what you can do is write a Lua postdissector that adds a new treeitem that is a modification of the existing tree item. For example:

    local zbee_post = Proto("zbee_post", "zbee_post dissector")
    
    -- The table of attribute IDs you care about.
    local attr_id_vals = {
        [0x0001] =  "Foo",
        [0x0004] = "Temperature"
    }
    
    local pf = {
        attr_id = ProtoField.uint16("zbee_post.attr.id", "Attribute", base.HEX, attr_id_vals)
    }
    zbee_post.fields = pf
    
    local attr_id = Field.new("zbee_zcl.attr.id")
    
    function zbee_post.dissector(tvbuf, pinfo, tree)
    
        local attr_id_ex = attr_id()
        if attr_id_ex == nil then
            return
        end
    
        pinfo.cols.protocol:set("zbee_post")
    
        local attr_id_tvbr = attr_id_ex.range
        local zbee_post_tree = tree:add(zbee_post, attr_id_tvbr)
    
        zbee_post_tree:add(pf.attr_id, attr_id_tvbr, attr_id_ex.value)
    end
    
    register_postdissector(zbee_post)