shc でシェル・スクリプトをコンパイル

ファイル名 説明
example シェル・スクリプト
example.x.c C言語に変換されたシェル・スクリプト
example.x 実行形式(バイナリ)にコンパイルされたシェル・スクリプト

example(シェル・スクリプト)

スクリプト・ファイルを作成する。

#!/usr/bin/bash

function calc() {

    local -i result

    result=$(( ${1} * ${2} ))
    echo ${result}

    unset result

}

echo $(calc 256 256)
exit 0

このサンプルは、単純に 256 × 256 の結果を求めるシェル・スクリプトだ。

コンパイル

次のコマンドを実行し、シェル・スクリプトをコンパイルする。

shc -r -v -f 'スクリプト・ファイル'
  • -r セキュリティを緩和する。再配布可能なバイナリを作成する。
  • -v 冗長コンパイル。
  • -f コンパイルするスクリプトのファイル名。

example.x.c(C言語に変換されたシェル・スクリプト)

#if 0
	shc Version 3.8.9b, Generic Script Compiler
	Copyright (c) 1994-2015 Francisco Rosales <frosal@fi.upm.es>

	shc -r -v -f example 
#endif

static  char data [] = 
#define      chk2_z	19
#define      chk2	((&data[2]))
	"\157\317\107\167\342\026\000\072\013\067\201\252\341\207\336\207"
	"\332\035\241\022\106\173\074\323\056"
#define      opts_z	1
#define      opts	((&data[25]))
	"\160"
#define      tst2_z	19
#define      tst2	((&data[30]))
	"\152\014\053\307\031\131\300\377\254\207\232\037\137\270\116\042"
	"\146\141\254\240\001\141\116"
#define      xecc_z	15
#define      xecc	((&data[50]))
	"\140\244\042\241\333\032\330\021\136\271\174\115\237\254\154\367"
#define      date_z	1
#define      date	((&data[65]))
	"\034"
#define      pswd_z	256
#define      pswd	((&data[81]))
	"\066\234\104\304\206\264\224\001\360\147\057\275\163\001\246\140"
	"\266\074\100\211\101\252\012\162\205\240\116\205\124\143\170\324"
	"\100\206\116\200\125\114\123\174\215\302\115\271\237\106\031\125"
	"\202\132\336\304\005\350\067\212\211\205\020\336\351\210\262\051"
	"\017\000\252\145\115\375\341\332\300\057\223\137\165\255\265\370"
	"\007\224\274\015\175\364\227\006\171\247\344\143\060\227\214\077"
	"\230\066\244\345\064\206\300\364\265\123\124\053\001\011\043\010"
	"\236\340\026\033\324\255\042\116\125\007\262\205\237\076\304\067"
	"\165\151\035\251\357\336\235\245\062\361\320\063\373\364\074\231"
	"\324\122\265\251\377\330\370\125\337\252\332\176\351\237\266\136"
	"\010\324\010\367\262\245\234\344\227\155\027\223\141\123\054\066"
	"\246\342\340\245\272\331\373\232\204\325\030\155\164\317\314\174"
	"\243\324\164\125\172\021\071\022\177\121\245\340\244\321\027\113"
	"\264\367\361\156\320\354\010\124\301\041\302\066\360\216\263\224"
	"\142\047\351\334\071\043\356\270\164\224\231\031\146\260\144\032"
	"\247\125\210\170\101\221\315\003\262\217\071\243\035\354\067\200"
	"\024\041\135\115\105\114\005\271\340\236\323\106\117\070\140\154"
	"\013\021\170\066\331\323\247\355\063\322\343\062\320\036\200\007"
	"\273\305"
#define      text_z	158
#define      text	((&data[366]))
	"\137\102\152\306\162\047\072\163\316\246\215\172\111\160\367\333"
	"\176\334\001\355\065\055\111\006\366\141\011\205\220\240\071\273"
	"\367\316\263\160\144\256\340\053\133\315\361\221\035\305\261\160"
	"\161\225\366\023\332\203\330\164\273\314\054\316\016\222\275\333"
	"\367\227\226\214\216\221\136\243\220\212\167\142\141\143\331\047"
	"\206\365\300\033\150\214\331\160\346\136\122\353\325\070\201\100"
	"\236\275\244\210\143\267\135\364\050\262\154\135\106\066\135\254"
	"\110\243\315\365\117\244\147\243\134\054\305\237\215\343\004\355"
	"\135\076\121\006\354\310\050\343\037\147\030\024\377\214\246\307"
	"\164\166\061\144\343\151\322\142\314\312\147\372\357\301\112\061"
	"\041\110\334\133\112\135\302\047\177\340\037\265\271\362\135\247"
	"\045\057\212\130\000\251\331\007\144\236\323"
#define      msg2_z	19
#define      msg2	((&data[543]))
	"\353\117\155\331\337\122\143\367\100\113\227\061\141\002\265\267"
	"\245\037\215"
#define      msg1_z	42
#define      msg1	((&data[567]))
	"\133\253\064\317\172\252\371\045\267\131\102\154\151\141\052\214"
	"\205\116\325\343\017\132\321\341\020\313\154\026\137\042\114\152"
	"\333\057\336\361\346\057\213\027\024\202\265\366\134\305\077\332"
	"\116\132\372\004\024\354\141\273\022"
#define      chk1_z	22
#define      chk1	((&data[620]))
	"\222\052\132\017\375\233\225\213\304\373\141\155\003\074\107\264"
	"\364\061\114\141\217\272\107\357\104"
#define      rlax_z	1
#define      rlax	((&data[644]))
	"\124"
#define      lsto_z	1
#define      lsto	((&data[645]))
	"\207"
#define      shll_z	14
#define      shll	((&data[649]))
	"\231\076\052\327\161\226\154\071\040\273\135\154\176\134\217\222"
	"\252\315"
#define      inlo_z	3
#define      inlo	((&data[664]))
	"\013\044\333"
#define      tst1_z	22
#define      tst1	((&data[672]))
	"\137\023\217\301\316\037\150\250\101\177\211\051\301\164\013\304"
	"\152\365\032\217\072\341\001\036\235\324\346\242\122\025"/* End of data[] */;
#define      hide_z	4096
#define DEBUGEXEC	0	/* Define as 1 to debug execvp calls */
#define TRACEABLE	0	/* Define as 1 to enable ptrace the executable */

/* rtc.c */

#include <sys/stat.h>
#include <sys/types.h>

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

/* 'Alleged RC4' */

static unsigned char stte[256], indx, jndx, kndx;

/*
 * Reset arc4 stte. 
 */
void stte_0(void)
{
	indx = jndx = kndx = 0;
	do {
		stte[indx] = indx;
	} while (++indx);
}

/*
 * Set key. Can be used more than once. 
 */
void key(void * str, int len)
{
	unsigned char tmp, * ptr = (unsigned char *)str;
	while (len > 0) {
		do {
			tmp = stte[indx];
			kndx += tmp;
			kndx += ptr[(int)indx % len];
			stte[indx] = stte[kndx];
			stte[kndx] = tmp;
		} while (++indx);
		ptr += 256;
		len -= 256;
	}
}

/*
 * Crypt data. 
 */
void arc4(void * str, int len)
{
	unsigned char tmp, * ptr = (unsigned char *)str;
	while (len > 0) {
		indx++;
		tmp = stte[indx];
		jndx += tmp;
		stte[indx] = stte[jndx];
		stte[jndx] = tmp;
		tmp += stte[indx];
		*ptr ^= stte[tmp];
		ptr++;
		len--;
	}
}

/* End of ARC4 */

/*
 * Key with file invariants. 
 */
int key_with_file(char * file)
{
	struct stat statf[1];
	struct stat control[1];

	if (stat(file, statf) < 0)
		return -1;

	/* Turn on stable fields */
	memset(control, 0, sizeof(control));
	control->st_ino = statf->st_ino;
	control->st_dev = statf->st_dev;
	control->st_rdev = statf->st_rdev;
	control->st_uid = statf->st_uid;
	control->st_gid = statf->st_gid;
	control->st_size = statf->st_size;
	control->st_mtime = statf->st_mtime;
	control->st_ctime = statf->st_ctime;
	key(control, sizeof(control));
	return 0;
}

#if DEBUGEXEC
void debugexec(char * sh11, int argc, char ** argv)
{
	int i;
	fprintf(stderr, "shll=%s\n", sh11 ? sh11 : "");
	fprintf(stderr, "argc=%d\n", argc);
	if (!argv) {
		fprintf(stderr, "argv=\n");
	} else { 
		for (i = 0; i <= argc ; i++)
			fprintf(stderr, "argv[%d]=%.60s\n", i, argv[i] ? argv[i] : "");
	}
}
#endif /* DEBUGEXEC */

void rmarg(char ** argv, char * arg)
{
	for (; argv && *argv && *argv != arg; argv++);
	for (; argv && *argv; argv++)
		*argv = argv[1];
}

int chkenv(int argc)
{
	char buff[512];
	unsigned long mask, m;
	int l, a, c;
	char * string;
	extern char ** environ;

	mask  = (unsigned long)&chkenv;
	mask ^= (unsigned long)getpid() * ~mask;
	sprintf(buff, "x%lx", mask);
	string = getenv(buff);
#if DEBUGEXEC
	fprintf(stderr, "getenv(%s)=%s\n", buff, string ? string : "");
#endif
	l = strlen(buff);
	if (!string) {
		/* 1st */
		sprintf(&buff[l], "=%lu %d", mask, argc);
		putenv(strdup(buff));
		return 0;
	}
	c = sscanf(string, "%lu %d%c", &m, &a, buff);
	if (c == 2 && m == mask) {
		/* 3rd */
		rmarg(environ, &string[-l - 1]);
		return 1 + (argc - a);
	}
	return -1;
}

#if !TRACEABLE

#define _LINUX_SOURCE_COMPAT
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

#if !defined(PTRACE_ATTACH) && defined(PT_ATTACH)
#	define PTRACE_ATTACH	PT_ATTACH
#endif
void untraceable(char * argv0)
{
	char proc[80];
	int pid, mine;

	switch(pid = fork()) {
	case  0:
		pid = getppid();
		/* For problematic SunOS ptrace */
#if defined(__FreeBSD__)
		sprintf(proc, "/proc/%d/mem", (int)pid);
#else
		sprintf(proc, "/proc/%d/as",  (int)pid);
#endif
		close(0);
		mine = !open(proc, O_RDWR|O_EXCL);
		if (!mine && errno != EBUSY)
			mine = !ptrace(PTRACE_ATTACH, pid, 0, 0);
		if (mine) {
			kill(pid, SIGCONT);
		} else {
			perror(argv0);
			kill(pid, SIGKILL);
		}
		_exit(mine);
	case -1:
		break;
	default:
		if (pid == waitpid(pid, 0, 0))
			return;
	}
	perror(argv0);
	_exit(1);
}
#endif /* !TRACEABLE */

char * xsh(int argc, char ** argv)
{
	char * scrpt;
	int ret, i, j;
	char ** varg;
	char * me = argv[0];

	stte_0();
	 key(pswd, pswd_z);
	arc4(msg1, msg1_z);
	arc4(date, date_z);
	if (date[0] && (atoll(date)<time(NULL)))
		return msg1;
	arc4(shll, shll_z);
	arc4(inlo, inlo_z);
	arc4(xecc, xecc_z);
	arc4(lsto, lsto_z);
	arc4(tst1, tst1_z);
	 key(tst1, tst1_z);
	arc4(chk1, chk1_z);
	if ((chk1_z != tst1_z) || memcmp(tst1, chk1, tst1_z))
		return tst1;
	ret = chkenv(argc);
	arc4(msg2, msg2_z);
	if (ret < 0)
		return msg2;
	varg = (char **)calloc(argc + 10, sizeof(char *));
	if (!varg)
		return 0;
	if (ret) {
		arc4(rlax, rlax_z);
		if (!rlax[0] && key_with_file(shll))
			return shll;
		arc4(opts, opts_z);
		arc4(text, text_z);
		arc4(tst2, tst2_z);
		 key(tst2, tst2_z);
		arc4(chk2, chk2_z);
		if ((chk2_z != tst2_z) || memcmp(tst2, chk2, tst2_z))
			return tst2;
		/* Prepend hide_z spaces to script text to hide it. */
		scrpt = malloc(hide_z + text_z);
		if (!scrpt)
			return 0;
		memset(scrpt, (int) ' ', hide_z);
		memcpy(&scrpt[hide_z], text, text_z);
	} else {			/* Reexecute */
		if (*xecc) {
			scrpt = malloc(512);
			if (!scrpt)
				return 0;
			sprintf(scrpt, xecc, me);
		} else {
			scrpt = me;
		}
	}
	j = 0;
	varg[j++] = argv[0];		/* My own name at execution */
	if (ret && *opts)
		varg[j++] = opts;	/* Options on 1st line of code */
	if (*inlo)
		varg[j++] = inlo;	/* Option introducing inline code */
	varg[j++] = scrpt;		/* The script itself */
	if (*lsto)
		varg[j++] = lsto;	/* Option meaning last option */
	i = (ret > 1) ? ret : 0;	/* Args numbering correction */
	while (i < argc)
		varg[j++] = argv[i++];	/* Main run-time arguments */
	varg[j] = 0;			/* NULL terminated array */
#if DEBUGEXEC
	debugexec(shll, j, varg);
#endif
	execvp(shll, varg);
	return shll;
}

int main(int argc, char ** argv)
{
#if DEBUGEXEC
	debugexec("main", argc, argv);
#endif
#if !TRACEABLE
	untraceable(argv[0]);
#endif
	argv[1] = xsh(argc, argv);
	fprintf(stderr, "%s%s%s: %s\n", argv[0],
		errno ? ": " : "",
		errno ? strerror(errno) : "",
		argv[1] ? argv[1] : ""
	);
	return 1;
}

実行結果の確認

スクリプトを実行し、結果を確認する。


./example


./example.x


CentOS 7 shc コンパイラ - 1

shc 機能説明(和訳)

コンパイルされたバイナリはシェル・コードの最初の行(#!/bin/sh)で指定されたシェルに依然依存するため、shc は完全に独立したバイナリを作成しません。

shc 自体は cc のようなコンパイラではなく、シェル・スクリプトをエンコードして暗号化し、追加された有効期限機能を持つCソースコードを生成します。
次に、コンパイラを使用して、元のスクリプトとまったく同じように動作する、バイナリを生成します。
実行時に、コンパイルされたバイナリはシェル -c オプションでコードを解読して実行します。
残念ながら、本当のCプログラムのようなスピードの改善をもたらしません。

shc の主な目的は、変更または検査からシェルスクリプトを保護することです。
スクリプトを配布したいが、他の人がスクリプトを見やすくしたくない場合に使用することができます。

shc の説明にもあるように実行速度は最適化されないが、パスワード等を含むシェル・スクリプトの難読化には有効だ。