/* 转载请注明出处:http://www.cnblogs.com/Martinium/p/wechall_stegano.html */

  最近迷上了 www.wechall.net 网站,里面都是些与计算机相关的题目挑战。题目又分很多类型,例如:加密与解密、隐写术、网络攻防、趣味编程、数学逻辑等。题目有的简单,有的很难,需要一些知识和技巧。与其他题目挑战的网站不同的是,在其他类似性质的网站注册的用户可以绑定到 WeChall 网站,然后 WeChall 提供排名信息,而且也分得很细,什么按总分全球排名、什么在自己国家的排名、什么解答某种语言网站题目的排名等。可以从解题的人数判断题目的难易程度,有兴趣的朋友可以去注册,解题中也能学到很多知识。国内的学校和公司也有举办网络攻防大赛。与玩 ACM 解题不同的是:ACM 提供给定的输入和输出,中间的黑盒部分需要你来完成,需要提供一种更好更快的算法;这类攻防挑战题目可能需要更多的计算机知识,从一堆垃圾数据中发现有用的信息、越过不同的障碍终于发现答案等。

  注:Steganography 是隐写术,它与 Cryptography (密码术)是不同的。

题目1 stegano1 给了一张彩色的 BMP 图片,让你从中发现答案。

  不管是图片、音频文件,还是二进制文件,首先都可以尝试以文本文件打开,查看是否存在可疑的字符串。文件太大了就不要这么做了,鼠标会转圈好久的。
图片很小,才 102 bytes。将图片文件拖到文本编辑器里,就可以看到答案了。Notepad++ 是 Windows 上最好的文本编辑器,没有之一。体积小巧,功能齐全。
Linux 上用 gedit 打开,或者使用 vim 命令,输入 :%!xxd 切换到二进制模式,答案显而易见。
xxd 是另外一个命令,以十六进制现实内容。再输入 :%!xxd -r 命令执行,切换到原来的摸样(r 是 reverse )。
注意 vim 的 -b 选项,是否以二进制模式打开。图片是二进制文件,所以要加此选项,否则处理会有问题。
还有一个命令 strings,来的更快,直接输出文件中可打印的字符,通常用来显示二进制中的字符串常量。
strings 命令的选项 -n VALUE 或 --bytes=VALUE 选项控制输出连续字符的最短长度,这个的默认值 VALUE 是 4。
还有 -t RADIX 或 --radix=RADIX 选项以后也会用上的,用来显示每个字符串的偏移量。C++ 的 static 字符串变量需要这个偏移量索引字符串。

  你我都是做题的,但是怎么出题呢?破解别人的软件,写一个序列号注册机相对容易。但是如何写一个加壳、混淆的难以破解的程序就有点难度了,毕竟建房子拆房子简单。
我们需要了解一下 BMP 文件格式,BMP 图片很占空间,没有压缩,或者用 RLE (Run-Length Encoding)压缩一下,体积大所以很少在网络上传播。 BITMAPFILEHEADERBITMAPINFOHEADER

    const char* buffer = "put your message here";
FILE *file = fopen(path, "wb");
if (!file)
{
fprintf(stderr, "can't open %s\n", path);
return false;
} BITMAPFILEHEADER file_header;
BITMAPINFOHEADER info_header; file_header.bfType = (WORD)('B'|'M'<<); // Windows BMP file tag
file_header.bfSize = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+len;
file_header.bfReserved1 = ;
file_header.bfReserved2 = ;
file_header.bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER); info_header.biSize = sizeof(BITMAPINFOHEADER);
info_header.biWidth = width;
info_header.biHeight = height;
info_header.biPlanes = ;
info_header.biBitCount = ;
info_header.biCompression = ; // BI_RGB, An uncompressed format.
info_header.biSizeImage = len; // size in bytes. This may be set to zero for BI_RGB bitmaps
info_header.biXPelsPerMeter = ;
info_header.biYPelsPerMeter = ;
info_header.biClrUsed = ;
info_header.biClrImportant = ; fwrite(&file_header, sizeof(file_header), , file);
fwrite(&info_header, sizeof(info_header), , file);
fwrite(buffer, len, , file);
fclose(file);

  这样写,太容易被发现了。我们可以通过对字符串先加密再隐写,或者用循环移位、异或操作增加难度。
  知道 BMP 格式支持颜色索引模式(GIF、PNG 格式也支持),图片里会有一个调色板用来存储使用的颜色值,相当于一个数组存储 RGB 值,然后图片只用数组索引值找到对应的颜色并显示出来,于是你也可以这样隐藏你的信息到图片中。我们用两种颜色作图,在白纸写黑字,存储为索引模式图,于是调色板会有两个值 #000000 和 #FFFFFF,颜色只需一位 0/1 就可以表示黑白颜色了(查表法从24位压缩到1位,这个压缩是可观的,其实还可以用 RLE 行程编码对图片继续无损压缩)。将数组中的 #000000 值篡改成 #FFFFFF,也就是黑色改成白色,其他数据不用动,保存后用再打开图片看发现,字消失了!写一些想说的话,然后把图片发给暗恋对象,对方会说你怎么发了一张空白图片,于是留给对方想象的空间。说了这么多,自己动手尝试一下吧~

题目2 Stegano Attachment 
答案在图片中吗?不,答案在附件(attachment)里。等你做出了这道题,就明白前面一句话的意思。有时候,题目会给出提示容易忽视的信息,而这个可能就是解题的关键。
稍微留心一下图片 http://www.wechall.net/challenge/training/stegano/attachment/attachment.php ,是不是很坏?后缀被人改成 PHP 网页后缀。用文本编辑软件打开,发现一堆乱码。不同文件会有不同的文件头,即不同的 Magic Number,如上面的 BMP 格式图片的 “BM” 标志,PNG 图片有八字节头 “\x89PNG\r\n\x1A\n”,用 Visual Studio 调式 C++ 代码出现很多的“烫烫烫烫”,其实是编译期初始化栈空间为 “\xCC\xCC\xCC\xCC”(0xCC 对应 x86 的 INT 3 中断指令,你应该知道为什么这么做),编译 Java 文件生成的 .class 文件的文件头有 “CAFEBABE”。从 attachment.php 里我们看到了字符 "JFIF",应该就是 JPG 图片了。更好的方法是用 linux 上的 file 命令查看文件类型,文件名后缀随你怎么该。改成 .jpg 后缀后,打开图片后,是电影 Ghostbusters (1984) 的海报,我们追到了这里,电影我没看过,看介绍好像很不错的样子。一边下载一边解题吧~

martin@M2037:~/Desktop$ file attachment.php
attachment.php: JPEG image data, JFIF standard 1.01

我们稍微了解一下 JPEG 格式规范。JPEG 文件以 “\xFF\xD8” 打头,以 “\xFF\xD9” 结尾。JPEG/JFIF 文件会出现 “JFIF” 字符,JPEG/Exif 文件会出现 "Exif" 字符。

jpg 图片采用有损压缩,不支持 alpha 通道。编码时,颜色从 RGB 色彩空间转到 YUV 色彩空间,根据颜色分量的重要性和人眼的感知剔除不重要的信息,缩减采样 4:4:4 4:2:2 4:2:0,接着 分割图片成 8x8 子区域进行离散余弦变换,量化编码,支流系数用差分编码,交流系数用 RLE 编码。

我们发现图片是以 "\xFF\xD8" 开头的,但是结尾却不是 "\xFF\xD9",好像明白了什么。
我们用题目1学到的 strings 命令搜到了可疑字符串 solution.txt 和 solution.txtPK,恰恰是放文件最后的。
PK 字眼熟悉不?最常用的 ZIP 格式压缩文件头的 magic number 是 “PK\x03\x04”,而且文件中确实存在这四个字节。而且这四个字节的前面也恰好是 "\xFF\xD9"。想想之前的题目 attachment。看来,是谁把一个 ZIP 压缩文件追加到一个 JEPG 图像文件的后面了。我们需要提取出 ZIP 文件,答案离我们不远了。。

ZIP 文件的偏移量用题目1学到的 vim :%!xxd 搜索定位查看并计算,或者用 grep 命令搜索下。
我们需要复制从 0004f06 到最后 0004f8d (长度 0x88=136)到新的文件里去。

复制粘贴文件或者段落用 Ctrl+C、Ctrl+V,可是复制二进制文件流呢,也可以这么做吗?好像不行。
Notepad++ 不愧是最好用最实用的文本编辑器,选择菜单 Edit -> Paste Special,二级菜单中出现了 Copy|Cut|Paste Binary Cotent,接下来你知道怎么做啦~
或者使用 linux 的 dd 命令,还记得大学初学 linux 操作系统时,让记那么多命令和参数干嘛,现在就派上用场了。
如果你不嫌麻烦,甚至可以自己编写小段 C/C++ 代码,打开文件、复制文件、关闭文件。

martin@M2037:~/Desktop$ printf "%d\n" 0x0004f06
20230
martin@M2037:~/Desktop$ dd if=attachment.jpg of=solution.zip skip=20230 bs=1 count=136
136+0 records in
136+0 records out
136 bytes (136 B) copied, 0.0021692 s, 62.7 kB/s

martin@M2037:~/Desktop$ unzip solution.zip
Archive: solution.zip
inflating: solution.txt

打开 solution.txt 文件,就是最终的答案了。

从中,我们学习了另外一种隐藏文件的方法,将一个文件附属到另外一个文件末尾,打开的时候只解析第一个文件,附属文件被忽略了。
将信息 message.txt 隐藏到图片 carrier.jpg 中(注意命令行中的先后顺序):
linux 终端下, cat carrier.jpg message >> mixed.jpg
Windows 控制台下,copy carrier.jpg /b + message.txt /a mixed.jpg
cat 是 concatenate 的简写,如果有多个序列文件(比如 file1.txt file2.txt file3.txt)合并,可以优雅地写成 cat file{1,2,3}.txt >> new.txt
/A /B 分别代表文本文件和二进制文件,详情请查看 copy /? 命令。

题3 LSB - The least significant bit

  WeChall 网站使用了 cookie 和 session 来产生动态的答案,所以不同的人在每一次登录时,答案是不同的。我在这里贴出我的答案,你复制过去一般就是错误的答案,这样可以提防不动脑子只粘贴答案的人。
这道题目有链接提示,链接的颜色是 #EEE,接近背景色白色 #FFF,还是很明显看得出来的,看来作者没有有意隐藏这一提示。家里网没能打开这个链接,原因还没查出,公司网可以打开。steganabara 原来是一个 .jar 包,下载 steganabara.jar 文件并运行程序 java -jar steganabara-1.1.1.jar ,将隐藏信息的图片拖进来。似乎是很正常的一张图片,LED 数码管显示了作者的大名 Gizmore。查了一下,gizmor 英文是小发明的意思。当然,这只是作者创作题目的署名,与解题没有任何关系。解题偶尔能有额外的收获。

  这张图片是 RGB 格式,没有 alpha 通道,随便勾选 RGB 某一通道的某一位,共有 3*8=24 种单项选择(复合选择暂未考虑进来,从简单到复杂嘛~)。点击菜单 Filter -> Bit Mask,从低位到高位一个一个勾选盲试的话,很容易试出答案来。思路是这样的,你的答案不一定是这样的。注意勾上 Amplify 选项,否则很难辨认。
  借助事实分析和判断,点击菜单 Analyse -> Histogram 给出 RGB 各分量的柱状图,范围是 0~255,有 3 个连续分布图和 1 个离散分布(斑马带)图,可以怀疑出是哪个通道有问题。相邻柱子的间距是周期,可以推断出是哪个比特位被篡改,导致出现重复的模式。

  LSB (least significant bit) 最低有效位,权重最小的位,也是修改最不容易引起注意的位;与之对应的是 MSB (most significant bit) 最高有效位,修改后很容易被察觉。上面之所以需要增强信号(Amplify) 就是因为隐藏的信息在 LSB 上不易被发觉。这个是图片与图片的操作,我想到了一种字符串与图片混合的办法。
  将字符串信息(也就是需要隐藏的信息 ASCII 码值)拆开成比特位,分别藏在图像中的连续像素中的最低位。思路是读取承载信息的图片,然后把需要隐藏的信息以比特流的形式连续覆盖图片 RGB 值的最低位,为了进一步增加难度,可以只覆盖其中某一个分量,可以不用放最低位,如果比特位用完了,信息可以从头循环再覆盖,直到覆盖所有。例如此题采用的是 Blue 通道的第 4 位。

#include <stddef.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h> #include "libpng16/png.h" int main()
{
png_image image; memset(&image, , sizeof image);
image.version = PNG_IMAGE_VERSION;
image.format = PNG_FORMAT_RGB; // no alpha channel const char* message = "No 300 taels of silver buried here.";
const int BITS = strlen(message) * CHAR_BIT; const char* original = "g2YMC634NSqoEhXe.png";
const char* stegano = "stegano.png";
if(png_image_begin_read_from_file(&image, original))
{
size_t size = PNG_IMAGE_SIZE(image);
png_bytep buffer = (png_bytep)malloc(size);
if(buffer != NULL)
{
// blend text bits to image's least significant bit
int bit = ;
for(size_t i = ; i < size; ++i)
{
char test = (original[bit/] & (<<(bit%))) ?:;
buffer[i] = (buffer[i]&0xFE) | test; ++bit;
if(bit >= BITS)
bit = ; // repeat message
} if(png_image_finish_read(&image, NULL/*background*/, buffer,
/*row_stride*/, NULL/*colormap for PNG_FORMAT_FLAG_COLORMAP*/))
{
if(png_image_write_to_file(&image, stegano, /*convert_to_8bit*/,
buffer, /*row_stride*/, NULL/*no colormap*/))
fprintf(stdout, "write to file %s successfully.\n", stegano);
else
fprintf(stderr, "write %s: %s\n", stegano, image.message); free(buffer);
}
else
{
fprintf(stderr, "read %s: %s\n", original, image.message); /* This is the only place where a 'free' is required; libpng does
* the cleanup on error and success, but in this case we couldn't
* complete the read because of running out of memory.
*/
png_image_free(&image);
}
}
else
fprintf(stderr, "out of memory: %lu bytes\n",
(unsigned long)PNG_IMAGE_SIZE(image));
}
else
fprintf(stderr, "%s: %s\n", original, image.message); return ;
}

lsb.cpp

to be continued...

wechall.net/stegano 解题心得的更多相关文章

  1. leetcode网解题心得——61. 旋转链表

    目录 leetcode网解题心得--61. 旋转链表 1.题目描述 2.算法分析: 3.用自然语言描述该算法 4.java语言实现 5.C语言实现 leetcode网解题心得--61. 旋转链表 1. ...

  2. Zerojudge解题心得

    我进入娄山中学已经有1年多了,也就是说我学习编程也有1年多了,在这一年多的时间中,我已经对编程有了初步的了解.其实只要抓住平时的空闲时间加以利用,哪怕每个星期就做那么三四题,经过了一段时间沉淀,也会有 ...

  3. 我的ZJ解题心得

    想要学好程序设计第一是要养成你的编程思维,也就是你对编程的一种概念和思维定式,长期的解题会让你产生解题经验进而形成一种思维定式,比如看到一个题目就立即想出这题要用什么方法解题这样.编程思维我认为还包括 ...

  4. POJ1159解题心得

    题目:http://poj.org/problem?id=1159 刚开始,从样例的特征去思考.总让我从回文数的角度去思考,想出几个方案,可都用了数据去检验,发现不行.如:ABCDDCB,BACDCA ...

  5. 树状数组:CDOJ1583-曜酱的心意(树状数组心得)

    曜酱的心意 Time Limit: 3000/1000MS (Java/Others) Memory Limit: 131072/131072KB (Java/Others) Description ...

  6. 题目1203:IP地址

    题目: http://ac.jobdu.com/problem.php?pid=1203 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:3052 解决:1504 题目描述: 输入一个ip地 ...

  7. HDU 2602 Bone Collector WA谁来帮忙找找错

    Problem Description Many years ago , in Teddy’s hometown there was a man who was called “Bone Collec ...

  8. 2106 Problem F Shuffling Along 中石油-未提交-->已提交

    题目描述 Most of you have played card games (and if you haven’t, why not???) in which the deck of cards ...

  9. 2101 Problem A Snake Filled

    题目描述 “What a boring world!”Julyed felt so bored that she began to write numbers on the coordinate pa ...

随机推荐

  1. 给RecyclerView最纯粹的下拉刷新和上拉加载更多

    转自 http://blog.csdn.net/jerrywu145/article/details/52225898 http://www.jianshu.com/p/3bf125b4917d

  2. jsonp 跨域请求

    背景: JavaScript是一种在Web开发中经常使用的前端动态脚本技术.在JavaScript中,有一个很重要的安全性限制,被称为"Same-Origin Policy"(同源 ...

  3. jsp通过session传递checkbox中的值

    获取checkbox中的值(第一个页面) <% String foodName[]=request.getParameterValues("chioce"); //“chio ...

  4. 【BZOJ-1340】Escape逃跑问题 最小割

    1340: [Baltic2007]Escape逃跑问题 Time Limit: 5 Sec  Memory Limit: 162 MBSubmit: 264  Solved: 121[Submit] ...

  5. jcaptcha sample 制作验证码

    Skip to end of metadata Created by marc antoine garrigue, last modified by Jeremy Waters on Feb 23, ...

  6. bzoj2503&poj3387[NEERC2006]IdealFrame

    其实只是把别人的题解强行扩写了 写这篇题解之前我不会的预备知识: 欧拉通路:从图中一个点出发不重复地遍历所有边的路径(可以停在另一个点) 欧拉回路:从图中一个点出发不重复地遍历所有边的回路(必须回到出 ...

  7. IBatis 批量插入数据之SqlBulkCopy

    public void AddLetters(IList<int> customerIds, string title, string content, LetterEnum.Letter ...

  8. margin和padding的区别

    目前web2.0已经越来被人们认可,因为喜欢搞web开发的人员不得不硬着头皮去学习web2.0的标准,其中很重要的一条就是新的布局规则,div+css.以前基本上是用table布局的,这种传统的方式简 ...

  9. C# byte[]、struct、intptr等的相互转换

    1.struct byte[]互相转换 //struct转换为byte[] public static byte[] StructToBytes(object structObj) { int siz ...

  10. Java构造方法