Skip to content

Instantly share code, notes, and snippets.

@Ex-32
Last active March 6, 2024 18:04
Show Gist options
  • Save Ex-32/669a5802786e9d50727128a258614454 to your computer and use it in GitHub Desktop.
Save Ex-32/669a5802786e9d50727128a258614454 to your computer and use it in GitHub Desktop.
Linux Transparent Binary Compressor
#!/bin/sh
# This file is licensed under the MIT license,
# see the bottom of the file for the full text.
#
# script dependencies: sed, xz-utils, gcc/clang
# generated binary dependencies: xz-util
# *note* if using gcc version < 12, every invocation of `cc -Oz` needs to be
# changed to `cc -Os`
#
# this script takes an executable and compresses it using `xz` into a binary
# blob and then embeds the blob into a small decompressor stub (whose source
# code can be seen inlined in this script). the resulting binary is placed in
# the same directory as the script with the same name as the original.
#
# USAGE: xzbin.sh EXECUTABLE_FILENAME
#
# TECHNICAL DISCLAIMER:
# the decompressor stub this program script uses utilizes the linux specific
# system call: memfd_create
# while in *theory* this could be avoided using POSIX compliant tools like
# shm_create, it adds significant complexity (and therefore size) to the
# decompressor stub, and I couldn't get it to behave correctly in some cases.
# an additional consequence of using memfd_create is that the file stops
# exisiting on exec, so this programs doesn't work on shebang scripts.
set -e
# arugment sanity check
if [ ! $# -eq 1 ] || [ ! -x "$1" ]; then
echo "USAGE: $(basename "$0") EXECUTABLE_FILENAME"
exit 1
fi
BIN_NAME="$(basename "$1")"
if [ "$BIN_NAME" = "xzdec" ]; then
echo "WARNING: xzdec is used by decompression stub at runtime"
echo "!! compressing the xzdec binary will create an inadvertent fork" \
"bomb when running *ANY* compressed program !!"
fi
# cd to location of script and setup a temp directory
cd "$(dirname "$(realpath "$0")")"
TMPDIR="$(mktemp -d)"
trap 'rm -rf $TMPDIR' EXIT
# creates object file containing two symbols:
# BIN_BLOB (a byte array containing the xz compressed program)
# BIN_BLOB_LEN (the length, in bytes, of BIN_BLOB)
# this is accomplished through the hexdump program, some questionable regex,
# and a whole lot of pipes :)
{
printf "#include <stddef.h>\n";
printf "char BIN_BLOB[] = {\n";
xz -z -c < "$1" |\
hexdump -v |\
sed -r 's/^[0-9a-f]+ ?//' |\
sed -r 's/\s{2,}/ /g' |\
sed -r 's/([0-9a-f]{2})([0-9a-f]{2})/0x\2, 0x\1,/g' |\
sed -r 's/,\s+$//g';
printf "};\nsize_t BIN_BLOB_LEN = sizeof(BIN_BLOB);\n";
} | cc -Oz -c -x c - -o "$TMPDIR/blob.o"
# compile stub program from heredoc
cc -Oz -c -x c - -o "$TMPDIR/stub.o" << EOF
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <unistd.h>
extern size_t BIN_BLOB_LEN;
extern char BIN_BLOB;
int main(int argc, char** argv) {
int mem_fd = syscall(SYS_memfd_create, argv[0], 0);
int pipe_fd[2];
if (pipe(pipe_fd) == -1) goto FAILURE;
pid_t pid = fork();
if (pid == -1) goto FAILURE;
if (pid == 0) {
dup2(pipe_fd[0], STDIN_FILENO);
dup2(mem_fd, STDOUT_FILENO);
close(pipe_fd[0]);
close(pipe_fd[1]);
close(mem_fd);
execlp("xzdec", "xzdec", NULL);
goto FAILURE;
}
if (write(pipe_fd[1], (void*)&BIN_BLOB, BIN_BLOB_LEN) != BIN_BLOB_LEN) {
goto FAILURE;
}
close(pipe_fd[0]);
close(pipe_fd[1]);
char buf[30];
snprintf(buf, 30, "/proc/self/fd/%i", mem_fd);
fcntl(mem_fd, F_SETFD, FD_CLOEXEC);
wait(NULL);
fsync(mem_fd);
execv(buf, argv);
FAILURE:
printf("decompressor stub encountered an error: %u", errno);
return -1;
}
EOF
# link stub program and binary blob into result
cc "$TMPDIR/stub.o" "$TMPDIR/blob.o" -o "./$BIN_NAME"
if [ \
"$(du --apparent-size --block-size=1 "$1" | grep -o -E '^[0-9]+')" -le \
"$(du --apparent-size --block-size=1 "./$BIN_NAME" | grep -o -E '^[0-9]+')" \
]; then
echo "WARNING: output larger than input, compression ineffective"
fi
# MIT License
#
# Copyright 2023 Jenna Fligor
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment