J
John Williams
I'm writing a stagenography program to experiment with how it works.
The algorithm I'm using appears to be producing the correct
result...however I'm struggling with the file input. I never learned
file input/output very well (I self taught all the programming I
know...and my c++ book was not good) and so I'm not sure what's wrong
with this. The problem is the function void encodemsg(fstream *img,
fstream *msg, fstream *out, char key[])
on line 69 I attempt to set img->seekg(int) to the point I want to begin
the embedding at. However it constantly stays at -1 as the img->tellg()
on line 77 shows. I don't understand why since it should start at 87
since that is what I intend to have it set to on line 69. Any help
would be appreciated as well if anyone has a good c++ file operations
tutorial they know of it would be appreciated.
/* An example of stranography. This program hides another file (most likely a txt message) inside a
* 24 bit bmp image file. It changes the LSB of each byte of the image data. This alters the color
* of each pixel by a small amount that should be undetectable visually.
*/
#include <fstream>
#include <iostream>
#define BITS_CHAR (sizeof(char) * 8)
#define BMPSTART 55
using namespace std;
void encodechar(char *, char , char *);
void encodemsg(fstream *, fstream *, fstream *, char *);
void encodeint(char *, int, char*);
void decodechar(char *, char );
void decodemsg(fstream *, fstream *, char *);
int decodeint(char*);
int filesizer(fstream *);
bool fileopener(fstream *, char *, bool);
char cipher(char *, char *);
int cipherint(int , int);
bool checksize(fstream *, fstream *);
using namespace std;
bool checksize(fstream *image, fstream *message)
{
// Verifies the message is small enough to fit in the bitmap
if ((filesizer(message) * 8) > (filesizer(image) - (BMPSTART + 8 * sizeof(int))))
return false;
else
return true;
}
char cipher(char message, char key)
{
// XOR crypts the message to aid in protecting the message.
// Todo: Add better encryption
return (message ^ key);
}
int cipherint(int len, int key)
{
return (len ^ key);
}
void encodemsg(fstream *img, fstream *msg, fstream *out, char key[])
{
/* Acts as a driver to hide the work that goes into encoding the message from main.
*/
/* Possibly rewrite...since we are using an image copy already we might be able to
* change the fstream out to do both input and output saving us having to pass it in
* as well as simplifying the code.
*/
int i, j, key_size, msg_size, cryptsize, msgkey;
char imgbuffer[BITS_CHAR], outbuffer[BITS_CHAR], msgbuffer, bite, msgcrypt;
char imgintbuff[BITS_CHAR * sizeof(int)], intkey[sizeof (int)];
char outintbuff[BITS_CHAR * sizeof(int)];
key_size = strlen(key);
msg_size = filesizer(msg);
img->seekg(BMPSTART + 8 * (sizeof(int))); // Leave room for an int (msgsize)
out->seekp(BMPSTART + 8 * (sizeof(int))); // So we can encode the message size
msg->seekg(0);
for (i = 0; !msg->eof(); i++) { // Read the image and message, ecrypt the message
if (i >= key_size) // then encode the message into the output image
i = 0;
cout << img->tellg() << endl; // debug info
cout << out->tellp() << endl; // debug info
img->read(imgbuffer, BITS_CHAR);
msg->read(&msgbuffer, sizeof(char));
msgcrypt = cipher(msgbuffer, key);
encodechar(imgbuffer, msgcrypt, outbuffer);
out->write(outbuffer, BITS_CHAR);
}
out->seekp(BMPSTART);
img->seekg(BMPSTART);
for (i = 0; i < sizeof(int); i++) {
intkey = key;
}
msgkey = atoi(key);
img->read(imgintbuff, (BITS_CHAR * sizeof(int)));
cryptsize = cipherint(msg_size, msgkey);
encodeint(imgintbuff, cryptsize, outintbuff);
out->write(outintbuff, (BITS_CHAR * sizeof(int)));
return;
}
void decodemsg(fstream *img, fstream *msg, char key[])
{
// Acts as a driver between main and the function to decode the chars
int i, j, key_size, msg_len, msgkey;
char imgbuffer[BITS_CHAR], msgbuffer, msg_lenbuff[sizeof(int)];
char imgintbuff[BITS_CHAR * sizeof(int)], intkey[sizeof (int)];
char outintbuff[BITS_CHAR * sizeof(int)];
key_size = strlen(key);
img->seekg(BMPSTART);
msg->seekp(0);
for (i = 0; i < sizeof(int); i++) { // Decode and decrypt the message length from the image
intkey = key;
}
msgkey = atoi(key);
img->read(imgintbuff, (BITS_CHAR * sizeof(int)));
msg_len =decodeint(imgintbuff);
msg_len = cipherint(msg_len, msgkey);
for (i = 0, j = 0; i < msg_len; i++, j++) { //Read image then send it to be decoded and deciphered
if (j >= key_size)
j = 0;
img->read(imgbuffer, BITS_CHAR);
decodechar(imgbuffer, msgbuffer);
msgbuffer = cipher(msgbuffer, key[j]);
msg->write(&msgbuffer, sizeof(char));
}
return;
}
void encodechar(char bitmap[BITS_CHAR], char message, char retbitmap[BITS_CHAR])
{
// Encdodes a char into a bitmap takes 8 bytes of the bit map.
// Todo: Make it so larger messages can be encoded at the exchange of image quality (use more bits)
int i, shift;
shift = (BITS_CHAR - 1);
for (i = 0; i < BITS_CHAR; i++, shift--) {
retbitmap = (bitmap & 0xFF) | ((message >> shift) & 0x01);
}
return;
}
void decodechar(char bitmap[BITS_CHAR], char message)
{
// Decode chars from the bytes of image data
int i;
message = 0x00;
for (i = 0; i < BITS_CHAR; i++) {
bitmap = bitmap & 0x01; // Mask out all except last bit
message = message | (bitmap << (BITS_CHAR - i)); //move bit to correct spot and OR it in
}
return;
}
void encodeint(char bitmap[BITS_CHAR * sizeof(int)], int len, char retbitmap[BITS_CHAR * sizeof(int)])
{
int i, shift;
shift = ((BITS_CHAR * sizeof(int)) - 1);
for (i = 0; i < (BITS_CHAR * sizeof(int)); i++, shift--) {
retbitmap = (bitmap & 0xFF) | ((len >> shift) & 0x01);
}
return;
}
int decodeint(char bitmap[BITS_CHAR * sizeof(int)])
{
int i, len;
len = 0x00;
for (i = 0; i < (BITS_CHAR * sizeof(int)); i++) {
bitmap = bitmap & 0x01; // Mask out all except last bit
len = len | (bitmap << ((BITS_CHAR * sizeof(int)) - i)); //move bit to spot and OR it in
}
return len;
}
void copyimage(fstream *image, fstream *output)
{
/* Duplicates the image. The encode work is all done on the copy of the image
*/
char buffer;
int i;
image->seekg(0);
image->seekp(0);
while (!image->eof()) {
image->read(&buffer, sizeof(char));
output->write(&buffer, sizeof(char));
}
return;
}
int filesizer(fstream *file)
{
// Determines the file size by seeking the end of the file
int size1 = 0, size2 = 0;
file->seekg(0, ios::end);
size1 = file->tellg();
file->seekp(0, ios::end);
size2 = file->tellp();
if (size2 > size1) {
size1 = size2;
}
file->seekg(0);
file->seekp(0);
return size1;
}
bool fileopener(fstream *file, char filename[], bool mode)
{
/* Used to open a file. Tests to make sure it's open and if it's goint to be used for input
* that the file has data in it.
*/
// Todo: make it confirm overwrite of nonempty output files
int size;
if (mode == 1) {
file->open(filename, ios::in);
if (file->is_open()) {
if (filesizer(file) <= 0) {
cerr << "Input file is empty!\n";
return false;
}
else return true;
}
else {
cerr << "File " << filename << " Failed to open!\n";
return false;
}
}
if (mode == 0) {
file->open(filename, ios:ut);
if (file->is_open()) {
return true;
}
else {
cerr << "File " << filename << " Failed to open!\n";
return false;
}
}
return false;
}
int main(int argc, char *argv[])
{
int i;
fstream image, message, image_out;
if ((argc < 4) || (argc < 5)) {
cerr << "Usage: bmpmess <image_file> <message_file> [image_out] <key>\n";
return EXIT_FAILURE;
}
if (!fileopener(&image, argv[1], 1))
return EXIT_FAILURE;
if (argc == 4) {
if (!fileopener(&message, argv[2], 0))
return EXIT_FAILURE;
decodemsg(&image, &message, argv[3]);
}
if (argc == 5) {
if (!fileopener(&image_out, argv[3], 0))
return EXIT_FAILURE;
if (!fileopener(&message, argv[2], 1))
return EXIT_FAILURE;
if (!checksize(&image, &message)) {
cerr << "Image too small for message\n";
return EXIT_FAILURE;
}
copyimage(&image, &image_out);
encodemsg(&image, &message, &image_out, argv[4]);
}
return 0;
}
The algorithm I'm using appears to be producing the correct
result...however I'm struggling with the file input. I never learned
file input/output very well (I self taught all the programming I
know...and my c++ book was not good) and so I'm not sure what's wrong
with this. The problem is the function void encodemsg(fstream *img,
fstream *msg, fstream *out, char key[])
on line 69 I attempt to set img->seekg(int) to the point I want to begin
the embedding at. However it constantly stays at -1 as the img->tellg()
on line 77 shows. I don't understand why since it should start at 87
since that is what I intend to have it set to on line 69. Any help
would be appreciated as well if anyone has a good c++ file operations
tutorial they know of it would be appreciated.
/* An example of stranography. This program hides another file (most likely a txt message) inside a
* 24 bit bmp image file. It changes the LSB of each byte of the image data. This alters the color
* of each pixel by a small amount that should be undetectable visually.
*/
#include <fstream>
#include <iostream>
#define BITS_CHAR (sizeof(char) * 8)
#define BMPSTART 55
using namespace std;
void encodechar(char *, char , char *);
void encodemsg(fstream *, fstream *, fstream *, char *);
void encodeint(char *, int, char*);
void decodechar(char *, char );
void decodemsg(fstream *, fstream *, char *);
int decodeint(char*);
int filesizer(fstream *);
bool fileopener(fstream *, char *, bool);
char cipher(char *, char *);
int cipherint(int , int);
bool checksize(fstream *, fstream *);
using namespace std;
bool checksize(fstream *image, fstream *message)
{
// Verifies the message is small enough to fit in the bitmap
if ((filesizer(message) * 8) > (filesizer(image) - (BMPSTART + 8 * sizeof(int))))
return false;
else
return true;
}
char cipher(char message, char key)
{
// XOR crypts the message to aid in protecting the message.
// Todo: Add better encryption
return (message ^ key);
}
int cipherint(int len, int key)
{
return (len ^ key);
}
void encodemsg(fstream *img, fstream *msg, fstream *out, char key[])
{
/* Acts as a driver to hide the work that goes into encoding the message from main.
*/
/* Possibly rewrite...since we are using an image copy already we might be able to
* change the fstream out to do both input and output saving us having to pass it in
* as well as simplifying the code.
*/
int i, j, key_size, msg_size, cryptsize, msgkey;
char imgbuffer[BITS_CHAR], outbuffer[BITS_CHAR], msgbuffer, bite, msgcrypt;
char imgintbuff[BITS_CHAR * sizeof(int)], intkey[sizeof (int)];
char outintbuff[BITS_CHAR * sizeof(int)];
key_size = strlen(key);
msg_size = filesizer(msg);
img->seekg(BMPSTART + 8 * (sizeof(int))); // Leave room for an int (msgsize)
out->seekp(BMPSTART + 8 * (sizeof(int))); // So we can encode the message size
msg->seekg(0);
for (i = 0; !msg->eof(); i++) { // Read the image and message, ecrypt the message
if (i >= key_size) // then encode the message into the output image
i = 0;
cout << img->tellg() << endl; // debug info
cout << out->tellp() << endl; // debug info
img->read(imgbuffer, BITS_CHAR);
msg->read(&msgbuffer, sizeof(char));
msgcrypt = cipher(msgbuffer, key);
encodechar(imgbuffer, msgcrypt, outbuffer);
out->write(outbuffer, BITS_CHAR);
}
out->seekp(BMPSTART);
img->seekg(BMPSTART);
for (i = 0; i < sizeof(int); i++) {
intkey = key;
}
msgkey = atoi(key);
img->read(imgintbuff, (BITS_CHAR * sizeof(int)));
cryptsize = cipherint(msg_size, msgkey);
encodeint(imgintbuff, cryptsize, outintbuff);
out->write(outintbuff, (BITS_CHAR * sizeof(int)));
return;
}
void decodemsg(fstream *img, fstream *msg, char key[])
{
// Acts as a driver between main and the function to decode the chars
int i, j, key_size, msg_len, msgkey;
char imgbuffer[BITS_CHAR], msgbuffer, msg_lenbuff[sizeof(int)];
char imgintbuff[BITS_CHAR * sizeof(int)], intkey[sizeof (int)];
char outintbuff[BITS_CHAR * sizeof(int)];
key_size = strlen(key);
img->seekg(BMPSTART);
msg->seekp(0);
for (i = 0; i < sizeof(int); i++) { // Decode and decrypt the message length from the image
intkey = key;
}
msgkey = atoi(key);
img->read(imgintbuff, (BITS_CHAR * sizeof(int)));
msg_len =decodeint(imgintbuff);
msg_len = cipherint(msg_len, msgkey);
for (i = 0, j = 0; i < msg_len; i++, j++) { //Read image then send it to be decoded and deciphered
if (j >= key_size)
j = 0;
img->read(imgbuffer, BITS_CHAR);
decodechar(imgbuffer, msgbuffer);
msgbuffer = cipher(msgbuffer, key[j]);
msg->write(&msgbuffer, sizeof(char));
}
return;
}
void encodechar(char bitmap[BITS_CHAR], char message, char retbitmap[BITS_CHAR])
{
// Encdodes a char into a bitmap takes 8 bytes of the bit map.
// Todo: Make it so larger messages can be encoded at the exchange of image quality (use more bits)
int i, shift;
shift = (BITS_CHAR - 1);
for (i = 0; i < BITS_CHAR; i++, shift--) {
retbitmap = (bitmap & 0xFF) | ((message >> shift) & 0x01);
}
return;
}
void decodechar(char bitmap[BITS_CHAR], char message)
{
// Decode chars from the bytes of image data
int i;
message = 0x00;
for (i = 0; i < BITS_CHAR; i++) {
bitmap = bitmap & 0x01; // Mask out all except last bit
message = message | (bitmap << (BITS_CHAR - i)); //move bit to correct spot and OR it in
}
return;
}
void encodeint(char bitmap[BITS_CHAR * sizeof(int)], int len, char retbitmap[BITS_CHAR * sizeof(int)])
{
int i, shift;
shift = ((BITS_CHAR * sizeof(int)) - 1);
for (i = 0; i < (BITS_CHAR * sizeof(int)); i++, shift--) {
retbitmap = (bitmap & 0xFF) | ((len >> shift) & 0x01);
}
return;
}
int decodeint(char bitmap[BITS_CHAR * sizeof(int)])
{
int i, len;
len = 0x00;
for (i = 0; i < (BITS_CHAR * sizeof(int)); i++) {
bitmap = bitmap & 0x01; // Mask out all except last bit
len = len | (bitmap << ((BITS_CHAR * sizeof(int)) - i)); //move bit to spot and OR it in
}
return len;
}
void copyimage(fstream *image, fstream *output)
{
/* Duplicates the image. The encode work is all done on the copy of the image
*/
char buffer;
int i;
image->seekg(0);
image->seekp(0);
while (!image->eof()) {
image->read(&buffer, sizeof(char));
output->write(&buffer, sizeof(char));
}
return;
}
int filesizer(fstream *file)
{
// Determines the file size by seeking the end of the file
int size1 = 0, size2 = 0;
file->seekg(0, ios::end);
size1 = file->tellg();
file->seekp(0, ios::end);
size2 = file->tellp();
if (size2 > size1) {
size1 = size2;
}
file->seekg(0);
file->seekp(0);
return size1;
}
bool fileopener(fstream *file, char filename[], bool mode)
{
/* Used to open a file. Tests to make sure it's open and if it's goint to be used for input
* that the file has data in it.
*/
// Todo: make it confirm overwrite of nonempty output files
int size;
if (mode == 1) {
file->open(filename, ios::in);
if (file->is_open()) {
if (filesizer(file) <= 0) {
cerr << "Input file is empty!\n";
return false;
}
else return true;
}
else {
cerr << "File " << filename << " Failed to open!\n";
return false;
}
}
if (mode == 0) {
file->open(filename, ios:ut);
if (file->is_open()) {
return true;
}
else {
cerr << "File " << filename << " Failed to open!\n";
return false;
}
}
return false;
}
int main(int argc, char *argv[])
{
int i;
fstream image, message, image_out;
if ((argc < 4) || (argc < 5)) {
cerr << "Usage: bmpmess <image_file> <message_file> [image_out] <key>\n";
return EXIT_FAILURE;
}
if (!fileopener(&image, argv[1], 1))
return EXIT_FAILURE;
if (argc == 4) {
if (!fileopener(&message, argv[2], 0))
return EXIT_FAILURE;
decodemsg(&image, &message, argv[3]);
}
if (argc == 5) {
if (!fileopener(&image_out, argv[3], 0))
return EXIT_FAILURE;
if (!fileopener(&message, argv[2], 1))
return EXIT_FAILURE;
if (!checksize(&image, &message)) {
cerr << "Image too small for message\n";
return EXIT_FAILURE;
}
copyimage(&image, &image_out);
encodemsg(&image, &message, &image_out, argv[4]);
}
return 0;
}