符号化処理芸人衆
シェル芸ではテキスト処理だけでなくバイナリの符号化処理とかも扱うことが出来る。こういう処理がサラッと出来るようになるとヤバい人ステキな人と危険視尊敬されること間違いなし。
元々のキッカケが、MicrosoftエバンジェリストちょまどさんのTwitterつぶやき。
01010101 01110011 01100101 00100000 01011000 01100001 01101101 01100001 01110010 01101001 01101110 00101110
— ちょまど (@chomado) 2016年9月2日
文字列をバイナリのビット文字列にしたってことかー。シェル芸でちょっくら解読してみよう。16進数に変換してxxdコマンドに突っ込もう。まずはその準備。
$ echo 01010101 01110011 01100101 00100000 01011000 01100001 01101101 01100001 01110010 01101001 01101110 00101110 | tr -d ' ' | sed 's/^/obase=16;ibase=2;/' obase=16;ibase=2;010101010111001101100101001000000101100001100001011011010110000101110010011010010110111000101110
ここからbcコマンドに突っ込んで16進数に変換
$ echo 01010101 01110011 01100101 00100000 01011000 01100001 01101101 01100001 01110010 01101001 01101110 00101110 | tr -d ' ' | sed 's/^/obase=16;ibase=2;/' | bc 5573652058616D6172696E2E
あとはxxdコマンドに突っ込んでバイナリ変換。ということでみんなXamarin使おう!
$ echo 01010101 01110011 01100101 00100000 01011000 01100001 01101101 01100001 01110010 01101001 01101110 00101110 | tr -d ' ' | sed 's/^/obase=16;ibase=2;/' | bc | xxd -p -r Use Xamarin.
私もモノマネって01のビット列でiOS10のダウンロード失敗しまくる愚痴をTwitterに吐いていた。
111001011011000110001010111000111000000110010001011010010100111101010011001100010011000000001010
— ぱぴろんちゃん😱🙀 (@papiron) 2016年9月18日
PerlやRubyでpack
使う解答は@ebanさんより。
Rubyだとruby -ne 'puts [$_].pack("B*")'
— eban (@eban) 2016年9月18日
% echo ※数字 | perl -ne 'print pack("B*", $_), "\n"'#シェル芸
— eban (@eban) 2016年9月18日
そしてしばらくすると@grethlenさんからこんな暗号?問題が出ていた。
1302140411021101140213011103110511011103110211
— ぐれさん (@grethlen) 2016年9月18日
暗号文だよ。解けるかな。
んんん???すぐには分からくていろいろ考えた・・・2文字ずつ切ってみると気が付いたことが。これは1が3つ、0が2つ、1が4つ、0が4つってこと??
$ echo 1302140411021101140213011103110511011103110211 | fold -w 2 13 02 14 04 ....
じゃあその前提で復号化してみよう。awkでビット列を復元する。FS=
を指定することで1文字ずつフィールド分割して処理。
$ echo 1302140411021101140213011103110511011103110211 | fold -w 2 | awk '{for(i=1;i<=$2;i++){printf $1}}' FS= 111001111000010010111100111010001000001010001001
あとは上記の通り、16進数変換してxxdコマンドでバイナリ変換。ステルス飯テロ・・・
$ echo 1302140411021101140213011103110511011103110211 | fold -w 2 | awk '{for(i=1;i<=$2;i++){printf $1}}' FS= | sed 's/^/obase=16;ibase=2;/' | bc | xxd -p -r 焼肉
ちなみにこの01と続く数のペアの符号化をランレングス圧縮(連長圧縮)というらしい。なるほろ。
逆に文字列をランレングス圧縮する方法。まずは16進数にダンプしてbcコマンドで2進数に変換する。bcコマンドに16進数を突っ込む時は大文字にする必要があるため、xxdコマンドに-u
オプションを付けるか別途trコマンドなどで変換する。
$ echo -n おでん | xxd -u -p | sed 's/^/obase=2;ibase=16;/' | bc 11100011100000011000101011100011100000011010011111100011100000101001\ 0011
ビット列が80文字を超えると折り返されてしまうので、trコマンドでバックスラッシュと改行を消して1文字ずつ改行する。
$ echo -n おでん | xxd -u -p | sed 's/^/obase=2;ibase=16;/' | bc | tr -d '\n\' | grep -o . 1 1 1 ..... 0 1 1
ここからuniq -c
を使って1と0が続く数を数えればいい。あえて並べ替えないでuniq処理をするのがポイントだ。
$ echo -n おでん | xxd -u -p | sed 's/^/obase=2;ibase=16;/' | bc | tr -d '\n\' | grep -o . | uniq -c 3 1 3 0 3 1 ..... 1 1 2 0 2 1
あとはawkを使って並べて完成。
$ echo -n おでん | xxd -u -p | sed 's/^/obase=2;ibase=16;/' | bc | tr -d '\n\' | grep -o . | uniq -c | awk '{printf $2$1}' 1303130612031101110113031306120111021603130511011102110212
Tukubaiのcountコマンド使うならこんな感じ。
$ echo -n おでん | xxd -u -p | sed 's/^/obase=2;ibase=16;/' | bc | tr -d '\n\' | grep -o . | count 1 1 | tr -d ' \n' 1303130612031101110113031306120111021603130511011102110212
さてここまで考えた後、xxdコマンドのオプションを調べていると-b
オプションなるものが。
xxdに-bオプションが。-pと併用出来ないのが惜しい、マジ惜しい。
— ぱぴろんちゃん😱🙀 (@papiron) 2016年9月18日
$ echo -n iOS10 | xxd -b
0000000: 01101001 01001111 01010011 00110001 00110000 iOS10#シェル芸
さらにxxdコマンドに-cオプションがあった。
xxdに-cオプションってのがあった。 #シェル芸 的に扱いやすいかも?
— ぱぴろんちゃん😱🙀 (@papiron) 2016年9月20日
$ echo factor | xxd -b -c 1
0000000: 01100110 f
0000001: 01100001 a
0000002: 01100011 c
...
そしてxxdコマンドの-b
オプションと-c
オプションを活用した事例が早速登場。
$ echo 'In48FEBACHw8CEACCEBCCH48' | base64 -d | xxd -b -c 3 | awk '$1="";$NF="";1' | sed 'y/01/ y/'
— ぐれさん (@grethlen) 2016年9月20日
Macの人はbase64 -Dで。#yes #シェル芸
つまり0と1でマッピングしてしまえということ。このビットパターンになる文字列を地道に探したということか??
$ echo 'In48FEBACHw8CEACCEBCCH48' | base64 --decode | xxd -b -c 3 0000000: 00100010 01111110 00111100 "~< 0000003: 00010100 01000000 01000000 .@@ 0000006: 00001000 01111100 00111100 .|< 0000009: 00001000 01000000 00000010 .@. 000000c: 00001000 01000000 01000010 .@B 000000f: 00001000 01111110 00111100 .~<
後は余計な列を消し、1をyへ、0をスペースへ変換。
$ echo 'In48FEBACHw8CEACCEBCCH48' | base64 --decode | xxd -b -c 3 | awk '$1="";$NF="";1' | sed 'y/01/ y/' y y yyyyyy yyyy y y y y y yyyyy yyyy y y y y y y y y yyyyyy yyyy
追記
この記事を書いた後、上記のビットパターンの作り方を教えていただいた。なるほどbannerコマンドか。そんなのあった。
ちなみに、あのyesのやつですが、作り方があります。文字を地道に探したのではなく、作成も #シェル芸 でやりました。
— ぐれさん (@grethlen) 2016年9月22日
bannerコマンドというのがあってですね。。
> @papiron pic.twitter.com/sWFpUBLVUm
そしてこれらをキッカケに、様々な符号化処理や暗号処理がTwitter上に流れだしたのであった。@grethlenさんがTogetterにまとめてくれているのでこちらをご覧あれ。コワクナイヨ。多分・・・きっと・・・キット・・・KITTO・・・
2016/09/24 追記
PowerShellで解いてくれた方が!反響ありがたや。スクリプトブロックがbeginとendもあってawkっぽい。PowerShellの文字コードってUTF-8なのか。ややこしい処理は.NET Frameworkの力を借りる。 stknohg.hatenablog.jp