111
51

More than 3 years have passed since last update.

難読化シェル芸の解読: 1

Last updated at Posted at 2020-01-18

はじめに

全体

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 <filecmdfileの内容を入力する
  • ここでは<$のコマンドがないので、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となり、00の代入を行う
  • 非変数への代入は不正なので、エラーが起き、そのメッセージがファイル$に書き込まれる
$
/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が返り実行される

結論

こわ

追記:あわせて読みたい本質情報群

111
51
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
111
51