如何使用隐写术在Python中隐藏图像中的秘密数据?

2021年11月16日15:33:06 发表评论 1,256 次浏览

本文带你学习如何在 Python 中使用隐写术最低有效位技术隐藏图像中的秘密数据,如何在Python中隐藏图像中的秘密数据?下面开始介绍:

什么是隐写术

隐写术是将文件、消息、图像或视频隐藏在另一个文件、消息、图像或视频中的做法。这个词隐写术是从希腊字衍生steganos(意为隐藏或覆盖)和graphe(指写作)。

Python如何隐藏图像中的秘密数据?黑客经常使用它来隐藏媒体文件(如图像、视频或音频文件)中的秘密消息或数据。尽管隐写术有许多合法用途,例如水印,但也发现恶意软件程序员使用它来掩盖恶意代码的传输。

在本教程中,我们将编写一个 Python 代码来使用称为最低有效位的技术隐藏文本消息。

什么是最低有效位

Python隐藏图像中的秘密数据示例 - 最低有效位(Least Significant Bit, LSB)是其中每个像素的最后比特被修改,并与数据位替换的技术。这种方法只对无损压缩的图片有效,也就是说文件是以压缩格式存储的,但是这种压缩不会导致数据丢失或修改,例如PNG、TIFF、BMP都是无损的——压缩图像文件格式。

你可能已经知道,图像由多个像素组成,每个像素包含三个值(红色、绿色、蓝色),这些值的范围从0到255,换句话说,它们是 8 位值。例如,值225是二进制的11100001,依此类推。

让我们举一个例子来说明这种技术是如何工作的,假设我想将消息“hi”隐藏到4x4图像中,以下是示例图像像素值:

[(225, 12, 99), (155, 2, 50), (99, 51, 15), (15, 55, 22),
(155, 61, 87), (63, 30, 17), (1, 55, 19), (99, 81, 66),
(219, 77, 91), (69, 39, 50), (18, 200, 33), (25, 54, 190)]

通过查看ASCII 表,我们可以将此消息转换为十进制值,然后再转换为二进制:

0110100 0110101

现在,我们逐个迭代像素值,在将它们转换为二进制之后,我们将每个最低有效位依次替换为该消息位 (例如225是11100001,我们将最后一位,右侧的位(1)替换为第一个数据位(0)等等)。

这只会将像素值修改为+1或-1 ,这在视觉上根本不明显,你也可以使用 2-Least Significant Bits 也可以将像素修改为-3到+3的范围。

这是生成的像素值(你可以自行检查):

[(224, 13, 99),(154, 3, 50),(98, 50, 15),(15, 54, 23),
(154, 61, 87),(63, 30, 17),(1, 55, 19),(99, 81, 66),
(219, 77, 91),(69, 39, 50),(18, 200, 33),(25, 54, 190)]

相关: 如何使用 hashlib 在 Python 中使用哈希算法。

Python 实现

Python如何隐藏图像中的秘密数据?现在我们了解了我们要使用的技术,让我们深入研究 Python 实现,我们将使用OpenCV来操作图像,你可以使用任何其他你想要的图像库(例如PIL):

pip3 install opencv-python numpy

打开一个新的 Python 文件并按照以下步骤操作:

import cv2
import numpy as np

Python隐藏图像中的秘密数据示例:让我们从实现一个将任何类型的数据转换为二进制的函数开始,我们将使用它在编码和解码阶段将秘密数据和像素值转换为二进制:

def to_bin(data):
    """Convert `data` to binary format as string"""
    if isinstance(data, str):
        return ''.join([ format(ord(i), "08b") for i in data ])
    elif isinstance(data, bytes) or isinstance(data, np.ndarray):
        return [ format(i, "08b") for i in data ]
    elif isinstance(data, int) or isinstance(data, np.uint8):
        return format(data, "08b")
    else:
        raise TypeError("Type not supported.")

下面的函数将负责将secret_data编码到图像中:

def encode(image_name, secret_data):
    # read the image
    image = cv2.imread(image_name)
    # maximum bytes to encode
    n_bytes = image.shape[0] * image.shape[1] * 3 // 8
    print("[*] Maximum bytes to encode:", n_bytes)
    if len(secret_data) > n_bytes:
        raise ValueError("[!] Insufficient bytes, need bigger image or less data.")
    print("[*] Encoding data...")
    # add stopping criteria
    secret_data += "====="
    data_index = 0
    # convert data to binary
    binary_secret_data = to_bin(secret_data)
    # size of data to hide
    data_len = len(binary_secret_data)
    for row in image:
        for pixel in row:
            # convert RGB values to binary format
            r, g, b = to_bin(pixel)
            # modify the least significant bit only if there is still data to store
            if data_index < data_len:
                # least significant red pixel bit
                pixel[0] = int(r[:-1] + binary_secret_data[data_index], 2)
                data_index += 1
            if data_index < data_len:
                # least significant green pixel bit
                pixel[1] = int(g[:-1] + binary_secret_data[data_index], 2)
                data_index += 1
            if data_index < data_len:
                # least significant blue pixel bit
                pixel[2] = int(b[:-1] + binary_secret_data[data_index], 2)
                data_index += 1
            # if data is encoded, just break out of the loop
            if data_index >= data_len:
                break
    return image

以下是该encode()函数的作用:

  • 使用cv2.imread()函数读取图像。
  • 计算可用于编码数据的最大字节数。
  • 检查我们是否可以将所有数据编码到图像中。
  • 添加一个停止标准,这将作为解码器在看到这一点时停止解码的指标(随意实现一个更好、更有效的标准)。
  • 最后,修改每个像素的最后一位并将其替换为数据位。

现在这里是解码器功能:

def decode(image_name):
    print("[+] Decoding...")
    # read the image
    image = cv2.imread(image_name)
    binary_data = ""
    for row in image:
        for pixel in row:
            r, g, b = to_bin(pixel)
            binary_data += r[-1]
            binary_data += g[-1]
            binary_data += b[-1]
    # split by 8-bits
    all_bytes = [ binary_data[i: i+8] for i in range(0, len(binary_data), 8) ]
    # convert from bits to characters
    decoded_data = ""
    for byte in all_bytes:
        decoded_data += chr(int(byte, 2))
        if decoded_data[-5:] == "=====":
            break
    return decoded_data[:-5]

我们读取图像,然后获取图像每个像素的所有最后一位。之后,我们继续解码,直到我们看到停止标准。

Python隐藏图像中的秘密数据示例 - 让我们使用这些函数:

if __name__ == "__main__":
    input_image = "image.PNG"
    output_image = "encoded_image.PNG"
    secret_data = "This is a top secret message."
    # encode the data into the image
    encoded_image = encode(image_name=input_image, secret_data=secret_data)
    # save the output image (encoded image)
    cv2.imwrite(output_image, encoded_image)
    # decode the secret data from the image
    decoded_data = decode(output_image)
    print("[+] Decoded data:", decoded_data)

如何在Python中隐藏图像中的秘密数据?我在这里有一个示例PNG图像,使用你真正想要的任何图像,只需确保它是如前所述的无损压缩图像格式。

上面的代码将获取image.PNG图像并将secret_data字符串编码到其中并将其保存到encoded_image.PNG. 之后,我们使用decode()加载新图像并解码其中隐藏信息的函数。

执行脚本后,它将编写另一个文件“encoded_image.PNG”,该文件具有完全相同的图像外观,但其中编码了秘密数据,输出如下:

[*] Maximum bytes to encode: 125028
[*] Encoding data...
[+] Decoding...
[+] Decoded data: This is a top secret message.

所以我们可以在这个特定的图像上解码大约122KB(125028字节),这会因图像的分辨率大小而异。

结论

惊人的 !你刚刚学会了如何自己在 Python 中实现 Steganoggraphy!

Python如何隐藏图像中的秘密数据?你可能会注意到,生成的图像看起来与原始图像完全相同,这是因为我们仅将像素值修改了 1。因此,无论何时人们看到此图像,他/她都无法检测到里面是否有隐藏数据。

此外,如果你熟悉 Linux 命令,你还可以使用标准 linux 命令执行隐写术

最后,这里有一些你可以做的想法和挑战:

  • 在图像中编码之前加密数据(这通常用于隐写术)。
  • 对图像中的任何类型的整个文件进行编码。
  • 使用 2-Least Significant Bits 技术来编码更多数据。
  • 在视频而不是图像中编码大量数据(你可以使用OpenCV执行此操作,因为视频只是图像序列)。
木子山

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: