图片隐写术

Posted by mjTree on April 8, 2024

更新于:2024-04-11 16:40

一、图片隐写术

隐写术是一门信息隐藏的技巧与科学,所谓信息隐藏指的是不让除预期的接收者之外的任何人知晓信息的传递事件或者信息的内容。载体文件相对隐秘文件的大小(指数据含量)越大,隐藏后者就越加容易。因为这个原因,数字图像在因特网和其他传媒上被广泛用于隐藏消息。

传闻早期有一家公司B爬取了同行另一家公司A官网的照片挂在自己的网站主页上,最后B被A告了。证据就是A公司官网的图片中通过隐写术的技术将信息写入图片中,而这种方法,肉眼是看不出来的。

二、图片隐写术的实现

left right

上面两张图片肉眼基本看不出来有什么差别,但是右边那张是处理过的,里面添加了一些信息,原理是 LSB(Least Significant Bit) 逻辑。

具体做法就是将秘密信息嵌入媒体文件的最低有效位:

在一份图像文件中,每个像素由三个字节的数据组成,对应于红、绿、蓝三种颜色。某些图像格式(png)会额外添加第四个字节,对应于透明度,即alpha。
LSB 隐写术改变了其中每个字节的最后一位,以此隐藏一位数据,而修改像素值的最后一位从图片上以肉眼看不出任何变化。

例如:一个24位的位图中的每个像素的三个颜色分量(红绿蓝)各使用8个比特来表示。如果我们只考虑蓝色的话,就是说有2种不同的数值来表示深浅不同的蓝色。而像 11111111 和 11111110 这两个值所表示的蓝色,人眼几乎无法区分。因此,这个最低有效位就可以用来存储颜色之外的信息,而且在某种程度上几乎是检测不到的。如果对红色和绿色进行同样的操作,就可以在差不多三个像素中存储一个字节的信息。

操作图片像素点的话,我们这边需要用到pythonpillow库来完成,下面的代码是针对 png 格式实现的逻辑。

from PIL import Image


def makeImageEven(image):
    pixels = list(image.getdata())
    evenPixels = [(r >> 1 << 1, g >> 1 << 1, b >> 1 << 1, t >> 1 << 1) for [r, g, b, t] in pixels]
    evenImage = Image.new(image.mode, image.size)
    evenImage.putdata(evenPixels)
    return evenImage


def constLenBin(int):
    binary = "0" * (8 - (len(bin(int)) - 2)) + bin(int).replace('0b', '')
    return binary


def encodeDataInImage(image, data):
    evenImage = makeImageEven(image)
    binary = ''.join(map(constLenBin, bytearray(data, 'utf-8')))
    if len(binary) > len(image.getdata()) * 4:
        raise Exception('在此图像中不能编码超过' + len(evenImage.getdata()) * 4 + '位')
    encodedPixels = [(r + int(binary[index * 4 + 0]), g + int(binary[index * 4 + 1]), \
                      b + int(binary[index * 4 + 2]), t + int(binary[index * 4 + 3])) \
                         if index * 4 < len(binary) else (r, g, b, t) for index, (r, g, b, t) \
                     in enumerate(list(evenImage.getdata()))]
    encodedImage = Image.new(evenImage.mode, evenImage.size)
    encodedImage.putdata(encodedPixels)
    return encodedImage


def binaryToString(binary):
    index = 0
    string = []
    rec = lambda x, i: x[2:8] + (rec(x[8:], i - 1) if i > 1 else '') if x else ''
    fun = lambda x, i: x[i + 1:8] + rec(x[8:], i - 1)
    while index + 1 < len(binary):
        chartype = binary[index:].index('0')
        length = chartype * 8 if chartype else 8
        string.append(chr(int(fun(binary[index:index + length], chartype), 2)))
        index += length
    return ''.join(string)


def decodeImage(image):
    pixels = list(image.getdata())
    binary = ''.join([str(int(r >> 1 << 1 != r)) + str(int(g >> 1 << 1 != g)) + str(int(b >> 1 << 1 != b)) \
                      + str(int(t >> 1 << 1 != t)) for (r, g, b, t) in pixels])
    locationDoubleNull = binary.find('0000000000000000')
    endIndex = locationDoubleNull + (
            8 - (locationDoubleNull % 8)) if locationDoubleNull % 8 != 0 else locationDoubleNull
    data = binaryToString(binary[0:endIndex])
    return data


encodeDataInImage(Image.open("doraemon.png"), '我腿短呀').save('doraemon1.png')
print(decodeImage(Image.open("doraemon1.png")))

实际使用时,我们可以借用stegano库提供的方法来完成图片隐写术,代码如下:

import base64

from stegano import lsb


def lsb_hide(image_path, message):
    content = str(base64.b64encode(message.encode('utf-8')), 'utf-8')
    hide_image = lsb.hide(image_path, content)
    hide_img_path = image_path.rsplit('.', 1)[0] + '_hide.' + image_path.rsplit('.', 1)[1]
    hide_image.save(hide_img_path)
    hide_image.close()
    return hide_img_path


def lsb_detection(image_path):
    result = ''
    content = lsb.reveal(image_path)
    if content:
        result = str(base64.b64decode(content), 'utf-8')
    return result


img_path = lsb_hide('doraemon.png', '我腿短呀')
print(lsb_detection(img_path))

三、其他的隐写术

从数字角度看,隐写术主要有五种类型。分别是:文本隐写术、图像隐写术、视频隐写术、音频隐写术、网络隐写术,其中图像隐写术上面已经介绍了。

1、文本隐写术

文本隐写术指的是将信息隐藏到文本文件中。这包括更改现有文本的格式、更改文本中的字词、使用上下文无关语法生成可读文本或生成随机字符序列。

这里推荐一个pythontext_blind_watermark库, 用于给文本加盲水印,可以将一段隐秘信息嵌入到明文中,嵌入前后的明文肉眼观察无变化。实际上字符串前后的长度不一致,原理是在一些位置插入了不可见字符,具体可以看源码实现逻辑。

from text_blind_watermark import TextBlindWatermark2
 
password = 'get off work on time'
text = '这句话中有盲水印,你能提取出来吗?'
watermark = 'get off work on time'
 
text_blind_wm = TextBlindWatermark2(password=password)
 
text_with_wm = text_blind_wm.embed(text, watermark)
print(text_with_wm)

2、音频隐写术

这种方法是将秘密信息嵌入音频信号中,从而改变相应音频文件的二进制序列。与其他方法相比,将秘密信息隐藏到数字声音里的操作更加困难。

3、视频隐写术

这种方法是将数据隐藏到数字格式的视频中。视频隐写术可以将大量数据隐藏到动态的图像和声音流中(将数据嵌入未压缩的原始视频中,然后再压缩)。

4、网络隐写术

网络隐写术,有时也称协议隐写术,是一种将信息嵌入数据传输所使用的网络控制协议(如 TCP、UDP、ICMP 等)的技术。

近年来,隐写术主要用在以数字数据为载体、以网络为高速传输通道的计算机上。用途包括:避免审查、数字水印、保护信息等。但是也有不少网络攻击是利用隐写术来实施的,如何检测隐写术以及避免其攻击也是一件很重要的事情。

四、小结

本篇文章通过介绍了图片隐写术,来引出隐写术的作用。隐写术作为一种隐藏消息或信息的艺术和科学,可以在不引起注意的情况下传递敏感信息,保护信息的机密性和安全性。但是隐写术也是一把双刃剑,使用的同时也要防止被人攻击。

老博客原文链接