たいちょーの雑記

ぼくが3日に一度くらい雑記をかくところ

第51回シェル芸勉強会に参加しました

参加しました

今回もリモート開催ということでした。そろそろここの前置きに書くこともなくなってきました。

さて毎回の通りですが、時間中に出したzsh/bashでの解答と、これを書きながら考えたPowerShellでの解答の両方を載せていこうと思います。PowerShell全然わからないので難しい。

問題と解答はここから見られます。リンク先はYouTubeのリストで、各問題ごとに分けられています。いつもの問題と解答ページは更新されたらここに追記しようと思います。

Q1

pepoファイルに隠れたうんこを探し出す問題。出うんこ

これはまぁなんというかターミナルのせいだけど、探す前から若干うんこが見えてました

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-fb-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

-replacesedy相当の者が有ればよかったんですがね~。

Q3

factorawkを使って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")BigIntegerHexな文字列として出力させる部分ですね。

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という文字列をランダムな文字列が書かれたファイルの中に隠す問題。隠し方は以下

  1. 元の文章を2進数にする
  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させていただきました。

www2.slideshare.net

t.co

12月なのでワタナベエンコーディングについて再考し、再実装しました。とてもいいものが出来たと思います。聴いてくださった方ありがとうございました!!

おわり

今回は基数などの変換が多かったイメージです。難読化系の問題は得意なんですが、制限時間があると、どうしてもよくある解答以外私から出てこなくなってしまうのが痛いですね。難読火力を磨かなければですね。

とても楽しかったです!企画・開催ありがとうございました!!