
Identity Resolving Keys(IRK)를 사용한 Resolvable Private Address(RPA) 해석 기능을 갖춘 Bluetooth Low Energy(BLE) 스캐너
고급 RPA(Resolvable Private Address) 해석 기능을 갖춘 BLE(Bluetooth Low Energy) 스캐너입니다. 주변 BLE 기기를 탐지하고, MAC 주소로 특정 기기를 추적하거나, IRK(Identity Resolving Key)를 사용하여 개인정보 보호를 위해 무작위화된 주소를 해석할 수 있습니다.
제작: David Kennedy (@HackingDave) 회사: TrustedSec
-o -로 stdout 지원).GPS 위치 스탬핑에는 GPS 수신기가 연결된 상태에서 gpsd 데몬이 실행되고 있어야 합니다. gpsd가 실행 중이 아니면 btrpa-scan은 GPS 없이 정상적으로 계속 작동합니다.
gpsd가 작동하는지 확인하려면:
# Check that gpsd is listening
gpspipe -w -n 5
# Or use the curses monitor
cgps
이 프로젝트는 최신 Python 패키징 표준인 pyproject.toml(PEP 621)을 사용합니다. 등록된 CLI 명령어가 있는 설치 가능한 패키지로 프로젝트를 정의하므로 .py 파일을 직접 실행할 필요가 없습니다.
uvx btrpa-scan --all
uvx --from git+https://github.com/hackingdave/btrpa-scan.git btrpa-scan --all
uv tool install btrpa-scan
또는 GitHub에서 직접:
uv tool install git+https://github.com/hackingdave/btrpa-scan.git
pip install btrpa-scan
GUI 지원(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)
브로드캐스팅 중인 모든 BLE 기기를 스캔합니다(기본 30초 제한 시간):
btrpa-scan --all
사용자 지정 제한 시간 사용:
btrpa-scan --all -t 60
MAC 주소로 특정 기기를 검색합니다:
btrpa-scan AA:BB:CC:DD:EE:FF
IRK(Identity Resolving Key)를 사용하여 RPA(Resolvable Private Address)를 해석합니다. 이 모드는 기본적으로 Ctrl+C로 중지할 때까지 무기한 실행됩니다:
btrpa-scan --irk 0123456789ABCDEF0123456789ABCDEF
IRK는 여러 형식으로 제공할 수 있습니다:
| 형식 | 예시 |
|---|---|
| 일반 16진수 | 0123456789ABCDEF0123456789ABCDEF |
| 콜론 구분 | 01:23:45:67:89:AB:CD:EF:01:23:45:67:89:AB:CD:EF |
파일에서 하나 이상의 IRK를 로드합니다. 각 줄에는 지원되는 16진수 형식의 IRK 하나가 포함되어야 합니다. #로 시작하는 줄은 주석으로 처리됩니다:
btrpa-scan --irk-file keys.txt
keys.txt 예시:
# Alice's phone
0123456789ABCDEF0123456789ABCDEF
# Bob's watch
FEDCBA9876543210FEDCBA9876543210
여러 IRK가 로드되면 감지된 각 RPA가 모든 키에 대해 검사됩니다. 요약에는 모든 키에 걸친 총 일치 횟수가 표시됩니다.
명령줄에 키를 전달하지 않으려면 BTRPA_IRK 환경 변수를 설정하세요:
export BTRPA_IRK=0123456789ABCDEF0123456789ABCDEF
btrpa-scan
우선순위: --irk > --irk-file > BTRPA_IRK
신호 강도가 임계값 이상인 기기만 표시:
btrpa-scan --all --min-rssi -70
BLE RSSI는 본질적으로 노이즈가 많습니다. 슬라이딩 윈도우 평균을 사용하면 더 안정적인 거리 추정과 허위 약한 탐지 필터링이 가능합니다:
btrpa-scan --all --rssi-window 5
윈도우링이 활성화되면 디스플레이에 원시 RSSI와 평균 RSSI가 모두 표시되며(예: RSSI: -65 dBm (avg: -62 dBm over 5 readings)), 거리 추정에는 평균 값이 사용됩니다. --min-rssi 필터도 평균 RSSI에 적용되므로 단일 노이즈 스파이크로 인해 기기가 목록에서 제외되지 않습니다.
대소문자를 구분하지 않는 부분 문자열 일치로 기기 이름을 필터링합니다:
btrpa-scan --all --name-filter "AirPods"
광고된 이름에 지정된 패턴이 포함된 기기만 표시됩니다. 이름 필터가 활성화되면 이름이 없는 기기는 제외됩니다.
패시브 스캐닝(기본값)은 광고 패킷만 볼 수 있습니다. 액티브 스캐닝은 SCAN_REQ를 전송하고 SCAN_RSP를 받아 패시브 스캐닝이 놓치는 추가 서비스 UUID와 기기 이름을 확인할 수 있습니다:
btrpa-scan --all --active
참고: macOS에서 CoreBluetooth는 이 플래그와 관계없이 항상 액티브 스캔을 수행합니다. Linux/BlueZ에서는 액티브 스캐닝에 root 또는
CAP_NET_ADMIN권한이 필요할 수 있습니다.
거리 추정은 환경에 따라 달라지는 경로 손실 지수를 사용합니다. 기본값(free_space, n=2.0)은 장애물이 없음을 가정합니다. 실내에서 더 현실적인 추정을 하려면:
btrpa-scan --all --environment indoor
| 프리셋 | 경로 손실 지수(n) | 사용 사례 |
|---|---|---|
free_space | 2.0 | 개방된 공간, 가시거리 |
outdoor |
더 높은 n 값은 동일한 RSSI에 대해 더 큰 거리 추정치를 산출하며, 이는 벽과 장애물로 인한 신호 감쇠를 반영합니다.
기본적으로 btrpa-scan은 실증적으로 검증된 59dB 오프셋(iBeacon 표준)을 사용하여 광고된 TX Power에서 1미터 거리의 예상 RSSI를 도출합니다. 더 나은 정확도를 위해 자체 환경에서 측정한 보정 값을 제공할 수 있습니다:
--ref-rssi에 전달합니다:btrpa-scan --all --ref-rssi -55
--ref-rssi가 설정되면 TX Power는 완전히 무시됩니다. 또한 TX Power를 광고하지 않는 기기에 대해서도 거리 추정이 가능해집니다.
기기가 지정된 거리 내에 있는 것으로 추정되면 청각적 벨과 시각적 알림을 발생시킵니다. 대상 기기가 TX Power를 광고해야 합니다:
btrpa-scan AA:BB:CC:DD:EE:FF --alert-within 5.0
IRK 해석을 포함한 모든 모드에서 작동합니다:
btrpa-scan --irk <key> --alert-within 3.0
스크롤 출력을 신호 강도로 정렬된 실시간 업데이트 터미널 테이블로 대체합니다:
btrpa-scan --all --tui
TUI는 주소, 이름, RSSI, 평균 RSSI, 예상 거리, 탐지 횟수, 마지막 감지 시간을 포함한 간결한 테이블로 모든 감지 기기를 표시합니다. 해석된 IRK 일치 항목은 굵게 표시되며, --alert-within 임계값 내의 기기는 강조 표시됩니다.
다른 플래그와 결합:
btrpa-scan --irk <key> --tui --rssi-window 5 --environment indoor --alert-within 5.0
애니메이션 스윕, 실시간 기기 추적, GPS 지도가 포함된 브라우저 기반 레이더 인터페이스를 실행합니다:
btrpa-scan --all --gui
GUI 기능:
GUI 모드는 기본적으로 연속 스캔합니다(30초 제한 시간 없음). Ctrl+C를 눌러 중지할 수 있습니다. -t를 사용하여 특정 스캔 시간을 설정할 수 있습니다:
# 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
참고:
--gui는 Flask와 flask-socketio가 필요합니다(pip install btrpa-scan[gui]).--tui또는--quiet와 함께 사용할 수 없습니다.
각 탐지를 발생 즉시 CSV 파일로 스트리밍합니다(증분 데이터가 필요한 장시간 스캔에 유용):
btrpa-scan --all --log scan.csv
별도의 일괄 내보내기를 위해 --output과 함께 사용할 수 있습니다:
btrpa-scan --all --log live.csv --output json -o results.json
스캔 종료 시 모든 결과를 CSV, JSON 또는 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
JSONL은 줄마다 하나의 JSON 객체를 작성하므로 jq로 파이프하기 쉽습니다:
btrpa-scan --all --output jsonl -o results.jsonl -t 10
cat results.jsonl | jq .
파이프를 위해 출력을 stdout으로 작성:
btrpa-scan --all --output json -o - -t 10 -q | jq .
더 넓은 커버리지를 위해 여러 블루투스 어댑터로 동시에 스캔합니다:
btrpa-scan --all --adapters hci0,hci1
각 어댑터는 동일한 탐지 콜백을 공유하는 자체 스캐너 인스턴스를 실행합니다. 모든 탐지는 단일 출력으로 병합됩니다.
GPS는 기본적으로 켜져 있습니다. 각 탐지에는 gpsd의 현재 GPS 좌표가 태깅됩니다. 또한 스캐너는 기기별 최적 GPS 픽스를 추적합니다 — 가장 강한 RSSI를 가진 탐지의 좌표입니다(가장 가까운 근접 = 가장 정확한 위치).
gpsd가 실행 중이지 않으면 스캐너는 안내 메시지를 출력하고 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
GPS 좌표가 표시되는 위치:
latitude, longitude, gps_altitude 필드조용 모드로 실행합니다(요약만 표시, 기기별 출력 없음 — --output 또는 --log와 함께 유용):
btrpa-scan --all -q --output json -t 30
상세 모드로 실행합니다(IRK 모드에서 일치하지 않는 RPA 표시):
btrpa-scan --irk 0123456789ABCDEF0123456789ABCDEF -v
BLE(Bluetooth Low Energy) 기기는 추적을 방지하기 위해 RPA(Resolvable Private Address)를 사용합니다. RPA는 주기적으로 변경되는 임시 MAC 주소이지만, 기기의 IRK(Identity Resolving Key)를 보유한 사람은 누구나 해석할 수 있습니다.
RPA는 다음으로 구성됩니다:
01로 설정된 임의 값AES-128-ECB(IRK, padding || prand)로 계산한 값을 3바이트로 절단한 값btrpa-scan은 Bluetooth Core Specification(Vol 3, Part H, Section 2.2.2)의 ah() 함수를 구현하여 이러한 주소를 실시간으로 해석합니다.
AES-ECB 관련 참고:
ah()함수에 ECB 모드의 AES를 사용하는 것은 Bluetooth Core Specification에서 요구하는 사항입니다. 단일 16바이트 블록만 암호화되므로 ECB의 블록 간 확산 부재는 문제가 되지 않습니다 — 이는 취약점이 아닙니다.
ps를 통해 시스템의 다른 사용자에게 노출될 수 있습니다. IRK가 노출되지 않도록 하려면 --irk-file을 사용하여 파일에서 읽거나 BTRPA_IRK 환경 변수를 설정하세요. 콘솔 출력은 기본적으로 IRK를 마스킹합니다(처음과 마지막 4개의 16진수 문자만 표시).--no-gps를 사용하세요.--output-file 및 --log 옵션은 지정된 경로에 파일을 작성합니다. 대상 위치에 사용 사례에 적합한 권한이 있는지 확인하세요.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
언제든지 Ctrl+C를 눌러 스캔을 정상적으로 중지하고 요약 통계를 표시할 수 있습니다.
| 플랫폼 | 설치 | 시작 |
|---|
| 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 | WSL 또는 MSYS2를 통해 gpsd 사용 | 위의 WSL 지침 참조 |
| 플랫폼 | 참고 사항 |
|---|
| macOS | CoreBluetooth를 사용합니다. IRK 모드는 UUID 대신 실제 블루투스 주소를 얻기 위해 문서화되지 않은 API를 활용합니다. --active는 효과가 없습니다 — CoreBluetooth는 항상 액티브 스캔을 수행합니다. |
| Linux | 스캔하려면 root 또는 CAP_NET_ADMIN 권한이 필요할 수 있습니다. |
| Windows | 네이티브 WinRT Bluetooth API — 실제 MAC 주소를 기본적으로 사용할 수 있습니다. TUI에는 pip install windows-curses가 필요합니다. |
| 하이픈 구분 | 01-23-45-67-89-AB-CD-EF-01-23-45-67-89-AB-CD-EF |
| 0x 접두사 | 0x0123456789ABCDEF0123456789ABCDEF |
| 2.2 |
| 공원, 주차장 |
indoor | 3.0 | 사무실, 주택, 건물 |