HackRFとGNU Radioを使ったオープンソースの BLE パケットスニファ

# はじめに

Bluetooth Low Energy Advent Calendar 2016 6日目担当の @yousuke です。普段はBLE を使ったサービスのサーバサイドの実装を担当しています。

今回はオープンソースの技術を組み合わせて、アドバタイズパケットの受信とコンソール上に表示することのみを目的とした、簡易的なBLEのパケットスニッフィングシステムを作ってみた話を書きます。

# 背景

BLE 対応のデバイスを始めとして、一般的な無線装置は次のような構成となっており、これらは専用のハードウェアにパッケージングされた状態で販売されています。

    (電波) -> アンテナ -> 増幅回路 -> フィルタ回路 -> A-Dコンバータ -> デジタル信号処理回路


いままで、それが当たり前すぎて他の選択肢があるなんていう発想がなかったので気づかなかったのですが、BLEの関連技術を調査している最中に**ソフトウェア無線**の存在を知りました。

どうやら、最近のコンピュータは上記のデジタル信号処理を実行するにあたっての十分なデータ処理性能をそなえているため、信号処理回路と同じ働きをするプログラムを、使い慣れたC言語などをつかって実装し、PC上でリアルタイムに動作させることが可能とのこと。また、電波をキャプチャする部分は帯域性能に問題がなければ、BLE に限らず他の無線通信技術にも汎用的に使えるらしいです。そのためのツールキットも存在しており、そのような、汎用的な無線ハードウェアとPC上で動作するソフトウェアをベースとした無線環境を「ソフトウェア無線」というようです。

というわけで論より証拠、試してみました。


# 環境
Mac OS 10.12 Sierra
HackRF 2015.07.2
GNU Radio 3.7.9.1


# 実装

## フロントエンド

とはいえ、上記で挙げた次の機能についてはまだソフトウェアでの実装は難しく、FPGAなどで実装されたハードウェアが必要です。

    （電波） -> アンテナ -> 増幅回路 -> フィルタ回路 -> A-Dコンバータ

これらの機能は相対的に前段に位置するため、「フロントエンド」と呼ばれてます。
BLEの信号を扱うためには、2.4GHz周辺の電波に対応したフロントエンドが必要です。

今回、わたしはオープンソースハードウェアの [HackRF One](https://greatscottgadgets.com/hackrf/) を使うこととしました(Ebayで購入し、費用は日本円で3万円弱程度)。他に候補として検討したのは BladeRF, USRP でしたが、これらと比較して安価かつ広い帯域の周波数に対応していること、そしてファームウェアや関連ソフトがオープンソースで公開されていることが決め手でした。

このデバイスは同時に提供されている`hackrf_transfer` というプログラムと組み合わせることで、 1MHz - 6GHz までの電波のうち、任意の帯域を最高で 20Msample/sec、8bit の性能でキャプチャし、直交振幅変調した結果を出力することが可能です。

![HackRF One](https://qiita-image-store.s3.amazonaws.com/0/90283/31058fec-2ae1-6005-9f77-bb01259eeac3.jpeg)



ちなみに、BLE では 2.400-2.4835 GHz の 83.5MHzの帯域を使います。対して HackRF で一度にキャプチャ可能な帯域幅のは 20MHz までなので、BLEの信号すべてをキャプチャは出来ません。また、３つあるアドバタイズチャンネルも、それぞれの帯域は20MHz 以上離れているため、同時のキャプチャは不可能です。そこで今回はすべてのBLEチャンネルのスニッフィングは諦めて、一つのチャンネルのみ(真ん中の38ch)をスニッフィングの対象とすることにしました。


> 
もし仮に、上記 83.5MHz の帯域すべての電波をリアルタイムにキャプチャ可能なフロントエンドデバイスを用意できれば、すべてのBLE信号を受信することが可能となるのですが、そのようなデバイスは少々高価です。
（例：NI USRP-2942R、120MHz、847,400円 http://sine.ni.com/nips/cds/view/p/lang/ja/nid/212434）
>
> 複数のHackRFを使えば全帯域をカバーできるかも・・・？というのは、将来試してみたいところです。




### インストール

HackRF をOSX で動作させるには、以下のようにHomeBrew を使うのが便利です。

    % brew install hackrf

デバイスが正しく使えるかどうかは、`hackrf_info` を使うことで確認できます。
OSから認識できている場合は以下のような結果が出力されます。

    % hackrf_info
    Found HackRF board 0:
    USB descriptor string: 000000000000000040a465c8385e234b
    Board ID Number: 2 (HackRF One)
    Firmware Version: 2015.07.2
    Part ID Number: 0xa000cb3c 0x0075436d
    Serial Number: 0x00000000 0x00000000 0x40a465c8 0x385e234b

（上手く接続できてない場合はこのような出力になります）

    % hackrf_info
    No HackRF boards found.




## デジタル信号処理

ここがソフトウェアラジオの肝なのですが、[GNU Radio](http://gnuradio.org/) というSDR用のツールキットがありまして、これを使うと非常に簡単にフロントエンドから得たデジタル信号からビット列を取り出す信号処理プログラムを実装できます。


### GNU Radio のインストール

本家では[macports によるインストールを案内](http://gnuradio.org/redmine/projects/gnuradio/wiki/MacInstall)していますが、わたしは HomeBrew 派だったのでこっちを使うことにしました。

さて、GNU Radio は、qt4 に依存しており、ビルドにはこれが必要です。しかし qt4 はすでにサポートが終了しており、 macOS Sierra では依存解決に失敗してビルドに成功しません。

しらべてみると、https://github.com/cartr/homebrew-qt4 で Sierra 向けにqt4 を公開してくれている方がいらっしゃったので、これを使わせていただきます。

    % brew tap cartr/qt4
    % brew install cartr/qt4/qt -vd
    % brew install cartr/qt4/pyqt -vd

ところで、GNU Radio は qwt にも依存しています。そして 2016/12現在提供されている qwt (6.1.3) は、qt5 に依存しています。しかし GNU Radio が直接依存している qt は qt4 なので、依存関係に qt5 がまざっているとビルドに失敗するようです。今回は、一度直接 qwt をインストールした上で、formula 直接編集し、qwt の依存ツリーから qt5 を一時的に削除することで対応しました。

    % brew install qwt
    % brew edit qwt
      # depends_on "qt5" # <- 17行目周辺にあるこの記述を # でコメントアウトします

上記の手順はGNU Radioをビルドさせるためだけのワークアラウンドであり、GNU Radio と qt5が正常にリンクできていないことから **この手順では GNU Radio 上の各種qtコンポーネントは使えません！** GNU RadioではQT以外に WX GUI も使えるので、利用の際はそっちで GUIの構築をすることとします（涙）。

これでようやくビルドするための準備が整いました。以下のコマンドを実行すると、GNU Radioをビルドしインストールすることができます。**なおビルドには数十分以上の時間がかかります。** 

    % brew install gnuradio --with-python -vd

成功した場合、`/usr/local/bin/gnuradio-companion` にリンクが作成されます。

なお、そのままgnuradio-companio を実行した場合は Waterfall Sink の表示ができなかったので、追加で PyOpenGL のインストールをしておきます。

    pip install PyOpenGL

また、コメントアウトした qwt のformula も戻しておきます。


### GNU Radio による信号処理プログラムの実装

BLE で使われている GFSK方式の周波数変調方式は、アンテナから入力した信号を直交振幅変調した後、高周波成分を除去した上で直交振幅復調することで元信号を復元することが可能です（らしい。このあたり、ちょっと自信がない）。

HackRF からUSB出力される信号はすでに直交振幅変調されたあとの信号なので、あとは Low Pass フィルタを通して直交振幅復調する信号処理を実装することでBLEの信号を復調することができます。

それをGNU Radio で実装するとこんな感じになります。ソースは[こちら](https://github.com/yohsuke/ble_demod)。



![](https://qiita-image-store.s3.amazonaws.com/0/90283/d71d6d04-55cb-0c03-b7ea-cd94622f3899.png)


以下のようなコマンドを事前に実行したうえで、`F6`キーを押して信号処理プログラムを実行すると、HackRFから出力される信号を処理して得られたビット列をコンソールに出力するとともに、以下のようなウィンドウの中に電波の受信状況やシグナルの強さが表示されるはずです。


    mkfifo /tmp/fifo
    cat /tmp/fifo | hexdump


<img width="1652" src="https://qiita-image-store.s3.amazonaws.com/0/90283/cd3eeee8-888f-823e-4e83-e742d1cb645d.png">


## ビット列のデコード

`/tmp/fifo` に出力されるデータは、NRF24-BTLE-Decoder というツールでデコードできます。

https://github.com/omriiluz/NRF24-BTLE-Decoder

チェックアウトしてきたソースを make したのち、次のように実行します。

    cat /tmp/fifo | ./bin/nrf24-btle-decoder -t btle




# 実行結果の確認

さて、ためにしHackRF のそばで iBeacon の信号を出力してみます UUID/Major/Minor はそれぞれ、`8492E75F-4FD6-469D-B132-043FE94921D8`/`0x19FA`/`0x0ED3`とします。するとおそらく、コンソール上に以下のような出力が出るはずです。


    1480964877.930937 BTLE Packet start sample 205236758, Threshold:0, Address: 0x8E89BED6, CRC:0x904DB4 length:36 data:40 24 68 00 6E A1 CC 64 02 01 1A 1A FF 4C 00 02 15 84 92 E7 5F 4F D6 46 9D B1 32 04 3F E9 49 21 D8 19 FA 0E D3 C5
    1480964877.996497 BTLE Packet start sample 205359237, Threshold:0, Address: 0x8E89BED6, CRC:0x904DB4 length:36 data:40 24 68 00 6E A1 CC 64 02 01 1A 1A FF 4C 00 02 15 84 92 E7 5F 4F D6 46 9D B1 32 04 3F E9 49 21 D8 19 FA 0E D3 C5
    1480964878.032862 BTLE Packet start sample 205419217, Threshold:0, Address: 0x8E89BED6, CRC:0x904DB4 length:36 data:40 24 68 00 6E A1 CC 64 02 01 1A 1A FF 4C 00 02 15 84 92 E7 5F 4F D6 46 9D B1 32 04 3F E9 49 21 D8 19 FA 0E D3 C5
    1480964878.074819 BTLE Packet start sample 205489196, Threshold:0, Address: 0x8E89BED6, CRC:0x904DB4 length:36 data:40 24 68 00 6E A1 CC 64 02 01 1A 1A FF 4C 00 02 15 84 92 E7 5F 4F D6 46 9D B1 32 04 3F E9 49 21 D8 19 FA 0E D3 C5




`0x8E89BED6` という値はadvertising data を示すAddressとしてBLEの仕様上で定義されているものですが、それが現れているのが見て取れます。
また、`FF 4C 00 02 15` というバイト列がなかに現れていますが、これはそれぞれ、BLEの仕様にあるManufacture Specific(0xFF), Apple の Company Identifier Code (0x004C), type (0x02), length (0x15)　と正しく対応しています。それに続く 16byte は指定したとおりの UUID/Major/Minor となっていますね。


# まとめ

というわけで、今回はソフトウェア無線の技術を使って、2.4GHz 帯の電波をキャプチャし、そのなかからBLEの信号をデコードしてコンソール上に表示するというシステムを構築してみました。

色々なところで落とし穴にハマってしまい、あまり新規性のある結果を出すことは出来なかったのが心残りなのですが、個人的には、今まで全く理解の外にあった無線工学に関する取っ掛かり的な知識と簡単な実験環境の獲得ができた気がします。今後ももっと知識を深め、色々なところに応用していきたいところです。


# 参考


misc:hacks:hackrf [Bitcraze Wiki] https://wiki.bitcraze.io/misc:hacks:hackrf

Great Scott Gadgets - HackRF One https://greatscottgadgets.com/hackrf/
GNU Radio http://gnuradio.org/

ワイヤレス技術をアタックで検証 by 堀合啓一 - CODE BLUE 2015 http://www.slideshare.net/codeblue_jp/by-code-blue-2015-59669683
SDRプラットフォームの比較 - OSQZSS http://blog.goo.ne.jp/osqzss/e/4fc61c87a8caaeeaf89158caa04b41eb
デジタル周波数変調・復調について - サーキットデザイン http://www.circuitdesign.jp/jp/technical/modulation/modulation_FSK.asp