#!/usr/bin/perl # version 0.0.1 # file version 00000001 # import ImageMagick perl wrapper and POSIX functions use Image::Magick; use POSIX; use integer; # check arguments if ($#ARGV < 2) { die <new; $image->Read($f); # set to 24bit colour $image->Set(depth=>8); # find dimensions ($xSize, $ySize) = $image->[0]->Get('width', 'height'); &debug("Image $f: x is $xSize, y is $ySize"); # prepare initial array @retval = (); # loop through each pixel of the image for ($x = 0; $x < $xSize; $x++) { for ($y = 0; $y < $ySize; $y++) { # look at the pixel colour $p = $image->Get("pixel[$x,$y]"); # split into RGBA components. discard alpha channel. ($r,$g,$b,$a) = split(/\,/, $p); # IMPORTANT: # Some versions of ImageMagic use 24-bit RGB, if this describes you, then # change the next line to read "if ( 1==0 ) {". if ( 1==1 ) { # ImageMagick is using 48-bit RGB, so divide through to get 24-bit. $r = $r >> 8; $g = $g >> 8; $b = $b >> 8; } # add these values to the array. @retval = (@retval, $r, $g, $b); } } # image is now contained into array. return @retval; } sub readMessageIntoArray { # open file $f = shift @_; open(F,$f) || die "Couldn't open $f!"; # initialise array @retval = (); # loop through file while ( !eof(F) ) { # read a character read (F, $char, 1); # get the binary representation. voodoo code. $b = substr(unpack("B*", pack("N", ord($char))), 24); # split into bits @bits = split(//, $b); # append bits to end of file @retval = (@retval, @bits); # increment counter! $msgSize += 8; } # close file close(F); # return array return @retval; } sub createHead { # a head is a 32-bit array. the first 8 bits are the file version. # the last 24 bits are an unsigned integer showing the length in # bytes of the hidden message. $fileVersion = '00000001'; $b = substr(unpack("B*", pack("N", ($msgSize / 8))), 8); &debug("Message Header: $fileVersion$b"); $msgSize += 32; return split(//, $fileVersion . $b); } sub steg { # initialise array @retval = (); # these values are useful for evening out disturbances. $shift{'r'} = 0; $shift{'g'} = 0; $shift{'b'} = 0; $now = 'r'; for ($i = 0; $i < $envSize; $i++) { $e = $envelope[$i]; $l = $letter[$i]; # if the letter bit is 0 and the envelope is already even # OR the letter bit is 1 and the envelope is already off # OF if we're already past the end of the message if ( ($i>$msgSize) || ($e%2==$l) ) { # simply pass on the original value unchanged $retval[$i] = $e; } # if the letter bit is 0, but the envelope is odd... elsif ($l==0) { # don't have any choice but to adjust 255 down to 254. if ($e == 255) { $retval[$i] = 254; # but rmember next time we have a choice, suggest to go up. $shift{$now}++; } # if we should be adjusting up, do so. elsif ($shift{$now} > 0) { $retval[$i] = $e + 1; $shift{$now}--; } # otherwise, adjust down. else { $retval[$i] = $e - 1; $shift{$now}++; } } # otherwise the letter bit must be 1 and the envelope byte even. else { # don't have any choice but to adjust 0 up to 1. if ($e == 0) { $retval[$i] = 1; # but rmember next time we have a choice, suggest to go down. $shift{$now}--; } # if we should be adjusting down, do so. elsif ($shift{$now} < 0) { $retval[$i] = $e - 1; $shift{$now}++; } # otherwise, adjust up. else { $retval[$i] = $e + 1; $shift{$now}--; } } # advance to next colour, for balancing. $now = &shiftColour($now); } return @retval; } sub writeStegFile { # get parameters $f = shift @_; @data = @_; # create empty image $image = undef; $size = $xSize . 'x' . $ySize; $image = Image::Magick->New(size=>$size); # fill with blank. seems this is needed. $image->Read('xc:white'); # loop through each pixel of the image for ($x = 0; $x < $xSize; $x++) { for ($y = 0; $y < $ySize; $y++) { # obtain r,g,b from array $r = shift @data; $g = shift @data; $b = shift @data; # combine rgb $colour=sprintf("#%02x%02x%02x", $r,$g,$b); # set the pixel $image->Set("pixel[$x,$y]"=>$colour); } } # output file debug("Writing file $f"); $x = $image->Write($f); warn $x if $x; } sub debug { $l = shift @_; print STDERR "$l\n"; } # little support function for "steg" sub shiftColour { $n = shift @_; if ($n eq 'r') { return 'g'; } elsif ($n eq 'g') { return 'b'; } else { return 'r'; } }