Next: About this document ...
Up: Steganography Using Computer Images
Previous: Bibliography
File 1, stegFr.cpp, is a steganography program for P2 or P3 PGM images.
//Copyright 2002 William Barratt
//stegFr.cpp
/* User-friendly steganography program for .pgm files. */
#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void handleArgs(int argc, char* argv[],
FILE *& infile, FILE *& outfile);
void readWriteHeaders(FILE * infile, FILE * outfile,
int & h, int & w, int & max);
void readIntoVector(FILE * infile, int* vector);
bool getMessage(char * input, int h, int w);
void modifyPixels(int* vector, char * input);
void writeNewPixels(FILE * outfile, int* vector, int h, int w);
int main(int argc, char* argv[])
{
FILE * infile;
FILE * outfile;
handleArgs(argc, argv, infile, outfile); //get filenames
int h, w, max; //image height, width,
//and max. pixel value
//read headers of source file and write headers of new file
readWriteHeaders(infile, outfile, h, w, max);
int* vector = new int[h*w]; //vector for pixel values
readIntoVector(infile, vector); //read pixels into vector
fclose(infile); //close source file
char * input = new char[h*w/7]; //string for message
if(!getMessage(input, h, w)) //get message, error test
{
fclose(outfile);
system("rm message.pgm");
exit(2); //return code 2
}
modifyPixels(vector, input); //put message in pixels
writeNewPixels(outfile, vector, h, w); //write pixels to file
fclose(outfile); //close output file
return 0;
}
void handleArgs(int argc, char* argv[],
FILE *& infile, FILE *& outfile)
{
char* file = new char[80]; //temporary string
if(argc > 3) //if 3 filenames entered
{
cout << "Too many command-line arguments; aborting."
<< endl;
exit(3);
}
if(argc == 2) //if 1 filename entered
{
infile = fopen(argv[1], "r");
cout << "Name of output image file: ";
cin >> file;
outfile = fopen(file, "w");
}
else if(argc == 3) //if 2 filenames entered
{
infile = fopen(argv[1], "r");
outfile = fopen(argv[2], "w");
}
else if(argc == 1) //if no filenames entered
{
cout << "Name of input image file: ";
cin >> file;
infile = fopen(file, "r");
cout << "Name of output image file: ";
cin >> file;
outfile = fopen(file, "w");
}
}
void readWriteHeaders(FILE * infile, FILE * outfile,
int &h, int &w, int & max)
{
char* str = new char[80]; //string for reading
fprintf(outfile,"%s\n", "P2"); //write initial filetype header
fgets(str, 80, infile); //reads first line
if(str[0] != 'P' || str[1] != '2') //check header
{
fclose(infile);
fclose(outfile);
system("rm message.pgm");
cout << "File header indicates wrong file type" << endl
<< "File must be .pgm with 'P2' in first line" << endl;
exit(1); //end with exit code 1
}
fgets(str, 80, infile); //second line (place for comments)
while(str[0] == '#') //detects comment line
{
fprintf(outfile, "%s", str); //copy comment line
fgets(str, 80, infile); //read next line
}
//retrieval and copying of width and height
char* width = new char[80];
char* height = new char[80];
int spaceLoc = strcspn(str, " "); //finds space between w & h
for(int x = 0; x < spaceLoc; x++)
{
width[x] = str[x]; //copies width from temp string
}
w = atoi(width); //convert to int
height = strstr(str, " "); //get substring for height
h = atoi(height); //convert to int
fprintf(outfile, "%d %d\n", w, h);
fgets(str, 80, infile); //read final header
max = atoi(str); //copy to max for later use
fprintf(outfile, "%d\n", max); //write max to new file
}
void readIntoVector(FILE *infile, int* vector)
{
char* line = new char[80]; //buffer for reading lines
char* tmpstr = new char[80]; //temp for manipulation
int place = 0; //vector index
while(!feof(infile)) //while not at file end
{
fgets(line, 80, infile); //get one line
tmpstr = strtok(line, " "); //gets pixel value
while(tmpstr != NULL) //while not at line end
{
if(strcspn(tmpstr, "0123456789") != strlen(tmpstr))
{ //checks for numbers in substring
vector[place] = atoi(tmpstr);//places number in vector
place++; //increments index
}
tmpstr = strtok(NULL, " "); //find next value
}
}
}
bool getMessage(char * input, int h, int w)
{
//get text message input from user
cout << "Enter a text message, up to "<<(h*w/7)-1<<" characters: ";
cin.getline(input, h*w);
//check to make sure the message is not too long
if(strlen(input) > ((h * w / 7) - 1))
{
cout<<"ERROR: Message is too long. Aborting program.";
return false;
}
else return true;
}
void modifyPixels(int* vector, char * input)
{
char * tmpstr = new char[1]; //temporary string
tmpstr[0] = char(7); //beep char. denotes msg end
strcat(input, tmpstr); //add message end character
int length = strlen(input), strIndex, shiftNum, vectorIndex = 0;
for(strIndex = 0; strIndex < length; strIndex++)
{
for(shiftNum = 6; shiftNum >= 0; shiftNum--)
{
//bitwise ops. mask set low order bit to desired value
vector[vectorIndex] = (vector[vectorIndex] & 0xFE) ^
((input[strIndex] >> shiftNum) & 1);
vectorIndex++; //increment vector position
}
if(input[strIndex] == char(7)) //end loop if end reached
break;
}
}
void writeNewPixels(FILE * outfile, int* vector, int h, int w)
{
int elementCount = 17; //for formatting purposes
for(int x = 0; x < (h * w); x++) //runs through all pixels
{
for(int y = 100; y > 1; y /= 10) //formatting
{
if(vector[x] / y == 0)
fprintf(outfile, " ");
}
fprintf(outfile, "%i ", vector[x]); //write pixel
elementCount--;
if(elementCount == 0) //detect when to end line
{
fprintf(outfile, "\n");
elementCount = 17;
}
}
}
File 2, destegFr.cpp, is an extraction program for P2 or P3 PGM files with data hidden by stegFr.cpp.
//William Barratt
//Copyright 2002 William Barratt
//destegFr.cpp
/* User-friendly steganography decoding program for .pgm files
Companion to stegFr.cpp */
#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void handleArgs(int argc, char* argv[], FILE *& infile);
void readThroughHeaders(FILE * infile, int & h, int & w);
void readIntoVector(FILE * infile, int* vector);
void interpretVector(int* vector, char * msg);
void outputMessage(char * msg);
int main(int argc, char* argv[])
{
FILE * infile; //source file
handleArgs(argc, argv, infile); //get filename
int h, w; //image height, width
readThroughHeaders(infile, h, w); //deal with headers
int * vector = new int[h*w]; //vector for pixel values
readIntoVector(infile, vector); //read pixel values
fclose(infile); //close source file
char * msg = new char[h*w/7]; //string for message
interpretVector(vector, msg); //decode message
outputMessage(msg); //print message to screen
return 0;
}
void handleArgs(int argc, char * argv[], FILE *& infile)
{
char* file = new char[80]; //temporary string
if(argc > 2) //if user enters 2 things
{
cout << "Too many command-line arguments; aborting."
<< endl;
exit(3);
}
else if(argc == 2) //if user enters 1 filename
{
infile = fopen(argv[1], "r");
}
else if(argc == 1) //if user enters no name
{
cout << "Name of source image file: ";
cin >> file;
infile = fopen(file, "r");
}
}
void readThroughHeaders(FILE * infile, int & h, int & w)
{
char* str = new char[80]; //string for reading
fgets(str, 80, infile); //reads first line
if(str[0] != 'P' || str[1] != '2') //check header
{
fclose(infile);
cout << "File header indicates wrong file type" << endl
<< "File must be .pgm with 'P2' in first line" << endl;
exit(1);
}
fgets(str, 80, infile); //second line (comments)
while(str[0] == '#')
{
fgets(str, 80, infile); //read through comments
}
//retrieval of width and height
char* width = new char[80];
char* height = new char[80];
int spaceLoc = strcspn(str, " "); //finds space between w & h
for(int x = 0; x < spaceLoc; x++)
{
width[x] = str[x];
}
w = atoi(width); //convert to int
height = strstr(str, " "); //gets height substring
h = atoi(height); //convert to int
fgets(str, 80, infile); //line with max value
}
void readIntoVector(FILE *infile, int* vector)
{
//identical to like-named function of steg.cpp
//
char* line = new char[80]; //buffer for reading lines
char* tmpstr = new char[80]; //temp for manipulation
int place = 0; //vector index
while(!feof(infile)) //while not at file end
{
fgets(line, 80, infile); //get one line
tmpstr = strtok(line, " "); //gets pixel value
while(tmpstr != NULL) //while not at line end
{
if(strcspn(tmpstr, "0123456789") != strlen(tmpstr))
{ //checks for numbers in substring
vector[place] = atoi(tmpstr);//places number in vector
place++; //increments index
}
tmpstr = strtok(NULL, " "); //find next value
}
}
}
void interpretVector(int * vector, char * msg)
{
char tmp = 0; //buffer char.
int vectorIndex = 0, strIndex = 0, shiftNum;
while(tmp != char(7)) //while end not reached
{
tmp = 0; //reset buffer
//runs through, bit-masking etc.
for(shiftNum = 6; shiftNum >= 0; shiftNum--)
{
tmp = tmp ^ ((vector[vectorIndex] & 1) << shiftNum);
vectorIndex++; //increment place in vector
}
if(tmp != char(7))
msg[strIndex] = tmp; //set output char (if not 7)
strIndex++; //increment place in string
}
}
void outputMessage(char * msg)
{
cout << "The message extracted from the file is:"
<< endl << msg << endl;
}
William Barratt
2003-06-13