前書き:Shell Scriptの中身を見せたくない

通常の開発業務では、Shell Scriptをバイナリ化(かつ暗号化)する利点はありません。自分しか解読できないShell Scriptは、他のチームメンバにとっては迷惑な存在(負債)になります。

が、Shell Scriptのバイナリ化に全く利点が無い訳ではありません。Shell Scriptのリリース後に発生する面倒事を避けるために、バイナリ化を検討する事もあります。

Shell Scriptをバイナリ化する利点

  • 機密保持(顧客にShell Scriptを解読をさせない)
  • Shell Scriptの改変防止

本記事では、shc(Shell Script Compiler)を用いてShell Scriptをバイナリ化する方法を紹介します。

検証環境

Ubuntu 21.04、Bash 5.1.4、shc 4.0.3を使用します。

           ./oydmMMMMMMmdyo/.              nao@nao 
        :smMMMMMMMMMMMhs+:++yhs:           ------- 
     `omMMMMMMMMMMMN+`        `odo`        OS: Ubuntu Budgie 21.04 x86_64 
    /NMMMMMMMMMMMMN- `sN/       Host: B450 I AORUS PRO WIFI 
  `hMMMMmhhmMMMMMMh               sMh`     Kernel: 5.11.0-31-generic 
 .mMmo- /yMMMMm`              `MMm.    Uptime: 12 hours, 45 mins 
 mN/       yMMMMMMMd- MMMm    Packages: 2830 (dpkg), 11 (snap) 
oN- oMMMMMMMMMms+//+o+:    :MMMMo   Shell: bash 5.1.4 
m/          +NMMMMMMMMMMMMMMMMm. :NMMMMm   Resolution: 2560x1080 
M`           .NMMMMMMMMMMMMMMMNodMMMMMMM   DE: Budgie 10.5.2 
M- sMMMMMMMMMMMMMMMMMMMMMMMMM   WM: Mutter(Budgie) 
mm`           mMMMMMMMMMNdhhdNMMMMMMMMMm   Theme: Yaru-dark [GTK2/3] 
oMm/        .dMMMMMMMMh:      :dMMMMMMMo   Icons: ubuntu-mono-dark [GTK2/3] 
 mMMNyo/:/sdMMMMMMMMM+          sMMMMMm    Terminal: tilix 
 .mMMMMMMMMMMMMMMMMMs           `NMMMm.    CPU: AMD Ryzen 5 3400G (8) @ 3.700GHz 
  `hMMMMMMMMMMM.oo+.            `MMMh`     GPU: AMD ATI 09:00.0 Picasso 
    /NMMMMMMMMMo                sMN/       Memory: 10025MiB / 30099MiB 
     `omMMMMMMMMy.            :dmo`
        :smMMMMMMMh+-`   `.:ohs:                                   
           ./oydmMMMMMMdhyo/.                                      

shcコマンドの概要

shcコマンドは、C言語で実装されたシェルスクリプトコンパイラです(GPL v3)。ARC4で暗号化したShell ScriptをC言語にソースコード中に埋め込み、そのコードをコンパイルする事によってバイナリを生成しています。生成したバイナリの有効期限を設定する事もできます。

以下、「バイナリ化の流れ」と「バイナリ化されたShell Scriptを実行する流れ」です。

  1. “shc -f $SCRIPT"で指定された$SCRIPTを暗号化
  2. $SCRIPT.x.cという名称のC言語コード(暗号化された$SCRIPTをコード中に含む)を生成
  3. $SCRIPT.x.cをコンパイルし、$SCRIPT.xを生成
  4. $SCRIPT.xの実行時、暗号化された$SCRIPTを復号
  5. 手順4.の完了後、“sh -c $復号した文字列"を実行

上記の2.と5.の通り、途中でC言語ソースコードに変換されていますが、生成されるのはshellに依存したバイナリです(Shellの-cオプションは、指定した文字列を実行するオプション)

shcコマンドのインストール

Ubuntuの場合は、aptパッケージマネージャーでインストールできます。

$ sudo apt install shc

最新版をビルドする場合は、以下の手順です。

$ sudo apt install git build-essential   (注釈) ビルドに必要なツールのインストール

$ git clone https://github.com/neurobin/shc.git
$ cd shc
$ ./configure               (注釈) システム環境に合わせた設定
$ make                      (注釈) ビルド
$ sudo make install         (注釈) インストール

shcコマンドのオプション

オプションオプション引数説明
-eあり失効日をdd/mm/yyyy形式で指定 (デフォルト:無効)
-mあり失効した場合のメッセージ (デフォルト:“Please contact your provider”)
-fありコンパイル(暗号化)対象のスクリプト名
-iありShellインタプリタへのインラインオプション(例:-e)
-xありprintfフォーマットでコマンドを実行(例:exec(’%s’,@ARGV);)
-lありLast shellオプション(例:–)
-oあり出力ファイル名 (デフォルト:元ファイル名の末尾に”.x”を付与した名前)
-rなしセキュリティを緩和する。再配布可能なバイナリを作る
-vなしコンパイル時の出力を増やす
-Sなしrootユーザが実行するプログラムとしてuidをセットする
-Dなしデバッグ実行を有効化
-Uなしuntraceable(トレース不可な)バイナリを作成
-Hなし追加のセキュリティ保護(まだ未完成のオプション)
-Cなしライセンスを表示して終了
-Aなしshcコマンドの概要を表示して終了
-BなしBusybox向けにコンパイル
-Hなしヘルプメッセージを表示して終了

shcコマンドによるバイナリ生成の例

今回の例では、ユーザが管理者権限を持つかどうかを表示するスクリプト(shell.sh)をバイナリ化します。shell.shの内容は、以下の通りです。

#!/bin/bash

function errMsg() {
    local message="$1"
    echo -n -e "\033[31m\c"  # 文字色を赤に変更                      
    echo "${message}" >&2
    echo -n -e "\033[m\c"   # 文字色を元に戻す                     
}

# isRoot()は、root権限を持つかどうかを確認する。
function isRoot() {
    if [ ${EUID:-${UID}} != 0 ]; then
        errMsg "${FUNCNAME[0]}: You are not root."
        exit 1
    fi
    echo "You are root."
}

isRoot

shcコマンドでshell.shをバイナリ化します。-oでバイナリの名前を指定できますが、今回は指定せずにデフォルトの挙動を確認します。

$ ls
shell.sh

(注釈) バイナリ化
$ shc -f shell.sh 

(注釈) *.xはバイナリ、*.cはバイナリ作成時に生成したC言語ソースコード
$ ls
shell.sh  shell.sh.x  shell.sh.x.c

オリジナルスクリプトとバイナリの実行結果を比較します。同じ結果です。

$ ./shell.sh
isRoot: You are not root.
$ ./shell.sh.x 
isRoot: You are not root.

$ sudo ./shell.sh
You are root.
$ sudo ./shell.sh.x 
You are root.