隐肌:第2部分 - 高级LSB
#教程 #python #安全 #steganography

Part 1中,我有一个简单的LSB隐肌术例子。今天,我将展示另一个简单的步骤如何提高弹性,并使经典的切解工具更难检测。

注意:第1部分中我没有提到的一件事是,这些代码将始终试图维护图像的完整性,而不是可以嵌入的数据的完整性和数量。我假设对手可以访问图像的原始副本。这意味着可以存储的数据量始终低于某些更严重地改变图像的其他算法。


改善LSB隐肌的最简单方法是改变数据的嵌入方式。有一些建议的方法,但就目前而言,让我们使用简单的陆循环来创建像素块,例如JPEG压缩中使用的过程。

from PIL import Image


def encode(filepath):
    start = '#####'
    stop = '*****'
    full = start + 'Some string that you want to encode into an image' + stop

    binary_text = ''.join('{0:08b}'.format(ord(x), 'b') for x in full)
    print(binary_text, len(binary_text))

    with Image.open(filepath) as im:
        i = 0
        w, h = im.size

        # A good block size is 8x8 or a multiple of 8
        min_block_size = 24

        print(f'Minimum block size: {min_block_size}')

        if min_block_size > w or min_block_size > h:
            print('Data too large to store in image')
            return

        for x in range(0, w - min_block_size, min_block_size):
            for y in range(0, h - min_block_size, min_block_size):

                for j in range(x, x + min_block_size):
                    for k in range(y, y + min_block_size):
                        if i >= len(binary_text):
                            i = 0

                        bit = binary_text[i]
                        pixel = im.getpixel((j, k))

                        if bit == "0":
                            # Is odd, should be even.
                            if pixel[0] % 2 != 0:
                                new_pix = (pixel[0] - 1, pixel[1], pixel[2])
                                im.putpixel((j, k), new_pix)

                        else:
                            # Is even, should be odd.
                            if pixel[0] % 2 == 0:
                                new_pix = (pixel[0] + 1, pixel[1], pixel[2])
                                im.putpixel((j, k), new_pix)

                        i += 1

        im.save(f'encoded_{filepath}')

您可以看到以上几乎与上一个示例相同,唯一的区别是循环的内部循环:

for j in range(x, x + min_block_size):
    for k in range(y, y + min_block_size):
        if i >= len(binary_text):
            i = 0

        bit = binary_text[i]
        pixel = im.getpixel((j, k))

        if bit == "0":
            # Is odd, should be even.
            if pixel[0] % 2 != 0:
                new_pix = (pixel[0] - 1, pixel[1], pixel[2])
                im.putpixel((j, k), new_pix)

            else:
                # Is even, should be odd.
                if pixel[0] % 2 == 0:
                    new_pix = (pixel[0] + 1, pixel[1], pixel[2])
                    im.putpixel((j, k), new_pix)

        i += 1

这将在min_block_size X min_block_size的正方形上进行迭代,并在此依次编码数据。从理论上讲,这使得编码更加坚固,更难为标准的切解工具提取,因为您必须知道块大小才能检索它。这是这种方法的优势和劣势。您必须定义一个块大小,输入文本的长度或某种方式发送块大小,以便任何人都可以知道要使用什么块大小。

说到解码,此方法基本上是相同的,只是再次使用内部双循环。

def decode(filepath, block_size=None):
    start = '#####'
    stop = '*****'
    found = False
    binary_stop = ''.join('{0:08b}'.format(ord(x), 'b') for x in stop)
    bit_count = 0
    message = ''

    with Image.open(filepath) as im:
        w, h = im.size
        binary_text = ''
        # A good block size is 8x8 or a multiple of 8
        min_block_size = block_size or 24

        while not found:
            for x in range(0, w - min_block_size, min_block_size):
                for y in range(0, h - min_block_size, min_block_size):

                    if message.endswith(stop):
                        found = True
                        break

                    for j in range(x, x + min_block_size):
                        for k in range(y, y + min_block_size):

                            if bit_count == 8:
                                char = chr(int(binary_text, 2))

                                if char in string.printable:
                                    message += char
                                    bit_count = 0
                                    binary_text = ''

                            pixel = im.getpixel((j, k))

                            # Since we always want to get the LSB, we 
                            # can just use the result of the modulo as 
                            # our value
                            binary_text += f'{pixel[0] % 2}'

                            bit_count += 1

    if found:
        start_point = message.find(start) + len(start)
        end = message.find(stop)
        message = message[start_point:end]
        return message

您可以看到,这本质上是与编码相同的循环,在本示例中,块大小作为参数传递,已知24作为备份。我还添加了对发现的char的检查,以确保其可打印(对本示例的帮助较小,但稍后我们尝试处理裁剪的图像)。

结论

将两个部分与一个小argparse一起放在一起,以便于命令行使用,我们得到:

import argparse
import string

from PIL import Image


def encode(filepath):
    start = '#####'
    stop = '*****'
    full = start + 'Some string that you want to encode into an image' + stop

    binary_text = ''.join('{0:08b}'.format(ord(x), 'b') for x in full)
    print(binary_text, len(binary_text))

    with Image.open(filepath) as im:
        i = 0
        w, h = im.size

        # A good block size is 8x8 or a multiple of 8
        min_block_size = 24

        print(f'Minimum block size: {min_block_size}')

        if min_block_size > w or min_block_size > h:
            print('Data too large to store in image')
            return

        for x in range(0, w - min_block_size, min_block_size):
            for y in range(0, h - min_block_size, min_block_size):

                for j in range(x, x + min_block_size):
                    for k in range(y, y + min_block_size):
                        if i >= len(binary_text):
                            i = 0

                        bit = binary_text[i]
                        pixel = im.getpixel((j, k))

                        if bit == "0":
                            # Is odd, should be even.
                            if pixel[0] % 2 != 0:
                                new_pix = (pixel[0] - 1, pixel[1], pixel[2])
                                im.putpixel((j, k), new_pix)

                        else:
                            # Is even, should be odd.
                            if pixel[0] % 2 == 0:
                                new_pix = (pixel[0] + 1, pixel[1], pixel[2])
                                im.putpixel((j, k), new_pix)

                        i += 1

        im.save(f'encoded_{filepath}')


def decode(filepath, block_size=None):
    start = '#####'
    stop = '*****'
    found = False
    binary_stop = ''.join('{0:08b}'.format(ord(x), 'b') for x in stop)
    bit_count = 0
    message = ''

    with Image.open(filepath) as im:
        w, h = im.size
        binary_text = ''
        # A good block size is 8x8 or a multiple of 8
        min_block_size = block_size or 24

        while not found:
            for x in range(0, w - min_block_size, min_block_size):
                for y in range(0, h - min_block_size, min_block_size):

                    if message.endswith(stop):
                        found = True
                        break

                    for j in range(x, x + min_block_size):
                        for k in range(y, y + min_block_size):

                            if bit_count == 8:
                                char = chr(int(binary_text, 2))

                                if char in string.printable:
                                    message += char
                                    bit_count = 0
                                    binary_text = ''

                            pixel = im.getpixel((j, k))

                            # Since we always want to get the LSB, we 
                            # can just use the result of the modulo as 
                            # our value
                            binary_text += f'{pixel[0] % 2}'

                            bit_count += 1

    if found:
        start_point = message.find(start) + len(start)
        end = message.find(stop)
        message = message[start_point:end]
        return message


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("-a", "--action", help="encode or decode")
    parser.add_argument("-f", "--filepath", help="path to image")
    parser.add_argument("-b", "--block_size", required=False, type=int, help="block size")
    args = parser.parse_args()

    if args.filepath:
        if args.action == "encode":
            encode(args.filepath)
        elif args.action == "decode":
            print(decode(args.filepath))
        else:
            print("Invalid action")
    else:
        print("No filepath provided")

使用这个非常简单的脚本,您拥有自己的PNG隐化工具。只需确保您安装了枕头,然后从终端运行类似:

python ./advanced.py -a encode -f file.png

然后:

python ./advanced.py -a decode -f encoded_file.png -b 24

您将拥有自己的,秘密编码的消息传递系统。即使是原始的。

,这些变化也无法发现。

Isis França的标题Unsplash