aboutsummaryrefslogtreecommitdiffhomepage
path: root/services/nuldoc/content/posts/2025-01-08
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-11-27 05:05:04 +0900
committernsfisis <nsfisis@gmail.com>2025-11-27 06:07:46 +0900
commitc754d24b162ecd504f3c4bdd8632045dd0398768 (patch)
tree362323710bb329ad609d379df4e4a429e4229fd2 /services/nuldoc/content/posts/2025-01-08
parentd1014de68415df8f0a5dc3389332e086119c6198 (diff)
downloadnsfisis.dev-c754d24b162ecd504f3c4bdd8632045dd0398768.tar.gz
nsfisis.dev-c754d24b162ecd504f3c4bdd8632045dd0398768.tar.zst
nsfisis.dev-c754d24b162ecd504f3c4bdd8632045dd0398768.zip
feat(nuldoc): Djot to Markdown
Diffstat (limited to 'services/nuldoc/content/posts/2025-01-08')
-rw-r--r--services/nuldoc/content/posts/2025-01-08/phperkaigi-2023-tokens-q1.md (renamed from services/nuldoc/content/posts/2025-01-08/phperkaigi-2023-tokens-q1.dj)46
1 files changed, 14 insertions, 32 deletions
diff --git a/services/nuldoc/content/posts/2025-01-08/phperkaigi-2023-tokens-q1.dj b/services/nuldoc/content/posts/2025-01-08/phperkaigi-2023-tokens-q1.md
index c3a5eb4..407e558 100644
--- a/services/nuldoc/content/posts/2025-01-08/phperkaigi-2023-tokens-q1.dj
+++ b/services/nuldoc/content/posts/2025-01-08/phperkaigi-2023-tokens-q1.md
@@ -18,10 +18,9 @@ remark = "公開"
date = "2025-01-11"
remark = "読みやすさのため一部の文言を調整"
---
-{#intro}
-# はじめに
+# はじめに {#intro}
-::: note
+:::note
これは PHPerKaigi 2023 の記事です。今は 2025 年ですが、PHPerKaigi 2023 の記事です。
:::
@@ -42,15 +41,13 @@ PHPerKaigi 当日も [PHPer チャレンジ解説セッション](/slides/2023-0
それぞれの問題はこちらの GitHub リポジトリ ( [nsfisis/PHPerKaigi2023-tokens](https://github.com/nsfisis/PHPerKaigi2023-tokens) ) からも閲覧できる。
-{#quiz}
-# Q1: An Art of Computer Programming
+# Q1: An Art of Computer Programming {#quiz}
第1問『An Art of Computer Programming』はこちら。
![全体がQRコードになっており、中央には小さな文字で「Password is one of the PHPer tokens.」と書かれている](/posts/2025-01-08/phperkaigi-2023-tokens-q1/Q1.png)
-{#how-to-solve}
-# 解き方
+# 解き方 {#how-to-solve}
まずはトークンを得る方法を解説抜きで説明する。次のように実行する。
@@ -60,11 +57,9 @@ $ echo "#iwillblog" | php Q1.png >/dev/null
無事に実行できていれば「#ModernPHPisStaticallyTypedLanguage」というトークンが得られる。
-{#commentary}
-# 解説
+# 解説 {#commentary}
-{#read-as-image}
-## 画像として解釈する
+## 画像として解釈する {#read-as-image}
まずは素直に画像として見てみよう。
全体は QR コードになっている。適当な QR コードリーダで読み込むと、次のようなテキストが表示されるはずだ。
@@ -78,8 +73,7 @@ Guess password. $ echo "password" | php Q1.png >/dev/null
次に QR コードの中央部に目を向けると、小さな文字で「Password is one of the PHPer tokens.」と書かれているのがわかる。
他の PHPer トークンの中から適切な1つを見つけだし、「パスワード」として渡すことで答えとなる PHPer トークンが得られるというわけだ。
-{#password}
-## パスワード
+## パスワード {#password}
不正なパスワードを使って実行してみると、次のようなエラーメッセージが表示される。
@@ -99,8 +93,7 @@ $ echo "foo" | php Q1.png >/dev/null
問題を置いていたリポジトリにヒントとしてパスワードのトークンが「i」で始まると書いていたのだが、これが意図せずミスリードになってしまった。
これは私のミスである。
-{#png-steganography}
-## PNG ステガノグラフィ
+## PNG ステガノグラフィ {#png-steganography}
QR コードも言っているように、このファイルは PNG 画像であるにもかかわらず PHP で実行することができる。なぜこのようなことが可能なのか。
@@ -122,8 +115,8 @@ CLI で実行する場合、PHP タグよりも前にあるデータは標準出
1. PNG ヘッダ (`IHDR` チャンク)
1. 実際の画像データ (`IDAT` チャンク)
1. PNG フッタ (`IEND` チャンク)
-1. *PHP タグ (`&lt;?php`)*
-1. *通常の PHP ソースコード*
+1. **PHP タグ (`&lt;?php`)**
+1. **通常の PHP ソースコード**
PNG ファイルとして読むときは PNG フッタ以降は無視され、PHP スクリプトとして読むときは PHP タグ以前が無視されるという仕掛けである。
@@ -155,8 +148,7 @@ Guess password. $ echo "password" | php Q1.png >/dev/null
なお、このように PNG 画像などに本来のデータとは異なる別のデータを隠すことを「ステガノグラフィ」( [Wikipedia「ステガノグラフィー」](https://ja.wikipedia.org/wiki/%E3%82%B9%E3%83%86%E3%82%AC%E3%83%8E%E3%82%B0%E3%83%A9%E3%83%95%E3%82%A3%E3%83%BC) ) と呼ぶ。
-{#php-program}
-## 実行される PHP プログラム
+## 実行される PHP プログラム {#php-program}
画像の正体がわかったところで、画像に隠されていた PHP プログラムについて見ていこう。
先ほどは一部しか記載しなかったので、全体を載せる。
@@ -282,8 +274,7 @@ $b = unpack('C*', file_get_contents(__FILE__));
そう、今回の問題の画像ファイル `Q1.png` は、PHP 製 Piet インタプリタであると同時に、Piet のソースコード画像でもあるのだ。
QR コード中央のカラフルな部分が Piet の命令になっている。
-{#piet-source-code}
-## Piet のソースコード
+## Piet のソースコード {#piet-source-code}
さて、Piet でどのようなコードが書かれて (いや、描かれて) いるのかを解説したいところだが、今の私にはできそうにない。
というのも、すでに述べたように Piet は「難解プログラミング言語」である。
@@ -293,19 +284,12 @@ QR コード中央のカラフルな部分が Piet の命令になっている
それぞれの部分はおおよそ次のようなことをやっている (再検証・再読解はしていないので大嘘かもしれない)。
* 左上: 入力受け付け
-
* 標準入力から1文字ずつ読み込み、入力がなくなるまでスタックに積む。多分。
-
* 上辺、右辺: パスワードの検証
-
* 入力がパスワードと一致するか (= `#iwillblog` かどうか) を調べる。多分。
-
* 下辺、左辺、上辺の3列目、右辺の3列目、下辺の2列目: トークンの出力
-
* パスワードと一致していればここに飛んでくる。正解のトークンを出力する。多分。
-
* 右辺の2列目、上辺の2列目: 不正解のメッセージ出力
-
* パスワードと一致していなければここに飛んでくる。不正解のときのメッセージを出力する。多分。
ところで、先ほど掲載した Piet のインタプリタのソースコード末尾には次のような箇所がある。
@@ -329,8 +313,7 @@ fwrite(STDERR, str_replace('403 Forbidden', '401 Unauthorized', $o));
これを解決するために私が選んだのは、インタプリタを改造し、本来のメッセージとは異なるメッセージを無理やり出力させて帳尻を合わせることだった。
そういうわけでこの Piet インタプリタは完全な Piet インタプリタではなく、「403 Forbidden」というテキストを絶対に出力できない。
-{#misc}
-## その他小ネタ
+## その他小ネタ {#misc}
ここまでで問題の核心部分は説明し終えたので、ここからは残った小ネタを紹介しておく。
@@ -338,8 +321,7 @@ fwrite(STDERR, str_replace('403 Forbidden', '401 Unauthorized', $o));
この問題で得られるトークン「#ModernPHPisStaticallyTypedLanguage」は特に元ネタがあるわけではない。当然のような顔で嘘を主張したかったのでこうなった。
-{#outro}
-# おわりに
+# おわりに {#outro}
この問題の自己評価はこちら。
問題の出題順はおおよそ作成した順になっているのだが、そのせいで難易度高めの問題が1問目に配置されてしまった。