先日書いたシェル・スクリプトを難読化するエントリーの続きで、今回はスクリプト・コンパイラーの shc について。先に書いておくが、この shc はシェル・スクリプトをバイナリ化するツールなので、難読化を期待して使うと、「難読化って言うか、読めねーよ」 となってしまうことに注意。「読みにくくしたいが、読めないのは困る」 と言った微妙な力加減が必要な場面では、使えない。
さて、ダウンロード・ページや付属ドキュメント等で確認できるマニュアルでは
You can compile any kind of shell script, but you need to supply valid -i, -x and -l options.
と謳われているものの、Web で検索してみると 「動かなかった」 と言う事例もあり、単にオプションの与え方がマズいだけなのかもしれないが、どこまで "any kind of shell script" なのかはわからない。一応ソースには、bash と csh と ksh のサンプル・コードが付属している。
Debian/GNU Linux のように shc がパッケージ化されていない環境でインストールするには、前述のダウンロード・ページからソース一式をダウンロードし、次のようにコンパイルする。
$ cd shc-3.8.7/
$ make
コンパイルに成功すると shc と言う実行ファイルが生成されるので、そのまま使ってもいいし、パスの通った場所にコピーしても OK。shc のオプションは次の通り。
shc Version 3.8.7, Generic Script Compiler shc Copyright (c) 1994-2009 Francisco Rosalesshc Usage: shc [-e date] [-m addr] [-i iopt] [-x cmnd] [-l lopt] [-rvDTCAh] -f script -e %s Expiration date in dd/mm/yyyy format [none] -m %s Message to display upon expiration ["Please contact your provider"] -f %s File name of the script to compile -i %s Inline option for the shell interpreter i.e: -e -x %s eXec command, as a printf format i.e: exec('%s',@ARGV); -l %s Last shell option i.e: -- -r Relax security. Make a redistributable binary -v Verbose compilation -D Switch ON debug exec calls [OFF] -T Allow binary to be traceable [no] -C Display license and exit -A Display abstract and exit -h Display help and exit Environment variables used: Name Default Usage CC cc C compiler command CFLAGS C compiler flags Please consult the shc(1) man page.
環境によっては -i や -l や -x 等で細かく指定する必要があるようだが、とりあえず単純な方法で、適当なシェル・スクリプト (sh) を変換してみる。
shc shll=sh
shc [-i]=-c
shc [-x]=exec '%s' "$@"
shc [-l]=
shc opts=
shc: cc sample.sh.x.c -o sample.sh.x
shc: strip sample.sh.x
shc: chmod go-r sample.sh.x
上記コマンドは、変換をしたマシン以外でのバイナリ実行禁止を解除し (-r)、処理の内容を出力し (-v)、オリジナルのスクリプトを指定 (-f) している。-r オプションを付けないと、変換後のバイナリを他のマシンにコピーしても使えない点に注意。
コマンドにミスがなければ、中間ファイル (C のソース) sample.sh.x.c と実行ファイル sample.sh.x が生成される。-r オプション付加時の sample.sh.x を実行して変換前のスクリプトと同じ結果が得られれば、正常に変換できている (はず)。
面白いのは、-e オプションで実行可能な期限、-m オプションで期限切れの際に表示されるメッセージを設定できることだ。例えば
とすると、2010年 6月 1日以降に変換後のバイナリを実行した際に、指定したメッセージを表示して停止する (=スクリプト本来の動作をしない) ようになる。
通常シェル・スクリプトは、その処理内容は別として実行自体は環境に依存しないが、この shc で生成されたバイナリは、file で見るとわかるように、CPU に依存してしまう。そのため 32ビットと 64ビットマシンが混在する環境では、アーキテクチャーごとにバイナリを用意する必要がある (32ビット用は 64ビット環境でも動くと思うが)。
ただ各マシン間のアーキテクチャーに互換性があればバイナリをコピーするだけでいいが、アーキテクチャーに互換性がなく、オリジナル・スクリプトは見せたくなく、shc のインストールもできない、と言うような状況があるかもしれない。その場合は -v オプションの出力で表示されるように、適切なオプション (-r 必須) で生成された中間ファイル sample.sh.x.c を目的のマシンにコピーして以下のようにコンパイルすれば、オリジナルのスクリプトが不要で shc も使わずに、実行可能なバイナリを生成できる。
#!/bin/sh
CC=cc
CFLAGS=''
cc sample.sh.x.c -o sample.sh.x
strip sample.sh.x
chmod go-r sample.sh.x
$ ./make_binary.sh ← コンパイル
プラットフォーム別にバイナリを用意する場合は、台数が多ければ多いほど管理が煩雑になる。「あれ? このマシンって 32ビットだっけ?」 とか 「なんか動かないんだけど、ひょっとして 64ビット用のバイナリがコピーされてる? 大至急全台で確認!」 と言った事態を想像するとゲンナリするので、そう言う場合は全マシンに全プラットフォーム用バイナリをコピーしてしまい、実行は環境を調べて適切なバイナリを呼び出すラッパーを経由させるようにした方が、間違いがないと思う。もしくは、全て 32ビット用にしてしまうと言う手もあるが。
このように shc はスクリプトをバイナリ化してしまう有用なツールだが、別にスクリプトの処理内容が C で書き直されるわけではなく、恐らくオリジナルのスクリプトを (暗号化して) バイナリの中に埋め込んでいるだけだ。そのためマニュアルでも言及されているように、主目的はスクリプトの隠蔽であって、実効速度には何のメリットもないどころか、むしろ遅くなる可能性があることに注意。僕が試した限りでは、バイナリ化のオーバーヘッドのために、ほんの僅かながら遅くなるケースがほとんどだった。処理内容にもよると思うが、シビアなパフォーマンスが要求される場面では、慎重にテストした方がいいだろう (と言うかそもそも、そんな場面ではバイナリ化スクリプトなんて代物は使わないか)。
とは言え、この shc は様々な場面で使えると思う。当初の目的の 「難読化」 とは少々趣が異なるものの、スクリプトの保護と言う観点では、思わぬ収穫だった。これで 「シェル・スクリプト難読化シェル・スクリプト」 を書かなくてもよくなる・・・か?