Added BlueShift
This commit is contained in:
@@ -15,3 +15,4 @@
|
||||
* [Mount Manager](./mm.sh): a simple script to mount/unmount removable media partitions
|
||||
* [PNGizer](./pngizer.sh): an utility (and a shell library) to convert any file or directory to/from a PNG image
|
||||
* [BitShade](./bitshade.sh): a steganographic tool for hiding any data file inside a PNG image
|
||||
* [BlueShift](./blueshift.sh): a high-density steganographic tool for hiding any data file inside a PNG image
|
||||
|
||||
Executable
+76
@@ -0,0 +1,76 @@
|
||||
#!/bin/sh
|
||||
# BlueShift: an experimental POSIX-compatible tool for
|
||||
# high-density (1 byte per pixel) image-based steganography
|
||||
# Depends on od, paste, dd, pr, awk, sed, ImageMagick and pngcrush
|
||||
# Usage:
|
||||
# - encoding: blueshift.sh e [cover image] [file to hide] [output PNG]
|
||||
# - decoding: blueshift.sh d [container PNG] [output file]
|
||||
# Created by Luxferre in 2026, released into public domain
|
||||
|
||||
# encode raw file as a netstring
|
||||
to_netstring() {
|
||||
printf '%d:' "$(cat "$1" | wc -c)"
|
||||
cat "$1"
|
||||
printf ','
|
||||
}
|
||||
|
||||
# decode a raw file from a netstring (inside another file)
|
||||
from_netstring() {
|
||||
# get the container length
|
||||
flen=$(head -n 1 "$1" | cut -f 1 -d :)
|
||||
# skip the length itself and the : char
|
||||
dd if="$1" of="$2" skip=$((${#flen} + 1)) bs=1 count=$flen
|
||||
}
|
||||
|
||||
# convert PPM to plain RGB list (pixel per line)
|
||||
ppm2rgb() {
|
||||
tail -n +4 | sed 's/\s\+/\n/g' | pr -3ta
|
||||
}
|
||||
|
||||
# for every input byte, print its 2-bit or 4-bit parts
|
||||
# R: lsb 2 bits, G: msb 2 bits, B: hsb 4 bits
|
||||
conv_input() {
|
||||
to_netstring "$1" | od -An -tu1 -v -w1 | awk '{n=int($0);printf("%u\t%u\t%u\n",n%4,int(n/4)%4,int(n/16))}'
|
||||
}
|
||||
|
||||
# stego_encode cover_image hidden_text out.png
|
||||
stego_encode() {
|
||||
coverfile="$(mktemp)"
|
||||
infile="$(mktemp)"
|
||||
tfile="$(mktemp)"
|
||||
temp_ppm="$(mktemp)"
|
||||
magick $1 -compress none ppm:$temp_ppm
|
||||
cat $temp_ppm | head -n 3 > $tfile
|
||||
cat $temp_ppm | ppm2rgb > $coverfile
|
||||
conv_input "$2" > $infile
|
||||
paste $coverfile $infile | awk '{if(NF>3)printf("%u %u %u\n", int(int($1)/4)*4+int($4), int(int($2)/4)*4+int($5), int(int($3)/16)*16+int($6));else print($0)}' >> $tfile
|
||||
magick ppm:$tfile png:$3
|
||||
pngcrush -ow $3
|
||||
rm -f $coverfile $infile $temp_ppm $tfile
|
||||
}
|
||||
|
||||
# stego_decode in.png out_file
|
||||
stego_decode() {
|
||||
temp_ppm="$(mktemp)"
|
||||
tfile="$(mktemp)"
|
||||
magick $1 -compress none ppm:- | ppm2rgb | awk '{printf("\\%03o", (int($3)%16)*16 + (int($2)%4)*4 + (int($1)%4))}' > $temp_ppm
|
||||
printf "$(cat $temp_ppm)" > $tfile
|
||||
from_netstring $tfile "$2"
|
||||
rm -f $temp_ppm $tfile
|
||||
}
|
||||
|
||||
# entry point
|
||||
|
||||
if [ "$1" = "e" ]; then
|
||||
[ -z "$2" ] && echo 'No cover file specified! Exiting...' && exit 1
|
||||
[ -z "$3" ] && echo 'No input file specified! Exiting...' && exit 1
|
||||
[ -z "$4" ] && echo "No output file specified! Exiting..." && exit 1
|
||||
stego_encode "$2" "$3" "$4"
|
||||
elif [ "$1" = "d" ]; then
|
||||
[ -z "$2" ] && echo 'No input file specified! Exiting...' && exit 1
|
||||
[ -z "$3" ] && echo "No output file specified! Exiting..." && exit 1
|
||||
stego_decode "$2" "$3"
|
||||
else
|
||||
echo 'Invalid mode! Use e for encoding or d for decoding'
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user