Compiling Bash Scripts with SHC

Slashdot it! Delicious Share on Facebook Tweet! Digg!
bekas007, 123RF

bekas007, 123RF

Wrapped up

The Shell Script Compiler converts scripts into binaries, which protects against accidental changes but also carries some pitfalls.

Compiling programs makes sense for several good reasons. For example, you gain speed when running them. Additionally, compilers ideally generate small, portable programs that largely run independently of the computer on which they're created. Furthermore, this method protects the inherent algorithms from unintended changes.

The first two reasons – the gain in speed and portability – are not a focus of the Shell Script Compiler SHC [1]. The programs translated by SHC still require Bash as interpreter, so no great speed is gained. However, this point should not play a major role, because shell scripts aren't used for time-critical applications anyway.

Nevertheless, translating the source into binary code protects it from the ambitions of those users who want to make something good even better – while completely wrecking it. Currently, SHC is the most popular free tool for translating (Bash) shell scripts into fully functioning programs.

The operation of SHC provides some special features based on these reasons. Compilation occurs in two steps. SHC first generates from the script a fairly extensive, highly specialized C source code, then translates it into a binary program with help from the C compiler (see Figure 1).

Figure 1: SHC works in two steps: It creates C code, then compiles it with the C compiler.

In the first step, SHC generates a file with the .x.c extension, which it then translates into a file with the .x ending using the $CC C compiler environmental variable.

Converting the script source into C code done by using an array to capture all the script content. During compilation, SHC makes stepwise access to the (encrypted) elements of the array and integrates these entries into the executable program.

Details of the array and conversion to the binary program are comprehensively described in a blog [2]. The blog also covers the topic of password security in scripts. The author also describes ways to re-decrypt the programs created by SHC later on.

Installing SHC

Installing SHC is easy in Ubuntu and its variants because there are PPAs with more or less recent versions.

With Arch Linux, you have two obstacles to overcome. The current versions are in the Arch User Repository (AUR), but with the wrong checksum in the PKGBUILD file (Listing 1).

Listing 1

Calculate Correct Checksum

source=("http://www.datsi.fi.upm.es/~frosal/sources/${pkgname}-${pkgver}.tgz")
sha256sums=(,\textbf{ef7bbf1252c9c791f711782870d00d6f19c42c08e0ee57e9a04d0e2b3d114d40}')

You can calculate the correct checksum by using the sha256sums tool and adding the corresponding query (Listing 2). The script opens an editor so that you can make the required change.

Listing 2

Add Corresponding Entry

...
( Unsupported package: Potentially dangerous ! )
==> Edit PKGBUILD ? [Y/n] ("A" to abort)
==> ------------------------------------
\textbf{Y}...

The SHC package also includes a series of test scripts (pru.sh , test.bash ) that the package manager with Arch Linux doesn't include by default, even though the original archive does. These test scripts check that SHC functions properly, so you should generally use them to check the results.

Alternatively, you can generate SHC directly from the sources and install it. After unpacking the archive, a simple make is enough; make install installs the program in /usr/local/ . The target make test no longer works in version 3.8.9, so use shc -f test.bash instead.

In Practice

For a first test, use the classic "Hello World," although echo "Hello SHC " returns an alternative text. If the first script line doesn't contain #! /bin/sh , using the

shc -f hello.sh

command returns the message shc: invalid first line in script:… . Translation works with the shebang, and the -v option adds commenting (Listing 3).

Listing 3

Test with "Hello World"

# shc -v -f hello.sh
shc shll=bash
shc [-i]=-c
shc [-x]=exec ,%s' "$@"
shc [-l]=
shc opts=
shc: cc  hello.sh.x.c -o hello.sh.x
shc: strip hello.sh.x
shc: chmod go-r hello.sh.x

The hello.sh.x.c source code thus generated is comparatively large at 9KB and largely incomprehensible at first, but it deals in large part with encrypting the script.

The executable program is not exactly small at 11KB and creates some problems on various platforms. Programs generated in Arch Linux and Ubuntu run on Arch Linux only when created with the -T option. This option actually allows observation of the program with external diagnostic tools such as Strace. Both systems run with no problem on Ubuntu.

Shell scripts have a few special properties that the compiler needs to understand and implement. This allows Bash to pass the script arguments that are position parameters inside the script. SHC can clearly understand them, even if you use set to reassign them.

The next important items that the compiler needs to check in shell scripts include return values ("exit codes") derived from internal and external commands.

With Bash, the $? variable contains this value for the last executed command in the foreground, and you read it with the echo $? prompt. SHC also supports this.

Particularly in shell scripts, return values occur in the context of linked commands. Bash provides some comfort with short-circuit tests: The double ampersand (&& ) connects two commands, where the shell executes the second one only if the first one terminates without an error (exit code 0 ).

Alternatively, you can use the double pipe (|| ). In this case, the shell executes the second command only if the first one terminates with an error (exit code not equal to 0 ). Short-circuit tests are easily combined with SHC (Listing 4).

Listing 4

Short-Circuit Test

$ true && echo "OK"   # OK
$ false && echo "OK"  # no output
$ true || echo "OK"   # no output
$ false || echo "OK"  # OK
$ true || echo "no" && echo "yes" # yes
$ true && echo "yes" || echo "no" # yes

Another, sometimes touchy, subject involves inputs and outputs. Without additional software, the shell provides very limited options; however, these options always work. If you ask for input using Zenity or its new variant YAD, SHC does not get out of step:

INPUT=$(yad --entry "input" --editable); yad --entry $INPUT

Here, the INPUT environment variable first gets its value through a YAD query. The input dialog is preset with the string input , which you can change because of the --editable option. The second call to YAD then shows the current content of the $INPUT variable.

When using external programs, most important to note is that SHC binds them into the binary program but calls them as in the script. This step assumes that the external programs are in the same path. The same goes for the calling Bash. Alternatively, SHC accepts absolute paths when running the compiled script.

Buy this article as PDF

Express-Checkout as PDF

Pages: 3

Price $0.99
(incl. VAT)

Buy Ubuntu User

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content