Class: PWN::SDR::Decoder::Bluetooth::ExtendedHeader

Inherits:
Object
  • Object
show all
Defined in:
lib/pwn/sdr/decoder/bluetooth.rb

Overview

Length-bounded extended advertising header parser.

Class Method Summary collapse

Class Method Details

.parse(opts = {}) ⇒ Object

Core Vol 6 Part B 2.3.4: flagged fields in fixed order, bounded by ExtHdrLen. Preserve ACAD/SyncInfo as bytes rather than invent fields.



281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/pwn/sdr/decoder/bluetooth.rb', line 281

public_class_method def self.parse(opts = {})
  payload = opts[:payload]
  size = payload[0] & 63
  mode = payload[0] >> 6
  return nil if mode == 3 || size + 1 > payload.length

  out = { adv_mode: mode }
  if size.positive?
    flags = payload[1]
    return nil if flags.anybits?(0x80)

    cursor = 2
    { adv_addr: 6, target_addr: 6, cte_info: 1, adi: 2, aux_ptr: 3, sync_info: 18, tx_power: 1 }.each_with_index do |(name, width), bit|
      next if flags[bit].zero?
      return nil if cursor + width > size + 1

      data = payload[cursor, width]
      cursor += width
      case name
      when :adv_addr, :target_addr
        out[name] = data.reverse.map { |b| format('%02X', b) }.join(':')
      when :adi
        out[:advertising_did] = data[0] | ((data[1] & 15) << 8)
        out[:advertising_sid] = data[1] >> 4
      when :tx_power
        out[name] = data[0] >= 128 ? data[0] - 256 : data[0]
      else
        out[:"#{name}_hex"] = data.pack('C*').unpack1('H*').upcase
      end
    end
    out[:acad_hex] = payload[cursor, size + 1 - cursor].pack('C*').unpack1('H*').upcase
  end
  out[:advertising_data_hex] = payload.drop(size + 1).pack('C*').unpack1('H*').upcase
  out
end