Skip to content

Commit 1971d59

Browse files
committed
Initial import
0 parents  commit 1971d59

File tree

6 files changed

+486
-0
lines changed

6 files changed

+486
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
build/
2+
releases/

Makefile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#
2+
# Makefile
3+
# Andres J. Diaz, 2016-10-18 08:57
4+
#
5+
6+
all:
7+
@./scripts/build.sh && rm -f ./releases/bash
8+
9+
clean:
10+
rm -rf build/* releases/*
11+
# vim:ft=make
12+
#

README

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄
2+
▐░░░░░░░░░░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░░░░░░░░░░░▌
3+
▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀▀▀ ▐░▌ ▐░▌▐░█▀▀▀▀▀▀▀▀▀
4+
▐░▌ ▐░▌▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌▐░▌
5+
▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄█░▌▐░▌
6+
▐░░░░░░░░░░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌
7+
▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀█░▌ ▀▀▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀█░▌▐░▌
8+
▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌▐░▌
9+
▐░█▄▄▄▄▄▄▄█░▌▐░▌ ▐░▌ ▄▄▄▄▄▄▄▄▄█░▌▐░▌ ▐░▌▐░█▄▄▄▄▄▄▄▄▄
10+
▐░░░░░░░░░░▌ ▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░░░░░░░░░░░▌
11+
▀▀▀▀▀▀▀▀▀▀ ▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀
12+
13+
14+
BashC is a tool which get your bash script and produces an static linked
15+
binary for Linux x86 and x86_64 machines (also support ARM and many other
16+
OS, but I cannot test them yet) which runs your script.
17+
18+
For example:
19+
20+
$ cat > myscript.sh << EOF
21+
#!/bin/bash
22+
echo "This is a test
23+
EOF
24+
25+
$ bashc myscript.sh myscript.bin
26+
$ file myscript.bin
27+
bashc: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically
28+
linked, stripped
29+
$ ./myscript.bin
30+
This is a test
31+
32+
Nice eh!
33+
34+
35+
---- Installation --------------------------------------------------------
36+
37+
From source code just run:
38+
39+
make
40+
41+
Easy :)
42+
43+
---- Internals ----------------------------------------------------------
44+
45+
This application is based on previous work from Robert Xu¹, which uses musl
46+
instead of glibc avoiding calls to dlopen(3) to produce a static binary.
47+
48+
I added some patches (in patch/ directory) to convert the bash binary in
49+
a compiler tool. The patch does the following:
50+
51+
1. Read he filesize of itself and jump (lseek) to the last byte of the
52+
binary.
53+
2. There read -20 chars as decimal number which represents the length of the
54+
script to be executed
55+
3. Jump again (lseek) to END - length readed in (2)
56+
4. Bash interpret the code in current fd position.
57+
58+
That is how bashc runs a script. To create the binary just concatenate to the
59+
static patched bash, the script to run. Because of ELF header (and in theory
60+
also Match) ensure us that execution will never reads after lenght scecified
61+
in ELF header, then our script still safe after that position. During the
62+
execution the algorithm explained above is running and the script is
63+
interpreted.
64+
65+
66+
¹ https://github.com/robxu9/bash-static
67+
68+
---- Limitations --------------------------------------------------------
69+
70+
* Not tested in any other platform than Linux X86 and X86_64.
71+
* The bash script code is concatenated in plain text, so no security here.
72+
* We cannot use any external dependency for the script unless it was
73+
previously compiled as builtin.
74+

patch/hack.diff

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
--- shell.orig.c 2016-06-08 19:42:37.010736398 +0200
2+
+++ shell.c 2016-06-08 21:12:42.051558743 +0200
3+
@@ -366,6 +366,7 @@
4+
#endif
5+
volatile int locally_skip_execution;
6+
volatile int arg_index, top_level_arg_index;
7+
+ char buf[21];
8+
#ifdef __OPENNT
9+
char **env;
10+
11+
@@ -456,59 +457,11 @@
12+
set_shell_name (argv[0]);
13+
shell_start_time = NOW; /* NOW now defined in general.h */
14+
15+
- /* Parse argument flags from the input line. */
16+
-
17+
- /* Find full word arguments first. */
18+
- arg_index = parse_long_options (argv, arg_index, argc);
19+
-
20+
- if (want_initial_help)
21+
- {
22+
- show_shell_usage (stdout, 1);
23+
- exit (EXECUTION_SUCCESS);
24+
- }
25+
-
26+
- if (do_version)
27+
- {
28+
- show_shell_version (1);
29+
- exit (EXECUTION_SUCCESS);
30+
- }
31+
-
32+
- /* All done with full word options; do standard shell option parsing.*/
33+
- this_command_name = shell_name; /* for error reporting */
34+
- arg_index = parse_shell_options (argv, arg_index, argc);
35+
-
36+
- /* If user supplied the "--login" (or -l) flag, then set and invert
37+
- LOGIN_SHELL. */
38+
- if (make_login_shell)
39+
- {
40+
- login_shell++;
41+
- login_shell = -login_shell;
42+
- }
43+
-
44+
set_login_shell ("login_shell", login_shell != 0);
45+
46+
- if (dump_po_strings)
47+
- dump_translatable_strings = 1;
48+
-
49+
- if (dump_translatable_strings)
50+
- read_but_dont_execute = 1;
51+
-
52+
if (running_setuid && privileged_mode == 0)
53+
disable_priv_mode ();
54+
55+
- /* Need to get the argument to a -c option processed in the
56+
- above loop. The next arg is a command to execute, and the
57+
- following args are $0...$n respectively. */
58+
- if (want_pending_command)
59+
- {
60+
- command_execution_string = argv[arg_index];
61+
- if (command_execution_string == 0)
62+
- {
63+
- report_error (_("%s: option requires an argument"), "-c");
64+
- exit (EX_BADUSAGE);
65+
- }
66+
- arg_index++;
67+
- }
68+
this_command_name = (char *)NULL;
69+
70+
cmd_init(); /* initialize the command object caches */
71+
@@ -521,7 +474,7 @@
72+
standard input is a terminal
73+
standard error is a terminal
74+
Refer to Posix.2, the description of the `sh' utility. */
75+
-
76+
+#if 0
77+
if (forced_interactive || /* -i flag */
78+
(!command_execution_string && /* No -c command and ... */
79+
wordexp_only == 0 && /* No --wordexp and ... */
80+
@@ -532,33 +485,8 @@
81+
init_interactive ();
82+
else
83+
init_noninteractive ();
84+
-
85+
- /*
86+
- * Some systems have the bad habit of starting login shells with lots of open
87+
- * file descriptors. For instance, most systems that have picked up the
88+
- * pre-4.0 Sun YP code leave a file descriptor open each time you call one
89+
- * of the getpw* functions, and it's set to be open across execs. That
90+
- * means one for login, one for xterm, one for shelltool, etc. There are
91+
- * also systems that open persistent FDs to other agents or files as part
92+
- * of process startup; these need to be set to be close-on-exec.
93+
- */
94+
- if (login_shell && interactive_shell)
95+
- {
96+
- for (i = 3; i < 20; i++)
97+
- SET_CLOSE_ON_EXEC (i);
98+
- }
99+
-
100+
- /* If we're in a strict Posix.2 mode, turn on interactive comments,
101+
- alias expansion in non-interactive shells, and other Posix.2 things. */
102+
- if (posixly_correct)
103+
- {
104+
- bind_variable ("POSIXLY_CORRECT", "y", 0);
105+
- sv_strict_posix ("POSIXLY_CORRECT");
106+
- }
107+
-
108+
- /* Now we run the shopt_alist and process the options. */
109+
- if (shopt_alist)
110+
- run_shopt_alist ();
111+
+#endif
112+
+ init_noninteractive();
113+
114+
/* From here on in, the shell must be a normal functioning shell.
115+
Variables from the environment are expected to be set, etc. */
116+
@@ -567,30 +495,6 @@
117+
set_default_lang ();
118+
set_default_locale_vars ();
119+
120+
- /*
121+
- * M-x term -> TERM=eterm EMACS=22.1 (term:0.96) (eterm)
122+
- * M-x shell -> TERM=dumb EMACS=t (no line editing)
123+
- * M-x terminal -> TERM=emacs-em7955 EMACS= (line editing)
124+
- */
125+
- if (interactive_shell)
126+
- {
127+
- char *term, *emacs;
128+
-
129+
- term = get_string_value ("TERM");
130+
- emacs = get_string_value ("EMACS");
131+
-
132+
- /* Not sure any emacs terminal emulator sets TERM=emacs any more */
133+
- no_line_editing |= term && (STREQ (term, "emacs"));
134+
- no_line_editing |= emacs && emacs[0] == 't' && emacs[1] == '\0' && STREQ (term, "dumb");
135+
-
136+
- /* running_under_emacs == 2 for `eterm' */
137+
- running_under_emacs = (emacs != 0) || (term && STREQN (term, "emacs", 5));
138+
- running_under_emacs += term && STREQN (term, "eterm", 5) && emacs && strstr (emacs, "term");
139+
-
140+
- if (running_under_emacs)
141+
- gnu_error_format = 1;
142+
- }
143+
-
144+
top_level_arg_index = arg_index;
145+
old_errexit_flag = exit_immediately_on_error;
146+
147+
@@ -648,21 +552,15 @@
148+
#endif
149+
150+
/* The startup files are run with `set -e' temporarily disabled. */
151+
- if (locally_skip_execution == 0 && running_setuid == 0)
152+
+ /*if (locally_skip_execution == 0 && running_setuid == 0)
153+
{
154+
old_errexit_flag = exit_immediately_on_error;
155+
exit_immediately_on_error = 0;
156+
157+
run_startup_files ();
158+
exit_immediately_on_error += old_errexit_flag;
159+
- }
160+
+ }*/
161+
162+
- /* If we are invoked as `sh', turn on Posix mode. */
163+
- if (act_like_sh)
164+
- {
165+
- bind_variable ("POSIXLY_CORRECT", "y", 0);
166+
- sv_strict_posix ("POSIXLY_CORRECT");
167+
- }
168+
169+
#if defined (RESTRICTED_SHELL)
170+
/* Turn on the restrictions after executing the startup files. This
171+
@@ -682,69 +580,13 @@
172+
}
173+
#endif
174+
175+
- if (command_execution_string)
176+
- {
177+
- arg_index = bind_args (argv, arg_index, argc, 0);
178+
- startup_state = 2;
179+
-
180+
- if (debugging_mode)
181+
- start_debugger ();
182+
-
183+
-#if defined (ONESHOT)
184+
- executing = 1;
185+
- run_one_command (command_execution_string);
186+
- exit_shell (last_command_exit_value);
187+
-#else /* ONESHOT */
188+
- with_input_from_string (command_execution_string, "-c");
189+
- goto read_and_execute;
190+
-#endif /* !ONESHOT */
191+
- }
192+
-
193+
/* Get possible input filename and set up default_buffered_input or
194+
default_input as appropriate. */
195+
- if (arg_index != argc && read_from_stdin == 0)
196+
- {
197+
- open_shell_script (argv[arg_index]);
198+
- arg_index++;
199+
- }
200+
- else if (interactive == 0)
201+
- /* In this mode, bash is reading a script from stdin, which is a
202+
- pipe or redirected file. */
203+
-#if defined (BUFFERED_INPUT)
204+
- default_buffered_input = fileno (stdin); /* == 0 */
205+
-#else
206+
- setbuf (default_input, (char *)NULL);
207+
-#endif /* !BUFFERED_INPUT */
208+
-
209+
+ open_shell_script (argv[0]);
210+
set_bash_input ();
211+
212+
/* Bind remaining args to $1 ... $n */
213+
- arg_index = bind_args (argv, arg_index, argc, 1);
214+
-
215+
- if (debugging_mode && locally_skip_execution == 0 && running_setuid == 0 && dollar_vars[1])
216+
- start_debugger ();
217+
-
218+
- /* Do the things that should be done only for interactive shells. */
219+
- if (interactive_shell)
220+
- {
221+
- /* Set up for checking for presence of mail. */
222+
- reset_mail_timer ();
223+
- init_mail_dates ();
224+
-
225+
-#if defined (HISTORY)
226+
- /* Initialize the interactive history stuff. */
227+
- bash_initialize_history ();
228+
- /* Don't load the history from the history file if we've already
229+
- saved some lines in this session (e.g., by putting `history -s xx'
230+
- into one of the startup files). */
231+
- if (shell_initialized == 0 && history_lines_this_session == 0)
232+
- load_history ();
233+
-#endif /* HISTORY */
234+
-
235+
- /* Initialize terminal state for interactive shells after the
236+
- .bash_profile and .bashrc are interpreted. */
237+
- get_tty_state ();
238+
- }
239+
+ arg_index = bind_args (argv, 1, argc, 1);
240+
241+
#if !defined (ONESHOT)
242+
read_and_execute:
243+
@@ -1424,22 +1266,6 @@
244+
filename = savestring (script_name);
245+
246+
fd = open (filename, O_RDONLY);
247+
- if ((fd < 0) && (errno == ENOENT) && (absolute_program (filename) == 0))
248+
- {
249+
- e = errno;
250+
- /* If it's not in the current directory, try looking through PATH
251+
- for it. */
252+
- path_filename = find_path_file (script_name);
253+
- if (path_filename)
254+
- {
255+
- free (filename);
256+
- filename = path_filename;
257+
- fd = open (filename, O_RDONLY);
258+
- }
259+
- else
260+
- errno = e;
261+
- }
262+
-
263+
if (fd < 0)
264+
{
265+
e = errno;
266+
@@ -1479,6 +1305,8 @@
267+
/* Only do this with non-tty file descriptors we can seek on. */
268+
if (fd_is_tty == 0 && (lseek (fd, 0L, 1) != -1))
269+
{
270+
+ char buf[21];
271+
+ long long siz=0;
272+
/* Check to see if the `file' in `bash file' is a binary file
273+
according to the same tests done by execute_simple_command (),
274+
and report an error and exit if it is. */
275+
@@ -1495,13 +1323,24 @@
276+
}
277+
exit (EX_NOEXEC);
278+
}
279+
- else if (sample_len > 0 && (check_binary_file (sample, sample_len)))
280+
+ /*else if (sample_len > 0 && (check_binary_file (sample, sample_len)))
281+
{
282+
internal_error (_("%s: cannot execute binary file"), filename);
283+
exit (EX_BINARY_FILE);
284+
- }
285+
+ }*/
286+
+ lseek(fd, -20L, SEEK_END);
287+
+ if (read(fd, buf, 20) != 20) {
288+
+ internal_error(_("%s: unable to read size"), filename);
289+
+ exit (EX_BADUSAGE);
290+
+ }
291+
+ siz = atoll(buf);
292+
+ if (siz == 0) {
293+
+ internal_error(_("%s: unable to get script size"), filename);
294+
+ exit (EX_BADUSAGE);
295+
+ }
296+
+ lseek(fd, -21-siz, SEEK_END);
297+
/* Now rewind the file back to the beginning. */
298+
- lseek (fd, 0L, 0);
299+
+ /*lseek (fd, 0L, 0);*/
300+
}
301+
302+
/* Open the script. But try to move the file descriptor to a randomly

scripts/bashc.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#! /bin/bash
2+
[ $# -ne 2 ] && echo "usage: $0 <source.sh> <out.bin>" && exit 2
3+
(cat "$0" "$1"; printf "#%020i" $(ls -l $1 | awk '{print $5}'); ) > "$2" &&
4+
chmod 755 "$2"

0 commit comments

Comments
 (0)