-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
1178 lines (829 loc) · 80.5 KB
/
Copy pathatom.xml
File metadata and controls
1178 lines (829 loc) · 80.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[Playground]]></title>
<link href="http://tech.beatrobo.com/atom.xml" rel="self"/>
<link href="http://tech.beatrobo.com/"/>
<updated>2015-12-22T03:30:57+09:00</updated>
<id>http://tech.beatrobo.com/</id>
<author>
<name><![CDATA[Beatrobo, Inc.]]></name>
</author>
<generator uri="http://octopress.org/">Octopress</generator>
<entry>
<title type="html"><![CDATA[Web Bluetooth API で BLE デバイスにブラウザから接続する]]></title>
<link href="http://tech.beatrobo.com/blog/2015/12/20/proximity-profile-with-web-bluetooth-api/"/>
<updated>2015-12-20T20:55:51+09:00</updated>
<id>http://tech.beatrobo.com/blog/2015/12/20/proximity-profile-with-web-bluetooth-api</id>
<content type="html"><![CDATA[<p>こんばんは。
Beatrobo竹井です。</p>
<p>この記事は <a href="http://qiita.com/advent-calendar/2015/ble">Bluetooth Low Energy Advent Calendar 2015</a> の20日目の記事です。</p>
<p>先日 <a href="http://jsbshibuya.connpass.com/event/22600/">JavaScriptでIoT !! JS Board Shibuya #6 LTナイト!</a> に行った際に <a href="https://webbluetoothcg.github.io/web-bluetooth/">Web Bluetooth API</a> の存在を知りました。</p>
<p>この Web Bluetooth API を利用すれば、ブラウザだけで Web サービスと BLE デバイスとがコミュニケーションできるようになるというわけです。</p>
<p>以前開発していた <a href="https://github.com/YUKAI/konashi-js-sdk">konashi-js-sdk</a> では、ブラウザ上の JS で BLE デバイスとコミュニケーションを実現するため、BLE と通信する Native 側とブラウザ内の JS とが双方向にコミュニケーションできるカスタム WebView を作り、それを konashi.js アプリの中に埋め込んでいました。</p>
<p>それが Web Bluetooth API だと特別なアプリのインストールいらずで、普通のブラウザ上で konashi.js 相当のことができるようになります。</p>
<p>今回は、Web Bluetooth API の現状を整理しつつ、Web Bluetooth API を使って Proximity Profile デバイスとコミュニケーションする JS を作って見ようと思います。</p>
<p>コードは <a href="https://github.com/hideyuki/proximity-profile-with-web-bluetooth-api">https://github.com/hideyuki/proximity-profile-with-web-bluetooth-api</a> に実行可能な状態で置いています。</p>
<!-- more -->
<h2>Web Bluetooth API の調査</h2>
<p>現在、Web Bluetooth API は <a href="https://webbluetoothcg.github.io/web-bluetooth/">W3C に Draft として上がっています</a>。この Draft にはサンプルコードもたくさん記載されています。</p>
<p>また、最新情報などは以下から取得できます。</p>
<ul>
<li><a href="https://www.w3.org/community/web-bluetooth/">Web Bluetooth Community Group</a></li>
<li><a href="https://github.com/webbluetoothcg/web-bluetooth/">GitHub: WebBluetoothCG/web-bluetooth</a></li>
</ul>
<p>そして Web Bluetooth API の実装状況は<a href="https://github.com/WebBluetoothCG/web-bluetooth/blob/gh-pages/implementation-status.md">こちら</a>。全体的には、やっと実装が始まってる & 実装すら始まっていないという感じです。</p>
<p>主要なブラウザの対応状況を見ていきましょう。</p>
<h3>Chrome</h3>
<p>Android 6 以上だと v48で GATT Notifications 以外はすべて実装されているようです。Mac OS X の Chromeだと Discovery のみ。</p>
<p>Mac OS X Chrome はまだアクティブではないようで、Android Chrome のほうをまず実装してからとなるようです。</p>
<p>ついでに、Windows, Linux はまだ実装も始まっていない状況です。</p>
<h3>Firefox</h3>
<p><a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1204396">Bug 1204396 – [meta] Align our GATT API with W3C spec</a> にあるように、Firefox OS での実装が進んでいるようですが、歴史的経緯で W3C の Web Bluetooth API とは異なっているようです。</p>
<h3>Windows Edge</h3>
<p>実装ステータスは<a href="https://dev.windows.com/en-us/microsoft-edge/platform/status/webbluetooth">こちら</a>。優先度は低でまだ評価中という段階のようです。</p>
<h3>ということで</h3>
<p>今回は Nexus5 の Android 6 で Web Bluetooth API にトライしてみましょう。</p>
<h2>Android Chrome Dev で Web Bluetooth API を使う</h2>
<p>現在の <a href="https://play.google.com/store/apps/details?id=com.chrome.dev&hl=ja">Android Chrome の Dev版</a> のバージョンは 49 です。</p>
<p>v48 だと <code>characteristicvaluechanged</code> イベントが発火しないようなのですが、v49 でも発火せず Notify はまだ使えない。残念。ということで今回は Read/Write ができるChracteristics を持つ Service を触っていきましょう。</p>
<p>Chrome で Web Bluetooth API を有効にするには、以下のフラグページで Web Bluetooth API を有効にする必要があります。</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>chrome://flags/#enable-web-bluetooth</span></code></pre></td></tr></table></div></figure>
<p><a href="https://raw.githubusercontent.com/hideyuki/proximity-profile-with-web-bluetooth-api/master/images/enable-web-bluetooth-api.jpg"><img src="https://raw.githubusercontent.com/hideyuki/proximity-profile-with-web-bluetooth-api/master/images/enable-web-bluetooth-api.jpg" width="400"/></a></p>
<p>BLE デバイスをお持ちでない方は、Androidアプリの <a href="https://play.google.com/store/apps/details?id=io.github.webbluetoothcg.bletestperipheral&hl=ja">BLE Peripheral Simulator</a> か、iOSアプリの <a href="https://itunes.apple.com/jp/app/lightblue-explorer-bluetooth/id557428110?mt=8">LightBlue Explorer</a> をもう一台のスマホにインストールすることで、スマホ自体が仮想BLEデバイス (Peripheral) として振る舞ってくれるのでオススメです。</p>
<h2>Proximity デバイスと通信!</h2>
<p>今回は私の開発用iPhoneに <a href="https://itunes.apple.com/jp/app/lightblue-explorer-bluetooth/id557428110?mt=8">LightBlue Explorer</a> をインストールし、仮想 Proximity デバイスを作って Android Chrome Dev の Web Bluetooth API を使って通信させてみます。</p>
<p>本題に入る前に、Proximity Profile の整理をさくっと。</p>
<p>(余談ですが、Profile や Service の詳細を調べたいときは、developer.bluetooth.org の <a href="https://developer.bluetooth.org/gatt/profiles/Pages/ProfilesHome.aspx">GATT Specifications > Profiles</a> や、<a href="https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx">GATT Specifications > Services</a> を見ると一目瞭然です!)</p>
<h3>Proximity Profile (PXP)</h3>
<p><a href="https://developer.bluetooth.org/gatt/profiles/Pages/ProfileViewer.aspx?u=org.bluetooth.profile.proximity.xml">Bluetooth Developer Portal: GATT Specifications > Profiles > Proximity</a></p>
<p>Proximity Profile は「近接」という単語の通り、デバイスとの距離をベースにアラートを生成するプロファイルで、スマートフォンの置き忘れなどのアプリケーションを作成する時に搭載されるものです。</p>
<p>この Proximity Profile には 以下の3つの Serices が定義されています。</p>
<h4>Link Loss Service (org.bluetooth.service.link_loss)</h4>
<p><a href="https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.link_loss.xml">Bluetooth Developer Portal: GATT Specifications > Services > Link Loss</a></p>
<p>接続が切れた時にアラートを鳴らすかの設定を行う Service です。Read/Write 可能な <code>alert_level</code> という Characteristic を持っています。</p>
<h4>Immediate Alert Service (org.bluetooth.service.immediate_alert)</h4>
<p><a href="https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.immediate_alert.xml">Bluetooth Developer Portal: GATT Specifications > Services > Immediate Alert</a></p>
<p>すぐさまアラートを鳴らせる Service です。WriteWithoutResponse のみ可能な <code>alert_level</code> という Characteristic を持っています。</p>
<h4>Tx Power Service (org.bluetooth.service.tx_power)</h4>
<p><a href="https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.tx_power.xml">Bluetooth Developer Portal: GATT Specifications > Services > Tx Power</a></p>
<p>BLE の送信パワーを読める Service です。Read のみ可能な <code>tx_power_level</code> という Characteristic を持っています。</p>
<h2>ブラウザ(BLEのセントラル)側のコード例</h2>
<p>BLE のセントラル側の一般的な処理の流れは以下です(ペアリングはややこしいので省きます)。</p>
<ul>
<li>① デバイスのスキャン</li>
<li>② デバイスに接続</li>
<li>③ デバイスの Services 情報を取得</li>
<li>④ ③で得た Services の Characteristics 情報を取得</li>
<li>⑤ Characteristics を操作する</li>
</ul>
<p>現状の Web Bluetooth API はこれ以上のことはできなさそうなので、今回はこの①〜⑤を実装してみましょう。</p>
<p>以下、コードです。ES6ですのでご注意を。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
</pre></td><td class='code'><pre><code class='js'><span class='line'><span class="c1">// ① デバイスのスキャン</span>
</span><span class='line'><span class="nx">navigator</span><span class="p">.</span><span class="nx">bluetooth</span><span class="p">.</span><span class="nx">requestDevice</span><span class="p">({</span>
</span><span class='line'> <span class="nx">filters</span><span class="o">:</span> <span class="p">[{</span>
</span><span class='line'> <span class="nx">services</span><span class="o">:</span> <span class="p">[</span>
</span><span class='line'> <span class="s1">'link_loss'</span><span class="p">,</span>
</span><span class='line'> <span class="s1">'immediate_alert'</span><span class="p">,</span>
</span><span class='line'> <span class="s1">'tx_power'</span>
</span><span class='line'> <span class="p">]</span>
</span><span class='line'> <span class="p">}]</span>
</span><span class='line'><span class="p">})</span>
</span><span class='line'> <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">device</span> <span class="o">=></span><span class="p">{</span>
</span><span class='line'> <span class="c1">// ② デバイス見つかったので、接続する</span>
</span><span class='line'> <span class="k">return</span> <span class="nx">device</span><span class="p">.</span><span class="nx">connectGATT</span><span class="p">();</span>
</span><span class='line'> <span class="p">})</span>
</span><span class='line'> <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">server</span> <span class="o">=></span><span class="p">{</span>
</span><span class='line'> <span class="c1">// ③ デバイスに接続できたので、そのデバイスのServiceを調べる</span>
</span><span class='line'> <span class="k">return</span> <span class="nx">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">([</span>
</span><span class='line'> <span class="nx">server</span><span class="p">.</span><span class="nx">getPrimaryService</span><span class="p">(</span><span class="s1">'link_loss'</span><span class="p">),</span>
</span><span class='line'> <span class="nx">server</span><span class="p">.</span><span class="nx">getPrimaryService</span><span class="p">(</span><span class="s1">'immediate_alert'</span><span class="p">),</span>
</span><span class='line'> <span class="nx">server</span><span class="p">.</span><span class="nx">getPrimaryService</span><span class="p">(</span><span class="s1">'tx_power'</span><span class="p">)</span>
</span><span class='line'> <span class="p">]);</span>
</span><span class='line'> <span class="p">})</span>
</span><span class='line'> <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">services</span> <span class="o">=></span><span class="p">{</span>
</span><span class='line'> <span class="c1">// ④ Serviceを全部調べたので、次はサービスに紐づくCharacteristicsを調べる</span>
</span><span class='line'> <span class="k">return</span> <span class="nx">Promise</span><span class="p">.</span><span class="nx">all</span><span class="p">([</span>
</span><span class='line'> <span class="nx">services</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">getCharacteristic</span><span class="p">(</span><span class="s1">'alert_level'</span><span class="p">),</span>
</span><span class='line'> <span class="nx">services</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">getCharacteristic</span><span class="p">(</span><span class="s1">'alert_level'</span><span class="p">),</span>
</span><span class='line'> <span class="nx">services</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="nx">getCharacteristic</span><span class="p">(</span><span class="s1">'tx_power_level'</span><span class="p">)</span>
</span><span class='line'> <span class="p">]);</span>
</span><span class='line'> <span class="p">})</span>
</span><span class='line'> <span class="c1">// Discovered characteristics</span>
</span><span class='line'> <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">characteristics</span> <span class="o">=></span><span class="p">{</span>
</span><span class='line'> <span class="c1">// ⑤ 全部の Chracteristics をゲット</span>
</span><span class='line'> <span class="c1">// あとはその Characteristics を Read/Writeしていく</span>
</span><span class='line'> <span class="p">})</span>
</span><span class='line'> <span class="p">.</span><span class="k">catch</span><span class="p">(</span><span class="nx">error</span> <span class="o">=></span><span class="p">{</span>
</span><span class='line'> <span class="c1">// エラー時はここにくる</span>
</span><span class='line'> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">error</span><span class="p">);</span>
</span><span class='line'> <span class="p">});</span>
</span></code></pre></td></tr></table></div></figure>
<p><code>.then (Promises)</code> や <code>=></code> などは ES6 (ES2015) で新しく定義されている文法です。 ES6 に関しては <a href="https://babeljs.io/docs/learn-es2015/">Learn ES2015</a> がわかりやすいです。</p>
<p>注意点としては、https 上でしか Web Bluetooth API が動作しないようですので、オレオレ証明書を作ってローカルサーバでも https ができるようにしておくのが良いでしょう。</p>
<h3>デモ</h3>
<p>このような Proximity の Services/Characteristics を表示する JS/HTML を作成してみました。</p>
<p><a href="https://proximity-web-bluetooth-api.herokuapp.com/">https://proximity-web-bluetooth-api.herokuapp.com/</a></p>
<p><a href="https://raw.githubusercontent.com/hideyuki/proximity-profile-with-web-bluetooth-api/master/images/app_screen.jpg"><img src="https://raw.githubusercontent.com/hideyuki/proximity-profile-with-web-bluetooth-api/master/images/app_screen.jpg" width="300"/></a></p>
<p>動作ムービーです。</p>
<p><img src="https://github.com/hideyuki/proximity-profile-with-web-bluetooth-api/raw/master/images/play.gif" alt="" /></p>
<p>Link Loss Service の <code>alert_level</code> の Read/Write はもちろんのこと、Proximity デバイス側から Link Loss Service の <code>alert_level</code> を書き換えてから、ブラウザから Read してもちゃんと反映されています。Characteristics の Read/Write は普通にできていますね。</p>
<p>Immediate Alert Service の <code>alert_level</code> Characteristic は <code>WriteWithoutResponse</code> なのですが、<code>writeValue()</code> だとエラーが出てしまうので Immediate Alert Service は非表示にしています。</p>
<h3>開発Tips</h3>
<h4>Android Chrome のリモートデバッグ</h4>
<p>現状の Web Bluetooth API だと、基本的には Android Chrome 上でした動作しないので、Android上で動作確認を行いながら開発を進めていくことになります。そんな時は、Android Chrome のリモートデバッグ機能を使うと、PCのChrome DevTools 上で Android の Chrome 上のページをデバッグできるようになり、とても楽です。</p>
<p>リモートデバッグのやり方は <a href="https://developer.chrome.com/devtools/docs/remote-debugging">Remote Debugging on Android with Chrome</a> が詳しいです。</p>
<h4>Mac から Android Chrome へのポートフォワード</h4>
<p>リモートデバッグ関連ですが、Mac 上で動作しているサーバ(JS などを https でホストしている)に Android から簡単にアクセスさせるために方法としてポートフォワードがいい感じです。ポートフォワードなら、Mac の IP を調べたり、同じ LAN 内にいなくても開発が進めることができます。設定は Chrome の DevTools から可能です。</p>
<p><a href="https://raw.githubusercontent.com/hideyuki/proximity-profile-with-web-bluetooth-api/master/images/remote_debug.jpg"><img src="https://raw.githubusercontent.com/hideyuki/proximity-profile-with-web-bluetooth-api/master/images/remote_debug.jpg" width="500"/></a></p>
<h2>まだ不安定。だが期待はできる!</h2>
<p>実装自体もまだ始まったばかりの Web Bluetooth API。とても不安定です。実際に開発している最中でも、同じデバイス名が2つ出てきたり、まったく接続できなくなったりします。その時はブラウザを再起動してやればまた正常に動きますのでご安心を。</p>
<p>あと1年くらいすれば Android Chrome は落ち着きそうな感じ。あとは他のブラウザが追従してくるか…</p>
<p>すでに、Chrome の Extension 領域では <a href="https://developer.chrome.com/apps/bluetooth">chrome.bluetooth</a> が利用可能ですので、OSX や Windows のChrome への実装もそう遠くない未来に実現するでしょう。</p>
<p>ブラウザ上でハードウェアとコミュニケーションができる API があることによって、ハードウェアと連動するWebページなど、今までの Web をより拡張したものができるかもしれません。今後も引き続き BLE 情報はウォッチしつつ、何かおもしろいプロダクトが作れればと思います。</p>
<h2>BLE 好きな方、遊びにおいで!</h2>
<p><a href="https://www.plugair.com/en/jobs/">アルバイトや正社員のエンジニア募集中です!</a></p>
<p>BLEが好きな方、ハードが好きな方。ぜひ弊社の代々木オフィスに遊びに来て語り合いましょう!</p>
<p><a href="https://twitter.com/HideyukiTakei">@HideyukiTakei</a> もしくは、<a href="mailto:hide@beatrobo.com">hide@beatrobo.com</a> までご連絡頂ければ大歓迎いたします。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[SECCON 2015 Online CTF Writeup]]></title>
<link href="http://tech.beatrobo.com/blog/2015/12/06/seccon-2015-online-ctf-writeup/"/>
<updated>2015-12-06T23:44:04+09:00</updated>
<id>http://tech.beatrobo.com/blog/2015/12/06/seccon-2015-online-ctf-writeup</id>
<content type="html"><![CDATA[<p><a herf=""><img src="https://raw.githubusercontent.com/hideyuki/SECCON-2015-Online-CTF-Writeup/master/seccon.jpg" width="600" /></a></p>
<p>お久しぶりです。Beatrobo竹井です。</p>
<p>12/5 15:00 〜 12/6 15:00 に開催された <a href="http://score.quals.seccon.jp/announcement/">SECCON 2015 Online CTF</a> の参加レポートと正解できたチャレンジの解法の解説をお送りします。</p>
<p>今回初参加で、MITMというチーム名で私ひとりでのチャレンジ。まったく予習をしていなかったのでQRコードやパケット解析はほぼわからず、結局900点の202位で終了となりました。</p>
<p>時間内に解けた問題は以下。</p>
<ul>
<li>Start SECCON CTF (50)</li>
<li>SECCON WARS 2015 (100)</li>
<li>Reverse-Engineering Android APK 1 (100)</li>
<li>Reverse-Engineering Hardware 1 (400)</li>
<li>Connect the server (100)</li>
<li>Command-Line Quiz (100)</li>
<li>Last Challenge (Thank you for playing) (50)</li>
</ul>
<p>締め切りの10分後に”Reverse-Engineering Hardware 2 (500)“の答えがわかってしまい、1日目にぐっすり寝たのを後悔。</p>
<p>弊社では<a href="https://www.plugair.com">PlugAir</a>というハードウェアな認証キーを作っておりハードウェア周りが得意なので、ハードウェア系の問題 “Reverse-Engineering Hardware 1&2” の詳細な解説をしていきます。</p>
<p>ハードウェアのリバースエンジニアリング系の問題は、配点が高い割に回路構成さえ把握できれば悩むところがあまりない問題ばかりだったのでオススメです。</p>
<p>コードは <a href="https://github.com/hideyuki/SECCON-2015-Online-CTF-Writeup">https://github.com/hideyuki/SECCON-2015-Online-CTF-Writeup</a> に実行可能な状態で置いています。</p>
<!-- more -->
<hr />
<h1>解説の前に</h1>
<p>今回出題された問題は <a href="https://github.com/SECCON/SECCON2015_online_CTF">こちら</a>にすべて公開されています。</p>
<p>ハードウェア系の問題2つで共通だったことは、RaspberryPi B+と74HC系のICが使われていたということ、回路構成は多数の外観写真から推定すること、だいたいの動作は提供されたPythonのソースコードで把握できたことでした。<br/>
ということで、RaspberryPi B+のGPIOのピン配置を手元に置いておきましょう。<a href="https://www.raspberrypi.org/documentation/usage/gpio-plus-and-raspi2/README.md">GPIO: MODELS A+, B+ AND RASPBERRY PI 2</a> で公開されています。</p>
<p><a href="https://raw.githubusercontent.com/hideyuki/SECCON-2015-Online-CTF-Writeup/master/raspberrypi/gpio-numbers-pi2.png"><img src="https://raw.githubusercontent.com/hideyuki/SECCON-2015-Online-CTF-Writeup/master/raspberrypi/gpio-numbers-pi2.png" width="600"/></a></p>
<h1>Reverse-Engineering Hardware 1</h1>
<h2>設問</h2>
<p>私たちはデコーダーボードの写真と、すてきなテキストを生成するプログラムを入手した。<br/>
解読のお手伝いをしてくれない?<br/>
gpio.py<br/>
ChristmasIllumiations.zip</p>
<h2>提供されたもの</h2>
<ul>
<li>RaspberryPi上で動作するPythonのコード(プログラム中にGPIOを使っているので、実環境がないと動作しない)</li>
<li>LEDがチカチカする動作ムービー</li>
<li>回路構成を類推するための多数のデコーダーボード画像(以下のような画像が30枚ほど)</li>
</ul>
<p><a href="https://raw.githubusercontent.com/hideyuki/SECCON-2015-Online-CTF-Writeup/master/Reverse-Engineering-Hardware-1/ChristmasIllumiations/DSC_0125.jpg"><img src="https://raw.githubusercontent.com/hideyuki/SECCON-2015-Online-CTF-Writeup/master/Reverse-Engineering-Hardware-1/ChristmasIllumiations/DSC_0125.jpg" width="600"/></a></p>
<h2>手順</h2>
<p>上にあるデコーダーボード画像に写っているLEDのうち、ブレッドボード上部に刺さっている6個のLEDを左からL1からL6と、L6の下にあるLEDをL7と命名。</p>
<p>また、ブレッドボード左下部に74HC74という<a href="http://www.marutsu.co.jp/contents/shop/marutsu/datasheet/TC74HC74A.pdf">Dフリップフロップ</a>が1つあるのを発見。</p>
<p>まずはコードをざっくりみてRaspberryPiで利用しているGPIOのポートを確認。その後、多数の画像から回路構成を調査する。調査結果が以下。</p>
<p><a href="https://raw.githubusercontent.com/hideyuki/SECCON-2015-Online-CTF-Writeup/master/Reverse-Engineering-Hardware-1/circuit1.jpg"><img src="https://raw.githubusercontent.com/hideyuki/SECCON-2015-Online-CTF-Writeup/master/Reverse-Engineering-Hardware-1/circuit1.jpg" width="600"/></a></p>
<p>この適当な回路図ではL1とL2が下のほうに描かれていますが、紙面の関係上下に分けて書いただけで、L1〜L6は横一列に並んでいます。</p>
<p>LED周辺の回路は基本的に、<a href="http://markun.cs.shinshu-u.ac.jp/learn/lcirc/lcirc2/lcirc2-3.html">2つのダイオードでORを作り</a>、その出力にLEDとプルアップ抵抗を繋いでいます。所々にGPIOの入力ピンも接続されており負論理となっています(LEDは正論理で、ONだと点灯)。図で表すと以下の様な感じ。</p>
<p><a href="https://raw.githubusercontent.com/hideyuki/SECCON-2015-Online-CTF-Writeup/master/Reverse-Engineering-Hardware-1/nor.jpg"><img src="https://raw.githubusercontent.com/hideyuki/SECCON-2015-Online-CTF-Writeup/master/Reverse-Engineering-Hardware-1/nor.jpg" width="400"/></a></p>
<p>で、D-FFまわりの回路はごちゃごちゃしているんですが、以下のような感じでした。(図やプログラムではD-FF単体をIC<sub>1</sub>、IC<sub>2</sub>と書いちゃってますがお気になさらず…)</p>
<p><a href="https://raw.githubusercontent.com/hideyuki/SECCON-2015-Online-CTF-Writeup/master/Reverse-Engineering-Hardware-1/circuit2.jpg"><img src="https://raw.githubusercontent.com/hideyuki/SECCON-2015-Online-CTF-Writeup/master/Reverse-Engineering-Hardware-1/circuit2.jpg" width="600"/></a></p>
<p>これら回路構成調査の結果から、以下の関係式となることがわかりました。</p>
<ul>
<li>L<sub>1</sub> = DA・<span class="ol">Q</span><sub>IC2</sub> = <span class="ol">X</span><sub>3</sub></li>
<li>L<sub>2</sub> = <span class="ol">L</span><sub>1</sub>・<span class="ol">Q</span><sub>IC1</sub> = <span class="ol">X</span><sub>4</sub></li>
<li>L<sub>3</sub> = DB・Q<sub>IC2</sub> = <span class="ol">X</span><sub>5</sub></li>
<li>L<sub>4</sub> = <span class="ol">Q</span><sub>IC1</sub>・<span class="ol">Q</span><sub>IC2</sub></li>
<li>L<sub>5</sub> = <span class="ol">Q</span><sub>IC1</sub>・<span class="ol">L</span><sub>4</sub></li>
<li>L<sub>6</sub> = <span class="ol">Q</span><sub>IC2</sub>・<span class="ol">L</span><sub>4</sub></li>
<li>L<sub>7</sub> = <span class="ol">L</span><sub>5</sub>・<span class="ol">L</span><sub>6</sub> = <span class="ol">X</span><sub>6</sub></li>
<li>D<sub>IC1</sub> = DA</li>
<li>D<sub>IC2</sub> = DB</li>
<li>Q<sub>IC1</sub> = X<sub>1</sub></li>
<li>Q<sub>IC2</sub> = X<sub>2</sub></li>
</ul>
<p>(関係式を整理すれば、もうちょっとシンプルになるかも)</p>
<p>この関係式をPythonのプログラムのほうに追加で実装すれば、実機がなくともプログラムを実行できるようになります。</p>
<p>そして愚直に実装したコードがこちらです。</p>
<p><a href="https://github.com/hideyuki/SECCON-2015-Online-CTF-Writeup/blob/master/Reverse-Engineering-Hardware-1/gpio_sim.py">https://github.com/hideyuki/SECCON-2015-Online-CTF-Writeup/blob/master/Reverse-Engineering-Hardware-1/gpio_sim.py</a></p>
<p><code>gpio.py</code> からの主な変更点は</p>
<ul>
<li>DA, DBが変更する時やFFのQの値が変わるタイミングでLEDの状態を更新する<code>update_led()</code>を実行</li>
<li><code>encoder()</code>内で参照していたX<sub>1</sub>〜X<sub>6</sub>をFFのQやLEDの状態で置き換え</li>
<li>CLKを進めていたところで<code>update_ff_q()</code>を実行し、Qの値を更新する</li>
</ul>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="k">def</span> <span class="nf">encoder</span><span class="p">():</span>
</span><span class='line'> <span class="n">v</span> <span class="o">=</span> <span class="mi">0</span>
</span><span class='line'> <span class="n">v</span> <span class="o">=</span> <span class="n">ic1q</span> <span class="c"># x1 = ic1q</span>
</span><span class='line'> <span class="n">v</span> <span class="o">=</span> <span class="mi">2</span><span class="o">*</span><span class="n">v</span> <span class="o">+</span> <span class="n">ic2q</span> <span class="c"># x2 = ic2q</span>
</span><span class='line'> <span class="n">v</span> <span class="o">=</span> <span class="mi">2</span><span class="o">*</span><span class="n">v</span> <span class="o">+</span> <span class="nb">int</span><span class="p">(</span><span class="ow">not</span><span class="p">(</span><span class="n">l1</span><span class="p">))</span> <span class="c"># x3 = not l1</span>
</span><span class='line'> <span class="n">v</span> <span class="o">=</span> <span class="mi">2</span><span class="o">*</span><span class="n">v</span> <span class="o">+</span> <span class="nb">int</span><span class="p">(</span><span class="ow">not</span><span class="p">(</span><span class="n">l2</span><span class="p">))</span> <span class="c"># x4 = not l2</span>
</span><span class='line'> <span class="n">v</span> <span class="o">=</span> <span class="mi">2</span><span class="o">*</span><span class="n">v</span> <span class="o">+</span> <span class="nb">int</span><span class="p">(</span><span class="ow">not</span><span class="p">(</span><span class="n">l3</span><span class="p">))</span> <span class="c"># x5 = not l3</span>
</span><span class='line'> <span class="n">v</span> <span class="o">=</span> <span class="mi">2</span><span class="o">*</span><span class="n">v</span> <span class="o">+</span> <span class="nb">int</span><span class="p">(</span><span class="ow">not</span><span class="p">(</span><span class="n">l7</span><span class="p">))</span> <span class="c"># x6 = not l7</span>
</span><span class='line'> <span class="k">return</span> <span class="n">v</span>
</span><span class='line'>
</span><span class='line'><span class="k">def</span> <span class="nf">update_led</span><span class="p">():</span>
</span><span class='line'> <span class="k">global</span> <span class="n">l1</span><span class="p">,</span> <span class="n">l2</span><span class="p">,</span> <span class="n">l3</span><span class="p">,</span> <span class="n">l4</span><span class="p">,</span> <span class="n">l5</span><span class="p">,</span> <span class="n">l6</span><span class="p">,</span> <span class="n">l7</span>
</span><span class='line'> <span class="n">l1</span> <span class="o">=</span> <span class="n">da</span> <span class="ow">or</span> <span class="ow">not</span><span class="p">(</span><span class="n">ic2q</span><span class="p">)</span>
</span><span class='line'> <span class="n">l2</span> <span class="o">=</span> <span class="ow">not</span><span class="p">(</span><span class="n">l1</span><span class="p">)</span> <span class="ow">or</span> <span class="ow">not</span><span class="p">(</span><span class="n">ic1q</span><span class="p">)</span>
</span><span class='line'> <span class="n">l3</span> <span class="o">=</span> <span class="n">db</span> <span class="ow">or</span> <span class="n">ic2q</span>
</span><span class='line'> <span class="n">l4</span> <span class="o">=</span> <span class="ow">not</span><span class="p">(</span><span class="n">ic1q</span><span class="p">)</span> <span class="ow">or</span> <span class="ow">not</span><span class="p">(</span><span class="n">ic2q</span><span class="p">)</span>
</span><span class='line'> <span class="n">l5</span> <span class="o">=</span> <span class="ow">not</span><span class="p">(</span><span class="n">ic1q</span><span class="p">)</span> <span class="ow">or</span> <span class="ow">not</span><span class="p">(</span><span class="n">l4</span><span class="p">)</span>
</span><span class='line'> <span class="n">l6</span> <span class="o">=</span> <span class="ow">not</span><span class="p">(</span><span class="n">ic2q</span><span class="p">)</span> <span class="ow">or</span> <span class="ow">not</span><span class="p">(</span><span class="n">l4</span><span class="p">)</span>
</span><span class='line'> <span class="n">l7</span> <span class="o">=</span> <span class="ow">not</span><span class="p">(</span><span class="n">l5</span><span class="p">)</span> <span class="ow">or</span> <span class="ow">not</span><span class="p">(</span><span class="n">l6</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="k">def</span> <span class="nf">update_ff_q</span><span class="p">():</span>
</span><span class='line'> <span class="k">global</span> <span class="n">ic1q</span><span class="p">,</span> <span class="n">ic2q</span>
</span><span class='line'> <span class="n">ic1q</span> <span class="o">=</span> <span class="n">da</span>
</span><span class='line'> <span class="n">ic2q</span> <span class="o">=</span> <span class="n">db</span>
</span></code></pre></td></tr></table></div></figure>
<p>で、結果は</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="err">$</span> <span class="o">./</span><span class="n">gpio_sim</span><span class="o">.</span><span class="n">py</span>
</span><span class='line'><span class="n">The</span> <span class="n">flag</span> <span class="ow">is</span> <span class="n">SECCON</span><span class="p">{</span><span class="c">###FD80UY#!8880UY#!8}</span>
</span></code></pre></td></tr></table></div></figure>
<hr />
<h1>Reverse-Engineering Hardware 2</h1>
<h2>設問</h2>
<p>我々は2つの74HC161を使ったエンコーダーボードによるバイナリを入手した。
復元を手伝ってほしい。<br/>
gpio2.py<br/>
encripted<br/>
counterhardware.zip</p>
<h2>提供されたもの</h2>
<ul>
<li>RaspberryPi上で動作するPythonのコード(プログラム中にGPIOを使っているので、実環境がないと動作しない)</li>
<li>LEDがチカチカする動作ムービー</li>
<li>回路構成を類推するための多数のエンコーダーボード画像(以下のような画像が30枚ほど)</li>
</ul>
<p><a href="https://raw.githubusercontent.com/hideyuki/SECCON-2015-Online-CTF-Writeup/master/Reverse-Engineering-Hardware-2/counterhardware/DSC_0003.jpg"><img src="https://raw.githubusercontent.com/hideyuki/SECCON-2015-Online-CTF-Writeup/master/Reverse-Engineering-Hardware-2/counterhardware/DSC_0003.jpg" width="600"/></a></p>
<h2>手順</h2>
<p>ブレッドボード上に74HC161という<a href="http://toshiba.semicon-storage.com/info/lookup.jsp?pid=TC74HC161AP&lang=ja">4bitのバイナリーカウンター</a>が2つ載っています。74HC161はCLKが入るとカウンターが+1されるものです。</p>
<p>そして、Pythonのコードを眺めているとP系の出力は一切動いていないのと、データはXORでエンコードされているのでもう一度同じ操作をすればデコードできることがわかりました。</p>
<p>そして多数の画像から回路図をおこしてみたのが以下(GPIOのピンが変な配置になってますが気にせず…)</p>
<p><a href="https://raw.githubusercontent.com/hideyuki/SECCON-2015-Online-CTF-Writeup/master/Reverse-Engineering-Hardware-2/circuit.jpg"><img src="https://raw.githubusercontent.com/hideyuki/SECCON-2015-Online-CTF-Writeup/master/Reverse-Engineering-Hardware-2/circuit.jpg" width="600"/></a></p>
<p>動画はあまり参考にならず、とりあえずこの回路図通りにPythonのコードにバイナリカウンタの部分を追加で実装してみました。</p>
<p><a href="https://github.com/hideyuki/SECCON-2015-Online-CTF-Writeup/blob/master/Reverse-Engineering-Hardware-2/gpio2_sim.py">https://github.com/hideyuki/SECCON-2015-Online-CTF-Writeup/blob/master/Reverse-Engineering-Hardware-2/gpio2_sim.py</a></p>
<p><code>gpio2.py</code>からの変更点としては</p>
<ul>
<li><code>pulse(clock)</code> を <code>count_up()</code>に変更。カウンタがクロックで叩かれると1つ進むハードの構造をプログラムした
<ul>
<li><code>count_up()</code> は、バイナリカウンタ2つをそのまま実装。1つめのカウンタのCO(キャリーオーバー)が2つめのカウンタのCLKになっている。COがHighとなる条件は全部のQがHighの時</li>
</ul>
</li>
<li>バイナリカウンタの出力Qの状態を変数化(q0〜q7)</li>
<li>lenには入力ファイルのバイト数が自動で入るように</li>
</ul>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="k">def</span> <span class="nf">get_current_count_a</span><span class="p">():</span>
</span><span class='line'> <span class="k">return</span> <span class="n">q2</span><span class="o">*</span><span class="mi">8</span> <span class="o">+</span> <span class="n">q1</span><span class="o">*</span><span class="mi">4</span> <span class="o">+</span> <span class="n">q0</span><span class="o">*</span><span class="mi">2</span> <span class="o">+</span> <span class="n">q7</span>
</span><span class='line'>
</span><span class='line'><span class="k">def</span> <span class="nf">get_current_count_b</span><span class="p">():</span>
</span><span class='line'> <span class="k">return</span> <span class="n">q6</span><span class="o">*</span><span class="mi">8</span> <span class="o">+</span> <span class="n">q5</span><span class="o">*</span><span class="mi">4</span> <span class="o">+</span> <span class="n">q4</span><span class="o">*</span><span class="mi">2</span> <span class="o">+</span> <span class="n">q3</span>
</span><span class='line'>
</span><span class='line'><span class="k">def</span> <span class="nf">count_up</span><span class="p">():</span>
</span><span class='line'> <span class="k">global</span> <span class="n">q0</span><span class="p">,</span> <span class="n">q1</span><span class="p">,</span> <span class="n">q2</span><span class="p">,</span> <span class="n">q3</span><span class="p">,</span> <span class="n">q4</span><span class="p">,</span> <span class="n">q5</span><span class="p">,</span> <span class="n">q6</span><span class="p">,</span> <span class="n">q7</span>
</span><span class='line'> <span class="n">da</span> <span class="o">=</span> <span class="n">get_current_count_a</span><span class="p">()</span>
</span><span class='line'> <span class="n">dan</span> <span class="o">=</span> <span class="n">da</span> <span class="o">+</span> <span class="mi">1</span>
</span><span class='line'> <span class="n">q7</span> <span class="o">=</span> <span class="mh">0x01</span> <span class="o">&</span> <span class="n">dan</span>
</span><span class='line'> <span class="n">q0</span> <span class="o">=</span> <span class="mh">0x01</span> <span class="o">&</span> <span class="p">(</span><span class="n">dan</span><span class="o">>></span><span class="mi">1</span><span class="p">)</span>
</span><span class='line'> <span class="n">q1</span> <span class="o">=</span> <span class="mh">0x01</span> <span class="o">&</span> <span class="p">(</span><span class="n">dan</span><span class="o">>></span><span class="mi">2</span><span class="p">)</span>
</span><span class='line'> <span class="n">q2</span> <span class="o">=</span> <span class="mh">0x01</span> <span class="o">&</span> <span class="p">(</span><span class="n">dan</span><span class="o">>></span><span class="mi">3</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'> <span class="k">if</span> <span class="n">q7</span> <span class="o">==</span> <span class="mi">1</span> <span class="ow">and</span> <span class="n">q0</span> <span class="o">==</span> <span class="mi">1</span> <span class="ow">and</span> <span class="n">q1</span> <span class="o">==</span> <span class="mi">1</span> <span class="ow">and</span> <span class="n">q2</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
</span><span class='line'> <span class="n">db</span> <span class="o">=</span> <span class="n">get_current_count_b</span><span class="p">()</span>
</span><span class='line'> <span class="n">dbn</span> <span class="o">=</span> <span class="n">db</span> <span class="o">+</span> <span class="mi">1</span>
</span><span class='line'> <span class="n">q3</span> <span class="o">=</span> <span class="mh">0x01</span> <span class="o">&</span> <span class="n">dbn</span>
</span><span class='line'> <span class="n">q4</span> <span class="o">=</span> <span class="mh">0x01</span> <span class="o">&</span> <span class="p">(</span><span class="n">dbn</span><span class="o">>></span><span class="mi">1</span><span class="p">)</span>
</span><span class='line'> <span class="n">q5</span> <span class="o">=</span> <span class="mh">0x01</span> <span class="o">&</span> <span class="p">(</span><span class="n">dbn</span><span class="o">>></span><span class="mi">2</span><span class="p">)</span>
</span><span class='line'> <span class="n">q6</span> <span class="o">=</span> <span class="mh">0x01</span> <span class="o">&</span> <span class="p">(</span><span class="n">dbn</span><span class="o">>></span><span class="mi">3</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>
<p>このスクリプトを実行しても、出てくるのはテキストファイルではないバイナリでした。
試しにHexEditでバイナリの中身を確認しても、やっぱりよくわかりません。</p>
<p><a href="https://raw.githubusercontent.com/hideyuki/SECCON-2015-Online-CTF-Writeup/master/Reverse-Engineering-Hardware-2/binary_output.jpg"><img src="https://raw.githubusercontent.com/hideyuki/SECCON-2015-Online-CTF-Writeup/master/Reverse-Engineering-Hardware-2/binary_output.jpg" width="600"/></a></p>
<p>とりあえず頭の <code>1F 8B 08</code> でググってみると、gzip形式らしいとのことがわかったのでgunzipで解凍。すると、flagが書かれているテキストファイルが出てきました!</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='python'><span class='line'><span class="err">$</span> <span class="n">gunzip</span> <span class="n">output</span>
</span><span class='line'><span class="err">$</span> <span class="n">cat</span> <span class="n">output</span>
</span><span class='line'><span class="n">The</span> <span class="n">flag</span> <span class="ow">is</span> <span class="n">SECCON</span><span class="p">{</span><span class="mi">7</span><span class="n">xgxUbQYixmiJAvtniHF</span><span class="p">}</span><span class="o">.</span>
</span></code></pre></td></tr></table></div></figure>
<hr />
<h1>感想</h1>
<p>これ以外にもAndroidのAPKを初めて逆コンパイルするなどの体験ができたので非常に勉強になりました。組み込み系の問題があってちょっとびっくりしました。おもしろいですよね。</p>
<p>問題の傾向として、</p>
<ul>
<li>QRコードの人(QRコードの問題数多い印象)</li>
<li>パケット解析の人</li>
<li>Cryptoな人</li>
<li>画像(バイナリアン)な人</li>
<li>ハードウェアな人</li>
</ul>
<p>が揃っていれば結構上位が目指せそうな気がするので、次回は複数人で参加しようかと思います。運営の方々どうもありがとうございました。</p>
<p>—</p>
<h1>最後に</h1>
<p>こんなセキュリティとハードウェアが好きなアルバイトや社員を募集しております!<a href="mailto:jobs@beatrobo.com">jobs@beatrobo.com</a>にご連絡ください。</p>
<p>ぜひBeatrobo代々木オフィスに遊びに来てくださーい!</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Beatroboハードウェアハッカソン#1 を開催しました]]></title>
<link href="http://tech.beatrobo.com/blog/2014/07/24/beatrobo-hardware-hackathon/"/>
<updated>2014-07-24T23:56:59+09:00</updated>
<id>http://tech.beatrobo.com/blog/2014/07/24/beatrobo-hardware-hackathon</id>
<content type="html"><![CDATA[<p>2014年6月14日にBeatrobo引越記念でハードウェアハッカソンを開催しました!</p>
<p><a href="http://beatrobo.doorkeeper.jp/events/12214">【6/14】Beatrobo引越記念!ハードウェア・ロボットハッカソン!!【お題自由。Arduino、BLE、ロボットなんでもOK】</a></p>
<p><img src="http://tech.beatrobo.com/images/201406/hackathon1/hackathon-view.jpg" width="600"></p>
<p>ハードウェアハッカソンの雰囲気やみなさんの成果などを写真で報告しますっ!</p>
<!-- more -->
<h1>ハッカソンのスケジュール</h1>
<p>スケジュールはこんな感じです。</p>
<ul>
<li>13:00〜13:15 自己紹介 & やること発表</li>
<li>13:15〜20:00 ハッカソンタイム!</li>
<li>20:00〜20:30 成果発表</li>
<li>20:30〜22:00 ピザパーティ</li>
</ul>
<p>今回は土曜のお昼から集中して1日で成果を出すようにしてみました。</p>
<p>経験的に2日もあると疲れるし逆に短いと面白いものができないので、6時間以上の作業時間を設定。</p>
<h1>ハッカソンスタート!</h1>
<p>参加者はエンジニア、デザイナ、アーティストなど様々で(女性2名も!)、Arduino入門される方もいれば3Dプリンタご持参のガチ勢もいるという楽しい雰囲気でスタート。 3Dプリンタって持ち歩くものなのですね!</p>
<p><img src="http://tech.beatrobo.com/images/201406/hackathon1/3d_printer.jpg" width="600"></p>
<p>せっかくなので、私の水やりロボットのセンサケースを3Dプリンタで作っていただきました。</p>
<p><img src="http://tech.beatrobo.com/images/201406/hackathon1/3d_printing.jpg" width="600"></p>
<p>3D CAD(弊社では <a href="http://www.autodesk.co.jp/products/autodesk-inventor-family/overview">Autodesk Inventor</a> を利用しています)でロボット型のケースをデザインして、すぐにプリント開始!1時間ほどで完成しました!</p>
<p><img src="http://tech.beatrobo.com/images/201406/hackathon1/watering_robot_3d_print.jpg" width="600"></p>
<p>かなり細かいところまでちゃんと出力できてて、精度もこの規模の3Dプリンタではかなり出ていました。このプリンタの性能を見て、Beatrobo社内でも3Dプリンタがほしくなりました(笑)</p>
<h1>みなさんの成果発表</h1>
<p>7時間もあっという間で過ぎ去り、夜8時からは成果発表タイムです。</p>
<h2>水やりロボット</h2>
<p><img src="http://tech.beatrobo.com/images/201406/hackathon1/watering_alert_robot.jpg" width="600"></p>
<ul>
<li>私が取り組んだ課題「水やりロボット」</li>
<li>いろいろ機構を考えているうちにめんどくさくなり、結局「水やりタイミングをhipchatで教えてくれるロボット」という形に着地</li>
<li>方式は「土の湿度を測るセンサ –> Arduino –> RaspberryPiが水やりhubotのHTTP API叩く –> 水やりhubotが水やりタイミングの湿度になっていたらHipChatにつぶやく」</li>
<li>そもそもどれくらいのセンサの値の時に「水やってくれー」とつぶやかせるかを確認するため、2週間くらいはずっとセンサの値をcronでつぶやかせてます</li>
</ul>
<h2>ウェアラブル楽器のプロトタイピング</h2>
<p><img src="http://tech.beatrobo.com/images/201406/hackathon1/wearable_instrument.jpg" width="600"></p>
<ul>
<li>スボンを叩けばパーカッションの音がなる、というコンセプト</li>
<li>服に導通するインクや、導通する素材をはったり</li>
<li>なんとかズボンを叩いて、PCの中が反応するというものが作れた</li>
</ul>
<h2>アングリーバードのハック</h2>
<p><img src="http://tech.beatrobo.com/images/201406/hackathon1/angry_bird_hack.jpg" width="600"></p>
<ul>
<li>今回ハードウェアハッカソンという体で開催した中、唯一ハードウェア要素を一切入れなかった猛者</li>
<li>アングリーバードをハックしてAI機能を作ることができるのを試したとのこと</li>
</ul>
<h2>腕時計のプロトタイピング</h2>
<p><img src="http://tech.beatrobo.com/images/201406/hackathon1/clock_prototype.jpg" width="600"></p>
<ul>
<li>サクラボードとRTCで腕時計のプロトタイピング</li>
<li>aitendo で買ったディスプレイを利用しているとのこと</li>
</ul>
<h2>アルコールがきついとロボットが怒る</h2>
<p><img src="http://tech.beatrobo.com/images/201406/hackathon1/angry_robot.jpg" width="600"></p>
<ul>
<li>かわいいロボット型アルコールセンサ</li>
<li>アルコール濃度が高いとロボットの目が赤くなる!</li>
</ul>
<h2>Arduino 入門</h2>
<p><img src="http://tech.beatrobo.com/images/201406/hackathon1/arduino.jpg" width="600"></p>
<ul>
<li><a href="http://www.amazon.co.jp/%E3%81%9F%E3%81%AE%E3%81%97%E3%81%84%E9%9B%BB%E5%AD%90%E5%B7%A5%E4%BD%9C-Arduino%E3%81%A7%E9%9B%BB%E5%AD%90%E5%B7%A5%E4%BD%9C%E3%82%92%E3%81%AF%E3%81%98%E3%82%81%E3%82%88%E3%81%86-%E9%AB%98%E6%A9%8B-%E9%9A%86%E9%9B%84/dp/4798032085">たのしい電子工作 Arduinoで電子工作をはじめよう!</a> で Arduino 入門</li>
<li>光の明るさでビープ音の音が変わる</li>
</ul>
<h2>ポケベル打ち用キーボード製作</h2>
<p><img src="http://tech.beatrobo.com/images/201406/hackathon1/poketbell_keyboard.jpg" width="600"></p>
<ul>
<li>テンキーのキーボードでポケベルうちを実現…のはずがっ!</li>
<li>はんだづけが難しい</li>
</ul>
<h2>温め続けないと再生され続けない動画</h2>
<p><img src="http://tech.beatrobo.com/images/201406/hackathon1/warming_movie.jpg" width="600"></p>
<ul>
<li>ペルチェ素子とArduino</li>
<li>温もりを与え続けないと動画が止まる</li>
</ul>
<h2>自作のプリント基板の動作確認</h2>
<ul>
<li>かなり複雑なプリント基板の動作確認</li>
<li>仲間といっしょに作っているプロダクトとのこと</li>
</ul>
<h2>周辺の画像をパシャパシャとってくれるロボット</h2>
<p><img src="http://tech.beatrobo.com/images/201406/hackathon1/image_robot.jpg" width="600"></p>
<ul>
<li>RaspberryPiとマインドストームの組み合わせ</li>
<li>チャットからコントロールできる</li>
</ul>
<h2>水やりロボット プロトタイピング</h2>
<p><img src="http://tech.beatrobo.com/images/201406/hackathon1/watering_robot_prototype.jpg" width="600"></p>
<ul>
<li>こちらは水やりを自動化させるためのプロトタイピング!</li>
<li>ハムスターの水やりボトルをハックし、サーボを動かすことで水がちょろちょろと出てくるように!</li>
<li>このチームは7時間の作業時間中、4時間ほどを買い出しに費やした猛者</li>
</ul>
<h1>ピザパーティ</h1>
<p>そしてピザパーティへ。</p>
<p><img src="http://tech.beatrobo.com/images/201406/hackathon1/pizza_party.jpg" width="600"></p>
<p>アーティストさんのメディアアートの実装方法をみんなで検討したりとなかなかおもしろい議論もできました。</p>
<p>そして最後に記念写真を。</p>
<p><img src="http://tech.beatrobo.com/images/201406/hackathon1/hackathon_all.jpg" width="600"></p>
<h1>ハッカソンのKPT</h1>
<h3>Keep</h3>
<ul>
<li>時間的には長すぎず短すぎずぴったりだった</li>
<li><a href="https://pizzahut.jp/pc/top">ピザハット</a> の <a href="https://pizzahut.jp/pc/pizza/W000001176-001">ハッピー4</a> が好評</li>
<li>3Dプリンタがあるとハッカソンが楽しくなる!創れるものの幅が広がる。</li>
<li>12人くらいが現在のオフィスのキャパ & 1人でホストできるちょうど良い人数</li>
</ul>
<h3>Ploblem</h3>
<ul>
<li>ピザやつまみの食べ物系が少なかった</li>
<li>トイレがわかりにくい。場所のアナウンスとホワイトボードに書こう</li>
<li>ハッシュタグの周知がされていなくてまとめにくい。ハッシュタグの周知</li>
</ul>
<h3>Try</h3>
<ul>
<li>3Dプリンタの導入。ハッカソンでもすぐに使えるように</li>
<li>ハッシュタグ、トイレの場所などをホワイトボードに書く</li>
<li>食べ物の充実</li>
</ul>
<h1>次回</h1>
<p>ということで、<a href="http://genkei.thebase.in/items/508236">Trino 新型デルタ3Dプリンター 組立キット</a> を購入いたしました!納品が楽しみです。</p>
<p>次は3Dプリンタ購入記念でハードウェアハッカソンを開きたいと思います。乞うご期待。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[IRKitを使ってHipChatからエアコンを制御する]]></title>
<link href="http://tech.beatrobo.com/blog/2014/02/01/irkit-hipchat-onoff-air/"/>
<updated>2014-02-01T01:55:46+09:00</updated>
<id>http://tech.beatrobo.com/blog/2014/02/01/irkit-hipchat-onoff-air</id>
<content type="html"><![CDATA[<p>どうもはじめまして。Beatrobo竹井(<a href="https://twitter.com/HideyukiTakei">@HideyukiTakei</a>)です。</p>
<p>これからBeatroboエンジニアたちの技術的な話をここにまとめていこうと思います。</p>
<p>第一弾はIRKitに関して!</p>
<p><img src="http://tech.beatrobo.com/images/201405/irkit.jpg" width="600"></p>
<p><a href="http://getirkit.com/">IRKit</a>とは、<a href="https://twitter.com/maaash">@maaash</a>さんが開発しているWiFi機能の付いたオープンソースな赤外線リモコンデバイスです。<a href="https://github.com/irkit/device">Arduinoのソースコードや基板の回路図も公開</a>されています。また、IRKitをローカルネットワークから制御するための<a href="http://getirkit.com/#IRKit-Device-API">Device HTTP API</a>や、インターネット越しに制御するための<a href="http://getirkit.com/#IRKit-Internet-API">Internet HTTP API</a>も用意されています。</p>
<p>今回はこのIRKitを使って<a href="https://www.hipchat.com/%E2%80%8E">HipChat</a>からエアコンを制御する方法を順を追ってご紹介します!</p>
<!-- more -->
<hr />
<h3>1.リモコンON/OFF時の赤外線信号を取得する</h3>
<p>Internet HTTP API の<a href="http://getirkit.com/#IRKit-Internet-GET-1-messages">GET /1/messages</a>を用いることでリモコンの赤外線信号をJSONで取得することができます。</p>
<p>IRKitはBonjourに対応しているようですので、まずIRKitの名前を取得しましょう。</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ dns-sd -B _irkit._tcp
</span><span class='line'>Browsing for _irkit._tcp
</span><span class='line'>DATE: ---Sat 01 Feb 2014---
</span><span class='line'>15:41:50.027 ...STARTING...
</span><span class='line'>Timestamp A/R Flags if Domain Service Type Instance Name
</span><span class='line'>15:41:50.555 Add 2 5 local. _irkit._tcp. iRKitD1D1
</span><span class='line'>^C</span></code></pre></td></tr></table></div></figure>
<p>コマンド結果で表示されている <code>iRKitD1D1</code> (仮名)がIRKitの名前です。また、ドメインが <code>local</code> なので、<code>iRKitD1D1 .local</code> がIRKitの宛先となります。</p>
<p>Internet HTTP API を使用するためには clientkey, deviceid が必要で、これらを取得するためには clienttoken が必要です。clienttoken は Device HTTP API の<a href="http://getirkit.com/#IRKit-Device-POST-keys">POST /1/keys</a>で取得できます。</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ curl -i "http://iRKitD1D1.local/keys" -d ""
</span><span class='line'>{"clienttoken":"00112233445566778899AABBCCDDEEFF"}</span></code></pre></td></tr></table></div></figure>
<p>ここで得られた clienttoken を使って Internet HTTP API の <a href="http://getirkit.com/#IRKit-Internet-POST-1-keys">POST /keys</a> で clientkey, deviceid を取得します。</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$curl -i "http://api.getirkit.com/1/keys" -d "clienttoken= 00112233445566778899AABBCCDDEEFF"
</span><span class='line'>HTTP/1.1 200 OK
</span><span class='line'>Server: ngx_openresty
</span><span class='line'>Date: Tue, 07 Jan 2014 08:46:06 GMT
</span><span class='line'>Content-Type: application/json; charset=utf-8
</span><span class='line'>Content-Length: 94
</span><span class='line'>Connection: keep-alive
</span><span class='line'>X-Content-Type-Options: nosniff
</span><span class='line'>
</span><span class='line'>{"deviceid":"0123456789ABCDEF0123456789ABCDEF","clientkey":"FEDCBA9876543210FEDCBA9876543210"}</span></code></pre></td></tr></table></div></figure>
<p>これで Internet HTTP API を使う準備は完了です。</p>
<p>次は赤外線信号の取り込みです。 Internet HTTP API の <a href="http://getirkit.com/#IRKit-Internet-GET-1-messages">GET /1/messages</a> は最も新しい受信した赤外線信号を返してくれるAPIです。
新しい赤外線信号がIRKitデバイスから届いたらただちにレスポンスを返します。</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ curl -i "http://api.getirkit.com/1/messages?clientkey= FEDCBA9876543210FEDCBA9876543210&clear=1"</span></code></pre></td></tr></table></div></figure>
<p>ロングポーリングでレスポンスが返ってこない状態になります。</p>
<p>次に、IRKitにリモコンを向けながら、エアコンONボタンを1度だけ押してみましょう。青色LEDが点滅したら赤外線信号受信ができているようです。</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>HTTP/1.1 200 OK
</span><span class='line'>Server: ngx_openresty
</span><span class='line'>Date: Sat, 01 Feb 2014 07:43:49 GMT
</span><span class='line'>Content-Type: application/json; charset=utf-8
</span><span class='line'>Content-Length: 2506
</span><span class='line'>Connection: keep-alive
</span><span class='line'>Access-Control-Allow-Origin: *
</span><span class='line'>Access-Control-Allow-Headers: X-Requested-With
</span><span class='line'>ETag: "-1919363029"
</span><span class='line'>X-Content-Type-Options: nosniff
</span><span class='line'>
</span><span class='line'>{"message":{"format":"raw","freq":38,"data":[5408,6648,262,6424,322,1366,382,1232,205,1622,280,1275,395,1413,205,1275,539,1037,619,1111,539,1073,663,968,761,968,619,1232,470,1111,558,1111,619,1073,619,968,619,968,735,1073,663,904,735,968,735,968,735,2626,710,904,904,904,735,904,735,904,815,904,710,968,710,968,710,873,873,873,873,873,761,873,873,873,873,873,761,873,873,873,873,873,873,873,873,873,873,873,873,873,761,873,761,873,761,2537,873,873,873,2537,873,2537,873,2537,873,873,873,2537,873]},"hostname":"IRKitD2C7","deviceid":"32658096E6ED4AE3977E4B6BEFCA5493"}</span></code></pre></td></tr></table></div></figure>
<p>この<code>message</code>部分がエアコンONの赤外線信号のJSONです。</p>
<hr />
<h3>2.Internet HTTP APIを使ってエアコンをONする</h3>
<p>さきほど得られたmessageのJSONを使って、試しにエアコンをONにしてみましょう。Internet HTTP API の <a href="http://getirkit.com/#IRKit-Internet-POST-1-messages">POST /1/messages</a> を使います。</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ curl -i "http://api.getirkit.com/1/messages" \
</span><span class='line'>-d "clientkey=FEDCBA9876543210FEDCBA9876543210" \
</span><span class='line'>-d "deviceid=0123456789ABCDEF0123456789ABCDEF" \
</span><span class='line'>-d 'message={"format":"raw","freq":38,"data":[5408,6648,262,6424,322,1366,382,1232,205,1622,280,1275,395,1413,205,1275,539,1037,619,1111,539,1073,663,968,761,968,619,1232,470,1111,558,1111,619,1073,619,968,619,968,735,1073,663,904,735,968,735,968,735,2626,710,904,904,904,735,904,735,904,815,904,710,968,710,968,710,873,873,873,873,873,761,873,873,873,873,873,761,873,873,873,873,873,873,873,873,873,873,873,873,873,761,873,761,873,761,2537,873,873,873,2537,873,2537,873,2537,873,873,873,2537,873]}'
</span><span class='line'>HTTP/1.1 100 Continue
</span><span class='line'>
</span><span class='line'>HTTP/1.1 200 OK
</span><span class='line'>Server: ngx_openresty
</span><span class='line'>Date: Sat, 01 Feb 2014 07:47:26 GMT
</span><span class='line'>Content-Type: text/html; charset=utf-8
</span><span class='line'>Content-Length: 0
</span><span class='line'>Connection: keep-alive
</span><span class='line'>Access-Control-Allow-Origin: *
</span><span class='line'>Access-Control-Allow-Headers: X-Requested-With
</span><span class='line'>X-Content-Type-Options: nosniff</span></code></pre></td></tr></table></div></figure>
<p>これでエアコンがつくはずです!
次にこのAPIをHipChatから叩けるようにしてみましょう。</p>
<hr />
<h3>3.HUBOTをローカルで動かす</h3>
<p>HipChatからIRKitのInternet HTTP APIを使うために、チャットボット<a href="http://hubot.github.com/">HUBOT</a>とそのHipChatアダプタ<a href="https://github.com/hipchat/hubot-hipchat">hubot-hipchat</a>を利用します。今回は自分のMac上でHUBOTを動かしてみましょう。</p>
<p>まず、HUBOTをインストールします。</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ npm install --global coffee-script hubot@v2.6.4</span></code></pre></td></tr></table></div></figure>
<p>そしてHUBOTのプロジェクトテンプレートを作成。</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$ hubot --create hubot
</span><span class='line'>$ cd hubot
</span><span class='line'>$ ls
</span><span class='line'>Procfile README.md bin/ external-scripts.json hubot-scripts.json package.json scripts/</span></code></pre></td></tr></table></div></figure>