
Scanner Bluetooth Low Energy (BLE) com resolução de endereço privado resolvível (RPA) usando chaves de resolução de identidade (IRKs)
Um scanner Bluetooth Low Energy (BLE) com resolução avançada de Endereço Privado Resolvível (RPA). Descubra dispositivos BLE próximos, rastreie um dispositivo específico por endereço MAC ou resolva endereços randomizados por privacidade usando uma Chave de Resolução de Identidade (IRK).
Escrito por: David Kennedy (@HackingDave) Empresa: TrustedSec
-o -)O registro de localização GPS requer o daemon gpsd em execução com um receptor GPS conectado. Se o gpsd não estiver em execução, o btrpa-scan continua normalmente sem GPS.
Para verificar se o gpsd está funcionando:
# Check that gpsd is listening
gpspipe -w -n 5
# Or use the curses monitor
cgps
Este projeto usa pyproject.toml (PEP 621), o padrão moderno de empacotamento Python. Ele define o projeto como um pacote instalável com um comando CLI registrado — não é necessário executar arquivos .py diretamente.
uvx btrpa-scan --all
uvx --from git+https://github.com/hackingdave/btrpa-scan.git btrpa-scan --all
uv tool install btrpa-scan
Ou diretamente do GitHub:
uv tool install git+https://github.com/hackingdave/btrpa-scan.git
pip install btrpa-scan
Para suporte à interface gráfica (interface de radar baseada em Flask):
pip install btrpa-scan[gui]
git clone https://github.com/hackingdave/btrpa-scan.git
cd btrpa-scan
pip install .
usage: btrpa-scan [-h] [-a] [--irk HEX] [--irk-file PATH] [-t TIMEOUT]
[--output {csv,json,jsonl}] [-o FILE] [--log FILE]
[-v | -q] [--min-rssi DBM] [--rssi-window N] [--active]
[--environment {free_space,indoor,outdoor}]
[--ref-rssi DBM] [--name-filter PATTERN]
[--alert-within METERS] [--tui] [--gui] [--gui-port PORT]
[--no-gps] [--adapters LIST] [mac]
BLE Scanner — discover all devices or hunt for a specific one
positional arguments:
mac Target MAC address to search for (omit to scan all)
optional arguments:
-h, --help show this help message and exit
-a, --all Scan for all broadcasting devices
--irk HEX Resolve RPAs using this Identity Resolving Key (32 hex chars)
--irk-file PATH Read IRK(s) from a file (one per line, hex format)
-t, --timeout TIMEOUT Scan timeout in seconds (default: 30, or infinite for --irk)
--output {csv,json,jsonl}
Batch output format written at end of scan
-o, --output-file FILE
Output file path (default: btrpa-scan-results.<format>;
use - for stdout)
--log FILE Stream detections to a CSV file in real time
-v, --verbose Verbose mode — show additional details
-q, --quiet Quiet mode — suppress per-device output, show summary only
--min-rssi DBM Minimum RSSI threshold (e.g. -70) — ignore weaker signals
--rssi-window N RSSI sliding window size for averaging (default: 1 = no averaging)
--active Use active scanning (sends SCAN_REQ for additional data)
--environment {free_space,indoor,outdoor}
Distance estimation path-loss model (default: free_space)
--ref-rssi DBM Calibrated RSSI at 1 metre for distance estimation
--name-filter PATTERN Filter devices by name (case-insensitive substring match)
--alert-within METERS Proximity alert when device is within this distance
--tui Live-updating terminal table instead of scrolling output
--gui Launch web-based radar interface in the browser
--gui-port PORT Port for GUI web server (default: 5000)
--no-gps Disable GPS location stamping (GPS is on by default via gpsd)
--adapters LIST Comma-separated Bluetooth adapter names (e.g. hci0,hci1)
Escaneia todos os dispositivos BLE transmitindo na área (tempo limite padrão de 30 segundos):
btrpa-scan --all
Com tempo limite personalizado:
btrpa-scan --all -t 60
Procura um dispositivo específico por endereço MAC:
btrpa-scan AA:BB:CC:DD:EE:FF
Resolve Endereços Privados Resolvíveis usando uma Chave de Resolução de Identidade. Este modo é executado indefinidamente por padrão até ser interrompido com Ctrl+C:
btrpa-scan --irk 0123456789ABCDEF0123456789ABCDEF
A IRK pode ser fornecida em vários formatos:
| Formato | Exemplo |
|---|---|
| Hex simples | 0123456789ABCDEF0123456789ABCDEF |
| Separado por dois-pontos |
Carrega uma ou mais IRKs de um arquivo. Cada linha deve conter uma IRK em qualquer formato hex suportado. Linhas que começam com # são tratadas como comentários:
btrpa-scan --irk-file keys.txt
Exemplo de keys.txt:
# Alice's phone
0123456789ABCDEF0123456789ABCDEF
# Bob's watch
FEDCBA9876543210FEDCBA9876543210
Quando várias IRKs são carregadas, cada RPA detectada é verificada contra todas as chaves. O resumo mostra o total de correspondências em todas as chaves.
Defina a variável de ambiente BTRPA_IRK para evitar passar a chave na linha de comando:
export BTRPA_IRK=0123456789ABCDEF0123456789ABCDEF
btrpa-scan
Prioridade: --irk > --irk-file > BTRPA_IRK
Mostra apenas dispositivos com intensidade de sinal acima de um limite:
btrpa-scan --all --min-rssi -70
O RSSI de BLE é inerentemente ruidoso. Use uma média por janela deslizante para estimativas de distância mais estáveis e para filtrar detecções fracas espúrias:
btrpa-scan --all --rssi-window 5
Quando o janelamento está ativo, a exibição mostra tanto o RSSI bruto quanto o com média (ex.: RSSI: -65 dBm (avg: -62 dBm over 5 readings)), e a estimativa de distância usa o valor com média. O filtro --min-rssi também se aplica ao RSSI com média, impedindo que um único pico ruidoso derrube um dispositivo da lista.
Filtra dispositivos por nome usando correspondência de substring sem diferenciar maiúsculas/minúsculas:
btrpa-scan --all --name-filter "AirPods"
Apenas dispositivos cujo nome anunciado contenha o padrão informado serão exibidos. Dispositivos sem nome são excluídos quando um filtro por nome está ativo.
A varredura passiva (a padrão) só enxerga anúncios (advertisements). A varredura ativa envia SCAN_REQ e recebe SCAN_RSP, o que pode revelar UUIDs de serviço e nomes de dispositivos adicionais:
btrpa-scan --all --active
Nota: No macOS, o CoreBluetooth sempre faz varredura ativa independentemente desta flag. No Linux/BlueZ, a varredura ativa pode exigir root ou
CAP_NET_ADMIN.
A estimativa de distância usa um expoente de perda de percurso que varia conforme o ambiente. O padrão (free_space, n=2.0) assume que não há obstruções. Para estimativas mais realistas em ambientes internos:
btrpa-scan --all --environment indoor
| Predefinição | Expoente de perda de percurso (n) | Caso de uso |
|---|---|---|
free_space | 2.0 |
Valores maiores de n produzem estimativas de distância maiores para o mesmo RSSI, refletindo a atenuação do sinal por paredes e obstáculos.
Por padrão, o btrpa-scan deriva o RSSI esperado a 1 metro da TX Power anunciada usando um deslocamento de 59 dB validado empiricamente (o padrão iBeacon). Para precisão ainda melhor, você pode fornecer um valor calibrado medido no seu próprio ambiente:
--ref-rssi:btrpa-scan --all --ref-rssi -55
Quando --ref-rssi é definido, a TX Power é totalmente ignorada. Isso também permite estimativas de distância para dispositivos que não anunciam TX Power.
Emite um sinal sonoro e um alerta visual quando um dispositivo é estimado dentro de uma determinada distância. Requer que o dispositivo alvo anuncie TX Power:
btrpa-scan AA:BB:CC:DD:EE:FF --alert-within 5.0
Funciona em todos os modos, inclusive na resolução de IRK:
btrpa-scan --irk <key> --alert-within 3.0
Substitui a saída rolante por uma tabela de terminal com atualização em tempo real, ordenada por intensidade de sinal:
btrpa-scan --all --tui
A TUI mostra todos os dispositivos detectados em uma tabela compacta com endereço, nome, RSSI, RSSI médio, distância estimada, contagem de detecções e horário da última visualização. Correspondências de IRK resolvidas são exibidas em negrito, e dispositivos dentro do limite --alert-within são destacados.
Combine com outras flags:
btrpa-scan --irk <key> --tui --rssi-window 5 --environment indoor --alert-within 5.0
Inicia uma interface de radar baseada em navegador com varredura animada, rastreamento de dispositivos em tempo real e mapa GPS:
btrpa-scan --all --gui
A GUI oferece:
O modo GUI escaneia continuamente por padrão (sem tempo limite de 30 segundos). Pressione Ctrl+C para parar. Use -t para definir uma duração específica de varredura:
# Scan for 60 seconds with indoor path-loss model
btrpa-scan --all --gui -t 60 --environment indoor
# Custom port
btrpa-scan --all --gui --gui-port 8080
# Combine with RSSI averaging and proximity alerts
btrpa-scan --all --gui --rssi-window 5 --alert-within 5.0
Nota:
--guirequer Flask e flask-socketio (pip install btrpa-scan[gui]). Não pode ser combinado com--tuiou--quiet.
Transmite cada detecção para um arquivo CSV conforme ela ocorre (útil para varreduras de longa duração em que você deseja dados incrementais):
btrpa-scan --all --log scan.csv
Isso pode ser combinado com --output para uma exportação em lote separada:
btrpa-scan --all --log live.csv --output json -o results.json
Exporta todos os resultados ao final da varredura nos formatos CSV, JSON ou JSONL (JSON Lines):
btrpa-scan --all --output json -o results.json -t 30
btrpa-scan --all --output csv -t 30
btrpa-scan --all --output jsonl -o results.jsonl -t 30
O JSONL escreve um objeto JSON por linha, facilitando o envio via pipe para jq:
btrpa-scan --all --output jsonl -o results.jsonl -t 10
cat results.jsonl | jq .
Escreva a saída para stdout para usar com pipes:
btrpa-scan --all --output json -o - -t 10 -q | jq .
Escaneia com vários adaptadores Bluetooth simultaneamente para maior cobertura:
btrpa-scan --all --adapters hci0,hci1
Cada adaptador executa sua própria instância do scanner compartilhando o mesmo callback de detecção. Todas as detecções são mescladas em uma única saída.
O GPS está ativado por padrão. Cada detecção é marcada com as coordenadas GPS atuais do gpsd. O scanner também rastreia a melhor posição GPS por dispositivo — as coordenadas da detecção com o RSSI mais forte (proximidade mais próxima = localização mais precisa).
Se o gpsd não estiver em execução, o scanner imprime um aviso e continua normalmente sem GPS:
# With gpsd running — detections include lat/lon
btrpa-scan --all --output json
# Without gpsd — works fine, GPS fields are empty
btrpa-scan --all
# Explicitly disable GPS (skips connection attempt)
btrpa-scan --all --no-gps
As coordenadas GPS aparecem em:
latitude, longitude, gps_altitudeExecute em modo quieto (apenas resumo, sem saída por dispositivo — útil com --output ou --log):
btrpa-scan --all -q --output json -t 30
Execute em modo verboso (mostra RPAs sem correspondência no modo IRK):
btrpa-scan --irk 0123456789ABCDEF0123456789ABCDEF -v
Dispositivos Bluetooth Low Energy usam Endereços Privados Resolvíveis (RPAs) para impedir rastreamento. Um RPA é um endereço MAC temporário que muda periodicamente, mas que pode ser resolvido por qualquer pessoa que possua a Chave de Resolução de Identidade (IRK) do dispositivo.
Um RPA consiste em:
01AES-128-ECB(IRK, padding || prand) truncado para 3 bytesO btrpa-scan implementa a função ah() da Especificação Central Bluetooth (Vol 3, Parte H, Seção 2.2.2) para resolver esses endereços em tempo real.
Nota sobre AES-ECB: O uso de AES em modo ECB para a função
ah()é exigido pela Especificação Central Bluetooth. Como apenas um único bloco de 16 bytes é criptografado, a falta de difusão entre blocos do ECB é irrelevante — isso não é uma vulnerabilidade.
ps. Para evitar expor a IRK, use --irk-file para ler de um arquivo ou defina a variável de ambiente BTRPA_IRK. A saída do console mascara as IRKs por padrão (mostrando apenas os primeiros e os últimos 4 caracteres hexadecimais).--no-gps para desativar o GPS completamente.--output-file e --log gravam no caminho especificado. Garanta que o destino tenha as permissões apropriadas para o seu caso de uso.pip install pytest
python -m pytest test_btrpa_scan.py -v
Mode: DISCOVER ALL - showing every broadcasting device
Scanning: passive
GPS: connected (37.774929, -122.419418)
Timeout: 30s | Press Ctrl+C to stop
------------------------------------------------------------
============================================================
DEVICE #1 - seen 1x
============================================================
Address : AA:BB:CC:DD:EE:FF
Name : MyDevice
RSSI : -45 dBm
TX Power : -59 dBm
Est. Distance: ~0.4 m
Manufacturer : 0x004C -> 0215abcdef
Best GPS : 37.774929, -122.419418
Timestamp : 14:32:07
============================================================
------------------------------------------------------------
Scan complete - 30.0s elapsed
Total detections : 142
Unique devices : 12
Results written to btrpa-scan-results.json
Pressione Ctrl+C a qualquer momento para interromper a varredura normalmente e exibir as estatísticas de resumo.
| Plataforma | Instalação | Inicialização |
|---|
| macOS | brew install gpsd | gpsd -n /dev/tty.usbserial-* |
| Debian/Ubuntu | sudo apt install gpsd gpsd-clients | sudo systemctl start gpsd |
| Fedora/RHEL | sudo dnf install gpsd gpsd-clients | sudo systemctl start gpsd |
| Arch | sudo pacman -S gpsd | sudo systemctl start gpsd |
| Windows | Use o gpsd via WSL ou MSYS2 | Veja as instruções do WSL acima |
| Plataforma | Notas |
|---|
| macOS | Usa CoreBluetooth. O modo IRK aproveita uma API não documentada para obter endereços Bluetooth reais em vez de UUIDs. --active não tem efeito — o CoreBluetooth sempre faz varredura ativa. |
| Linux | Pode exigir root ou a capacidade CAP_NET_ADMIN para varredura. |
| Windows | API nativa WinRT Bluetooth — endereços MAC reais disponíveis nativamente. A TUI exige pip install windows-curses. |
01:23:45:67:89:AB:CD:EF:01:23:45:67:89:AB:CD:EF |
| Separado por hífen | 01-23-45-67-89-AB-CD-EF-01-23-45-67-89-AB-CD-EF |
| Prefixado com 0x | 0x0123456789ABCDEF0123456789ABCDEF |
| Ar livre, linha de visada |
outdoor | 2.2 | Parques, estacionamentos |
indoor | 3.0 | Escritórios, residências, edifícios |