Serenityを使ってDiscordをRustから遊ぶ

はじまり

ちょっと作りたいものがあったのでDiscordのAPIを調べてました。

ぶっちゃけた話、Pythonでdiscord.pyを使った方が楽なんだと思うんですけど、Pythonをみたら発狂して転げ回る病気にかかったということにしているので今回はRustで。


RustでDiscordAPIを叩くライブラリを探してみるとSorenityというライブラリが見つかりました。

github.com

日本語で説明しているページが見当たらなかったので、しばらくあちこちにらめっこしながら、とりあえずサンプルが動いたのでそこまで書いておきます。

下準備

まずはDiscord向けのbotを作る準備をしないといけません。Discord向けのアプリを作るためにまずデベロッパーポータルへ行きましょう。

discordapp.com

Discordにログインした状態でアクセスすると、下のようなページが出ると思います。

f:id:sorenuts:20190604213214p:plain
アプリケーションを作る

右上の"New Application"をクリックし、作成するアプリケーションの名前を入力して作成します。

日本語でも大丈夫みたいですね。

作成すると下のようなページが出るはずです。

f:id:sorenuts:20190604214002p:plain
アプリケーションを作った

そうしたら上図の赤丸のBOTをクリックします。

f:id:sorenuts:20190604214209p:plain
BOTページ

右のADDBOTをクリックすると、

"ADD A BOT TO THIS APP? "
"Adding a bot user gives your app visible life in Discord. However, this action is irrevocable! Choose wisely."

と、「bot追加したらもう戻れねえぜ!賢い選択を。」的なことを聞かれるので"Yes, do it !"を力強くクリックしましょう。


余談ですが、"Choose wisely"は『インディー・ジョーンズ/最後の聖戦』を元ネタとするミームみたいですね

それはさておき、クリックすると、

f:id:sorenuts:20190604215502p:plain
BOTが生まれた

上図のようなページに移動します。

"A wild bot has appeared!"
「あ!やせいの BOTが とびだしてきた!」ですかね。
Discordは本当にゲーム関連の洒落が多くて素敵ですね。

それもさておき、上図の赤丸部分がプログラムで使用することになるトークンです。後で使います。覚えておいてください。

訪れるたびに毎回少しずつ変化しますが、いつ作ったものを使っても問題ないです。


次に、左カラムのOAuth2をクリックします。

移動先のページをスクロールすると、下図のようなチェックボックスが大量に出てくるかと思います。

f:id:sorenuts:20190604220335p:plain
OAuth2のURLジェネレータ

ここではbotにチェックを入れます。するとさらに下に下図のようなチェックボックス群が出てきます。

f:id:sorenuts:20190604220521p:plain
BOTの権限設定

このチェックボックスbotに与える権限を設定できます。これはbotを招待する際に権限オフにもできます。

とりあえず試すために管理者にチェックを入れます。

その後2つ上の図の赤丸でリンクをコピーし、それにアクセスすると下図のような画面が出ます。

f:id:sorenuts:20190604220823p:plain
BOTをサーバーに追加する

そこから追加するサーバーを選んで認証を押してやると

f:id:sorenuts:20190604221006p:plain
BOTがスポーンした

BOTがサーバーに参加します。
が、当然このままでは何もできません。
プログラムで動かしてやる必要があります。

SerenityでDiscord BOTを動かす

とりあえずSerenityをcloneしてきましょう。

git clone https://github.com/serenity-rs/serenity.git

そうしたらexampleディレクトリ内の01_basic_ping_botに移動しましょう。

cd serenity/examples/01_basic_ping_bot/

それでもってcargo run・・・といきたいところですが、このままだと「トークンの環境変数がないぞ」と怒られて動きません。
トークンをプログラム内か環境変数に書いてやる必要があります。

コードをいじらないで動かす場合は、

export DISCORD_TOKEN=<ここにトークン>

とでもしておけばいいと思います。

コードをいじって動かす場合は45,46行目の

    let token = env::var("DISCORD_TOKEN")
        .expect("Expected a token in the environment");

    let token = "ここにトークン"

としておきましょう。

そうしたらcargo runします。


無事起動し、サーバーに接続できると、コンソールに

<BOTの名前> is connected!

が出ますので、そうしたらDiscord上で"!ping"と書き込んでやると・・・

f:id:sorenuts:20190604222931p:plain
こいつ・・・動くぞ・・・!
動きましたね。

まとめ

というわけで、SerenityでRustからDiscord BOTを動かしてみました。
学校から動かしたらプロキシに引っかかって動かなかったり、cargo run自体が通らなかったり、そもそもサンプルコードを真剣に読んでなかったりと手こずりましたが、無事動いたのでよしとします。

今後はこれを使って、前からやりたかったことをやってみようと思います。

最後まで読んでいただき、ありがとうございました。


PS:どこかインターンに行きたい・・・

バーニラバニラで練習題

ことの始まり

サークルの先輩方との飲み会にてズンドコキヨシプログラムを知った。
→パイセン「バニラの求人のやつもありやな」
→詳細な仕様がサークルのSlackに上がってきた(!?)
Golangの練習がてら書いてみるか〜
→書 き ま し た

バニラの歌詞

こんな感じだったような

※
V A N I L LA バニラ!
V A N I L LA バニラ!

※※
バーニラ バニラ バーニラ 求人
バーニラ バニラ 高収入
バーニラ バニラ バーニラ 求人
バーニラ バニラでアルバイト

※※繰り返し

※繰り返し

バニラの求人見たーいー 見たーいー 見たーいー
バニラの求人見たーいー ニャニャニャーニャーニャニャーニャー

仕様

下記のSTEP1からSTEP6までを実行。「見たーいー見たーいー・・・」の部分については考えないことにした(「にゃにゃにゃーにゃーにゃにゃーにゃー」は含めてもよさそう?)

  • STEP1

V!, a!, n!, i!, l!, la!の中からランダムに出力
V!, a!, n!, i!, l!, la!の順番で出てきたら”バニラ!”を出力。

  • STEP2

STEP1をもう一回

  • STEP3

“バーニラ”, “バニラ”, “バニラで” の中からランダムに出力
バーニラ, バニラ, バーニラ の順番で出てきたら”求人!”を出力

  • STEP4

“バーニラ”, “バニラ”, “バニラで” の中からランダムに出力
バーニラ, バニラ の順番で出てきたら”高〜収〜入〜!”を出力

  • STEP5

STEP3をもう一回

  • STEP6

“バーニラ”, “バニラ”, “バニラで” の中からランダムに出力
バーニラ, バニラで の順番で出てきたら”高~収~入~!”を出力しておわり。

コード

gitに投げた。
github.com

感想

Golangのいい練習になった。

ズンドコ節を知らない世代が増えてきた頃にはもしかすると需要があるのではないだろうか(本当に?)
頭の中に残る繰り返しを含む歌は練習用の題材としていいのかもしれない。多分。

.pcapファイルについてちょっと調べた

パケットキャプチャではおなじみのpcapファイルについて、調べたのでまとめます。

動機

研究室で取り扱っているpcapファイルを管理するためにpcapパーサーを作ろうと思った。
そのためにpcapファイルの構造を調べたが、知りたい情報のある日本語の解説が見つからなかったので調べてまとめた。

pcapファイルの構造

HTTPパケットの入力の概要
上記ページはHitachiのuCosminexus Stream Data Platform - Application Framework システム構築・運用ガイドである。pcapファイルの構造についてはこちらに書かれてたので参考になった。
図を見た方が早いが、

  • Pcapファイル自体のファイルヘッダ(24バイト)
  • キャプチャしたパケット
    • キャプチャしたパケットのヘッダ(16バイト)
    • キャプチャしたパケットのデータ(可変長)

という構造になっているようだ。

Pcapファイル自体のファイルヘッダの構造

Development/LibpcapFileFormat - The Wireshark Wiki
上記のページによると、Pcapファイルのヘッダは下記のようになっているとのこと。

typedef struct pcap_hdr_s {
        guint32 magic_number;   /* magic number */
        guint16 version_major;  /* major version number */
        guint16 version_minor;  /* minor version number */
        gint32  thiszone;       /* GMT to local correction */
        guint32 sigfigs;        /* accuracy of timestamps */
        guint32 snaplen;        /* max length of captured packets, in octets */
        guint32 network;        /* data link type */
} pcap_hdr_t;

それぞれについての説明を見ていくと、

magic_number

ファイルの種類を識別する際に用いる。4バイト。

version_major

ファイルフォーマットのメジャーバージョン(ver:x.yのxの方)。2バイト。

version_minor

ファイルフォーマットのマイナーバージョン(ver:x.yのyの方)。2バイト。

thiszone

パケットヘッダのタイムゾーンGMTとどれだけずれているかの値。一時間のずれは3600になる。4バイト。

sigfigs

キャプチャしたパケットのタイムゾーンの精度?らしい。基本的に値は0になるとのこと。4バイト。

snaplen

"snapshot length"の略で、パケットのキャプチャサイズを制限するために用いるもの?らしい。通常は65535もしくはそれ以上とのこと。4バイト。

network

リンク層の種類。
www.tcpdump.org
上記のサイトに書かれている数値が入るらしい。Ethernetの場合、値は1になる。4バイト。

パケットのヘッダの構造

同じページを参照した。下記のような構造になっているとのこと。

typedef struct pcaprec_hdr_s {
        guint32 ts_sec; / *タイムスタンプ秒* /
        guint32 ts_usec; / *タイムスタンプマイクロ秒* /
        guint32 incl_len; / *ファイルに保存されたパケットのオクテット数* /
        guint32 orig_len; / *パケットの実際の長さ* /
} pcaprec_hdr_t;
ts_sec

パケットがキャプチャされた日時のUNIX時間。GMTに基づいたタイムスタンプではない場合にthiszoneを用いて調整する模様。4バイト。

ts_usec

パケットがキャプチャされた時間のマイクロ秒にあたる部分。4バイト。

incl_len

キャプチャした際に、実際に「保存された」パケットデータのバイト数

orig_len

キャプチャした際に、実際に「観測された」パケットデータのバイト数

パケットのデータの構造

incl_lenの長さで実際のパケットがそのまま現れる。

構造を確認してみる

SampleCaptures - The Wireshark Wiki
上記Wiresharkwikiから"http.pcap"をダウンロードし、Wiresharkバイナリエディタで開いて確認してみた。

f:id:sorenuts:20180607162829p:plain
WireShark上での表示

f:id:sorenuts:20180607162838p:plain
バイナリエディタ上での表示

pcap_hdr_s

magic_number

値は、
0xA1B2C3D4

D4 C3 B2 A1でググると、
File Signature Database:: D4C3B2A1 File Signatures
からwinpcapのキャプチャファイルだとわかります。

version_major,version_minor

それぞれ、
0x0002
0x0004
となっているので、バージョンは2.4だとわかる。
上記に挙げたWiresharkのページでも
"the commonly used format in its current version 2.4. This format version hasn't changed for quite a while (at least since libpcap 0.4 in 1998),"
と書いてあり、1998年からずっとバージョンは2.4の模様。

thiszone

0x0

sigfigs

0x0

snaplen

0xFFFF
10進数に直すと65535なので、通常の値だとわかる。

network

0x0001
Ethernetを示している模様

pcaprec_hdr_t

ts_sec

0x40A34B23
10進数に直すと1084443427である。下記のサイトでUnix時間からJSTに変換したところ、西暦2004年 3月 13日 19時 17分 7秒だとわかる。

f:id:sorenuts:20180607194351p:plain
Wireshark上での時刻の表示
UNIXタイムスタンプ変換ツール
Wiresharkで表示されている時刻と一致していることがわかる。

ts_usec

0x0004BFB8
10進数に直すと311224である。マイクロ秒と一致していることがわかる。

incl_len,orig_len

両方とも0x3Eで、10進数に直すと62
以降の62バイトとWiresharkで表示されているパケット本体が一致していることが確認できた。

結果

pcapの構造はpcap自体の情報を格納するヘッダ(Wiresharkwikiでは"global header"と記載されていた)、個々のパケットの情報を格納するヘッダ("Record(Packet) Header")、パケット本体("Packet Data")から成り立っているとわかった。

FEHの投票大戦からデータ取ってきてみた(Python)(スクレイピング)

事のはじめ

CTFで調べてみるとやっぱりPythonが有能
Python書くの練習すっぺ。スクレイピングでもやるか。
→ちょうどFEHの投票大戦が開催している
→いっちょやってみっか!(悟空)

とりあえず書いてみた

github.com

で、とりあえず書いてみました。
Twitterに投稿するまでを1セットという感じです。

最初に調べたこと

robots.txtを見てみる

スクレイピングする上ではやっぱり注意しないといけないのが「スクレイピング禁止」かどうかでしょうか
FEH投票大戦スコアページのrobots.txtをとりあえず見てきました。

f:id:sorenuts:20180601235734p:plain

よし(適当)多分大丈夫でしょう。

Pythonでのスクレイピング方法

ライブラリは前にBeautifulSoupというものがあるのを見たので、これについて使い方を調べてみました。

Python3 + urllib + BeautifulSoupでネット上の情報を取得する

HTMLのタグをもとに掘り下げていけば目的の部分を取り出せるようです。

スクレイピング対象の観察

今度はFEH投票大戦スコアページのソースを見に行きました。

f:id:sorenuts:20180602000619p:plain

ご存知、Chromeデベロッパーツール(うちはSRWareIronですが)でソースを見てみると、「n回戦」がarticleタグ単位で、「キャラVSキャラ」がliタグ単位で、「キャラ名とスコア」がpタグで表示されているとわかりました。
また、過去の大戦結果のページを見てみると、最新の対戦カードは上から二番目のarticleタグにくることがわかりました。
また、クラス名は'body-section-tournament'であることもわかりました。
articleタグのクラス名'body-section-tournament'に絞ってスクレイピングしてみると、一番上のarticleタグ、クラス名'body-section-tournament current'まで拾ってきてしまいました。調べてみた感じだと、どうやら同じ文字列が入ってることだけが原因みたいでした。

mankuro.hateblo.jp

仕方ないので常に上から二番目のarticle要素より、データを引っ張ってくることにしました。

実際にスクレイピング

articleタグ内のliタグ内のpタグの要素を引っ張ってきて出力してみると、取り出したかったデータが確認できました。

Twitterに投稿する

ついでなのでTwitterに取得したデータをPythonから投稿してみました。

Twitterへの投稿方法

いつも通りTwitter APIを使えるようにしました(書くの面倒)

pip install twitter

したら、あとはTwitter用のライブラリを導入して、キーやらなんやらを設定して投稿!だけなのですが、このままだと生のコンシューマキーとシークレットを晒すことになって危険です。
そこで、以前Rubyで作成したupdate_name*1でも環境変数をファイルに書いて外部に見えないようにしたのを思い出しました。Pythonでも同じことができると思い、環境変数からキーを読み込む方法を調べてみました。

【GitHub】に載せたくない環境変数の書き方 Python

こちらを参考に、.envにキーを書いて、コードをコピペして終わり!(書くの飽きてきた)

今後

とりあえず今の投票大戦期間で自動的に大戦経過をツイートできないかと考えている感じ。herokuだと止まっている時間が長いので、Red Hat Open Shiftに目をつけ始めている。
あとは劣勢時に通知が飛ばせればなぁとかそんな感じ。過去の傾向から予測して劣勢の予測とかもしてみたいなぁ(以前某所で見たことがあるけども)

*1:Twitterで特定の語尾のつぶやきを拾ってきて、自分のスクリーンネームにするお遊び

2018/5/26〜27のBeginnersCTFに参加してみた件

CTF4b参加してみた

去年も一昨年も参加できなかったCTF for Beginnersがオンラインで開催すると聞いて参戦しました。
CTF自体はガチの初心者で、CpawCTFに挑戦してつまづいたっきり放置してました。
開始してから研究室のSlack見るまでずっと忘れてたので遅めに開始しましたが、楽しかったです(小並感)

結果

一人ぼっちで参加したのでチーム名はハンドルにしようか悩んだのですが時期も考えて、

f:id:sorenuts:20180530155402p:plain

古 戦 場 か ら 逃 げ る な
にしました。解けた問題の割合みたいなやつは下です。

f:id:sorenuts:20180530155552p:plain

Pwn以外を1問ずつ、Miscは全完でした。

解法

とりあえず書くほどでもないのでしょうが、どんな風に解いたのか反省も込みで書き残したいと思います。解いた順番です。

[Warmup] Welcome

IRCチャンネルのトピックにあります」とだけ書いてあったので、(どこにあるんだよ・・・)と思って探し回りましたが、ルールページにIRCのリンクが貼ってありました。

[Warmup] plain mail

pcapファイルが渡されました。Wiresharkで開いてみると、SMTPプロトコルがちらほら見えるので、smtpでフィルタをかけてみていきました。
「最初のメールで秘密のファイルを、次のメールでパスワードを送るぜぇ〜(意訳)」みたいなことが書いてあったので、パケットからメールを復元する方法を調べると、IMFでフィルタをかけてeml形式でエクスポートしたら復元できる〜とのことだったので、「ファイル」→「オブジェクトをエクスポート」→「IMF」で復元できた。

[Warmup] Veni, vidi, vici

3つのtxtファイルが入ったzipが渡されました。
とりあえず初手、Googleで「Veni,vidi,vici」を検索。
ejje.weblio.jp
ガイウス・ユリウス・カエサルが...』なるほど。おなじみのシーザー暗号かな?ということで、シーザー暗号の複合ができるサイトで見てみることに。
part1は『pgs4o{a0zber』の部分がどうみてもflagなので"pgs4o"が"ctf4b"に対応するように鍵を指定して複合。
part2もどう見てもシーザー暗号なので、同じ鍵で複合してみるも文にならない。じっくり見てみると、コロンまでの文字数が一致していることから、全く同じ文章を別の鍵で暗号化したものだと推定。通るように複合。
part3はパッと見てひっくり返した文字になってるのがわかったので頑張ってモニターをひっくり返した。CTFは筋肉も使うんだなぁ...たまげた(?)

[Warmup] Simple Auth

よくわかんないファイルが渡されたので、とりあえずfileコマンドにかけてみる。するとどうやらelfファイルのようなので、Ubuntuにコピーしてreadelf
chmod +xして実行できる状態にして、ltraceで実行したらフラグの文字列が見えたのでおわり。

Find the messages

ディスクイメージが渡された。中身は1,2,3のディレクトリで、1はtxt、2はpng、3は何も入ってない状態だった。
1のtxtは終わりの文字が'='だったので、base64の匂いがするのでbase64でデコードしたらflagの破片がでてきた。
2は開こうとしても開けないので、file、stringで調査してみるも、怪しい点が見当たらない。試しにバイナリエディタに入れてみるとファイルヘッダが全部58になっていたので、正しいファイルヘッダに書き換えたら無事開くことができた。画像にflagの破片が書かれていた。
3はディスクイメージ自体の問題なのではないかと疑って、foremostコマンドにかけてやるとpdfが出てきた。中身はそのままflagの破片だったので無事AC

てけいさんえくすとりーむず

書いてある通りのコマンドを実行してみると、「3桁どうしの計算を300秒で100問解いたらフラグをやるよ(意訳)」といった内容だったので、どうみても手計算じゃできません本当にありがとうございました。
pythonとかrubyで標準出力から計算式持ってきてevalしたらいけそうだなーと思ったので頑張ってみるも、上手くできない。なんでだろうと思っていたら、どうもgetsだと改行が出てこないせいで処理が止まってしまう様子。仕方ないのでgetcで9文字取ってはevalして標準入力にwriteを繰り返す方法でクリア。

[Warmup] Greeting

割と早めに手を出したのに解けたのは最後になってしまった。
「adminだけがフラグを読めるで(意訳)」とあったので、とりあえず何も考えずにadminを入力したら「偽管理者」に書き換えられてしまった。
下の方にコードが書いてあり、噂に聞こえたすごいphpなのを見ていて、XSSかな?と思ったけどもhtmlspecialcharsでエスケープされているので無理なのを知る。
書き換えが発生する箇所の下をみるとcookieから名前を取ってくる際には書き換えないことが見て取れるのでEdit this Cookiechrome拡張でcookieを書き換えてみるも、上手くいかない。ここで後回しにすることを決めた。
後になってからpythonのcookiejar使えばいけそうじゃないか?と思ったのでcookiejar経由でwebページを取得したらフラグが出てきてAC
他の人の解説を見てみたら普通にEdit this cookieでいけてるので謎

感想

解けそうな問題もまだあったが(具体的にはRSA is Power、Gimme your commentあたり)、素因数分解のツール探しやら、XSSできることに気づいてもどうやって手元にフラグを得るかが分からなくて解けなかった。
競技プログラミングと違って発想力をものすごい使う感じがあるので、是非とも積極的に参加していきたいなぁと思いました。まる。

『OS自作入門』を読んでいくよ 1日目

図書館で目に付いた『OS自作入門』を借りてきて読むことにした。副題によると「30日でできる」らしいので(30個に記事を分けているという意味なので、30日で本当にできるかどうかは人次第らしいけども)、それなりに毎日読んで記事でも書こうかと思っている。

内容

バイナリエディタの導入に関してはStirlingを導入済みなので読み飛ばした

まとめ

CPUについて

電気信号を処理する装置としてのCPU
→ON/OFFを数字の1/0として対応して2進数を表現して計算ができるように
→数字と文字を対応させて文章を表現できるように

CPUへの命令も電気信号
→0と1で全部あらわせる
バイナリエディタで作れないものは無いな強いな

アセンブラ

  • DB命令

「data byte」の略。ファイルの内容を1バイトだけ直接書く。

  • RESB命令

「reserve byte」の略。「RESB n」でnバイト分を空けておく。本で扱っているアセンブラだと0x00を書き込む。

  • :

コメント。

  • DW命令

「data word」の略。WORDは16bitを指す。

  • DD命令

「data double-word」の略。DWORDは32bitを指す。

  • $

先頭から何バイトかを指す変数。違う意味になることもある模様。

uint32_tに代入したASCIIコードのビット列をchar[4]の文字列に変換したい

union uint32converter{
  uint32_t value;
  char str[4];
};

Usage:

uint32converter conv;
uint32_t val;

//この辺でvalになんか入れる

conv.value=val;

cout<<conv.str<<endl;

なるほど。