Module: PWN::SDR::Decoder::Bluetooth
- Defined in:
- lib/pwn/sdr/decoder/bluetooth.rb
Overview
Bluetooth LE (& BR/EDR sync-trailer) true-air decoder.
I/Q → PWN::FFI::Liquid.gmsk_demod (or DSP.fm_demod_iq→NRZ) at 1 Mbit/s → hunt LSB-first Access Address (adv = 0x8E89BED6) → dewhiten (7-bit LFSR seeded ch|0x40) → PDU header (type/len) → AdvA (6 bytes) → CRC-24 (poly 0x65B, init 0x555555). Emits per-PDU pdu_type:, adv_addr:, crc_ok: — ubertooth-parity advertising sniff with no external binary.
Defined Under Namespace
Classes: DemodIQ
Constant Summary collapse
- BLE_ADV_AA =
0x8E89BED6- BLE_CRC_POLY =
0x65B- BLE_CRC_INIT =
0x555555- BLE_PDU_TYPE =
{ 0 => 'ADV_IND', 1 => 'ADV_DIRECT_IND', 2 => 'ADV_NONCONN_IND', 3 => 'SCAN_REQ', 4 => 'SCAN_RSP', 5 => 'CONNECT_IND', 6 => 'ADV_SCAN_IND', 7 => 'ADV_EXT_IND' }.freeze
- BLE_ADV_CHANNELS =
{ 37 => 2_402_000_000, 38 => 2_426_000_000, 39 => 2_480_000_000 }.freeze
- GIAC_LAP =
BR/EDR: 64-bit sync word derived from LAP; general-inquiry LAP=0x9E8B33.
0x9E8B33
Class Method Summary collapse
-
.authors ⇒ Object
- Author(s)
0day Inc.
-
.ble_crc24(opts = {}) ⇒ Object
- Supported Method Parameters
crc = PWN::SDR::Decoder::Bluetooth.ble_crc24(bytes: Array
) BLE CRC-24 (LSB-first LFSR, poly 0x65B, init 0x555555).
-
.decode(opts = {}) ⇒ Object
- Supported Method Parameters
PWN::SDR::Decoder::Bluetooth.decode( freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq' ).
- .help ⇒ Object
- .parse_line(opts = {}) ⇒ Object
Class Method Details
.authors ⇒ Object
- Author(s)
0day Inc. support@0dayinc.com
189 190 191 |
# File 'lib/pwn/sdr/decoder/bluetooth.rb', line 189 public_class_method def self. "AUTHOR(S):\n 0day Inc. <support@0dayinc.com>\n" end |
.ble_crc24(opts = {}) ⇒ Object
- Supported Method Parameters
crc = PWN::SDR::Decoder::Bluetooth.ble_crc24(bytes: Array
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/pwn/sdr/decoder/bluetooth.rb', line 133 public_class_method def self.ble_crc24(opts = {}) bytes = opts[:bytes] || [] reg = BLE_CRC_INIT bytes.each do |byte| 8.times do |i| b = (byte >> i) & 1 fb = (reg ^ b) & 1 reg >>= 1 reg ^= (BLE_CRC_POLY << 0) | 0xB4C000 if fb == 1 reg &= 0xFFFFFF end end # Register holds CRC LSB-first — return as-is (matched LSB-first on air) reg end |
.decode(opts = {}) ⇒ Object
- Supported Method Parameters
PWN::SDR::Decoder::Bluetooth.decode( freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq' )
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/pwn/sdr/decoder/bluetooth.rb', line 154 public_class_method def self.decode(opts = {}) freq_obj = opts[:freq_obj] hz = PWN::SDR.hz_to_i(freq: freq_obj[:freq]) ble = freq_obj[:ble] || freq_obj[:mode].to_s.casecmp('ble').zero? || true # Nearest BLE advertising channel unless caller forces one. ch = opts[:channel] || BLE_ADV_CHANNELS.min_by { |_, f| (f - hz).abs }&.first || 37 rate = (opts[:sample_rate] || freq_obj[:iq_rate] || 4_000_000).to_i PWN::SDR::Decoder::Base.run_iq( freq_obj: freq_obj, protocol: ble ? 'BLE' : 'BT-BR/EDR', sample_rate: rate, source: opts[:source], file: opts[:file], demod: DemodIQ.new(rate: rate, channel: ch, ble: ble), note: '1 Mbit/s GFSK — I/Q→gmskdem→AA 0x8E89BED6→dewhiten→PDU/AdvA/CRC-24.', describe: proc { |b| { modulation: 'GFSK', channel: ch, hop_slots: (b[:duration_ms] / 0.625).round } } ) end |
.help ⇒ Object
193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/pwn/sdr/decoder/bluetooth.rb', line 193 public_class_method def self.help puts "USAGE (true-air I/Q via PWN::FFI + detector fallback): #{self}.decode( freq_obj: 'required - freq_obj returned from PWN::SDR::GQRX.init_freq', source: 'optional - :auto|:rtlsdr|:adalm_pluto|:file', file: 'optional - .cu8/.cs16 capture (≥2 Msps)', channel: 'optional - BLE adv channel 37|38|39 (default nearest to freq)' ) #{self}.ble_crc24(bytes: [..]) #{self}.authors " end |
.parse_line(opts = {}) ⇒ Object
174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/pwn/sdr/decoder/bluetooth.rb', line 174 public_class_method def self.parse_line(opts = {}) line = opts[:line].to_s out = { protocol: 'Bluetooth' } out[:lap] = ::Regexp.last_match(1) if line =~ /LAP[=: ]([0-9a-fA-F]{6})/ out[:uap] = ::Regexp.last_match(1) if line =~ /UAP[=: ]([0-9a-fA-F]{2})/ out[:bd_addr] = ::Regexp.last_match(1) if line =~ /(?:AdvA|BD_ADDR)[=: ]([0-9a-fA-F:]{12,17})/ out[:pdu_type] = ::Regexp.last_match(1) if line =~ /\b(ADV_\w+|SCAN_\w+|CONNECT_REQ)\b/ out[:channel] = ::Regexp.last_match(1) if line =~ /ch[=: ]?(\d{1,2})\b/i out[:rssi] = ::Regexp.last_match(1) if line =~ /rssi[=: ]?(-?\d+)/i out[:summary] = "BT #{out.values_at(:pdu_type, :bd_addr, :lap).compact.join(' ')}".strip out.compact end |