Created
January 10, 2022 22:31
-
-
Save psolyca/b91f4126bdbf5ef5e8c19f0007b59fbe to your computer and use it in GitHub Desktop.
Wireshark dissector for Huawei band protocol
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
---------------------------------------- | |
--- 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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Your welcome but I do not consider it as finished.