#include #include #include #include #include #include #include #include #include #define BUFSIZE 8192 #define SPLIT 128 // gcc -Wno-all -ggdb -O0 main.c -lz -o main && ./main hide mountains.png text.txt // Structure of each chunks // http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html#Chunk-layout // http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html#PNG-file-signature struct signature{ unsigned char bytes[8]; } __attribute__((packed)); // __attribute__((packed)) -> for removing padding // GCC add padding in the struct, 3 bytes //https://stackoverflow.com/questions/1841863/size-of-struct-in-c struct chunk{ unsigned char length[4]; char type[4]; } __attribute__((packed)); // http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.IHDR struct iHDR{ unsigned char width[4]; unsigned char height[4]; char depth; char type; char compression; char filter; char interlace; // Chunk crc unsigned char crc[4]; } __attribute__((packed)); /* PROTOTYPES */ static int hide_message(int, char **); static int unhide_message(int, char **); static int getHeader(int, char *); static size_t insertChunk(int, const char *, size_t, int); static void convert_from_uint(unsigned char *, const unsigned int); static void convert_from_bytes(const unsigned char *, unsigned int *); int main(int argc, char *argv[], char **envp){ int fd_r; // The two statements below check if the arguments passed are correct if (argc < 4){ printf("Usage: %s \n", argv[0]); return -1; } if (strcmp(argv[1], "hide") != 0 && strcmp(argv[1], "unhide") != 0){ printf("ok\n"); printf("usage: %s \n", argv[0]); return -1; } // https://man7.org/linux/man-pages/man2/open.2.html // The next two statements open the picture src and destination if ((fd_r = open(argv[2], O_RDONLY)) == -1){ printf("Failed to open the png file %s\n", argv[1]); return -1; } if (strcmp(argv[1], "hide") == 0){ hide_message(fd_r, argv); } if (strcmp(argv[1], "unhide") == 0){ unhide_message(fd_r, argv); } close(fd_r); } /* * In this function, we will hide the text provide in argument of the program to * the destination image */ static int hide_message(int fd_r, char **argv){ int fd_w; int fd_r_text; size_t length; size_t length_text_to_hide; char *buf; char text[BUFSIZE]; char tmp[8]; struct chunk *s_chunk = {0}; char buffer[sizeof(struct signature) + sizeof(struct chunk) + sizeof(struct iHDR)]; int end = 0; if ((fd_w = open("dest.png", O_WRONLY | O_CREAT, 00644)) == -1){ printf("Failed to create the dest file\n"); close(fd_r); return -1; } // The two next statements, we open the text to hide and buffer it if ((fd_r_text = open(argv[3], O_RDONLY)) == -1){ printf("Failed to open the png file %s\n", argv[3]); close(fd_r); close(fd_w); return -1; } if ((length_text_to_hide = read(fd_r_text, text, BUFSIZE)) == -1){ printf("Failed to read the file %s\n", argv[3]); close(fd_r); close(fd_w); return -1; } // Get the header and the IHDR and copy to the dest file length = getHeader(fd_r, buffer); if (length == 0){ printf("Failed to get the png header\n"); close(fd_w); return -1; } write(fd_w, buffer, length); // TODO // For the text, we split it into different chunk. We split the size defined by the global variable SPLIT // Each chunk are inserted between differents chunks // We insert the text /*if (length_text_to_hide > SPLIT){ }*/ length = insertChunk(fd_w, text, length_text_to_hide, 0); // We will parse each chunk until the IEND chunk do{ unsigned int s = 0; // read the 8 bytes (for the chunk length and type) length = read(fd_r, tmp, 8); s_chunk = (struct chunk*)(tmp); // Write the chunk block (length and type) without the CRC write(fd_w, tmp, length); // Get the size of the data convert_from_bytes(s_chunk->length, &s); // We add 4 bytes for the CRC s += 4; // Identify the chunk if (s_chunk->type[0] == 'I' && s_chunk->type[1] == 'E' && s_chunk->type[2] == 'N' && s_chunk->type[3] == 'D'){ end = 1; } // The next statements allocate the buf with the chunk data size // and read the chunk data with the CRC buf = malloc(s); // read the file length = read(fd_r, buf, s); write(fd_w, buf, length); memset(tmp, 0, 8); free(buf); }while(end != 1); close(fd_w); close(fd_r_text); return 0; } static int unhide_message(int fd_r, char **argv){ int fd_w; int length; char buffer[sizeof(struct signature) + sizeof(struct chunk) + sizeof(struct iHDR)]; struct chunk *s_chunk; char tmp[8]; char *buf; int end = 0; // The statement below check if we can create the file for the output if ((fd_w = open(argv[3], O_WRONLY | O_CREAT, 00644)) == -1){ printf("Failed to write the output text file %s\n", argv[3]); close(fd_r); return -1; } // Get the header and the IHDR length = getHeader(fd_r, buffer); if (length == 0){ printf("Failed to get the png header\n"); close(fd_w); return -1; } // We will parse each chunk until the IEND chunk do{ unsigned int s = 0; unsigned char crc[4]; // read the 8 bytes (for the chunk length and type) length = read(fd_r, tmp, 8); s_chunk = (struct chunk*)(tmp); // Identify the chunk if (s_chunk->type[0] == 'I' && s_chunk->type[1] == 'E' && s_chunk->type[2] == 'N' && s_chunk->type[3] == 'D'){ end = 1; } // Get the size of the data convert_from_bytes(s_chunk->length, &s); // The next statements allocate the buf with the chunk data size // and read the chunk data with the CRC buf = malloc(s); // read the file length = read(fd_r, buf, s); if (s_chunk->type[0] == 's' && s_chunk->type[1] == 'T' && s_chunk->type[2] == 'E' && s_chunk->type[3] == 'G'){ write(fd_w, buf, length); } // Read the crc read(fd_r, crc, 4); memset(tmp, 0, 8); free(buf); }while(end != 1); close(fd_w); return 0; } /* * In this function, we put our chunk to the file * this chunk contain our text message to hide */ static size_t insertChunk(int fd_w, const char *buf, size_t text_length, int start){ int length; unsigned char crc[4]; unsigned int crc_z; struct chunk s_chunk; memset(crc, 0, 4); // We create our chunk layout and write to the file convert_from_uint(s_chunk.length, text_length); s_chunk.type[0] = 's'; s_chunk.type[1] = 'T'; s_chunk.type[2] = 'E'; s_chunk.type[3] = 'G'; write(fd_w, s_chunk.length, 4); write(fd_w, s_chunk.type, 4); length = write(fd_w, buf, text_length); // Generate the CRC and write it to the file crc_z = crc32(0L, Z_NULL, 0); crc_z = crc32(crc_z, s_chunk.type, 4); crc_z = crc32(crc_z, buf, text_length); convert_from_uint(crc, crc_z); length = write(fd_w, crc, 4); return length; } /* * This function convert the unsigned int 32 bits to bytes and store to the buf variable */ static void convert_from_uint(unsigned char *buf, const unsigned int uint){ buf[0] = (unsigned char)((uint >> 24) & 0xFFU); buf[1] = (unsigned char)((uint >> 16) & 0xFFU); buf[2] = (unsigned char)((uint >> 8) & 0xFFU); buf[3] = (unsigned char)(uint & 0xFFU); } /* * This function convert the bytes buf to unsigned int 32 bits */ static void convert_from_bytes(const unsigned char *buf, unsigned int *uint){ *uint = buf[3] + (buf[2] << 8) + (buf[1] << 16) + (buf[0] << 24); } /* * This function read thefile for getting the signature, the 8 first bytes * and get the IHDR which contains some information regarding the picture */ static int getHeader(int fd_r, char *buf){ size_t size_hdr = sizeof(struct signature) + sizeof(struct chunk) + sizeof(struct iHDR); int length; int i; unsigned int width; unsigned int height; struct signature *s_sign = {0}; struct iHDR *s_ihdr = {0}; // Get the PNG signature and the header length = read(fd_r, buf, size_hdr); if (length == 0) return 0; // Display signature data s_sign = (struct signature *)buf; printf("Signature\n"); for (i = 0; i < sizeof(struct signature); i++) printf("%x ", s_sign->bytes[i]); printf("\n\n"); // Display header data s_ihdr = (struct iHDR*)(buf + sizeof(struct signature) + sizeof(struct chunk)); printf("Size of the image:\n"); convert_from_bytes(s_ihdr->width, &width); convert_from_bytes(s_ihdr->height, &height); printf("Width: %upx\nHeight: %upx\n", width, height); return length; }