先日書いたシェル・スクリプトを難読化するエントリーの続きで、今回はスクリプト・コンパイラーの 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 がパッケージ化されていない環境でインストールするには、前述のダウンロード・ページからソース一式をダウンロードし、次のようにコンパイルする。

$ tar xfzv shc-3.8.7.tgz

$ cd shc-3.8.7/

$ make

コンパイルに成功すると shc と言う実行ファイルが生成されるので、そのまま使ってもいいし、パスの通った場所にコピーしても OK。shc のオプションは次の通り。

shc Version 3.8.7, Generic Script Compiler
shc Copyright (c) 1994-2009 Francisco Rosales 
shc 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 -rv -f sample.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 オプションで期限切れの際に表示されるメッセージを設定できることだ。例えば

$ shc -e 01/06/2010 -m "This copy of Wxxxxxs is not genuine" -f sample.sh

とすると、2010年 6月 1日以降に変換後のバイナリを実行した際に、指定したメッセージを表示して停止する (=スクリプト本来の動作をしない) ようになる。

通常シェル・スクリプトは、その処理内容は別として実行自体は環境に依存しないが、この shc で生成されたバイナリは、file で見るとわかるように、CPU に依存してしまう。そのため 32ビットと 64ビットマシンが混在する環境では、アーキテクチャーごとにバイナリを用意する必要がある (32ビット用は 64ビット環境でも動くと思うが)。

ただ各マシン間のアーキテクチャーに互換性があればバイナリをコピーするだけでいいが、アーキテクチャーに互換性がなく、オリジナル・スクリプトは見せたくなく、shc のインストールもできない、と言うような状況があるかもしれない。その場合は -v オプションの出力で表示されるように、適切なオプション (-r 必須) で生成された中間ファイル sample.sh.x.c を目的のマシンにコピーして以下のようにコンパイルすれば、オリジナルのスクリプトが不要で shc も使わずに、実行可能なバイナリを生成できる。

$ cat make_binary.sh ← バイナリ生成処理をまとめたスクリプト
#!/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 は様々な場面で使えると思う。当初の目的の 「難読化」 とは少々趣が異なるものの、スクリプトの保護と言う観点では、思わぬ収穫だった。これで 「シェル・スクリプト難読化シェル・スクリプト」 を書かなくてもよくなる・・・か?

Comments are closed.