Skip to content

Commit

Permalink
add -M flag
Browse files Browse the repository at this point in the history
  • Loading branch information
liberize committed Oct 6, 2024
1 parent eb28127 commit ee35ace
Show file tree
Hide file tree
Showing 10 changed files with 365 additions and 10 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ docker run --rm -v $PWD:/workspace liberize/ssc:latest -s test.sh test
* g++, perl, binutils
* libc-dev, libstdc++-dev (only required by -s flag)
* libz-dev (only required by -E flag)
* libz-dev, libfuse-dev, git, gcc, make, automake, autoconf, pkg-config, libtool, squashfs-tools (only required by -M flag)

</p>
</details>
Expand All @@ -36,6 +37,7 @@ docker run --rm -v $PWD:/workspace liberize/ssc:latest -s test.sh test
* g++, perl, binutils
* glibc-static, libstdc++-static (only required by -s flag)
* zlib-devel (only required by -E flag)
* zlib-devel, fuse-devel, git, gcc, make, automake, autoconf, pkgconfig, libtool, squashfs-tools (only required by -M flag)

</p>
</details>
Expand Down Expand Up @@ -86,7 +88,7 @@ docker run --rm -v $PWD:/workspace liberize/ssc:latest -s test.sh test
More options

```
Usage: ./ssc [-4] [-u] [-s] [-r] [-e|-E file] [-0] <script> <binary>
Usage: ./ssc [-4] [-u] [-s] [-r] [-e|-E|-M file] [-0] [-d date] [-m msg] <script> <binary>
-u, --untraceable make untraceable binary
enable debugger detection, abort program when debugger is found
-s, --static make static binary
Expand All @@ -99,6 +101,8 @@ Usage: ./ssc [-4] [-u] [-s] [-r] [-e|-E file] [-0] <script> <binary>
the interpreter will be used no matter what shebang is
-E, --embed-archive embed specified tar.gz archive into binary, require zlib
set relative path in shebang to use an interpreter in the archive
-M, --mount-squashfs append specified gzipped squashfs to binary and mount it at runtime
linux only, works like AppImage. may specify a directory
-0, --fix-argv0 try to fix $0, may not work
if it doesn't work or causes problems, use $SSC_ARGV0 instead
-d, --expire-date expire date, for example, 11/30/2023
Expand Down Expand Up @@ -130,6 +134,7 @@ Usage: ./ssc [-4] [-u] [-s] [-r] [-e|-E file] [-0] <script> <binary>
3. [Call a bundled interpreter.](https://github.com/liberize/ssc/tree/master/examples/3_bundle_interpreter)
4. [Embed an interpreter into the binary.](https://github.com/liberize/ssc/tree/master/examples/4_embed_interpreter)
5. [Embed an archive into the binary.](https://github.com/liberize/ssc/tree/master/examples/5_embed_archive)
6. [Embed a squashfs file and mount it at runtime.](https://github.com/liberize/ssc/tree/master/examples/6_mount_squashfs)

## Builtin variables

Expand All @@ -139,6 +144,7 @@ The following builtin variables are available to the script (including shebang):
* `SSC_EXECUTABLE_PATH`: current executable path
* `SSC_ARGV0`: first command line argument (i.e. $0)
* `SSC_EXTRACT_DIR`: temporary extraction directory for embeded file, if -e or -E flag is used
* `SSC_MOUNT_DIR`: temporary mount directory for squashfs, if -M flag is used

## Interpreter selection

Expand All @@ -154,6 +160,8 @@ If the binary is generated with `-e`, the interpreter is built into the binary.

If the binary is generated with `-E`, the archive is built into the binary. Upon execution, the archive will be decompressed and extracted to /tmp/ssc/XXXXXX/ with permissions perserved. If the script has a relative-path shebang, the interpreter of the path relative to the extraction directory will be used, otherwise, a system intepreter will be used.

If the binary is generated with `-M`, the squashfs file is appended to the binary. Upon execution, the squashfs file will be mounted to /tmp/ssc/XXXXXX/. If the script has a relative-path shebang, the interpreter of the path relative to the mount directory will be used, otherwise, a system intepreter will be used.

## Cross compiling

Set `CROSS_COMPILE` variable just like using Makefile.
Expand Down
4 changes: 2 additions & 2 deletions examples/5_embed_archive/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ We can even embed an archive into our binary, and make a self-extracting depende

To embed an archive, use `-E` flag. The archive may or may not contain an interpreter.

The interpreter will be extracted to /tmp/ssc/XXXXXX, and be deleted after script execution. You may delete it like this at beginning of your script to avoid exposure of the interpreter:
The archive will be extracted to /tmp/ssc/XXXXXX, and be deleted after script execution. You may delete it like this at beginning of your script to avoid exposure of the interpreter:

```bash
rm -rf "$SSC_EXTRACT_DIR"
Expand All @@ -25,7 +25,7 @@ rm -rf python/lib/{*tcl*,thread*,Tix*,tk*}
tar -zcvf cpython.tar.gz python
rm -rf python

# use -s flag to make our binary static too
# use -s flag to make our binary static
../../ssc ./test_python.sh binary -s -E cpython.tar.gz

# test it
Expand Down
27 changes: 27 additions & 0 deletions examples/6_mount_squashfs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Embed a squashfs file and mount it at runtime

Another way to pack some data into the binary and make a dependency-free executable. Same method is used by AppImage. Currently only availble on Linux.

To embed a squashfs file, use `-M` flag.

The squashfs file is directly appended to the binary, upon execution it will be mounted to /tmp/ssc/XXXXXX. This method doesn't extract files, so it should be faster than `-E` flag.

## Example: python

We can embed a whole python package to our binary.

```bash
# download standalone python
wget https://github.com/indygreg/python-build-standalone/releases/download/20240814/cpython-3.10.14+20240814-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz -O cpython.tar.gz

# delete redundant files in the archive to make extraction faster
tar -zxvf cpython.tar.gz
rm -rf python/include python/share python/lib/pkgconfig python/bin/{2to3*,idle*,pip*,pydoc*,*-config}
rm -rf python/lib/{*tcl*,thread*,Tix*,tk*}

# use -s flag to make our binary static
../../ssc ./test_python.sh binary -s -M python

# test it
./binary
```
9 changes: 9 additions & 0 deletions examples/6_mount_squashfs/test_python.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#! PYTHONHOME=$SSC_MOUNT_DIR ./bin/python3
# use environment variables just like in shell

import os
import sys
import shutil

print(os.environ)
print(sys.argv)
108 changes: 108 additions & 0 deletions src/elf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
*
* Linux kernel
* Copyright (C) 2017 Linus Torvalds
* Modified work Copyright (C) 2017 @teras (https://github.com/teras)
* (Shortened version -- original work found here:
* https://github.com/torvalds/linux/blob/master/include/uapi/linux/elf.h)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/

#ifndef LIGHT_ELF_H
#define LIGHT_ELF_H

#include <inttypes.h>

typedef uint16_t Elf32_Half;
typedef uint16_t Elf64_Half;
typedef uint32_t Elf32_Word;
typedef uint32_t Elf64_Word;
typedef uint64_t Elf64_Xword;
typedef uint32_t Elf32_Addr;
typedef uint64_t Elf64_Addr;
typedef uint32_t Elf32_Off;
typedef uint64_t Elf64_Off;

#define EI_NIDENT 16

typedef struct elf32_hdr {
unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type;
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry; /* Entry point */
Elf32_Off e_phoff;
Elf32_Off e_shoff;
Elf32_Word e_flags;
Elf32_Half e_ehsize;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
} Elf32_Ehdr;

typedef struct elf64_hdr {
unsigned char e_ident[EI_NIDENT]; /* ELF "magic number" */
Elf64_Half e_type;
Elf64_Half e_machine;
Elf64_Word e_version;
Elf64_Addr e_entry; /* Entry point virtual address */
Elf64_Off e_phoff; /* Program header table file offset */
Elf64_Off e_shoff; /* Section header table file offset */
Elf64_Word e_flags;
Elf64_Half e_ehsize;
Elf64_Half e_phentsize;
Elf64_Half e_phnum;
Elf64_Half e_shentsize;
Elf64_Half e_shnum;
Elf64_Half e_shstrndx;
} Elf64_Ehdr;

typedef struct elf32_shdr {
Elf32_Word sh_name;
Elf32_Word sh_type;
Elf32_Word sh_flags;
Elf32_Addr sh_addr;
Elf32_Off sh_offset;
Elf32_Word sh_size;
Elf32_Word sh_link;
Elf32_Word sh_info;
Elf32_Word sh_addralign;
Elf32_Word sh_entsize;
} Elf32_Shdr;

typedef struct elf64_shdr {
Elf64_Word sh_name; /* Section name, index in string tbl */
Elf64_Word sh_type; /* Type of section */
Elf64_Xword sh_flags; /* Miscellaneous section attributes */
Elf64_Addr sh_addr; /* Section virtual addr at execution */
Elf64_Off sh_offset; /* Section file offset */
Elf64_Xword sh_size; /* Size of section in bytes */
Elf64_Word sh_link; /* Index of another section */
Elf64_Word sh_info; /* Additional section information */
Elf64_Xword sh_addralign; /* Section alignment */
Elf64_Xword sh_entsize; /* Entry size if section holds table */
} Elf64_Shdr;

#define ELFCLASS32 1
#define ELFDATA2LSB 1
#define ELFDATA2MSB 2
#define ELFCLASS64 2
#define EI_CLASS 4
#define EI_DATA 5

#endif /* elf.h */
8 changes: 7 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
#if defined(EMBED_INTERPRETER_NAME) || defined(EMBED_ARCHIVE) || defined(RC4_KEY)
#include "embed.h"
#endif
#ifdef MOUNT_SQUASHFS
#include "mount.h"
#endif
#ifdef RC4_KEY
#include "rc4.h"
#endif
Expand Down Expand Up @@ -58,7 +61,7 @@ int main(int argc, char* argv[]) {
#endif

std::string exe_path = get_exe_path(), base_dir = dir_name(exe_path);
std::string interpreter_path, extract_dir;
std::string interpreter_path, extract_dir, mount_dir;

#if defined(INTERPRETER)
interpreter_path = OBF(STR(INTERPRETER));
Expand All @@ -70,8 +73,11 @@ int main(int argc, char* argv[]) {
#elif defined(EMBED_ARCHIVE)
base_dir = extract_dir = extract_embeded_file();
atexit(remove_extract_dir);
#elif defined(MOUNT_SQUASHFS)
base_dir = mount_dir = mount_squashfs();
#endif
setenv(OBF("SSC_EXTRACT_DIR"), extract_dir.c_str(), 1);
setenv(OBF("SSC_MOUNT_DIR"), mount_dir.c_str(), 1);
setenv(OBF("SSC_EXECUTABLE_PATH"), exe_path.c_str(), 1);
setenv(OBF("SSC_ARGV0"), argv[0], 1);

Expand Down
Loading

0 comments on commit ee35ace

Please sign in to comment.