无良小编想和泥玩捉迷藏!(基于图像的LSB隐写术科普)

无良小编想和泥玩捉迷藏!(基于图像的LSB隐写术科普)

作者:@Cytosine
审校:@Murasaki

0x00 先放图

窝究竟在蕾(lao)姆(po)的皂片里面藏了什么?

泥也可以通过这个下载地址下载本科普中的全部内容 >ω<

大佬们现在就可以试一下辣!很简单!

嘛~ 如果泥还不知道该怎么办,那么阅读本文,泥就会找到答案辣!

0x01 什么是隐写术?

维基百科对隐写术的定义:

隐写术是一门关于信息隐藏的技巧与科学,所谓信息隐藏指的是不让除预期的接收者之外的任何人知晓信息的传递事件或者信息的内容。

一般来说,隐写的信息看起来像一些其他的东西,例如一张购物清单,一篇文章,一篇图画或者其他“伪装”(cover)的消息。

换而言之,隐写不同于加密,加密是一段你看不懂的东西,隐写是一段你似乎能看懂的东西,但是实际上不是表面看上去那么简单。

而对应把隐藏的东西提取出来的技术称为隐写分析

接下来,无良小编就来科普一个非常非常非常简单的以数字图像为载体的 LSB 算法与相应的隐写分析~

0x02 LSB算法

像素与RGB

计算机保存的图像是以数值保存每一个像素点。

数字图像有很多种,比如二值图像,每个像素点不是 0 ,就是 1 ;再比如灰度图像,每个像素取值从 0 到 255 。

通道:

通道,是数字图像中存储不同类型信息的灰度图像。一个图像最多可以有数十个个通道,常用的 RGB 和 Lab 图像默认有三个通道。(摘自维基百科)

RGB,这里指灰度图像里的以 RGB 模式存储数字图像的模型。其中,R 是 Red 的首字母,G 是 Green 的首字母,B 是 Blue 的首字母,它们分别代表一个通道,在每个通道上,保存一个代表该通道亮度的数字,取值范围从 0 到 255 。

举个例子:

以上面那张图为例,窝们用 Python 和第三方库 PIL(pillow) 读取它的第一个像素值。

1
2
3
4
from PIL import Image
img = Image.open('Rem_qrcode.png')
print(img.getpixel((0, 0)))

运行结果:

1
(73, 158, 225)

Python 返回了一个元组,其中有三个元素,分别对应 RGB 三个通道的亮度值,R 通道的亮度值为 73 ,G 通道亮度值为 158 ,B 通道亮度值为 225 ,三个通道的颜色混合在一起,也就构成了第一个像素点所显示的颜色~

肉眼难分的“色号”

窝们用 Python 生成一个每隔 30 个像素,R 通道上亮度值减 1 的图像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from PIL import Image
def putPixel(img, width_start, height, rgb):
"""
在 img 中,从 width_start 开始处,将 30 个像素填充为 rgb 。
"""
for h in range(height):
for i in range(30):
img.putpixel((width_start + i, h), rgb)
return
if __name__ == '__main__':
(width, height) = (30, 30)
img = Image.new('RGB', (width * 10, height))
(r, g, b) = (255, 0, 0)
for i in range(10):
putPixel(img, i * 30, height, (r - i, g, b))
img.save('tmp.png')
img.show()

生成的图像

似乎……比专柜上的口红色号还难分诶……QAQ

(扯个皮,口红试色的时候,专柜的灯光比平时的灯光亮好多倍,所以会导致一些深的颜色看起来很浅,选口红的时候要注意哦,窝交了好些智商税才想明白这个道理QAQ)

所以呢,如果把像素的加一或减一,肉眼根本分辨不出来~那么,窝们就可以利用这个藏一点儿小秘密辣!

在用二进制表示数值中,对应于把像素加一减一,也就是把这个二进制的最低位替换掉,也就是最低有效位(Least Significant Bit, LSB)算法

0x03 开篇那张图

开篇那张图,窝就是把一张神秘图像(此处手动滑稽)二值化了一下,藏到了蕾姆老婆的图像的 R 通道的最低位中惹。

所以,得到那张神秘图像的办法,就是读取蕾姆老婆的图像的 R 通道的最低位,根据最低位的值,0 或者 1 ,还原出原来的图像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from PIL import Image
img = Image.open('Rem_secret.png')
(width, height) = img.size
secretImg = Image.new('RGB', (width, height))
for h in range(height):
for w in range(width):
(r, g, b) = img.getpixel((w, h))
lsb = r % 2
if lsb == 0:
secretImg.putpixel((w, h), (0, 0, 0))
else:
secretImg.putpixel((w, h), (255, 255, 255))
secretImg.save('secret.png')
secretImg.show()

所以,快去试一下窝到底藏了什么吧~

参考资料

最后的最后

好吧,其实窝就是想让泥萌扫下二维码

为了让泥萌扫下这个二维码,窝连蕾(lao)姆(po)都不放过,内心不安,于是只好自称无良小编来缓解一下内心的愧疚QAQ 泥萌不要打窝QAQ

另外,本人水平有限,如有错误,敬请指正,非常感谢 >ω<

窝很可爱,请给窝钱