Skip to content

Commit

Permalink
Initial import
Browse files Browse the repository at this point in the history
  • Loading branch information
ajdiaz committed Oct 18, 2016
0 parents commit 1971d59
Show file tree
Hide file tree
Showing 6 changed files with 486 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
build/
releases/
12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#
# Makefile
# Andres J. Diaz, 2016-10-18 08:57
#

all:
@./scripts/build.sh && rm -f ./releases/bash

clean:
rm -rf build/* releases/*
# vim:ft=make
#
74 changes: 74 additions & 0 deletions README
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄
▐░░░░░░░░░░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░░░░░░░░░░░▌
▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀▀▀ ▐░▌ ▐░▌▐░█▀▀▀▀▀▀▀▀▀
▐░▌ ▐░▌▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌▐░▌
▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄█░▌▐░▌
▐░░░░░░░░░░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌
▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀█░▌ ▀▀▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀█░▌▐░▌
▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌▐░▌
▐░█▄▄▄▄▄▄▄█░▌▐░▌ ▐░▌ ▄▄▄▄▄▄▄▄▄█░▌▐░▌ ▐░▌▐░█▄▄▄▄▄▄▄▄▄
▐░░░░░░░░░░▌ ▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░░░░░░░░░░░▌
▀▀▀▀▀▀▀▀▀▀ ▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀


BashC is a tool which get your bash script and produces an static linked
binary for Linux x86 and x86_64 machines (also support ARM and many other
OS, but I cannot test them yet) which runs your script.

For example:

$ cat > myscript.sh << EOF
#!/bin/bash
echo "This is a test
EOF

$ bashc myscript.sh myscript.bin
$ file myscript.bin
bashc: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically
linked, stripped
$ ./myscript.bin
This is a test

Nice eh!


---- Installation --------------------------------------------------------

From source code just run:

make

Easy :)

---- Internals ----------------------------------------------------------

This application is based on previous work from Robert Xu¹, which uses musl
instead of glibc avoiding calls to dlopen(3) to produce a static binary.

I added some patches (in patch/ directory) to convert the bash binary in
a compiler tool. The patch does the following:

1. Read he filesize of itself and jump (lseek) to the last byte of the
binary.
2. There read -20 chars as decimal number which represents the length of the
script to be executed
3. Jump again (lseek) to END - length readed in (2)
4. Bash interpret the code in current fd position.

That is how bashc runs a script. To create the binary just concatenate to the
static patched bash, the script to run. Because of ELF header (and in theory
also Match) ensure us that execution will never reads after lenght scecified
in ELF header, then our script still safe after that position. During the
execution the algorithm explained above is running and the script is
interpreted.


¹ https://github.com/robxu9/bash-static

---- Limitations --------------------------------------------------------

* Not tested in any other platform than Linux X86 and X86_64.
* The bash script code is concatenated in plain text, so no security here.
* We cannot use any external dependency for the script unless it was
previously compiled as builtin.

302 changes: 302 additions & 0 deletions patch/hack.diff
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
--- shell.orig.c 2016-06-08 19:42:37.010736398 +0200
+++ shell.c 2016-06-08 21:12:42.051558743 +0200
@@ -366,6 +366,7 @@
#endif
volatile int locally_skip_execution;
volatile int arg_index, top_level_arg_index;
+ char buf[21];
#ifdef __OPENNT
char **env;

@@ -456,59 +457,11 @@
set_shell_name (argv[0]);
shell_start_time = NOW; /* NOW now defined in general.h */

- /* Parse argument flags from the input line. */
-
- /* Find full word arguments first. */
- arg_index = parse_long_options (argv, arg_index, argc);
-
- if (want_initial_help)
- {
- show_shell_usage (stdout, 1);
- exit (EXECUTION_SUCCESS);
- }
-
- if (do_version)
- {
- show_shell_version (1);
- exit (EXECUTION_SUCCESS);
- }
-
- /* All done with full word options; do standard shell option parsing.*/
- this_command_name = shell_name; /* for error reporting */
- arg_index = parse_shell_options (argv, arg_index, argc);
-
- /* If user supplied the "--login" (or -l) flag, then set and invert
- LOGIN_SHELL. */
- if (make_login_shell)
- {
- login_shell++;
- login_shell = -login_shell;
- }
-
set_login_shell ("login_shell", login_shell != 0);

- if (dump_po_strings)
- dump_translatable_strings = 1;
-
- if (dump_translatable_strings)
- read_but_dont_execute = 1;
-
if (running_setuid && privileged_mode == 0)
disable_priv_mode ();

- /* Need to get the argument to a -c option processed in the
- above loop. The next arg is a command to execute, and the
- following args are $0...$n respectively. */
- if (want_pending_command)
- {
- command_execution_string = argv[arg_index];
- if (command_execution_string == 0)
- {
- report_error (_("%s: option requires an argument"), "-c");
- exit (EX_BADUSAGE);
- }
- arg_index++;
- }
this_command_name = (char *)NULL;

cmd_init(); /* initialize the command object caches */
@@ -521,7 +474,7 @@
standard input is a terminal
standard error is a terminal
Refer to Posix.2, the description of the `sh' utility. */
-
+#if 0
if (forced_interactive || /* -i flag */
(!command_execution_string && /* No -c command and ... */
wordexp_only == 0 && /* No --wordexp and ... */
@@ -532,33 +485,8 @@
init_interactive ();
else
init_noninteractive ();
-
- /*
- * Some systems have the bad habit of starting login shells with lots of open
- * file descriptors. For instance, most systems that have picked up the
- * pre-4.0 Sun YP code leave a file descriptor open each time you call one
- * of the getpw* functions, and it's set to be open across execs. That
- * means one for login, one for xterm, one for shelltool, etc. There are
- * also systems that open persistent FDs to other agents or files as part
- * of process startup; these need to be set to be close-on-exec.
- */
- if (login_shell && interactive_shell)
- {
- for (i = 3; i < 20; i++)
- SET_CLOSE_ON_EXEC (i);
- }
-
- /* If we're in a strict Posix.2 mode, turn on interactive comments,
- alias expansion in non-interactive shells, and other Posix.2 things. */
- if (posixly_correct)
- {
- bind_variable ("POSIXLY_CORRECT", "y", 0);
- sv_strict_posix ("POSIXLY_CORRECT");
- }
-
- /* Now we run the shopt_alist and process the options. */
- if (shopt_alist)
- run_shopt_alist ();
+#endif
+ init_noninteractive();

/* From here on in, the shell must be a normal functioning shell.
Variables from the environment are expected to be set, etc. */
@@ -567,30 +495,6 @@
set_default_lang ();
set_default_locale_vars ();

- /*
- * M-x term -> TERM=eterm EMACS=22.1 (term:0.96) (eterm)
- * M-x shell -> TERM=dumb EMACS=t (no line editing)
- * M-x terminal -> TERM=emacs-em7955 EMACS= (line editing)
- */
- if (interactive_shell)
- {
- char *term, *emacs;
-
- term = get_string_value ("TERM");
- emacs = get_string_value ("EMACS");
-
- /* Not sure any emacs terminal emulator sets TERM=emacs any more */
- no_line_editing |= term && (STREQ (term, "emacs"));
- no_line_editing |= emacs && emacs[0] == 't' && emacs[1] == '\0' && STREQ (term, "dumb");
-
- /* running_under_emacs == 2 for `eterm' */
- running_under_emacs = (emacs != 0) || (term && STREQN (term, "emacs", 5));
- running_under_emacs += term && STREQN (term, "eterm", 5) && emacs && strstr (emacs, "term");
-
- if (running_under_emacs)
- gnu_error_format = 1;
- }
-
top_level_arg_index = arg_index;
old_errexit_flag = exit_immediately_on_error;

@@ -648,21 +552,15 @@
#endif

/* The startup files are run with `set -e' temporarily disabled. */
- if (locally_skip_execution == 0 && running_setuid == 0)
+ /*if (locally_skip_execution == 0 && running_setuid == 0)
{
old_errexit_flag = exit_immediately_on_error;
exit_immediately_on_error = 0;

run_startup_files ();
exit_immediately_on_error += old_errexit_flag;
- }
+ }*/

- /* If we are invoked as `sh', turn on Posix mode. */
- if (act_like_sh)
- {
- bind_variable ("POSIXLY_CORRECT", "y", 0);
- sv_strict_posix ("POSIXLY_CORRECT");
- }

#if defined (RESTRICTED_SHELL)
/* Turn on the restrictions after executing the startup files. This
@@ -682,69 +580,13 @@
}
#endif

- if (command_execution_string)
- {
- arg_index = bind_args (argv, arg_index, argc, 0);
- startup_state = 2;
-
- if (debugging_mode)
- start_debugger ();
-
-#if defined (ONESHOT)
- executing = 1;
- run_one_command (command_execution_string);
- exit_shell (last_command_exit_value);
-#else /* ONESHOT */
- with_input_from_string (command_execution_string, "-c");
- goto read_and_execute;
-#endif /* !ONESHOT */
- }
-
/* Get possible input filename and set up default_buffered_input or
default_input as appropriate. */
- if (arg_index != argc && read_from_stdin == 0)
- {
- open_shell_script (argv[arg_index]);
- arg_index++;
- }
- else if (interactive == 0)
- /* In this mode, bash is reading a script from stdin, which is a
- pipe or redirected file. */
-#if defined (BUFFERED_INPUT)
- default_buffered_input = fileno (stdin); /* == 0 */
-#else
- setbuf (default_input, (char *)NULL);
-#endif /* !BUFFERED_INPUT */
-
+ open_shell_script (argv[0]);
set_bash_input ();

/* Bind remaining args to $1 ... $n */
- arg_index = bind_args (argv, arg_index, argc, 1);
-
- if (debugging_mode && locally_skip_execution == 0 && running_setuid == 0 && dollar_vars[1])
- start_debugger ();
-
- /* Do the things that should be done only for interactive shells. */
- if (interactive_shell)
- {
- /* Set up for checking for presence of mail. */
- reset_mail_timer ();
- init_mail_dates ();
-
-#if defined (HISTORY)
- /* Initialize the interactive history stuff. */
- bash_initialize_history ();
- /* Don't load the history from the history file if we've already
- saved some lines in this session (e.g., by putting `history -s xx'
- into one of the startup files). */
- if (shell_initialized == 0 && history_lines_this_session == 0)
- load_history ();
-#endif /* HISTORY */
-
- /* Initialize terminal state for interactive shells after the
- .bash_profile and .bashrc are interpreted. */
- get_tty_state ();
- }
+ arg_index = bind_args (argv, 1, argc, 1);

#if !defined (ONESHOT)
read_and_execute:
@@ -1424,22 +1266,6 @@
filename = savestring (script_name);

fd = open (filename, O_RDONLY);
- if ((fd < 0) && (errno == ENOENT) && (absolute_program (filename) == 0))
- {
- e = errno;
- /* If it's not in the current directory, try looking through PATH
- for it. */
- path_filename = find_path_file (script_name);
- if (path_filename)
- {
- free (filename);
- filename = path_filename;
- fd = open (filename, O_RDONLY);
- }
- else
- errno = e;
- }
-
if (fd < 0)
{
e = errno;
@@ -1479,6 +1305,8 @@
/* Only do this with non-tty file descriptors we can seek on. */
if (fd_is_tty == 0 && (lseek (fd, 0L, 1) != -1))
{
+ char buf[21];
+ long long siz=0;
/* Check to see if the `file' in `bash file' is a binary file
according to the same tests done by execute_simple_command (),
and report an error and exit if it is. */
@@ -1495,13 +1323,24 @@
}
exit (EX_NOEXEC);
}
- else if (sample_len > 0 && (check_binary_file (sample, sample_len)))
+ /*else if (sample_len > 0 && (check_binary_file (sample, sample_len)))
{
internal_error (_("%s: cannot execute binary file"), filename);
exit (EX_BINARY_FILE);
- }
+ }*/
+ lseek(fd, -20L, SEEK_END);
+ if (read(fd, buf, 20) != 20) {
+ internal_error(_("%s: unable to read size"), filename);
+ exit (EX_BADUSAGE);
+ }
+ siz = atoll(buf);
+ if (siz == 0) {
+ internal_error(_("%s: unable to get script size"), filename);
+ exit (EX_BADUSAGE);
+ }
+ lseek(fd, -21-siz, SEEK_END);
/* Now rewind the file back to the beginning. */
- lseek (fd, 0L, 0);
+ /*lseek (fd, 0L, 0);*/
}

/* Open the script. But try to move the file descriptor to a randomly
4 changes: 4 additions & 0 deletions scripts/bashc.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#! /bin/bash
[ $# -ne 2 ] && echo "usage: $0 <source.sh> <out.bin>" && exit 2
(cat "$0" "$1"; printf "#%020i" $(ls -l $1 | awk '{print $5}'); ) > "$2" &&
chmod 755 "$2"
Loading

0 comments on commit 1971d59

Please sign in to comment.