追記:iconvを使ってシェル芸すれば実現容易であることがわかった、詳細はこちら)。ないなら作るしかない。はじめにUnicodeはマルチバイト文字をシングルバイト文字のみで表現できる規格である。その中でも様々な符号化スキームがあるが、今回はUTF-8を対象とする。ちなみにRFCを読んで気付いたのだが、コードポイント(U+65E5)→バイナリ(”日”)方向の変換が「エンコード」であり、バイナリ→コードポイント方向の変換を「デコード」と呼ぶらしい、感覚とは逆だった。以前、デコードをしようと思って結局オンラインのデコードツールに頼ったことがあり、手元でコマンド1発で変換できないのはおかしい!と思って今回デコードに挑戦することになった。当時何をしたかったちゃんと覚えていないが、確かpͪoͣnͬpͣoͥnͭpͣa͡inͥをデコードしたかったとかそんなしょうもない理由だったと思う(このネタをしばらく温めているうちに忘れた)。UTF-8のデコード法RFC3629に詳しく書いてあった。UTF-8のコードポイントと、UTF-8のバイナリ表現は以下のように対応している。10xxxxxx今回はデコードなので、右列から左列方向に変換を行う。その手順は以下のようになる。先頭オクテットの先頭ビットを見ることで、何オクテットを取得するか判断する。右列のx部分(1行目なら7桁、2行目なら5+6=11桁)のみを抽出して並べる。出来上がったビット列を16進変換する。BashにおけるUnicodeの扱い動作確認にはbash-5.0を使用した。bashに記載されている。digits)かいつまんで言うと、\uから始まる場合は最大4文字、\Uから始まる場合は最大8文字をUnicodeとして認識する。"\u3b1"でも"\u03b1"でも"\U3b1"でも"\U000003b1"でもエンコードしてくれる。"αa"を表現しようとした際に"\u3b1a"と書いてしまうと、期待と全く違う"㬚"が表示されてしまうため、ゼロ埋めをする方が無難だろう。"\u03b1a"は最初の4桁でエンコードが終わるので、正しく"αa"が出力される。デコードスクリプト英語の仕様を見ながら数日格闘した結果以下のスクリプトができた。1fidoneecho簡単に解説する。"))まず引数をxxdして2バイト1行で表示する。xxdの出力は左右に余計なものが出るので、cutでhex部分だけ抽出する。bytes)に格納する。]]後述の処理でtbtsの要素を先頭から取り出す。全てなくなるまで繰り返す。\\u${tbts[0]}tbts=(${tbts[@]:1})ifの部分は先頭オクテットのバイナリパターンによる条件分岐である。最初は0xxxxxxxの場合なので、先頭bit(0x${tbts[0]}で整数展開し、7ビット右シフト)が0であればよい。この場合は通常のascii文字と変わらないので、\\u${tbts[0]}をエスケープ文字列として出力する。これによって、\u0061ではなく"a"とダイレクトに出力することができる。最後の行は、${tbts[@]:1}でtbtsの1番目以降の要素を全出力する。これを再代入することによって、強引に先頭のシフトをしている。10xxxxxxのパターン。先頭3bitが5(0b110)であればよい。))この部分は先頭バイト(110xxxxx)のx部分だけ取るために00011111(31)との論理積を取り、左に6bitシフト、さらに2バイト目の後方6bitを取るために00111111(63)との論理積をとって結合(論理和)している。その後printfでゼロ埋め4桁hex出力("%04x")し、最後の行で配列の先頭シフトをしている。10xxxxxxパターンも同様の処理である。)tbts=(${tbts[@]:4})最後のパターンは表からもわかるように5桁以上のコードになってしまうため、"\Uxxxxxxxx"形式の8桁表示をする。2Fも同じ結果になっちゃうことがあるし、それを認めると思わぬ脆弱性を生み出すからな!ということらしい。invalid)/\u002e./たしかに実行してみると\u002eが...これはエンコードすると"."になってしまい、ディレクトリトラバーサルが実行されそうな匂い...本格的に作るにはこういう無効バイトをブロックしておく必要があるな。pͪoͣnͬpͣoͥnͭpͣa͡inͥp\u036ao\u0363n\u036cp\u0363o\u0365n\u036dp\u0363a\u0361in\u0365上手にできました。あとでリポジトリにでもしてみようかな。コメントをキャンセルメールアドレスが公開されることはありません。WordPress%d人のブロガーが「いいね」をつけました。