Steganography in C

user1978536 picture user1978536 · Mar 27, 2013 · Viewed 9.4k times · Source

Trying to do basic Steganography on a PPM Image.

I have the basic algorithm completed. Read in the file, check the header starts with P6, get the image width and height, and the pixel data.

I need to have a total of four methods: ReadPPM, WritePPM, WriteMsg and ReadMsg.

I have the ReadImg and WriteImg methods down, but where I am stuck is with my WriteMsg method. This is basic steganography that just writes each bit of the string to the last bit in each byte. The first 8 bytes are suppose to contain the size of the string being hidden, then each byte after that starts the hidden message.

My idea was to create a massive array that holds the binary code for the size of the string, then the binary code of the string itself. I'm just trying to figure out how I would take that array and add it to each byte in the image.

Any help is much appreciated. Here is my current code:

#include<stdio.h>
#include<stdlib.h>

typedef struct {
     unsigned char red,green,blue;
} PPMPixel;

typedef struct {
     int x, y;
     PPMPixel *data;
} PPMImage;

#define CREATOR "RPFELGUEIRAS"
#define RGB_COMPONENT_COLOR 255

static PPMImage *readPPM(const char *filename)
{
    char buff[16];
    PPMImage *img;
    FILE *fp;
    int c, rgb_comp_color;
    //open PPM file for reading
    fp = fopen(filename, "rb");
    if (!fp) {
         fprintf(stderr, "Unable to open file '%s'\n", filename);
         exit(1);
    }

    //read image format
    if (!fgets(buff, sizeof(buff), fp)) {
         perror(filename);
         exit(1);
    }

    //check the image format
    if (buff[0] != 'P' || buff[1] != '6') {
         fprintf(stderr, "Invalid image format (must be 'P6')\n");
         exit(1);
    }

    //alloc memory form image
    img = (PPMImage *)malloc(sizeof(PPMImage));
    if (!img) {
         fprintf(stderr, "Unable to allocate memory\n");
         exit(1);
    }

    //check for comments
    c = getc(fp);
    while (c == '#') {
    while (getc(fp) != '\n') ;
         c = getc(fp);
    }

    ungetc(c, fp);
    //read image size information
    if (fscanf(fp, "%d %d", &img->x, &img->y) != 2) {
         fprintf(stderr, "Invalid image size (error loading '%s')\n", filename);
         exit(1);
    }

    //read rgb component
    if (fscanf(fp, "%d", &rgb_comp_color) != 1) {
         fprintf(stderr, "Invalid rgb component (error loading '%s')\n", filename);
         exit(1);
    }

    //check rgb component depth
    if (rgb_comp_color!= RGB_COMPONENT_COLOR) {
         fprintf(stderr, "'%s' does not have 8-bits components\n", filename);
         exit(1);
    }

    while (fgetc(fp) != '\n') ;
    //memory allocation for pixel data
    img->data = (PPMPixel*)malloc(img->x * img->y * sizeof(PPMPixel));

    if (!img) {
         fprintf(stderr, "Unable to allocate memory\n");
         exit(1);
    }

    //read pixel data from file
    if (fread(img->data, 3 * img->x, img->y, fp) != img->y) {
         fprintf(stderr, "Error loading image '%s'\n", filename);
         exit(1);
    }

    fclose(fp);
    return img;
}

void writePPM(const char *filename, PPMImage *img)
{
    FILE *fp;
    //open file for output
    fp = fopen(filename, "wb");
    if (!fp) {
         fprintf(stderr, "Unable to open file '%s'\n", filename);
         exit(1);
    }

    //write the header file
    //image format
    fprintf(fp, "P6\n");

    //comments
    fprintf(fp, "# Created by %s\n",CREATOR);

    //image size
    fprintf(fp, "%d %d\n",img->x,img->y);

    // rgb component depth
    fprintf(fp, "%d\n",RGB_COMPONENT_COLOR);

    // pixel data
    fwrite(img->data, 3 * img->x, img->y, fp);
    fclose(fp);
}

void writeMsg(PPMImage *img, char *s)
{   

    int i;
    int len;
    len = sizeof(s);

    if (img)
    {
         j = 0;
         for (i=0; i < img->x * img->y; i++)
         {
              while(j < 8)
              {
                   if(len & 0x80)   
                   {
                        img->data[i].red= img->data[i].red | 0x01;
                   }
                   else 
                   {
                        img->data[i].red= img->data[i].red & 0xFE;
                   }

                   len=len << 1;
                   j++;

                   if (len & 0x80)  
                   {
                        img->data[i].green= img->data[i].green | 0x01;
                   }
                   else
                   {
                        img->data[i].green= img->data[i].green & 0xFE;
                   }


                   len = len << 1;
                   j++;

                   if (len & 0x80)  
                   {
                        img->data[i].blue= img->data[i].blue | 0x01;
                   }
                   else
                   {
                        img->data[i].blue= img->data[i].blue & 0xFE;
                   }
                   j++;
              }
         }
    }
}

Answer

Patashu picture Patashu · Mar 27, 2013

To extract just a single bit from a byte, do this:

bit(i) = byte >> i & 0x1

This shifts the byte's bits to the right i times, and then ands it with 0000 0001 (such that all bits except the lowest are zeroed, and the lowest bit is 0 for 0 and 1 for 1).

You can do similar for 16 bit short, 32 bit int, 64 bit long... And even for the chars of a string. You can use sizeof(char) to see how many bytes are in a char.

But of course you'll have more than one char (or long or int or...) to extra bits from. To decide which element to extra a bit from:

If you want the ith bit and elements are x bits wide, then get the i%x bit from element [i/x]

Now that you have this bit, to place it inside of a byte (or int or char or...), do:

steganographybyte = originalbyte&(~0x1) + bit

What this means is, you take the number ... 0000 0001, invert its bits so it's ... 1111 1110, and it with the original byte so all of its bits are preserved EXCEPT the lowest, then just add your bit in.