第51回シェル芸勉強会に参加しました
参加しました
今回もリモート開催ということでした。そろそろここの前置きに書くこともなくなってきました。
さて毎回の通りですが、時間中に出したzsh/bash
での解答と、これを書きながら考えたPowerShell
での解答の両方を載せていこうと思います。PowerShell
全然わからないので難しい。
問題と解答はここから見られます。リンク先はYouTubeのリストで、各問題ごとに分けられています。いつもの問題と解答ページは更新されたらここに追記しようと思います。
Q1
pepo
ファイルに隠れたうんこを探し出す問題。出うんこ
これはまぁなんというかターミナルのせいだけど、探す前から若干うんこが見えてました
うちのターミナルだとうっすら見えてんですよね…#シェル芸 pic.twitter.com/A4RdS8LlkR
— たいちょー (@xztaityozx_001) 2020年12月27日
bash/zsh
$ cat pepo | sed "s/$(echo -e 'ほ\U309a'/(/g" | sed "s/$(echo -e 'へ\U309a'/)/g"|sed 's/[ぽ|ぺ]/ /g'
(
と)
が他の「ぽ」や「ぺ」とは違い、「ほ」+「゜」になっていることがポイントですね。そのコードポイントは\U309a
なので、echo -e
で「ほ」や「へ」にくっつけてからsed
に渡し、置換。先の画像が有ったのですぐ気づいたのですが、本当はxxd
なんかを見た方がいいですね。
PowerShellの解答は以下のかんじで
PowerShell
$ (cat pepo -Encoding UTF8) -replace "ぽ"," " -replace "ぺ"," " -replace "ほ\u309a","(" -replace "へ\u309a",")" 人 ( ) ( ) ( ) ( ) ( ) ( ) ( )
sed
を重ねるよりも簡単に欠ける感じがして結構好きです。方針は先のものと同じですね。
Q2
暗号化されたC言語のソースを解読してプログラムを復元する問題。ヒントが無かったら解けなかったです。
bash/zsh
$ echo -e "$(cat q2 | sed 'y/1234567890bcdefg/0123456789abcdef/' | sed 's/../\\x&/g' )" | uconv -x FullWidth-HalfWidth #include <stdio.h> #include <stdlib.h> int main() { char a[] = {0x75,0x6e,0x6b,0x6f,0x00}; puts(a); exit(0); }
シーザー暗号ですというヒントがあったので、それを頼りに。数値は一つシフト、アルファベットはa-f
がb-g
になっているので、それぞれ置換。全角文字になっているので、uconv
を使って半角へ戻します。nkf -Z
でもいいんですが、全角スペースが半角にならないらしいです。なんでなんだろう。
ところでこれを実行すると
$ ... | gcc -x c - && ./a.out unko
うんこになります。PowerShellでもやってみますが、置換の部分だけ。半角<=>全角はuconv
でよくない?ということで・・・。まぁそんなこと言ったら全部さっきの解答でいいんですけどね。
PowerShell
$ $hash=@{b='a';c='b';d='c';e='d';f='e';g='a';'1'=0;'2'=1;'3'=2;'4'=3;'5'=4;'6'=5;'7'=6;'8'=7;'9'=8;'0'=9}; [regex]::Replace((cat .\q2), ($hash.Keys -join "|"), {$hash[$args.value]}) | xxd -ps -r
-replace
にsed
のy
相当の者が有ればよかったんですがね~。
Q3
factor
とawk
を使って127が素数なことを判定する問題。ただし数字を含めてはだめらしい。難読化だ。
bash/zsh
$ @ &>/dev/null; echo $? | factor | awk "NF==(($$/$$+$$/$$))"
PowerShell
$ echo (((([int]('a' -eq 'a')) -shl (([int]('' -eq '') -shl ([int]('b' -eq 'b')) -shl ([int]($null -eq $null)) -shl [int]$?))) -shr [int]$?) - [int]$?)
どちらも特に解説することは・・・ないですね。True
な値をint
へキャストすると、1
になるということさえ分かっていれば、PowerShellの方はわかるかと思います。
Q4
アルファベットを使わずにq2
ファイルの中身を出力する問題。zsh
だと<?2
でできるんだけど。
bash/zsh
$ $0 ?q
bash
に与えて、コマンドとして評価させます。エラーが出るけど、中身はチェックできるのでOKだよね。
Q5
message
ファイルを解読する問題、なんだけど順を追って解読していくので、ここではサブ問題みたいな感じ。この後の問題とつながとのこと。ここでは暗号をデカい16進数として見て、素数で順番に割って行く問題となっています。
bash
$ seq 20 | factor | awk -v V=$(cat She*/vol.51/message) 'NF==2{printf("%s/%X\n",V,$2)}' | sed 1i'obase=16;ibase=16' | bc 1E507BF014FBCE45D6079643F0151B79E27113EF90AA1B15EA94D9D 1435A7F563528983E4050ED7F56367A696F60D4A60716763F1B8913 C2031933B97EC1BEF363C1B2CD53E30C0FA07F96D10D7A25DD523E 8A947FB73B5A8A63D26BD37FB73BEB51C204ED6BB9E50E1B0BCD08 582FF42EC8ADFAF9B473297FD1B1C44B4CEBDCE7764BF1B41EC84B 4A9EBAEC826BD45D2288D458765B57536878CE9C77CA1B49A3E48E 390FF85A81D9FCBFB0FF38F85A82159A22B6BC1D4C8B7E475F364E 330E4A00235740759E5D9EC34381DD6EF6A37FE451F612ABA6082B
16進数での式を組み立ててbc
に計算させます。sed 1i
がポイントですね。これヘッダーを付けたりするときによく使います。
PoweShell
版はこちら
PowerShell
$ $V=([System.Numerics.BigInteger]::Parse((cat .\message), [System.Globalization.NumberStyles]::AllowHexSpecifier)); (1..100 | %{ openssl prime $_ } |sls "is prime" |%{ ($_ -split ' ')[1] }) -replace '[(|)]','' | %{ ($V/$_).ToString("X") }
まずはHex
な文字列をBigInteger
として読ませるために[System.Numerics.BigInteger]::Parse
を使います。その後は openssl prime
で素数判定、切り出し、計算の順番に処理をしていきます。($V/$_).ToString("X")
はBigInteger
をHex
な文字列として出力させる部分ですね。
Q6
上のあとに続けてメッセージを探す問題。雑にxxd
へ渡すだけなんだけど、検索が上手くできなくて、時間内に解けませんでした。にゃーん
bash/zsh
$ seq inf | factor | awk -v V=$(cat message) 'NF==2{printf("%s/%X\n",V,$2)}' | sed 1i'obase=16;ibase=16' | bc | xxd -ps -r | grep -m1 -aoP '[\p{Han}\p{Hiragana}!]{3,}' 目だ!目を狙え!
解説でもあったけど、grep
の-a
や\p{Han}
とか\p{Hiragana}
を良く忘れてしまう。好きなんだけどな~~。
PowerShell
のほうは多分こんな感じ
PowerShell
$ ... |xxd -ps -r |sls "[\p{IsHiragana}\p{IsCJKUnifiedIdeographs}!]{3,}"
.NETでは\p{IsHan}
がないっぽいのでこれで仕方なく代用してますが、CJKなので無限に引っかかる。いつか見つかると思います・・・
Q7
ssh localhost
した後、記号だけでbash
を立ち上げる問題。echo $SHLVL
の出力が2になるような記号ワンライナー
bash/zsh
$ ${!##-}
${!#}
は関節参照展開。${#}
が0
なので、${!#}
が${0}
と評価される。これでシェルが呼び出せそうなんだけど、$0
が-bash
とか-zsh
になることがあるので、${var#-}
で先頭のハイフンを削っているということ。解説通りなので、配信のアーカイブを見た方がいいかもです。
Q8
echo evil
という文字列をランダムな文字列が書かれたファイルの中に隠す問題。隠し方は以下
- 元の文章を2進数にする
- 各ビットをランダム文字列の各文字の最下位ビットとして仕込む
bash/zsh
$ echo echo evil | xxd -b -c1 | sel 2 | grep -o . | while read L; do [[ "$L" == "1" ]] && shuf -n1 -e {A..Z..2} || shuf -n1 -e {B..Z..2}; done ZMSXHSFYJCAFJJOYNCIZWJNLTKMHGAISJJYTFZVFLAMZREZYVKGWZQKDNKOBMXFWJKORKAJNPHZDCBOP
最初はAかBしか出力しないやつだったんだけど、ランダム化しました。ちなみに{A..Z..2}
という書き方、zsh
では出来ませんでした。
出来てほしいな~という感じですね。
さて復元もしてみます。
bash/zsh
$ echo -n ZMSXHSFYJCAFJJOYNCIZWJNLTKMHGAISJJYTFZVFLAMZREZYVKGWZQKDNKOBMXFWJKORKAJNPHZDCBOP | xxd -ps -u | sed 's@..@&%2\n@g' | sed 1i'obase=16;ibase=16;' | bc | tr -d \n | fold -b8 | sed 1i'obase=16;ibase=2'| awk 4 | bc | xxd -ps -r echo evil
基数変換が面倒すぎません・・・?なんかエイリアス貼るなどしたいですね。
PowerShell
版もサクッと
PowerShell
$ ((echo "echo evil" | xxd -b | %{($_ -split ' ')[1..6]}) -split '' |? {$_} | %{if($_%2){"ACEGIKMOQSUWY"[((Get-Random)%13)]}else{"BDFHJLNPRTVXZ"[((Get-Random)%13)]}}) -join "" HYIPJELUHUSXZFKKHGCTQFFJVIWTEMMMLFOHDTHJHWKHTWNMDAEGLKMRLWSPOBPQLWAFEAFNLLPHEKDUXPNNMTQN $ $A=((((echo HYIPJELUHUSXZFKKHGCTQFFJVIWTEMMMLFOHDTHJHWKHTWNMDAEGLKMRLWSPOBPQLWAFEAFNLLPHEKDUXPNNMTQN | xxd -ps -u -c1 | %{ [int]::Parse($_, [System.Globalization.NumberStyles]::AllowHexSpecifier)%2}) -join "") -replace "(.{8})",'$1 ') -split " " | %{[System.Convert]::ToInt32($_, 2).ToString("X2")});$A[0..($A.Length-2)] | xxd -ps -r
サクッとと言いましたがすごくggったりしました。むずい
LT
今回もLTさせていただきました。
12月なのでワタナベエンコーディングについて再考し、再実装しました。とてもいいものが出来たと思います。聴いてくださった方ありがとうございました!!
おわり
今回は基数などの変換が多かったイメージです。難読化系の問題は得意なんですが、制限時間があると、どうしてもよくある解答以外私から出てこなくなってしまうのが痛いですね。難読火力を磨かなければですね。
とても楽しかったです!企画・開催ありがとうございました!!