python — ошибка обработки файлов Steganographer для файлов, отличных от обычного текста

Я создал стеганограф Python и пытаюсь добавить к нему графический интерфейс. После моего предыдущего вопроса о чтении всех видов файлов в Питоне. Поскольку стеганограф может кодировать только байты изображения. Я хочу добавить поддержку прямого кодирования в нем файла любого расширения и кодировки. Для этого я читаю файл в двоичном виде и пытаюсь его закодировать. Он отлично работает с файлами, которые в основном содержат обычный текст UTF-8, потому что он может легко кодировать файлы .txt и .py.

Мой обновленный код:

from PIL import Image

import os

class StringTooLongException(Exception):
    pass

class InvalidBitValueException(Exception):
    pass

def str2bin(message):       
    binary = bin(int.from_bytes(message, 'big'))
    return binary[2:]

def bin2str(binary):
    n = int(binary, 2)
    return n.to_bytes((n.bit_length() + 7) // 8, 'big')

def hide(filename, message, bits=2):
    image = Image.open(filename)
    binary = str2bin(message) + '00000000'

    if (len(binary)) % 8 != 0:
        binary = '0'*(8 - ((len(binary)) % 8)) + binary

    data = list(image.getdata())

    newData = []

    if len(data) * bits < len(binary):
        raise StringTooLongException

    if bits > 8:
        raise InvalidBitValueException

    index = 0
    for pixel in data:
        if index < len(binary):
            pixel = list(pixel)
            pixel[0] >>= bits
            pixel[0] <<= bits
            pixel[0] += int('0b' + binary[index:index+bits], 2)
            pixel = tuple(pixel)
            index += bits

        newData.append(pixel)

    image.putdata(newData)
    image.save(os.path.dirname(filename) + '/coded-'+os.path.basename(filename), 'PNG')

    return len(binary)

def unhide(filename, bits=2):
    image = Image.open(filename)
    data = image.getdata()

    if bits > 8:
        raise InvalidBitValueException

    binary = ''

    index = 0

    while not (len(binary) % 8 == 0 and binary[-8:] == '00000000'):
        value = '00000000' + bin(data[index][0])[2:]
        binary += value[-bits:]
        index += 1

    message = bin2str(binary)
    return message

Теперь проблема возникает, когда я пытаюсь скрыть в нем файлы .pdf или .docx. Происходит несколько вещей:

1) Microsoft Word или Adobe Acrobat показывают, что файл поврежден.

2) Размер файла значительно уменьшен с 40 КБ до 3 КБ, что является явным признаком ошибки.

Я думаю, что причина этого может заключаться в том, что файл содержит символ NULL, который моя программа не читает дальше. У вас есть альтернативная идея для этого?

У меня есть идея изменить конечный байт, но он может иметь тот же результат, что и файл, который может содержать этот байт.

Спасибо еще раз!


person TheRandomGuy    schedule 11.06.2017    source источник
comment
Вы можете проверить, действительно ли в файловом потоке есть нулевой байт. Однако я совершенно уверен, что ваше подозрение верно, и я дам ответ в соответствии с этим предположением.   -  person Reti43    schedule 11.06.2017


Ответы (1)


Вы можете использовать маркер конца потока (EOS), если уверены, что последовательность маркеров не будет отображаться в вашем потоке сообщений. Если вы не можете этого гарантировать, у вас есть два варианта:

  • создать более сложный маркер EOS, состоящий из многих байтов. Это может быть довольно неприятно, чтобы доказать, что та же проблема не возникнет, как раньше, или
  • Добавьте заголовок в начале вашего сообщения, который кодирует, сколько битов/байтов нужно прочитать для полного извлечения сообщения.

Как правило, я бы использовал заголовок всякий раз, когда у меня есть заранее информация, которую я хочу передать, и полагался бы на маркеры EOS только тогда, когда я не знаю, когда мой поток байтов завершится, например, при сжатии на лету.

Для встраивания вы должны стремиться:

  • получить свою двоичную строку
  • измерить его длину
  • преобразовать это целое число в двоичный файл фиксированного размера, скажем, 32 бита
  • прикрепите эту битовую строку перед битовой строкой вашего сообщения
  • вставьте все это в свою обложку

И для извлечения:

  • извлечь первые 32 бита
  • преобразовать их в целое число, чтобы получить длину битовой строки вашего сообщения
  • начать с индекса 32 и извлечь необходимое количество битов
  • преобразовать обратно в байтовый поток и сохранить в файл

В качестве бонуса вы можете добавить в заголовок любую информацию, например, имя исходного файла. Пока все это закодировано таким образом, что вы можете извлечь его позже. Например.

header = 4 bytes for the length of the message string +
         1 byte for the number of characters in the filename +
         that many bytes for the filename
person Reti43    schedule 11.06.2017
comment
Большое большое спасибо. Я использовал эту идею и создал фиксированный стандарт для всех типов файлов. - person TheRandomGuy; 13.06.2017