とうふ荘の手記てき!

プログラムとか

Raspberry Pi Zero W (WH) をAndroidからBluetoothで遠隔操作して入力するキーボードにする

どういうこと?


  1. Raspberry Pi Zero WをBluetoothによるシリアル通信でAndroidから制御できるようにします。
  2. Raspberry Pi Zero WをUSBキーボードにします。
  3. Androidで入力した文字をRaspberry PiがUSBキーボードとしてパソコンに打ち込みます。

どうして?(使用例)


大学にパソコン室というのがあって大学指定のノートパソコンよりも高機能なパソコンを講義中に使うことができます。
しかし、ログイン時には自分の大学アカウントのIDとパスワードを手打ちしなければなりません。
大学のアカウントということでパスワードはランダム文字列にしてあり講義の度に手打ちというのはなかなか……
パスワードマネージャーソフトを勝手にインストールはまずいでしょうし、ログイン前なので、ポータブル版も使えません……

f:id:tofu-so-shioaji:20180923210051p:plain

そこで、ログイン前でも使えて、インストール不要の入力機器として、USBキーボードに白羽の矢が立ちました。
遠隔操作可能なUSBキーボードを学校PCに挿して、スマホからパスワードマネージャーでパスを打ち込んでやれば、ラクラクログインできるという算段です。

今回はそれを作る方法を備忘録的に残しておこうと思います。
あと、同じような悩みを抱えている人のお役に立てれば幸いです。

やり方

用意・Raspberry Piのセットアップ


今回使用するのは、

  1. Raspberry Pi Zero W(OSは2018-06-27-raspbian-stretch-lite)
  2. Windows10(TeraTermを用いたSSHでRaspberryPiを操作)
  3. Android 7.1.1

ラズパイはBluetoothが使えるものでRaspberry Pi Zero WHでも大丈夫ですが、Raspberry Pi3などはUSBポートが複数あるので、USBデバイスにはできないそうです。
AndroidではBluetoothでシリアル通信が可能なターミナルアプリを利用するだけなので、iPhoneにて代替アプリがあるのならば、iPhoneでも同様のことはできると思います。
microSDにRaspbianを焼く工程や、Raspberry Piにログインする方法は省略します。 こちらの方が紹介している方法などでRaspberry Piにコマンドを打ち込める状態にしてください。

www.agilegroup.co.jp

準備が終わったら、Raspberry Piは普段使われる電源ポートではなく、USBポートを用いてパソコンのUSBコネクタと接続させます。
(USBポートからでも電力は供給されるみたいです。)

また、接続させるパソコンはRaspberryPiにSSHを行っているものには使わないことをおすすめします。(操作を間違え暴走すると、RaspberryPiを止める手立てがなくなるため)
AndroidにはOTGケーブル(microUSBと普通のパソコンに使われるUSB Type-Aの変換アダプタ)があれば、RaspberryPiを接続しキーを入力させることができます。


次に、aptコマンドで各種パッケージを更新し、RaspberryPiを再起動します。

sudo apt update
sudo apt -y upgrade
sudo reboot

そして、カーネルバージョンを確認します。

uname -a

今回実行した結果は以下の通りでした。

Linux raspberrypi 4.14.70+ #1144 Tue Sep 18 17:20:50 BST 2018 armv6l GNU/Linux

Raspberry Pi Zero (W) をUSBキーボード(USBガジェット)にする - Qiita

によると、4.4以上あれば大丈夫だそうです。
これで下準備は完了です。

RaspberryPiをUSBキーボードにする


次に、RaspberryPiを接続したパソコンに対してUSBキーボードとして認識されるようにします。
こちらの記事を参考にしました。

qiita.com

まず、USBデバイスとしてのセットアップ用シェルスクリプトを作成し実行します。
ホームディレクトリで作業をすることとして、フォルダを作ります。

cd ~
mkdir otg
cd otg

次に、otgフォルダ内で以下のシェルスクリプトsetupOTG.shを作成します。

nano setupOTG.sh

これでテキストエディタnanoが開いて、以下のシェルスクリプトを打ち込んだら、ctr+xで保存です。

#!/bin/bash

# See https://gist.github.com/gbaman/50b6cca61dd1c3f88f41 for more info

echo "dtoverlay=dwc2" | sudo tee -a /boot/config.txt
echo "dwc2" | sudo tee -a /etc/modules

これを実行します。

sudo bash setupOTG.sh

そうしたら、一度再起動して設定を反映させてください。

sudo reboot

再度、RaspberryPiにログインしたら、作業用のディレクトリに戻ります。

cd ~/otg

次に、hid.shを作成します。これを実行した瞬間からPCにキーボードとして認識されますが、起動するたびに実行し直さなければならないスクリプトです。
まず作成します。

nano hid.sh

シェルスクリプトの内容は以下の通りです。

#!/bin/bash
modprobe libcomposite
cd /sys/kernel/config/usb_gadget/
mkdir -p g1
cd g1
echo 0x1d6b > idVendor # Linux Foundation
echo 0x0104 > idProduct # Multifunction Composite Gadget
echo 0x0100 > bcdDevice # v1.0.0
echo 0x0200 > bcdUSB # USB2
mkdir -p strings/0x409
echo "deadbeef01234567890" > strings/0x409/serialnumber
echo "example.com" > strings/0x409/manufacturer
echo "Generic USB Keyboard" > strings/0x409/product
N="usb0"
mkdir -p functions/hid.$N
echo 1 > functions/hid.$N/protocol
echo 1 > functions/hid.$N/subclass
echo 8 > functions/hid.$N/report_length
echo -ne \\x05\\x01\\x09\\x06\\xa1\\x01\\x05\\x07\\x19\\xe0\\x29\\xe7\\x15\\x00\\x25\\x01\\x75\\x01\\x95\\x08\\x81\\x02\\x95\\x01\\x75\\x08\\x81\\x03\\x95\\x05\\x75\\x01\\x05\\x08\\x19\\x01\\x29\\x05\\x91\\x02\\x95\\x01\\x75\\x03\\x91\\x03\\x95\\x06\\x75\\x08\\x15\\x00\\x25\\x65\\x05\\x07\\x19\\x00\\x29\\x65\\x81\\x00\\xc0 > functions/hid.$N/report_desc
C=1
mkdir -p configs/c.$C/strings/0x409
echo "Config $C: ECM network" > configs/c.$C/strings/0x409/configuration
echo 250 > configs/c.$C/MaxPower
ln -s functions/hid.$N configs/c.$C/
# End functions
ls /sys/class/udc > UDC

管理者権限で実行します。

sudo bash hid.sh

これでls /dev等で確認すると/dev/hidg0というファイルが現れているはずです。このファイルにデータを書き込むことで、キーボードからパソコンに文字が入力されます。
しかし、このままではls -l /dev/hidg0をすると権限が足りません。なので、以下のコマンドで権限を追加しておきます。

sudo chmod 777 /dev/hidg0

Raspberry Piからキーボードとして入力してみる


ここまでで、一応Raspberry Piによるキーボードが完成しました。
(hid.shを実行した時点でパソコンによってはキーボードが接続されたというメッセージが出ているはずです。)
ではキーボードとして使ってみます。
以下のコマンドは一行目が「a」を打ち込むもので、二行目が押されたキーを離すものです。
(操作を間違えてRaspberryPiによるキー入力でパソコンが操作できなくなった際は基本的に2行目のコマンドを実行すれば暴走は止まります。それでも止まらなかったら、RaspberryPiを引っこ抜いてマウス操作でPCを再起動するしかありません……)

sudo echo -ne "\0\0\x4\0\0\0\0\0" > /dev/hidg0
sudo echo -ne "\0\0\0\0\0\0\0\0" > /dev/hidg0

そして、キーボードとして認識させるための処理が起動時に自動的に行われるようにします。

sudo nano /etc/rc.local

以下の内容を/etc/profileの最後に追記します。

sudo bash ~/otg/hid.sh
sudo chmod 777 /dev/hidg0

これで、再起動してもキーボードとして認識されるようになりました。
もう少し扱いやすくします。以下のパッケージを使います。

github.com

まず、gitが標準ではついていなかったので、以下のコマンドで入手

sudo apt -y install git

以下のコマンドでパッケージを入手し、ビルドします。

cd ~
git clone https://github.com/girst/hardpass-sendHID
cd hardpass-sendHID
make

ビルドしたら、以下のコマンドを実行することでより簡単に文字が打ち込めます。

echo -n "hello world!" | sudo ./scan /dev/hidg0 1 2

Raspberry PiAndroidからBluetoothで操作する


Bluetoothには擬似的なシリアル通信を行うプロファイルが存在し、AndroidBluetoothに対応したシリアルコンソールとRaspberry Piを通信させることでログインが行えるようになります。
以下のサイトを参考にしました。

qiita.com

tokina.hatenadiary.jp

まず、Raspberry PiAndroidBluetoothで接続します。
以下のコマンドでBluetoothを操作します。

sudo bluetoothctl

すると、対話モードに移行するので以下のコマンドを順次実行してください。

power on
discoverable on
agent on
default-agent

そして、AndroidBluetooth設定画面を開くと「raspberrypi」という項目が現れているはずなので、ペアリングを開始してください。

f:id:tofu-so-shioaji:20180928020049p:plain

ペアリングが開始されると、AndrodとRaspberryPiにPINコードが表示され、接続するか聞いてきます。
RaspberryPiとAndroidのPINコードが一致していたら承認して接続を確立してください。接続が確定したら、次回からは自動的に接続されるように設定します。

f:id:tofu-so-shioaji:20180928020106p:plain

承認後に表示されたDevice xx:xx:xx:xx:xx:xx Paired: yesxx:xx:xx:xx:xx:xxという部分を用いて、(人によって表示される内容は異なります。)

trust xx:xx:xx:xx:xx:xx

を実行すれば、再起動後も自動で繋がります。

quit

で対話モードを終了します。
そして、RaspberryPiのBluetoothにシリアル通信機能を追加します。一旦、Bluetoothサービスを終了させ、シリアル通信を追加するのは以下のコマンドです。

sudo systemctl stop bluetooth
sudo bluetoothd -C &
sudo hciconfig hci0 up
sudo sdptool add SP

これで、シリアル通信機能が使えるようになりました。
以下のコマンドで、シリアル通信を用いてRaspberry Piへログインできるようになります。

sudo rfcomm watch 0 1 agetty rfcomm0 115200

ここまで、実行したら次にAndroidを操作します。
以下のアプリを使います。

play.google.com

このアプリをインストールし開いたら、まずデバイスを選択します。
左上からメニューを開き、Devicesを選択し、raspberrypiを選択します。

f:id:tofu-so-shioaji:20180928020142p:plain

次に、メニューからTerminalに戻り、画面上部の接続アイコンを接続すれば接続が開始され、成功するとRaspberryPiにログインできます。

f:id:tofu-so-shioaji:20180928020156p:plain

(結構な頻度で接続に失敗するので何度かリトライしたり、RaspberryPiの方で一旦Ctr+Cで接続待機状態を終了させ、再度実行してみてください。)

ここまで確認したら、再度画面上部のアイコンを再度タッチして、接続を解除します。
RaspberryPiに戻り、ctr+Cでシリアル通信を終了します。
次に、RaspberryPiが起動したら、すぐにAndroidで操作できるようにします。
/etc/rc.localというファイルに記述した内容は起動時に実行されます。以下のコマンドで編集画面を開きます。

sudo nano /etc/rc.local

次に、以下のコマンドをrc.localexit 0の上に記述します。
exit 0の下に記述しないでください。)

sudo systemctl stop bluetooth
sudo bluetoothd -C &
sudo hciconfig hci0 up
sudo sdptool add SP
sudo rfcomm watch 0 1 agetty rfcomm0 115200

(参考にした記事ではsdptoolにSPを追加するまでの一連の処理は一度きりでいいはずなんですけどね……)
これで、再起動してもAndroidから接続することができます。

遠隔キーボードソフトremipiを作りました


最後に、USBキーボードとなったRaspberryPiで最初に述べたパスワード遠隔入力作戦をしやすいようにremipiというソフトを作りました。
このソフトは、日本語もある程度は送れます。

なお、入力対象は日本語キーボードを持ったWindowsを想定しています。MacLinux等で実行すると一部の機能が使えないかもしれません。

インストール方法

バイナリファイルをアップしているので、wgetで入手します。以下のコマンドで、バイナリファイルを実行可能なディレクトリにダウンロードします。

cd /usr/local/bin
sudo wget https://github.com/tofuso/remipi/releases/download/ver1.0/remipi
sudo chmod 777 remipi
cd ~

これで、ソフトがインストールされました。

remipi -h

を実行すると、以下のように帰ってくるはずです。

Usage of remipi:
  -d string
        デバイスファイル (default "/dev/hidg0")
  -s string
        キーボードに出力させる文字を指定してください。 (default "Hello World!")
  -t    指定すると対話モードで起動します。Ctr+Cまたは_quit_を実行して終了。

これでインストール完了です。

使い方

入力させるPCのUSBポートとRaspberryPiのUSBポートをケーブルで繋ぎ、BluetoothターミナルやSSH等でログインします。
そして、入力されるPC側は半角入力状態で、メモ帳等を開いて、入力待機状態にしてください。
この状態で、以下のコマンドを実行します。

remipi -s こんにちは!

すると、メモ帳に日本語が入力されているはずです。
このようにコマンドから、一発でひらがなを打ち込むことができます。
今度はメモ帳を閉じて、デスクトップ画面にして、以下のコマンドを実行してみてください。

remipi -s _win-r__sec_notepad_enter__sec_このPCをあやつっている!

文字ではないキーボードの操作も_で囲うことで行うことができます。
(詳しくはこちらで確認してみてください。)
なので、下のようなシャットダウン操作だってできてしまいます!

remipi -s "_win-r__sec_cmd.exe_enter__sec_shutdown -s -t 0_enter_"

このようなキーボードショートカットを利用してUSBキーボード単体で色々な事ができます。 これで、大学のパソコンに楽々ログインできるようになるはずです!

f:id:tofu-so-shioaji:20180928020510p:plain

ここまでお付き合いいただきありがとうございました!

それからどうしたの?(PS.)


無事に大学のPCにUSBキーボードとして認識させて、パスワードを遠隔操作で打ち込むことができました!
本当はセキュリティとかで弾かれるんじゃないかと思ってた……

ちなみにそのパソコンは授業では使用しないそうです。そんなぁ……