这个题目来自上周的BCTF比赛,题目是海报探秘(300),一张png图片中隐藏了KEY,解出KEY,具体报告,
请下载:http://download.csdn.net/detail/l0g1n/7042787
我对217战队的这个题目报告进行了学习,对他的代码进行注释,理解。
我至少学习到了三点:png文件的解压,文件对齐的处理,还有这优雅的代码。分享给大家:
#include <bits/stdc++.h> #include <zlib.h> typedef unsigned char Byte; //把二进制的大小转换为长度 inline unsigned convert_uint(Byte *b) { unsigned ret = 0; for(int i = 0;i < 4; i++) ret = ret << 8 | b[i]; return ret; } Byte chunk_len_buf[4]; Byte chunk_name[4]; Byte chunk_data[8 << 10]; Byte raw_data[8 << 20]; int main(int argc, char *argv[]) { //原始文件路径 FILE *fin = fopen(argv[1], "r"); //要输出的文件路径 FILE *fout = fopen(argv[2], "w"); //跳过png文件头部分 fseek(fin, 8 + 4 + 4 + 13 + 4, SEEK_CUR); //zlib中用来解压的模块 z_stream zs; //清0 memset(&zs, 0, sizeof(zs)); //初始化zs inflateInit(&zs); //设置用来保存解压后数据的路径 zs.next_out = raw_data; //大小 zs.avail_out = sizeof(raw_data); //读取节的长度 while(fread(chunk_len_buf, 1, 4, fin) == 4) { //转换为unsigned unsigned chunk_len = convert_uint(chunk_len_buf); //读取每个节的名称 fread(chunk_name, 1, 4, fin); //读取每一节的数据 fread(chunk_data, 1, chunk_len, fin); //跳过CRC校验部分 fseek(fin, 4, SEEK_CUR); //节的名称比较,只解压IDAT节 if(memcmp(chunk_name, "IDAT", 4) == 0) { //输入的数据 zs.next_in = chunk_data; //输入的长度 zs.avail_in = chunk_len; //执行解压 inflate(&zs, Z_SYNC_FLUSH); } } //解压结束 inflateEnd(&zs); //这里看了很长时间,查了资料才明白,1003×654是像素点的个数乘以4,转换为bits,最后加654,是因为1003除以4不整除,这里是字节对齐的处理。 unsigned raw_png_len = 1003 * 654 * 4 + 654; //out_len要生成新文件(被隐藏的数据)的长度,申请的内存空间-剩余可用的内存空间-图片应有的长度 unsigned out_len = sizeof(raw_data) - zs.avail_out - raw_png_len; //生成数据。 fwrite(raw_data + raw_png_len, 1, out_len, fout); fclose(fin); fclose(fout); return 0; }