Wireshark iAP2 协议解析插件

Wireshark iAP2 协议解析插件

什么是 iAP2

iAP2(iPod Accessory Protocol 2)是 Apple 设备与外部配件之间的通信协议,广泛用于 CarPlay、MFi 配件认证、蓝牙配对、媒体播放控制等场景。iAP2 的数据包是二进制格式,直接看原始数据非常痛苦。

插件功能

iap2.lua 是一个 Wireshark Lua 解析插件,能将 iAP2 二进制数据自动解析为可读的协议树,主要功能包括:

  • 链路层解析:SYN/ACK/RST 等控制标志、序列号、会话 ID、校验和验证(错误时红色高亮)
  • 消息类型识别:覆盖认证、设备识别、CarPlay、媒体播放、蓝牙配对、Wi-Fi 共享、通话控制、VoiceOver 等数十种 CSM 消息
  • 关键参数深度解析:设备识别信息(名称/型号/制造商)、Wi-Fi 配置(SSID/密码/安全类型)、设备时间、UUID 等
  • 蓝牙 UUID 自动匹配:蓝牙抓包时自动识别 iAP2 流量,无需手动配置

安装方法

iap2.lua 复制到 Wireshark 插件目录:

  • Windows%APPDATA%\Wireshark\plugins\
  • macOS / Linux~/.local/lib/wireshark/plugins/

重启 Wireshark,通过 Help → About Wireshark → Plugins 确认加载成功。

使用方法

蓝牙场景:打开 btsnoop / HCI 日志文件,插件根据蓝牙 UUID 自动识别,直接就能看到解析结果。

其他场景(USB/串口):右键数据包 → Decode As → 选择 iAP2

加载后 Wireshark 会在 Protocol 列显示 iAP2,Info 列显示具体消息类型(如 AuthenticationSucceededIdentificationInformation),展开数据包可查看完整协议字段。

-- IAP2 协议解析器
-- 基于 Apple 的 iAP2 协议规范

local csm_msg_id_types = {
    --1)AUTH
    --AA表示Accessory Authentication
    [0xAA00] = "RequestAuthenticationCertificate",
    [0xAA01] = "AuthenticationCertificate",
    [0xAA02] = "RequestAuthenticationChallengeResponse",
    [0xAA03] = "AuthenticationResponse",
    [0xAA04] = "AuthenticationFailed",
    [0xAA05] = "AuthenticationSucceeded",
    [0xAA06] = "AccessoryAuthenticationSerialNumber",
    [0xAA10] = "RequestDeviceAuthenticationCertificate",
    [0xAA11] = "DeviceAuthenticationCertificate",
    [0xAA12] = "RequestDeviceAuthenticationChallengeResponse",
    [0xAA13] = "DeviceAuthenticationResponse",
    [0xAA14] = "DeviceAuthenticationFailed",
    [0xAA15] = "DeviceAuthenticationSucceeded",

    --Power
    --AE
    [0xAE00] = "StartPowerUpdates",
    [0xAE01] = "PowerUpdate",
    [0xAE02] = "StopPowerUpdates",
    [0xAE03] = "PowerSourceUpdate",

    --2)Identification
    --1D表示Identification
    [0x1D00] = "StartIdentification",
    [0x1D01] = "IdentificationInformation",
    [0x1D02] = "IdentificationAccepted",
    [0x1D03] = "IdentificationRejected",
    [0x1D05] = "CancelIdentification",
    [0x1D06] = "IdentificationInformationUpdate",

    --3)HID over iAP2
    [0x6800] = "StartHID",
    [0x6801] = "DeviceHIDReport",
    [0x6802] = "AccessoryHIDReport",
    [0x6803] = "StopHID",
    [0x6806] = "StartNativeHID",

    --4)iPod音频播放模式
    --DA表示Digital Audio
    [0xDA00] = "StartUSBDeviceModeAudio",
    [0xDA01] = "USBDeviceModeAudioInformation",
    [0xDA02] = "StopUSBDeviceModeAudio",

    --5)媒体库访问
    --- 媒体库信息
    [0x4C00] = "StartMediaLibraryInformation",
    [0x4C01] = "MediaLibraryInformation",
    [0x4C02] = "StopMediaLibraryInformation",

    --- 媒体库信息更新
    [0x4C03] = "StartMediaLibraryUpdates",
    [0x4C04] = "MediaLibraryUpdate",
    [0x4C05] = "StopMediaLibraryUpdates",

    --- 媒体库播放命令
    [0x4C06] = "PlayMediaLibraryCurrentSelection",
    [0x4C07] = "PlayMediaLibraryItems",
    [0x4C08] = "PlayMediaLibraryCollection",
    [0x4C09] = "PlayMediaLibrarySpecial",

    --6)Now Playing - 显示正在播放歌曲的metadata信息
    [0x5000] = "StartNowPlayingUpdates",
    [0x5001] = "NowPlayingUpdate",
    [0x5002] = "StopNowPlayingUpdates",
    [0x5003] = "SetNowPlayingInformation",

    --7)External Accessory Protocol
    --EA表示External Accessory
    [0xEA00] = "StartExternalAccessoryProtocolSession",
    [0xEA01] = "StopExternalAccessoryProtocolSession",
    [0xEA02] = "RequestAppLaunch",
    [0xEA03] = "StatusExternalAccessoryProtocolSession",

    --Device Notifications
    --4E
    [0x4E01] = "BluetoothComponentInformation",
    [0x4E03] = "StartBluetoothConnectionUpdates",
    [0x4E04] = "BluetoothConnectionUpdate",
    [0x4E05] = "StopBluetoothConnectionUpdates",
    [0x4E09] = "DeviceInformationUpdate",
    [0x4E0A] = "DeviceLanguageUpdate",
    [0x4E0B] = "DeviceTimeUpdate",
    [0x4E0C] = "DeviceUUIDUpdate",
    [0x4E0D] = "WirelessCarPlayUpdate",
    [0x4E0E] = "DeviceTransportIdentifierNotification",

    --Wi-Fi Information Sharing
    --57
    [0x5700] = "RequestWiFiInformation",
    [0x5701] = "WiFiInformation",
    [0x5702] = "RequestAccessoryWiFiConfigurationInformation",
    [0x5703] = "AccessoryWiFiConfigurationInformation",

    --Carplay new
    --43
    [0x4300] = "CarPlayAvailability",
    [0x4301] = "CarPlayStartSession",

    --vehicle_status
    --A1
    [0xA100] = "StartVehicleStatusUpdates",
    [0xA101] = "VehicleStatusUpdate",
    [0xA102] = "StopVehicleStatusUpdates",

    [0x4170] = "StartListUpdates",
    [0x4171] = "ListUpdate",
    [0x4172] = "StopListUpdates",

    --Assistive Touch
    --54
    [0x5400] = "StartAssistiveTouch",
    [0x5401] = "StopAssistiveTouch",
    [0x5402] = "StartAssistiveTouchInformation",
    [0x5403] = "AssistiveTouchInformation",
    [0x5404] = "StopAssistiveTouchInformation",

    --VoiceOver
    --56
    [0x5601] = "RequestVoiceOverMoveCursor",
    [0x5602] = "RequestVoiceOverActivateCursor",
    [0x5603] = "RequestVoiceOverScrollPage",
    [0x5606] = "RequestVoiceOverSpeakText",
    [0x5608] = "RequestVoiceOverPauseText",
    [0x5609] = "RequestVoiceOverResumeText",
    [0x560B] = "StartVoiceOverUpdates",
    [0x560C] = "VoiceOverUpdate",
    [0x560D] = "StopVoiceOverUpdates",
    [0x560E] = "RequestVoiceOverConfiguration",
    [0x560F] = "StartVoiceOverCursorUpdates",
    [0x5610] = "VoiceOverCursorUpdate",
    [0x5611] = "StopVoiceOverCursorUpdates",
    [0x5612] = "StartVoiceOver",
    [0x5613] = "StopVoiceOver",

    --Communications
    --41
    [0x4154] = "StartCallStateUpdates",
    [0x4155] = "CallStateUpdate",
    [0x4156] = "StopCallStateUpdates",
    [0x4157] = "StartCommunicationUpdates",
    [0x4158] = "CommunicationsUpdate",
    [0x4159] = "StopCommunicationsUpdates",
    [0x415A] = "InitiateCall",
    [0x415B] = "AcceptCall",
    [0x415C] = "EndCall",
    [0x415D] = "SwapCalls",
    [0x415E] = "MergeCalls",
    [0x415F] = "HoldStatusUpdate",
    [0x4160] = "MuteStatusUpdate",
    [0x4161] = "SendDTMF",

    --Out-of-Band Bluetooth Pairing
    --00B
    [0x00B0] = "StartOOBBTPairing",
    [0x00B1] = "OOBBTPairingAccessoryInformation",
    [0x00B2] = "OOBBTPairingLinkKeyInformation",
    [0x00B3] = "OOBBTPairingCompletionInformation",
    [0x00B4] = "StopOOBBTPairing",

    --Location
    --FF
    [0xFFF0] = "GPRMCDataStatusValuesNotification",
    [0xFFFA] = "StartLocationInformation",
    [0xFFFB] = "LocationInformation",
    [0xFFFC] = "StopLocationInformation",

}

IdentificationInformationParamId = {
    [0x00] = "name",
    [0x01] = "model_identifier",
    [0x02] = "manufacturer",
    [0x03] = "serial_number",
    [0x04] = "fireware_version",
    [0x05] = "hardware_version",
    [0x06] = "messages_sent_by_accessory",
    [0x07] = "messages_received_from_accessory",
    [0x08] = "power_providing_capability",
    [0x09] = "maximum_current_drawn_from_device",
    [0x0A] = "supported_external_accessory_protocol",
    [0x0B] = "app_match_team_id",
    [0x0C] = "current_language",
    [0x0D] = "supported_language",
    [0x0E] = "serial_transport_component",
    [0x0F] = "usb_device_transport_component",
    [0x10] = "usb_host_transport_component",
    [0x11] = "bluetooth_transport_component",
    [0x14] = "vehicle_information_component",
    [0x15] = "vehicle_status_component",
    [0x18] = "wireless_car_play_transport_component"
}

IdentificationInformationParamType = {
    [0x00] = "string",
    [0x01] = "string",
    [0x02] = "string",
    [0x03] = "string",
    [0x04] = "string",
    [0x05] = "string",
    [0x06] = "uint32",
    [0x07] = "uint32",
    [0x08] = "bool",
    [0x09] = "uint16",
    [0x0A] = "string",
    [0x0B] = "string",
    [0x0C] = "string",
    [0x0D] = "string",
    [0x0E] = "bytes",
    [0x0F] = "bytes",
    [0x10] = "bytes",
    [0x11] = "bytes",
    [0x12] = "bytes",
    [0x13] = "bytes",
    [0x14] = "bytes"
}


SecurityType = {
    [0] = 'NONE',
    [1] = 'WEP_NEW',
    [2] = 'WPA_WPA2',
}

AccessoryWiFiConfigurationInformation = {
    [0x0] = "unknown",
    [0x1] = "ssid",
    [0x2] = "passphrase",
    [0x3] = "security_type",
    [0x4] = "channel",
}

-- 定义协议
local iap2_proto = Proto("iAP2", "Apple iAP2 Protocol")

-- 定义字段
local f = iap2_proto.fields
f.info = ProtoField.string("iap2.info", "Info")

f.sop = ProtoField.uint8("iap2.sop", "SOP", base.HEX)
f.len = ProtoField.uint16("iap2.len", "Length", base.DEC)
f.ctl = ProtoField.uint8("iap2.ctl", "Control", base.HEX)
f.ctl_syn = ProtoField.bool("iap2.ctl.syn", "SYN", 8, nil, 0x80)
f.ctl_ack = ProtoField.bool("iap2.ctl.ack", "ACK", 8, nil, 0x40)
f.ctl_eak = ProtoField.bool("iap2.ctl.eak", "EAK", 8, nil, 0x20)
f.ctl_rst = ProtoField.bool("iap2.ctl.rst", "RST", 8, nil, 0x10)
f.ctl_sus = ProtoField.bool("iap2.ctl.sus", "SUS", 8, nil, 0x08)
f.seq = ProtoField.uint8("iap2.seq", "Sequence Number", base.DEC)
f.ack = ProtoField.uint8("iap2.ack", "Acknowledgement Number", base.DEC)
f.sess = ProtoField.uint8("iap2.sess", "Session ID", base.DEC)
f.chk_header = ProtoField.uint8("iap2.hchk", "Header Checksum", base.HEX)
f.payload = ProtoField.bytes("iap2.payload", "Payload")
f.chk_payload = ProtoField.uint8("iap2.pchk", "Payload Checksum", base.HEX)

-- 定义 SYN 数据包特殊字段
f.syn_version = ProtoField.uint8("iap2.syn.version", "Version", base.DEC)
f.syn_maxOutstanding = ProtoField.uint8("iap2.syn.maxOutstanding", "Max Outstanding Packets", base.DEC)
f.syn_maxPacketSize = ProtoField.uint16("iap2.syn.maxPacketSize", "Max Packet Size", base.DEC)
f.syn_retransTimeout = ProtoField.uint16("iap2.syn.retransTimeout", "Retransmit Timeout (ms)", base.DEC)
f.syn_cumAckTimeout = ProtoField.uint16("iap2.syn.cumAckTimeout", "Cumulative ACK Timeout (ms)", base.DEC)
f.syn_maxRetrans = ProtoField.uint8("iap2.syn.maxRetrans", "Max Retransmissions", base.DEC)
f.syn_maxCumAck = ProtoField.uint8("iap2.syn.maxCumAck", "Max Cumulative ACK", base.DEC)

f.csm_control = ProtoField.uint8("iap2.csm.control", "Csm Control", base.HEX)
f.csm_len = ProtoField.uint8("iap2.csm.len", "Csm Length", base.DEC)
f.csm_msg_id = ProtoField.uint8("iap2.csm.msg_id", "Csm Message Id", base.HEX, csm_msg_id_types)
f.csm_param_list = ProtoField.bytes("iap2.csm.param_list", "Csm Param List")
f.csm_param_length = ProtoField.uint8("iap2.csm.length", "Csm Param Length", base.DEC)
f.csm_param_id = ProtoField.uint8("iap2.csm.param_id", "Csm Param Id", base.HEX)
f.csm_param = ProtoField.bytes("iap2.csm.param", "Csm Param")
f.csm_param_str = ProtoField.string("iap2.csm.param", "Csm Param")
f.IdentificationInformationParamId = ProtoField.uint8("iap2.IdentificationInformationParamId", "Identification Information Param Id", base.Dec, IdentificationInformationParamId)
f.AccessoryWiFiConfigurationInformation = ProtoField.uint8("iap2.AccessoryWiFiConfigurationInformation", "Accessory WiFi Configuration Information", base.Dec, AccessoryWiFiConfigurationInformation)
f.SecurityType = ProtoField.uint8("iap2.SecurityType", "Security Type", base.Dec, SecurityType)
f.ssid = ProtoField.string("iap2.ssid", "SSID")
f.passphrase = ProtoField.string("iap2.passphrase", "Passphrase")
f.channel = ProtoField.uint8("iap2.channel", "Channel", base.DEC)
f.uuid = ProtoField.string("iap2.uuid", "UUID")
f.name = ProtoField.string("iap2.name", "Name")
f.language = ProtoField.string("iap2.language", "Language")
f.bluetooth_id = ProtoField.string("iap2.bluetooth_id", "Bluetooth ID")
f.usb_id = ProtoField.string("iap2.usb_id", "USB ID")
f.available = ProtoField.bool("iap2.available", "Available")
f.time = ProtoField.absolute_time("iap2.time", "Time")

-- 会话信息字段
f.session = ProtoField.bytes("iap2.session", "Session Information")
f.sess_id = ProtoField.uint8("iap2.session.id", "ID", base.DEC)
f.sess_type = ProtoField.uint8("iap2.session.type", "Type", base.DEC, {[0] = "Control", [1] = "Buffer", [2] = "EA"})
f.sess_version = ProtoField.uint8("iap2.session.version", "Version", base.DEC)

f.AuthenticationCertificate = ProtoField.bytes("iap2.AuthenticationCertificate", "Authentication Certificate")

-- Detect/Handshake 包字段 (固定序列 FF 55 02 00 EE 10)
f.detect_bytes = ProtoField.bytes("iap2.detect", "Detect")

-- 计算校验和
local function calc_checksum(buffer, start, length)
    local sum = 0
    for i = start, start + length - 1 do
        sum = sum + buffer(i, 1):uint()
    end
    return bit.band(0x100 - sum, 0xFF)
end

--解析器函数
---@param tvb Tvb buffer对象
---@param pinfo Pinfo 数据包信息
---@param tree TreeItem 树节点
---@return boolean
function iap2_proto.dissector(tvb, pinfo, tree)
    local total = 0

::restart::
    if tvb:len() < total + 6 then
        return true
    end

    local buffer = tvb(total)
    local length = buffer:len()

    -- 检查 Detect/Handshake 包: 固定序列 FF 55 02 00 EE 10
    if buffer(0, 1):uint() == 0xFF and buffer(1, 1):uint() == 0x55
        and buffer(2, 1):uint() == 0x02 and buffer(3, 1):uint() == 0x00
        and buffer(4, 1):uint() == 0xEE and buffer(5, 1):uint() == 0x10 then
        pinfo.cols.protocol:set("iAP2")
        local subtree = tree:add(iap2_proto, buffer(0, 6), "Apple iAP2 Detect")
        subtree:add(f.detect_bytes, buffer(0, 6)):set_text("Detect: FF 55 02 00 EE 10")
        pinfo.cols.info:append(" Detect")
        total = total + 6
        goto restart
    end

    if tvb:len() < total + 9 then
        if tvb:len() > total then
            local subtree = tree:add(iap2_proto, tvb(total), "Apple iAP2 Protocol")
        end
        return true
    end

    -- 检查 SOP 标记
    if buffer(0, 1):uint() ~= 0xFF or buffer(1, 1):uint() ~= 0x5A then
        return false
    end

    -- 设置协议列
    pinfo.cols.protocol:set("iAP2")

    -- 计算总长度
    local packet_len = buffer(2, 1):uint() * 256 + buffer(3, 1):uint()
    total = total + packet_len

    -- 创建协议树
    if packet_len > length then packet_len = length end
    local subtree = tree:add(iap2_proto, buffer(0, packet_len), "Apple iAP2 Protocol")

    -- 解析包头
    subtree:add(f.sop, buffer(0, 2))
    subtree:add(f.len, buffer(2, 2))

    -- 控制字节
    local ctl = buffer(4, 1):uint()
    local ctl_tree = subtree:add(f.ctl, buffer(4, 1))
    ctl_tree:add(f.ctl_syn, buffer(4, 1))
    ctl_tree:add(f.ctl_ack, buffer(4, 1))
    ctl_tree:add(f.ctl_eak, buffer(4, 1))
    ctl_tree:add(f.ctl_rst, buffer(4, 1))
    ctl_tree:add(f.ctl_sus, buffer(4, 1))

    -- 序列号、确认号和会话ID
    subtree:add(f.seq, buffer(5, 1))
    subtree:add(f.ack, buffer(6, 1))
    subtree:add(f.sess, buffer(7, 1))

    -- 头校验和
    local hchk = buffer(8, 1):uint()
    local hchk_calc = calc_checksum(buffer, 0, 8)
    local hchk_item = subtree:add(f.chk_header, buffer(8, 1))
    if hchk ~= hchk_calc then
        hchk_item:add_expert_info(PI_CHECKSUM, PI_ERROR, "Header checksum incorrect")
    end

    -- 确定包类型并添加到信息列
    local packet_type = ""
    if bit.band(ctl, 0x80) ~= 0 then packet_type = packet_type .. "SYN " end
    if bit.band(ctl, 0x40) ~= 0 then packet_type = packet_type .. "ACK " end
    if bit.band(ctl, 0x20) ~= 0 then packet_type = packet_type .. "EAK " end
    if bit.band(ctl, 0x10) ~= 0 then packet_type = packet_type .. "RST " end
    if bit.band(ctl, 0x08) ~= 0 then packet_type = packet_type .. "SUS " end

    if packet_type == "" then packet_type = "DATA " end

    --pinfo.cols.info = tostring(pinfo.cols.info) .. " " .. packet_type .. "Seq=" .. buffer(5, 1):uint() .. " Ack=" .. buffer(6, 1):uint()

    if packet_len == 9 then
        pinfo.cols.info:append(" ACK")
    end
    -- 检查是否有负载数据
    local payload_len = packet_len - 9 - 1  -- 包长度减去头部和负载校验和
    if payload_len < 0 then
        goto restart
    end

    if length < packet_len then
        return true
    end

    local payload_data = buffer(9, payload_len)
    local payload_tree = subtree:add(f.payload, payload_data)

    -- 如果是SYN包,解析SYN数据
    if bit.band(ctl, 0x80) ~= 0 and payload_len >= 10 then
        pinfo.cols.info:append(" SYN")

        subtree:add(f.syn_version, payload_data(0, 1))
        subtree:add(f.syn_maxOutstanding, payload_data(1, 1))

        -- 最大包大小 (2字节)
        subtree:add(f.syn_maxPacketSize, payload_data(2, 2))

        -- 重传超时 (2字节)
        subtree:add(f.syn_retransTimeout, payload_data(4, 2))

        -- 累积确认超时 (2字节)
        subtree:add(f.syn_cumAckTimeout, payload_data(6, 2))

        subtree:add(f.syn_maxRetrans, payload_data(8, 1))
        subtree:add(f.syn_maxCumAck, payload_data(9, 1))

        -- 会话数量
        if payload_len > 10 then
            for i = 10, payload_len - 1, 3 do
                local sess_tree = subtree:add(f.session, payload_data(i, 3))
                sess_tree:add(f.sess_id, payload_data(i, 1))
                sess_tree:add(f.sess_type, payload_data(i + 1, 1))
                sess_tree:add(f.sess_version, payload_data(i + 2, 1))
            end
        end
    elseif payload_data(0,1):uint() == 0x40 and  payload_data(1,1):uint() == 0x40 then
        subtree:add(f.csm_control, payload_data(0, 2))
        subtree:add(f.csm_len, payload_data(2, 2))
        subtree:add(f.csm_msg_id, payload_data(4, 2))

        local csm_msg_id = payload_data(4, 2):uint()
        local csm_msg_id_str = csm_msg_id_types[csm_msg_id] or string.format("0x%X", csm_msg_id)
        pinfo.cols.info:append(" " .. csm_msg_id_str)

        if payload_len <= 6 then goto checksum end

        local pos = 6

        while pos + 4 <= payload_len do
            local csm_param_length = payload_data(pos, 2):uint()
            local csm_param_id = payload_data(pos + 2, 2):uint()

            -- 添加 CSM 参数到树中
            local csm_param_tree = subtree:add(f.csm_param_list, payload_data(pos, csm_param_length))
            csm_param_tree:add(f.csm_param_length, payload_data(pos, 2))
            if csm_msg_id == 0x1D01 then
                csm_param_tree:add(f.IdentificationInformationParamId, payload_data(pos + 2, 2))
            elseif csm_msg_id == 0x5703 then
                csm_param_tree:add(f.AccessoryWiFiConfigurationInformation, payload_data(pos + 2, 2))
            else
                csm_param_tree:add(f.csm_param_id, payload_data(pos + 2, 2))
            end

            if csm_param_length > 4 then
                if csm_msg_id == 0x1D01 then
                    if IdentificationInformationParamType[csm_param_id] == "string" then
                        csm_param_tree:add(f.csm_param_str, payload_data(pos + 4, csm_param_length - 4))
                        local param_value = payload_data(pos + 4, csm_param_length - 4):string()
                        local param_name = IdentificationInformationParamId[csm_param_id] or string.format("0x%X", csm_param_id)
                        csm_param_tree:set_text(param_name .. ": " .. param_value)
                    else
                        csm_param_tree:add(f.csm_param, payload_data(pos + 4, csm_param_length - 4))
                        local param_value = payload_data(pos + 4, csm_param_length - 4)
                        local param_name = IdentificationInformationParamId[csm_param_id] or string.format("0x%X", csm_param_id)
                        csm_param_tree:set_text(param_name .. ": " .. param_value)
                    end
                elseif csm_msg_id == 0x5703 then
                    if csm_param_id == 0x1 then
                        csm_param_tree:add(f.ssid, payload_data(pos + 4, csm_param_length - 4))
                        csm_param_tree:set_text("SSID: " .. payload_data(pos + 4, csm_param_length - 4):string())
                        pinfo.cols.info:append(" SSID:" .. payload_data(pos + 4, csm_param_length - 4):string())
                    elseif csm_param_id == 0x2 then
                        csm_param_tree:add(f.passphrase, payload_data(pos + 4, csm_param_length - 4))
                        csm_param_tree:set_text("Passphrase: " .. payload_data(pos + 4, csm_param_length - 4):string())
                        pinfo.cols.info:append(" Passphrase:" .. payload_data(pos + 4, csm_param_length - 4):string())
                    elseif csm_param_id == 0x3 then
                        csm_param_tree:add(f.SecurityType, payload_data(pos + 4, csm_param_length - 4))
                        local security_type = payload_data(pos + 4, csm_param_length - 4):uint()
                        local security_type_str = SecurityType[security_type] or string.format("0x%X", security_type)
                        csm_param_tree:set_text("Security Type: " .. security_type_str)
                        pinfo.cols.info:append(" SecurityType:" .. security_type_str)
                    elseif csm_param_id == 0x4 then
                        csm_param_tree:add(f.channel, payload_data(pos + 4, csm_param_length - 4))
                        csm_param_tree:set_text("Channel: " .. payload_data(pos + 4, csm_param_length - 4):uint())
                        pinfo.cols.info:append(" Channel:" .. payload_data(pos + 4, csm_param_length - 4):uint())
                    else
                        csm_param_tree:add(f.csm_param, payload_data(pos + 4, csm_param_length - 4))
                        csm_param_tree:set_text(string.format("Param 0x%X", csm_param_id) .. ": " .. payload_data(pos + 4, csm_param_length - 4))
                    end
                elseif csm_msg_id == 0x4E09 then
                    if csm_param_id == 0x00 then
                        csm_param_tree:add(f.name, payload_data(pos + 4, csm_param_length - 4))
                        csm_param_tree:set_text("Name: " .. payload_data(pos + 4, csm_param_length - 4):string())
                        pinfo.cols.info:append(" Name:" .. payload_data(pos + 4, csm_param_length - 4):string())
                    else
                        csm_param_tree:add(f.csm_param, payload_data(pos + 4, csm_param_length - 4))
                        csm_param_tree:set_text(string.format("Param 0x%X", csm_param_id) .. ": " .. payload_data(pos + 4, csm_param_length - 4))
                    end
                elseif csm_msg_id == 0x4E0A then
                    if csm_param_id == 0x00 then
                        csm_param_tree:add(f.language, payload_data(pos + 4, csm_param_length - 4))
                        csm_param_tree:set_text("Language: " .. payload_data(pos + 4, csm_param_length - 4):string())
                        pinfo.cols.info:append(" Language:" .. payload_data(pos + 4, csm_param_length - 4):string())
                    else
                        csm_param_tree:add(f.csm_param, payload_data(pos + 4, csm_param_length - 4))
                        csm_param_tree:set_text(string.format("Param 0x%X", csm_param_id) .. ": " .. payload_data(pos + 4, csm_param_length - 4))
                    end
                elseif csm_msg_id == 0x4E0B then
                    if csm_param_id == 0x00 then
                        local time_seconds = payload_data(pos + 8, csm_param_length - 8):uint()
                        csm_param_tree:add(f.time, payload_data(pos + 8, csm_param_length - 8))
                        local time_str = tostring(os.date("%Y-%m-%d %H:%M:%S", time_seconds))
                        csm_param_tree:set_text("Time: " .. time_str)
                        pinfo.cols.info:append(" Time:" .. time_str)
                    else
                        csm_param_tree:add(f.csm_param, payload_data(pos + 4, csm_param_length - 4))
                        csm_param_tree:set_text(string.format("Param 0x%X", csm_param_id) .. ": " .. payload_data(pos + 4, csm_param_length - 4))
                    end
                elseif csm_msg_id == 0x4E0C then
                    if csm_param_id == 0x00 then
                        csm_param_tree:add(f.uuid, payload_data(pos + 4, csm_param_length - 4))
                        csm_param_tree:set_text("UUID: " .. payload_data(pos + 4, csm_param_length - 4):string())
                        pinfo.cols.info:append(" UUID:" .. payload_data(pos + 4, csm_param_length - 4):string())
                    else
                        csm_param_tree:add(f.csm_param, payload_data(pos + 4, csm_param_length - 4))
                        csm_param_tree:set_text(string.format("Param 0x%X", csm_param_id) .. ": " .. payload_data(pos + 4, csm_param_length - 4))
                    end
                elseif csm_msg_id == 0x4E0D then
                    if csm_param_id == 0x00 then
                        csm_param_tree:add(f.available, payload_data(pos + 4, csm_param_length - 4))
                        local available = payload_data(pos + 4, csm_param_length - 4):uint() ~= 0
                        csm_param_tree:set_text("Available: " .. tostring(available))
                        pinfo.cols.info:append(" Available:" .. tostring(available))
                    else
                        csm_param_tree:add(f.csm_param, payload_data(pos + 4, csm_param_length - 4))
                        csm_param_tree:set_text(string.format("Param 0x%X", csm_param_id) .. ": " .. payload_data(pos + 4, csm_param_length - 4))
                    end
                elseif csm_msg_id == 0x4E0E then
                    if csm_param_id == 0x00 then
                        csm_param_tree:add(f.bluetooth_id, payload_data(pos + 4, csm_param_length - 4))
                        csm_param_tree:set_text("Bluetooth ID: " .. payload_data(pos + 4, csm_param_length - 4):string())
                        pinfo.cols.info:append(" BluetoothID:" .. payload_data(pos + 4, csm_param_length - 4):string())
                    elseif csm_param_id == 0x01 then
                        csm_param_tree:add(f.usb_id, payload_data(pos + 4, csm_param_length - 4))
                        csm_param_tree:set_text("USB ID: " .. payload_data(pos + 4, csm_param_length - 4):string())
                        pinfo.cols.info:append(" USBID:" .. payload_data(pos + 4, csm_param_length - 4):string())
                    else
                        csm_param_tree:add(f.csm_param, payload_data(pos + 4, csm_param_length - 4))
                        csm_param_tree:set_text(string.format("Param 0x%X", csm_param_id) .. ": " .. payload_data(pos + 4, csm_param_length - 4))
                    end
                else
                    csm_param_tree:add(f.csm_param, payload_data(pos + 4, csm_param_length - 4))
                    csm_param_tree:set_text(string.format("Param 0x%X", csm_param_id) .. ": " .. payload_data(pos + 4, csm_param_length - 4))
                end
            end

            pos = pos + csm_param_length
        end
    end

::checksum::
    -- 负载校验和 (如果存在)
    if length >= 9 + payload_len + 1 then
        local pchk = buffer(9 + payload_len, 1):uint()
        local pchk_calc = calc_checksum(buffer, 9, payload_len)
        local pchk_item = subtree:add(f.chk_payload, buffer(9 + payload_len, 1))
        if pchk ~= pchk_calc then
            pchk_item:add_expert_info(PI_CHECKSUM, PI_ERROR, "Payload checksum incorrect, length:" .. tostring(length))
        end
    end

    if tvb:len() >= total + 9 then
        goto restart
    end

    return true
end

-- iAP2 协议 UUID,请替换为实际的 iAP2 UUID
local IAP2_SERVICE_UUID1 = "00000000-deca-fade-deca-deafdecacaff"  --device
local IAP2_SERVICE_UUID2 = "00000000-deca-fade-deca-deafdecacafe"  --phone
local IAP2_SERVICE_UUID3 = "02030302-1d19-415f-86f2-22a2106a0a77"  --iap2

-- 注册蓝牙 iAP2 协议
local bt_table = DissectorTable.get("bluetooth.uuid")
bt_table:add(IAP2_SERVICE_UUID1, iap2_proto)
bt_table:add(IAP2_SERVICE_UUID2, iap2_proto)
bt_table:add(IAP2_SERVICE_UUID3, iap2_proto)
posted @ 2026-05-27 10:28  www378660084  阅读(1)  评论(0)    收藏  举报