#!/usr/bin/perl -w # # Very simple steganographic image hiding. # # Takes two images of the same size, benign.ppm and evil.ppm, and hides # evil.ppm in benign.ppm. The upper two bits of image information from # evil.ppm get put in the lower two bits of benign.ppm # # Images need to be the same size -- in number of pixels, not in height/width, # so benign.ppm could be landscape oriented and evil.ppm could be portrait. # If the images have different heights/widths ppmlowbit won't know that and # will use benign.ppm's dimensions. You'll need to resize it by hand. # # Usage: ppmstegan benign.ppm evil.ppm > steganography.ppm # Use "ppmlowbit steganography.ppm > newevil.ppm" to extact. # # See also pnmchdim # # 3 Jan 2005 use strict; use Image::PBMlib; use vars qw( $id $inn $evil $keepmask $losemask $shiftbits $refin $refevil $i $j @pixelsin $pin @pixelsevil $pevil ); $id = $0; $id =~ s:.*/::; $losemask = 192; # 11000000 $keepmask = 252; # 11111100 above reversed and inverted $shiftbits = 6; # number of zeros in losemask / ones in keepmask while(defined($ARGV[0]) and substr($ARGV[0], 0, 1) eq '-') { if (($ARGV[0] eq '-b') or ($ARGV[0] eq '--bits')) { shift; $shiftbits = shift; if(!defined($shiftbits) or ($shiftbits !~ /^[1-7]$/)) { print STDERR "$id: -b (--bits) requires a number from 1 to 7\n"; exit 2; } $keepmask = (255 << $shiftbits) & 255; $shiftbits = 8 - $shiftbits; $losemask = (255 << $shiftbits) & 255; print STDERR "lose: $losemask keep: $keepmask bits: $shiftbits\n"; } elsif ($ARGV[0] eq '--help' or $ARGV[0] eq '--version') { print STDERR "$id: usage\n", < stegan.ppm Options: -b --bits NUM put upper NUM bits of evil in lower bits of benign, default 2 PGM and PPM images supported, but both files must be the same type. The benign and evil pictures must have the same number of pixels, but dimensions can vary. The stegan output will use the benign dimensions. See pnmchdim. USAGEinfo exit 2; } else { print STDERR "$id: $ARGV[0] not a recognized option, use --help for help\n"; exit 2; } } $inn = shift; $evil = shift; if(!defined($inn)) { die "$id: Missing benign input file\n"; } if(!defined($evil)) { die "$id: Missing evil input file\n"; } if(!open(PPMI, "< $inn")) { die "$id: Can't open benign input $inn: $!\n"; } if(!open(PPME, "< $evil")) { die "$id: Can't open evil input $evil: $!\n"; } $refin = readppmheader(\*PPMI); $refevil = readppmheader(\*PPME); if(defined($$refin{error})) { die "$id: PPM error, $inn: $$refin{error}\n"; } if(defined($$refevil{error})) { die "$id: PPM error, $evil: $$refevil{error}\n"; } if(($$refin{bgp} eq 'b') or ($$refevil{bgp} eq 'b')) { die "$id: PPM error: bitmaps do not have enough bits per pixel\n"; } if($$refin{bgp} ne $$refevil{bgp}) { die "$id: Images are different types (color/gray)\n"; } if(($$refin{width} * $$refin{height}) != ($$refevil{width} * $$refevil{height})) { die "$id: Images have different numbers of pixels\n"; } # Just dump the same header print $$refin{fullheader}; # Read everything @pixelsin = readpixels_raw(\*PPMI, $$refin{type}, ($$refin{width} * $$refin{height}) ); @pixelsevil = readpixels_raw(\*PPME, $$refevil{type}, ($$refevil{width} * $$refevil{height}) ); for($i = 0; $i < ($$refevil{width} * $$refevil{height}); $i ++) { $pin = $pixelsin[$i]; $pevil = $pixelsevil[$i]; if($$refin{bgp} eq 'g') { print chr( (ord($pin) & $keepmask) | ((ord($pevil) & $losemask) >> $shiftbits) ); } else { for ($j = 0; $j <= 2; $j ++) { print chr( (ord($$pin[$j]) & $keepmask) | ((ord($$pevil[$j]) & $losemask) >> $shiftbits) ); } } } exit; __END__ =pod =head1 NAME ppmstegan - merge two images stegangraphically =head1 DESCRIPTION Very simple steganographic image hiding. Most basic usage: ppmstegan benign.ppm evil.ppm > steganography.ppm Takes two images of the same size, I and I, and hides I in I. The upper two bits of image information from I get put in the lower two bits of I. The I image can be extracted with C. Images need to be the same size -- in number of pixels, not in height/width, so I could be landscape oriented and I could be portrait. If the images have different heights/widths C won't know that and will use I's dimensions. You'll need to resize it by hand, eg with C. =head1 USAGE Usage: ppmstegan [options] benign.ppm evil.ppm > stegan.ppm Options: =over 4 =item * -b --bits NUM Put the upper NUM bits of evil.ppm in lower bits of benign.ppm, defaults to 2. =item * --help Prints a usage message and exits. =back Both PGM and PPM images supported, but both files must be the same type. The I and I pictures must have the same number of pixels, but dimensions can vary. The stegan output will use the I dimensions. The C tool can be usaged to massage one file type into another, if the two require the same number of bits to represent. =head1 SEE ALSO C - extract an image from the lowbits of an image C - change dimensions or type of a PBM/PGM/PPM file =head1 COPYRIGHT Copyright 2005 by Eli the Bearded / Benjamin Elijah Griffin. Released under the same license(s) as Perl. =head1 AUTHOR Eli the Bearded wrote the C, C, and C set of tools to examine a demo steganographic image. =head1 CPAN INFO =head1 SCRIPT CATEGORIES Image =head1 README ppmstegan - merge two images stegangraphically =head1 PREREQUISITES This uses the C, C, and C modules. =head1 COREQUISITES The C and C scripts are recommended. =head1 OSNAMES Should be OS independent. =cut