サーバー管理者であれば、OS を問わず様々なスクリプトを書く機会が多いと思う。僕は Linux 上の bash に触れる機会が多いこともあって、最も馴染みがあるのはやはりシェル・スクリプト (sh) だ。スクリプトは作業を省力化したり、確実性向上やミス防止等の手段にすぎないが、困ったことにスクリプトを書くのは結構楽しいので、気分転換に始めたつもりがいつも間にか目的とすり替わってしまい、思いの外長時間没頭していたりすることがたまにある。
ただ自分専用だったり、同じ組織内で 「どんどん改良して OK」 と言う類のスクリプトであれば、いつでも書き直せると言う特性は非常に便利だが、時と場合によっては、中を見られたり改変されたくないこともある。そんなときは、暗号化や難読化が (ある程度) 有効な手段となる。VBScript や JScript を暗号化する Windows Script Encoder は、ややクセがあってデコードも可能ではあるものの、お手軽な方法としてよく使っている。
ところが先日必要になったのは、シェル・スクリプトの内容隠蔽。最初はシェル・スクリプト難読化シェル・スクリプトを自力で書くつもりだったが、どうせ書くなら汎用性の高いツールにしたい。しかし実際に作業を始めてみると、人によって異なる記述の癖を吸収しつつ 「こりゃ厳しいな」 と言うレベル (=解読を試みる気が失せる) の難読化をするのは、一筋縄では行かないことがわかって来た。その時はあまり時間に余裕もなく、「車輪の再発明」 は避けたかったので、既存のツールを探してみることにした。
すぐに見つかったのが、obfsh と shc と言う、2つのツールだ。shc はソースをコンパイルする必要があるが、obfsh は純粋なシェル・スクリプトで手軽そうだったので、まずこちらから試してみた。
前述のダウンロード・ページで使い方をよく読み、次のように適当なシェル・スクリプトを難読化してみる。
上記例は、変換前のスクリプト (-f) に、gibberish なヘッダーとフッターを付加 (-g) し、空白や空白行、コメントを削除 (-i) し、任意の間隔でスペースを挿入 (-j) している。実行結果は標準出力に送られるだけなので、ファイルにリダイレクトして保存する。
-g オプションの使い方は難解だったが、ソースを見て意味がわかった。上記例では、40文字の行を 5行で、その中で登場するランダムな文字のバリエーションとして、配列 _sign (string_randomSigns() 内で定義) のインデックス 100~309 を使う、と言う意味になる。この配列のインデックスは 100 から始まって 309 で終わるので、自分で改変していない限り、オプションの "+" 以降で指定できるのは 100~309 までになる。
n 行ごとにコメントを挿入する -c オプションと、同じく n 行ごとにコードを挿入する -d オプションも、少々戸惑った。ヘルプには
-c, insert deceiving comments for every n line -d , insert deceiving code for every n line
としか書かれていないので、-c 5 や -d 5 と指定すれば、無意味なコメントやコードが 5行ごとにスクリプトの終わりまで挿入されることを期待してしまうが、実際には 2回しか挿入されない。しかし前述のダウンロード・ページにも表示されているマニュアルにはもう少し詳しく、
-c, insert deceiving comment for every n line. In the User Defined Variables section, you can add deceiving comments that will be added to the obfuscated script to further the obfuscation. -d , insert deceiving code for every n line. In the User Defined Variables section, you can write deceiving code that will be added to the obfuscated script to further the obfuscation. Make sure that the deceiving code does not interfere with the real code. This option should be used with caution.
と書かれている。何だよ、自分で定義するのかよ (=自動生成ではない)、と言うことで、これらのオプションを活用するには、コメント用配列 deceiving_comment とコード用配列 deceiving_code を、適当に増やしておく必要がある。当然のことながら、deceiving_code に追加する欺瞞用コードは、変換するスクリプト本来の動作に影響を与えない人畜無害なものでなければいけないので、要注意。
ランダムなスペースを挿入する -e オプションは、例えば -e 0-10 とすると、各行頭に 0~10文字のスペースが入る。これは元のスペースやタブ、空白行、コメント等を削除する -i オプションと併用すると、見た目的には具合がいい (=難解度が高まる) が、僕がいくつかの自作スクリプトで試した限りでは、変換後に動かなくなるケースが多かった。
さてこの obfsh は、確かに難読化はできる。しかし変換後のコードを見ればわかるように、基本的にはスペースや空白行、コメント等を削除したり、余計な文字列を挿入したりするだけだ。個人的には関数や変数名 (だけ) を適当な文字列に置換したり、(某 JavaScript ライブラリのように) 改行を削除して 1行につなげたり、と言うイヤらしい動作を期待していたので、少々物足りなさを感じてしまう。より陰湿な難読化のためには、関数&変数のリネームは必須だと思うが、別ファイル (スクリプト) で定義されている関数&変数を該当スクリプト独自の関数&変数と区別できないと使い物にならない。さらに一部が重複する関数&変数名は、より条件が厳しい (=文字列が長い) ものから優先的に置換しなければいけないので、難しいのは間違いない。
obfsh の話が長くなったので、shc は別のエントリーで改めて。