Skip to content

Instantly share code, notes, and snippets.

@psolyca
Created January 10, 2022 22:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save psolyca/b91f4126bdbf5ef5e8c19f0007b59fbe to your computer and use it in GitHub Desktop.
Save psolyca/b91f4126bdbf5ef5e8c19f0007b59fbe to your computer and use it in GitHub Desktop.
Wireshark dissector for Huawei band protocol
----------------------------------------
--- Definitions
----------------------------------------
--- Huawei protocol used in Wireshark to dissect packets
--- @class Proto
local huawei_proto = {}
--- @class hpFields
local hpFields = {}
----------------------------------------
--- State of all packets
--- @type table<number, Packet>
local packets = {}
----------------------------------------
--- A ByteArray used to concatenate partial packets
--- @type ByteArray
local partialPacket
----------------------------------------
--- Internal data to parse Huawei Link Protocol
--- @class parser
--- @field masterKey ByteArray
--- @field serverNonce ByteArray
--- @field clientNonce ByteArray
--- @field serverMac string -- Device MAC
--- @field clientMac string
--- @field parseTvb function -- Parse Tvb
--- @field parseService function
--- @field parseTLV function
local parser = {}
---@class varInt
--- @field value function -- Retrieve the VarInt value
--- @field size function -- Retrieve the size of the VarInt value
local varInt = {}
--- @type function
local checkService
----------------------------------------
----------------------------------------
huawei_proto = Proto("Huawei", "Huawei Protocol")
hpFields = {
Slice = ProtoField.uint8("huawei.slice", "Slice", base.HEX), -- 0: Non sliced 1,2,3: Sliced
SliceFlag = ProtoField.uint8("huawei.sliceflag", "Slice Flag", base.HEX),
Body = ProtoField.bytes("huawei.body", "Body", base.SPACE),
}
huawei_proto.fields = hpFields
huawei_proto.init = function()
--- reset the saved Packets
packets = {}
partialPacket = nil
end
--- @param tvb Tvb
--- @param pinfo Pinfo
--- @param tree TreeItem
huawei_proto.dissector = function(tvb, pinfo, tree)
local length = tvb:len()
if length == 0 then return end
if parser.serverMac == nil then parser.serverMac = pinfo.dst end
if parser.clientMac == nil then parser.clientMac = pinfo.src end
--- State of a packet
--- @class Packet
--- @field bytearray ByteArray
local packet = packets[pinfo.number]
--- @type ByteArray
local payload
pinfo.cols.protocol = huawei_proto.name
--- @class TreeItem
HuaweiTree = tree:add(huawei_proto, tvb, "Huawei Protocol Data")
if packet ~= nil then --- Packet already exists
if packet.bytearray ~= nil then -- Treat it
tvb = ByteArray.tvb(packet.bytearray, "Complete packet")
else
return
end
else
packet = {}
if partialPacket == nil then -- First packet - complete or not - tvb start with magic
partialPacket = tvb:bytes()
else
partialPacket:append(tvb:bytes())
end
local expectedLength = tonumber(partialPacket:subset(1, 2):tohex(), 16)
local slice = tonumber(partialPacket:subset(3, 1):tohex(), 16) - 1
--- if slice == 0 then
payload = partialPacket:subset(4, partialPacket:len() - 6) -- 6 = 1 + 2 + 1 + 2
--- expectedLength = expectedLength - 1
if slice ~= 0 then
payload = partialPacket:subset(5, partialPacket:len() - 6) -- 6 = 1 + 2 + 1 + 2
expectedLength = expectedLength - 1
end
HuaweiTree:add("expectedLength: " .. expectedLength)
if expectedLength ~= (payload:len()) then -- packet is partial
return
end
-- packet is complete
packet.bytearray = partialPacket
packets[pinfo.number] = packet
partialPacket = nil
end
--- --- @class TreeItem
--- HuaweiTree = tree:add(huawei_proto, tvb, "Huawei Protocol Data")
return parser.parseTvb(tvb)
end
--- @param tvb Tvb
--- @return number
function parser.parseTvb(tvb)
--- @type TvbRange
local payload
local slice = tvb:range(3, 1):int()
HuaweiTree:add(hpFields.Slice, slice)
if slice == 0 then
payload = tvb:range(4, tvb:len() - 6) -- 6 = 1 + 2 + 1 + 2
else
HuaweiTree:add(hpFields.SliceFlag, tvb(4, 1)) -- check slice before
payload = tvb:range(5, tvb:len() - 6) -- 6 = 1 + 2 + 1 + 2
end
BodyTree = HuaweiTree:add(hpFields.Body, payload)
parser.parseService(payload:bytes())
end
--- @param bytearray ByteArray
function parser.parseService(bytearray)
local serviceId = bytearray:get_index(0)
local commandId = bytearray:get_index(1)
BodyTree:add("Service ID: " .. serviceId .. " - Command ID: " .. commandId)
if serviceId == 1 then
checkService(commandId)
end
bytearray = bytearray:subset(2, bytearray:len() - 2)
while true do
local tag, tlvSize, value = parser.parseTLV(bytearray)
local label = "tag=" .. tag .. ", value:(" .. tostring(value) .. ")"
BodyTree:add(label)
if tlvSize == bytearray:len() then
break
end
bytearray = bytearray:subset(tlvSize, bytearray:len() - tlvSize)
end
end
--- @param bytearray ByteArray
--- @return number, number, ByteArray
function parser.parseTLV(bytearray)
local tag = bytearray:get_index(0)
local lengthValue = varInt.value(bytearray:subset(1, bytearray:len() - 1)) -- length of value
local lengthSize = varInt.size(lengthValue) -- size of VarInt length
--- @type ByteArray
local value = nil
if lengthValue ~= 0 then
value = bytearray:subset(1 + lengthSize, lengthValue)
end
return tag, (1 + lengthSize + lengthValue), value
end
--- @param bytearray ByteArray
--- @return number
function varInt.value(bytearray)
local result = 0
for i = 0, bytearray:len() - 1 do
local var = bytearray:get_index(i)
result = result + bit32.band(var, 0x7F)
if bit32.band(var, 0x80) == 0 then
return result
end
result = bit32.lshift(result, 7)
end
end
--- @param value number
--- @return number
function varInt.size(value)
local result = 0
while true do
result = result + 1
value = bit32.rshift(value, 7)
if value == 0 then break end
end
return result
end
--- @param commandId ByteArray
function checkService(commandId)
parser.masterKey = commandId
end
local btatt = DissectorTable.get("btatt.handle")
btatt:add(0x002c, huawei_proto)
btatt:add(0x002e, huawei_proto)
@griffi-gh
Copy link

thanks

@psolyca
Copy link
Author

psolyca commented Oct 13, 2022

Your welcome but I do not consider it as finished.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment