
Identity Resolving Keys (IRKs) を用いて Resolvable Private Address (RPA) を解決する Bluetooth Low Energy (BLE) スキャナー
高度な Resolvable Private Address(RPA)解決機能を備えた Bluetooth Low Energy(BLE)スキャナー。近くの BLE デバイスの発見、MAC アドレスによる特定デバイスの追跡、Identity Resolving Key(IRK)を使用したプライバシーランダム化アドレスの解決が可能です。
作者: David Kennedy (@HackingDave) 会社: TrustedSec
-o - で標準出力に対応)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
Identity Resolving Key を使用して Resolvable Private Address を解決します。このモードはデフォルトで、Ctrl+C で停止されるまで無期限に実行されます:
btrpa-scan --irk 0123456789ABCDEF0123456789ABCDEF
IRK は複数の形式で指定できます:
| 形式 | 例 |
|---|---|
| プレーン hex | 0123456789ABCDEF0123456789ABCDEF |
| コロン区切り | 01:23:45:67:89:AB:CD:EF:01:23:45:67:89:AB:CD:EF |
ファイルから 1 つ以上の IRK を読み込みます。各行には、サポートされている任意の hex 形式で 1 つの 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 | 2.2 |
n の値が大きいほど、同じ RSSI に対して距離推定値が大きくなります。これは壁や障害物による信号減衰を反映しています。
デフォルトでは、btrpa-scan は実証済みの 59 dB オフセット(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 は 1 行に 1 つの JSON オブジェクトを書き込むため、jq にパイプするのが簡単です:
btrpa-scan --all --output jsonl -o results.jsonl -t 10
cat results.jsonl | jq .
パイプ処理用に出力を標準出力へ書き込む:
btrpa-scan --all --output json -o - -t 10 -q | jq .
より広いカバレッジのために、複数の Bluetooth アダプタで同時にスキャンします:
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
Bluetooth Low Energy デバイスは、追跡を防ぐために Resolvable Private Address(RPA)を使用します。RPA は定期的に変更される一時的な MAC アドレスですが、デバイスの Identity Resolving Key(IRK)を所有する人は誰でも解決できます。
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 桁の hex 文字のみを表示)。--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 の代わりに実際の Bluetooth アドレスを取得するために非公開 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 |
| 公園、駐車場 |
indoor | 3.0 | オフィス、住宅、建物 |