😀
難読化シェル芸の解読: 1
はじめに
全体
post
# https://twitter.com/hiro_poke91/status/1218084119504048128
.>&$
"$(<$)"
! __="${_##*. ??}"
$?
___=${__:$_$_:_}${__:~_<<_:_}${__:~_:_}${__:_:_}${__:~_-_:_} __=${__::_}${__:$#$_$#:_}${__:~_<<_:_}${__:$_$#:_}
$___ ${__^^}
(($?=$?))>&$
_=($(<$))
${_[${##}$#>>++_]:~$#}${_[$_$#/++_]::_}${_[_**++_]:~$#}
返り値は
result
# https://twitter.com/minyoruminyon/status/121808415852362957
Fri Jan 17 17:14:26 JST 2020
どうやらロケールが英語圏のdate
らしい
???なんだこれ おかしいよ
解読開始
.>&$
- これを実行すると
$
というファイルに.
コマンドのエラー文が書き込まれる
$
/task.sh: 1 行: .: ファイル名が引数として必要です
.: 使用法: . filename [arguments]
-
>&
はStdOut+StdErr
のリダイレクト
"$(<$)"
-
cmd <file
はcmd
へfile
の内容を入力する - ここでは
<$
のコマンドがないので、cat
を入力先としているので$(cat $)
と同値 - そのため
"$の中身"
が実行され変数_
(後述)に格納される(Thanks:@qwertanusさん) - ちなみにファイル入出力はどこでやっても良い
example
echo unko >unko
>unko echo unko
echo >unko unko
! __="${_##*. ??}"
- 変数
_
は現在実行しているシェルを格納する(コマンド名とかshファイル名) - 前の行で実行された
$
の中身が変数_
に格納されている - そしてこの行を実行すると変数
__
にlename [arguments]
が代入される -
${v##exp}
はv
の値のexp
にマッチした部分以降を最長一致で取り出す展開 - ここで入力されたファイル
$
で*. ??
がマッチするのは以下
matched
/task.sh: 1 行: .: ファイル名が引数として必要です
.: 使用法: . fi
- これ以降の
lename [arguments]
が変数__
に代入される - 行頭の
!
は代入後の終了ステータス0
を否定/反転させ1
にしている - よって次行の
$?
が1
となる(Thanks:@qwertanusさん)
$?
- 変数
?
は直前のコマンドの終了ステータスの参照 - ここでは
1
が返る - そして
1
という名前のコマンドは当然存在しないので127
が変数?
に格納される - またここで変数
_
に1
が格納される(次で使用)
___=${__:$_$_:_}${__:~_<<_:_}${__:~_:_}${__:_:_}${__:~_-_:_}
-
$_
は1
,$__
はlename [arguments]
(18文字) - これを実行すると変数
__
にunset
が代入される -
${v:offset:length}
はv
の値のoffset
のインデックスからlength
文字分切り出す展開 - Pythonだと
v[offset:length]
みたいな -
~
はビット反転,<<
は左ビットシフト ___=${__:11:1}${__:-4:1}${__:-2:1}${__:1:1}${__:-3:1}
- よって
___=unset
となる
__=${__::_}${__:$#$_$#:_}${__:~_<<_:_}${__:$_$#:_}
-
$_
は1
,$__
はlename [arguments]
(18文字) - 変数
#
は関数の引数の数で、今は関数内でないので0
が返る - 上と同様に、
__=${__::1}${__:010:1}${__:-4:1}${__:10:1}
-
010(8)=8(10)
(Thanks:@hiroさん) -
offset
を省略すると0番目からとなる - よって
__=lang
となる
$___ ${__^^}
-
${v^^}
はvの値の小文字を全て大文字に変換する展開(upcase) -
unset LANG
となる - シェル芸Botのロケール(LANGの設定値)は
ja_JP.UTF-8
- 解除するとコマンドのデフォルトでのロケールで実行されるので
date
が英語に
(($?=$?))>&$
-
(())
は算術演算を行う - ここでは
$?=$?
は0=0
となり、0
に0
の代入を行う - 非変数への代入は不正なので、エラーが起き、そのメッセージがファイル
$
に書き込まれる
$
/task.sh: line 7: ((: 0=0: attempted assignment to non-variable (error token is "=0")
_=($(<$))
-
var=()
は配列の宣言 - ここでは
$
の内容がスペースで区切りで配列_
に代入される - 参照は
${arr[index]}
で行う
${_[${##}$#>>++_]:~$#}${_[$_$#/++_]::_}${_[_**++_]:~$#}
_=('' line 7: ((: 0=0: attempted assignment to non-variable (error token is "=0"))
- 0番目は空文字列(Thanks:@hiroさん)
-
${v:offset}
はvの値のoffsetのインデックスから末尾までを切り出す展開 -
++
はインクリメント,>>
は右ビットシフト
terminal
root@628d738436d9:/# echo $[++a]
1
root@628d738436d9:/# echo $[++a]
2
root@628d738436d9:/# echo $[++a]
3
-
${#v}
はvの文字列長を返す展開で、vが配列なら${#v[*]}
で配列の要素数が返る -
${##}
は$#
の長さ1が返る ${_[10>>1]:-1}${_[10/2]::2}${_[2**3]:-1}
-
${_[5]:-1}${_[5]::2}${_[8]:-1}
でdate
が返り実行される
結論
こわ
Discussion