BLE Info (bleinfo / bi) β GATT Enumeration & Interaction
Overview
bleinfo (short: bi) is a BLE GATT client built into AL-ANQA. It connects to a Bluetooth Low Energy device, reads its full service/characteristic tree, decodes values using standard GATT type descriptors, and provides interactive tools for passive monitoring and active interaction.
It is the BLE equivalent of a port scanner + banner grabber + packet injector in one command.
Quick Start
sbl # scan for BLE devices first
bi 0 # connect to device at index 0
bi 3 # connect to device at index 3
bi aa:bb:cc:dd:ee:ff # connect by raw MAC address
bi all # enumerate every device from last sbl scan
Navigation Keys
GATT view:
| Key | Action |
|---|---|
a / l | Previous / next page |
n | Start notify/indicate sniff (30 s) |
r | Write-cap β replay a captured notification value back to the device |
w | Write to a writable characteristic (reconnects, then writes) |
f | Fuzz a writable characteristic (seq / random / boundary / oversized / flood) |
g | Abuse scan β read-hammer every characteristic, flag no-auth read leaks |
b | Security audit β link posture (encryption/pairing/bonding, no-auth read/write counts) + value leak scan |
p | Toggle pairing / bonding mode |
s | Save GATT tree to SD card |
q | Quit |
Sniff screen (during [n]):
| Key | Action |
|---|---|
w | Write to a char without disconnecting β response appears in sniff stream |
q | Stop sniff, save log, return to GATT view |
Keys only appear in the footer when relevant:
[n]if notify/indicate chars exist,[r]if a.blecapture file exists,[w]/[f]if writable chars exist.[b]auditand[g]abuseare always available.
What It Shows
After connecting, bi reads the full GATT structure and displays it paginated:
[GATT] aa:bb:cc:dd:ee:ff 1/3
ββββββββββββββββββββββββββββββββ
SVC 0x180a DeviceInfo
0x2a29 [R ] Manufacturer:"Polar"
0x2a24 [R ] ModelNum:"H10"
0x2a26 [R ] FirmwareRev:"3.1.1"
0x2a25 [R ] SerialNum:"12345678"
SVC 0x180f Battery
0x2a19 [R ] BattLevel:87
SVC 0xfb005c~ [RWN]
0xfb005c~ [RWN] 12 00 ..
!0xfb005d~ [W ] (UnlockKey)
ββββββββββββββββββββββββββββββββ
[q]qt [a/l]pg [n]sniff [w]wr[f]fz [b]audit [g]abuse [s]save [p]pair
Risk colour coding (inline, on every line):
!prefix β HIGH risk (binary 16 or 32 bytes = AES key size, or base64-looking blob)~prefix β MED risk (hex string β₯ 8 chars = encoded secret, or 4β8 digit value = PIN)- orange line β LOW risk (writable + sensitive-looking user description)
- white / normal β no flag
Column breakdown:
- UUID β 16-bit shown as
0x1234, 128-bit truncated as12345678~ - Props β
Rreadable Β·Wwritable Β·Nnotifiable Β·Iindicate - Value β decoded using 0x2904 Presentation Format if available, otherwise:
- Printable ASCII β
"quoted string" - Single byte β
0xXX - Binary β hex dump (
DE AD BE EF..)
- Printable ASCII β
- Name β well-known UUIDs resolved to human names (DeviceName, BattLevel, etc.)
- (desc) β 0x2901 User Description shown in parens for unnamed write-only chars
Descriptor Reading
bi reads two GATT descriptors beyond the value:
0x2901 β User Description
Free-text label set by the device manufacturer. Often reveals the purpose of proprietary 128-bit characteristics that arenβt in the GATT spec.
Example: a write-only char on a smart lock might expose (UnlockCommand) or (SetPIN) via its User Description, even though you canβt read its current value.
0x2904 β Presentation Format
Tells bi how to decode the raw bytes:
| Type | Decodes as |
|---|---|
0x04 | uint8 |
0x06 | uint16 LE |
0x08 | uint32 LE |
0x0C | int8 |
0x0D | int16 LE |
0x16 | UTF-8 string |
A battery level without 0x2904 shows 0x57. With it: 87. A heart rate without it shows 48 00. With it: 72.
Notify / Indicate Sniff ([n])
Subscribes to every N and I characteristic simultaneously, then streams live values for 30 seconds.
[SNIFF] aa:bb:cc:dd:ee:ff 00:24
ββββββββββββββββββββββββββββββββ
1.2s 0xfb005~ 48 00 00 00 1A 2B
2.4s 0xfb005~ 49 00 00 00 1A 2C
3.6s 0xfb005~ 47 00 00 00 1A 2B
4.8s 0x2a37 49 00
ββββββββββββββββββββββββββββββββ
subs:2 total:4 [w]write [q]stop
- Notify β device pushes without acknowledgement (fire and forget)
- Indicate β device pushes and waits for your ACK before sending next
Both are captured in the same ring buffer. After the session ends, the log is automatically saved to /apps/bleinfo/<mac>_sniff.txt if SD is present.
Stop early: press q. The partial log is still saved.
Write during sniff ([w] inside sniff screen)
Press [w] while sniff is running to send a command without disconnecting. The subscription stays active β any response the device sends appears immediately in the sniff stream. This is the correct way to probe request/response protocols:
[n] β subscribe to all notify chars
[w] β pick char β type payload β Enter β "Sent! Watch sniff for reply."
β response appears on screen within milliseconds
[q] β stop sniff, auto-save log
This avoids the reconnect delay that a separate [w] from the GATT view would require.
Write ([w])
Sends bytes to a writable characteristic. Use for:
- Sending commands (start/stop, config changes)
- Testing input validation
- Triggering response notifications
Flow:
- Press
[w]β select target char from numbered list - Enter payload as hex (
DE AD BE EF) or ASCII (hello) then Enter biconnects, finds the char by UUID, tries write-with-response then falls back to write-without-response automatically- Shows
Write OK.orWrite failed.
Hex input rules:
- Spaces optional:
DEADBEEFandDE AD BE EFboth work - If input is not valid hex, treated as raw ASCII bytes
- Max 20 bytes
- Trackpad left/right moves the cursor within the input β fix a typo without deleting everything
- Backspace deletes the character to the left of the cursor
Fuzz ([f])
Sends a sequence of write requests to a writable characteristic to find input validation bugs, trigger unexpected state transitions, or crash the device.
Modes:
| Mode | Payloads | Use case |
|---|---|---|
1 seq | 0x00 β 0xFF (256 writes, 1 byte each) | Full single-byte space coverage |
2 rand | 64 writes, 1β4 random bytes (LCG) | Multi-byte protocol fuzzing |
3 boundary | 0x00 0x01 0x7F 0x80 0xFE 0xFF | Off-by-one and overflow testing |
4 oversized | 5 escalating writes: 20 β 64 β 128 β 255 β 509 B (incrementing pattern) | MTU / long-write / buffer-overflow handling |
5 flood | 500 rapid 4-byte writes with no pacing, live writes/sec shown | DoS / hang / rate-limit behaviour |
What to look for:
- Error count rises β device is rejecting bad values (good security)
- Error count stays 0 β device accepts everything including garbage (bad)
- βDevice disconnected!β β crash or protection triggered; note the payload
- Watch screen reacts β you hit an active control path
- Error jumps at specific byte β boundary condition found
- Oversized accepted β no length validation on writes (may overflow a fixed buffer)
- Flood rate stays high / device hangs β no rate limiting; DoS surface
Pacing is mode-dependent: 80 ms (seq/rand/boundary), 250 ms (oversized), unthrottled (flood). [q] stops early.
The result screen waits for
[q]β after the run finishes (or the device disconnects) thesent/errsummary stays on screen until you press[q]; it does not auto-close, so you can read the outcome.
Why it matters: proves whether a device validates the input it accepts. Oversized-accepted = no length checking (possible buffer overflow); a disconnect/crash during the run = a firmware bug you triggered; flood that never slows = no rate-limiting (DoS surface).
Abuse Scan ([g])
Read-hammer for broken access control. Reconnects and attempts a GATT read on every characteristic β ignoring the advertised Read property β then classifies what came back:
| Tag | Meaning |
|---|---|
LEAK (red) | Characteristic has no Read property but still returned data β the server should have rejected the read |
open (yellow) | Readable characteristic that returned data over an unencrypted link (no pairing required) |
The summary line shows tried / leak / open counts (with (abrt) appended if you stopped it early). LEAK findings are the important ones: they mean the peripheral relies on the client honouring the property flags rather than enforcing access control server-side. [a]/[l] page through results, [q] returns. This goes beyond [b]audit, which only reads characteristics that advertise Read.
Why it matters: answers βcan I read data this device tried to hide, without pairing?β A LEAK on a lock/wearable/sensor can expose config, keys, or telemetry to anyone in range. An all-green result means the device enforces access control properly.
Exit / result screen: the results page waits for
[q]and does not auto-close. If instead you see βReconnect failed.β, the abuse scan drops the initial connection and opens a fresh one β some devices allow only a single BLE connection at a time and refuse it (that screen now also waits for[q]and explains the likely cause). Retry on a device that accepts reconnects.
Security Audit ([b])
Two-part report, always available while connected. First the access-control posture of the link (captured live from the connection β bi connects without pairing, so anything readable proves broken access control), then the value leak scan (characteristics whose contents look like secrets).
[AUDIT] BLE Security Posture
ββββββββββββββββββββββββββββββββ
Link: OPEN - not encrypted
Pairing: none (no auth needed)
Bonded: no
No-auth: R:7 W:2 chars
ββββββββββββββββββββββββββββββββ
Value leak scan:
! 0xfb005d~ [W ] (UnlockKey)
RISK: binary 16B (AES key size?)
~ 0xfb005e~ [RW ] 31 32 33 34 35 36
RISK: 6-digit value (PIN?)
ββββββββββββββββββββββββββββββββ
2 flagged [q] back
Posture fields:
| Field | Meaning |
|---|---|
Link | ENCRYPTED (NB key) (green) or OPEN - not encrypted (red) at enumerate time |
Pairing | none (no encryption), Just Works (no MITM) (yellow), or authenticated (MITM) (green) |
Bonded | Whether the connection is bonded |
No-auth | Count of characteristics readable / writable without encryption β the core access-control finding |
The posture (and a leaks=yes/no flag) is also written to the saved /apps/bleinfo/<mac>.txt report on [s].
Risk scoring logic:
| Flag | Condition | Meaning |
|---|---|---|
! HIGH | Value is exactly 16 or 32 bytes of binary | Matches AES-128 / AES-256 key size |
! HIGH | Value looks like a base64-encoded blob (β₯16 chars, trailing =) | Encoded secret or key material |
~ MED | Hex string β₯ 8 hex chars | Could be a key/token in hex encoding |
~ MED | 4β8 digit numeric string | Looks like a PIN code |
| LOW | Writable char with a sensitive-sounding User Description | Direct write access to a sensitive control |
Risk is scored at enumeration time. [b]audit is always shown in the footer β the posture report is meaningful even when no values are flagged.
Press q to return to the main GATT view.
Write-Cap ([r]wcap)
Write-cap lets you take a value captured during [n] sniff and write it back to a writable characteristic on the same (or a different) device. It is the BLE equivalent of packet replay at the GATT value layer.
When this works:
- Custom/proprietary IoT devices that use the same characteristic for both output (notify) and input (write) β e.g., write a target heart-rate zone to the same char the watch uses to push readings
- Devices with simple command protocols where commands are sent as characteristic writes and acknowledged as notifications on the same char
When this does NOT work:
- Standard GATT profiles: notify-only chars canβt be written (connection will reject the write)
- Challenge-response authentication: even if you replay the right bytes, the server changes its nonce each session
- Devices that validate sequence numbers or timestamps embedded in the payload
Saved captures: [n] sniff auto-saves a .ble file to /apps/bleinfo/<mac>_replay.ble after each session. Write-cap reads from this file or from the current in-memory sniff buffer.
Flow:
- Press
[r]β source picker appears:[1]Use current sniff session (in memory)[2]Load from SD β lists.blefiles in/apps/bleinfo/
- Packet picker β shows captured packets, select one
- Char picker β shows writable characteristics, select target
bireconnects and writes the selected bytes to the selected char
.ble Capture File Format
Sniff sessions are saved as plain-text .ble files:
ALANQA_BLE_REPLAY
MAC aa:bb:cc:dd:ee:ff
TYPE 1
PKT 0x2a37 1234 4800
PKT 0xfb005~ 2500 DEADBEEF01020304
MACβ device MAC addressTYPEβ address type (0=public, 1=random)PKT <uuid> <elapsed_ms> <hex_bytes>β one captured notification per line; hex bytes are concatenated without spaces
These files can be loaded back into write-cap on the same or a different T-Deck.
Pairing ([p])
Toggles bonding + MITM + Secure Connections for subsequent reconnects.
When enabled ([p]PAIR ON shown in footer), bi sets:
- Bonding β saves long-term key for future reconnects
- MITM protection β requires user interaction to confirm
- Secure Connections β uses ECDH key exchange
If the device requests a passkey, a prompt appears:
Passkey (6 digits):
> _
Type the 6-digit PIN shown on the device.
If numeric comparison is used:
Confirm PIN 123456? [y/n]
When to use: some devices hide services or restrict characteristic access until bonded. Enable [p], then press [n] or [w] β bi will bond on the next reconnect and the full service tree becomes accessible.
When not to use: recon and passive sniffing. Bonding writes a long-term key to the deviceβs memory. For pure recon, leave pairing off.
Multi-Device Scan (bi all)
Connects to every device from the last sbl scan in sequence, enumerates GATT, and saves each result to SD automatically.
bi all
[1/8] aa:bb:cc:dd:ee:ff
ok (3 svcs)
[2/8] 11:22:33:44:55:66
failed
[3/8] ...
Done. Results in /apps/bleinfo/
- Timeout per device: 4 seconds
- Press
qto abort the sweep early - Saved files:
/apps/bleinfo/<mac>.txtfor each device that responded - Devices that reject connection or have no services are logged as
failedon screen
Use this to map all BLE devices in a space without interacting with each manually.
SD Card Output
GATT structure β /apps/bleinfo/<mac>.txt
Saved by [s] key or bi all:
MAC: aa:bb:cc:dd:ee:ff
SVC 0x180a DeviceInfo
0x2a29 [R ] Manufacturer:"Polar"
0x2a26 [R ] FirmwareRev:"3.1.1"
SVC 0xfb005c~ []
0xfb005d~ [W ] (Control Point)
Notify log β /apps/bleinfo/<mac>_sniff.txt
Auto-saved after each sniff session (appended, not overwritten):
MAC: aa:bb:cc:dd:ee:ff
--- notify sniff ---
1.2s 0xfb005~ 48 00 00 00..
2.4s 0xfb005~ 49 00 00 00..
Write-cap capture β /apps/bleinfo/<mac>_replay.ble β οΈ Not yet tested
Auto-saved alongside the sniff log. Contains raw bytes for every captured notification. Can be loaded into [r]wcap on any T-Deck to replay the packets:
ALANQA_BLE_REPLAY
MAC aa:bb:cc:dd:ee:ff
TYPE 1
PKT 0x2a37 1234 4800
PKT 0xfb005~ 2500 DEADBEEF01020304
Detection Use Cases
1. Identify a device without its app
Run bi <index>. Read the DeviceInfo service (0x180A) β Manufacturer, ModelNum, FirmwareRev, SerialNum are usually readable without bonding on consumer devices. Cross-reference ModelNum and Manufacturer to identify the exact hardware.
Useful when you find an unknown BLE device on a network audit and need to name it without installing its companion app.
2. Find hidden control channels
Most BLE devices expose more than their app uses. bi shows all characteristics including those the official app never touches. Look for:
[W]chars the app never writes to β undocumented commands[R]chars with unusual values β debug flags, internal counters[N]chars the app never subscribes to β hidden telemetry
Use [n] sniff to catch what the device broadcasts before any pairing. Some devices leak MAC, device name, or sensor data in unauthenticated notifications.
3. Detect insecure BLE devices
Signs of a poorly secured device (visible from bi without touching it):
| Observation | Implication |
|---|---|
| All services visible without bonding | No access control |
Write chars accept any value ([f] errors=0) | No input validation |
| DeviceInfo returns real serial/firmware | No obfuscation |
| Notify pushes data before authentication | Unauthenticated telemetry |
| Same static MAC every boot | Trackable, no randomization |
4. Map BLE attack surface before a pentest
Run bi all after sbl. Review /apps/bleinfo/ on a PC. For each device note:
- Services present β identifies device class
- Write characteristics present β active attack surface
- Notify without bonding β data leakage
- Whether pairing is required β authentication posture
This gives you a structured inventory of every BLE device in range ranked by attack surface before you connect to any of them interactively.
5. Validate your own devices
Run bi against your own BLE hardware to verify:
- Only expected services are exposed
- Write characteristics reject invalid input (
[f]mode) - Sensitive characteristics require bonding (
[p]mode needed to read them) - No unintended data leaks via unauthenticated notifications
Address Type Notes
Most modern BLE peripherals use random addresses (type 1). Older or embedded devices use public addresses (type 0, burned into hardware).
bi <index>β uses the address type captured duringsblscan (correct automatically)bi <mac>β tries random first, falls back to public if connection fails
If connection keeps failing on a known device, it may have changed its random address (resolvable private address). Run sbl again to get the current address.
Workflow Example: Smart Watch Audit
sbl # scan β find watch at index 2
bi 2 # connect and enumerate full GATT tree
# DeviceInfo: Manufacturer, Model, FirmwareRev
# proprietary services show [W][N] chars
# footer shows [b]audit β security posture + value scan
[b] # audit β link posture (enc/JustWorks/MITM/bonded, no-auth R/W) + value leak scan
[s] # save full GATT tree to SD
[n] # sniff β all notify/indicate chars subscribed simultaneously
# live stream: heart rate, steps, battery, sensor data
[w] # (inside sniff) pick command char β type payload β Enter
# response appears in sniff stream within milliseconds
# this is how you probe request/response protocols
[q] # stop sniff β auto-saves _sniff.txt + _replay.ble to SD
[r] # write-cap β load captured packet, replay to writable char
[f] β mode 3 # boundary fuzz the control char
# "Device disconnected!" = crash found, note the payload
[p] β [n] # enable pairing, sniff again β more services visible?
q
Results in /apps/bleinfo/:
aa-bb-cc-dd-ee-ff.txtβ full GATT map with complete hex values and full UUIDsaa-bb-cc-dd-ee-ff_sniff.txtβ captured notification stream with full raw bytesaa-bb-cc-dd-ee-ff_replay.bleβ packet archive for write-cap replay
Protocol Reverse Engineering
bi is particularly useful for understanding proprietary BLE protocols with no public documentation. The sniff+write flow lets you map a deviceβs command structure without any app or source code.
Approach:
[n]sniff while the device is idle β note baseline packets (keep-alives, sensor polls)- Interact with the device physically (press a button, set an alarm, change a setting)
- Note any new packet that appeared β that packet encodes the action you just took
- Try sending that packet back on a writable char using
[w]inside sniff - Watch for a response β if the device reacts, youβve found the command channel
Decoding packets:
Most proprietary BLE protocols follow a pattern:
[START] [CMD] [BRAND/SYNC] [LENGTH] [DATA...] [CHECKSUM?]
- Fixed bytes that appear in every packet = protocol header/sync bytes
- Bytes that change with physical actions = the meaningful payload
0x57in a health sensor packet at a consistent position = likely a sensor value (e.g. 87 bpm)- Alternating
00/FD= signed delta or idle/active flag
Write-without-response: Many proprietary command chars use write-without-response (no BLE ACK). bi tries both automatically β Write OK. means the bytes were delivered regardless of write type.
Multiple response channels: Some devices respond on a different char than the one you wrote to. If [w] shows Write OK. but you see a new packet type appear in the sniff stream on a different UUID, thatβs the response channel.