Статья Как скрыть данные в QR-коде методом LSB

Сегодня рассмотрим один из примитивных, но рабочих способов сокрытия информации в QR коде методом LSB. Содержание этого способа заключается в следующем: QR-код состоит из чёрных квадратов, расположенных в квадратной сетке на белом фоне, которые считываются с помощью устройств обработки изображений. Проще говоря, QR код это совокупность пикселей белого и черного цвета определенной последовательности.

Каждый пиксель имеет свой уникальный битовый код. Метод LSB (наименее значащий бит) подразумевает собой замену последних значащих битов в контейнере (изображения, аудио или видеозаписи) на биты скрываемого сообщения. Разница между пустым и заполненным контейнерами должна быть не ощутима для органов восприятия человека.

Суть заключается в следующем: имеется изображение трех пикселей RGB со значениями [0, 0, 0], каждый из них – абсолютно черный цвет. Если мы запишем в канал R значение, равное не нулю, а единице, то для нашего восприятия цвет не поменяется. Таким образом, в канал R изображения из трех пикселей мы можем спрятать три бита информации. Так же и в следующие каналы – по три бита в каждый. Получается 8 сочетаний нулей и единиц в группе по три. В конечном итоге мы сможем спрятать 24 бита информации, либо 3 байта.

Стоит отметить недостатки метода. Методы LSB являются неустойчивыми ко всем видам атак и могут быть использованы только при отсутствии шума в канале передачи данных. Обнаружение LSB-кодированного стего осуществляется по аномальным характеристикам распределения значений диапазона младших битов отсчётов цифрового сигнала.

Это мы рассмотрели возможность внедрения при глубине цвета от 8 бит. Глубина цвета (качество цветопередачи, би́тность изображения) - термин компьютерной графики, означающий количество бит (объём памяти), используемое для хранения и представления цвета при кодировании одного пикселя растровой графики или видео.

28948


Изменив методом LSB черный цвет со значения «0» на значение «1» для нулевого бита и на значение «2» для бита единицы, мы сможем внедрить попиксельно определенный объем данных.

Ниже представлен QR-код, в котором уже закодированы данные поверх сообщения.

28981


Если визуализировать данный метод, то получается следующее:

28980


Используя специализированный сайт "ФотоФорензик" выявить данный метод также затруднительно:

28983


Zsteg также не выявил метод LSB:
Код:
zsteg qr_encoded.png
/usr/lib/ruby/2.5.0/open3.rb:199: warning: Insecure world writable dir /mnt/c in PATH, mode 040777
[=] nothing :(
Алгоритм сокрытия данных в QR коде заключается в следующем:
  • Генерируем QR-код;
  • Переводим текстовую информацию для сокрытия в бинарный код из единиц и нулей;
  • Зашифровываем в пикселях черного цвета информацию по вышеописанному алгоритму.
Для реализации нашего проекта воспользуемся интерпретируемым языком Python версии 2.7.

Первостепенно установим необходимую библиотеку Pillow, позволяющую работать с изображениями. Данная библиотека является форком, то есть ответвлением, оригинальной библиотеки PIL. Этот форк был принят в качестве замены оригинальной библиотеки и включён в некоторые дистрибутивы Linux по умолчанию.

Для установки необходимых зависимостей воспользуйтесь командой от имени администратора:
Bash:
pip install Pillow qrcode
Теперь приступим к разбору алгоритма нашей программы.

Импортируем библиотеки в скрипт:
Python:
from PIL import Image, ImageDraw
import qrcode
Image, ImageDraw являются компонентами библиотеки PIL для взаимодействия с изображениями на уровне чтения, создания и редактирования.

Модуль qrcode нужен нам для генерации нужных размеров QR-кода.

Объявляем класс qrHide:
Python:
class qrHide(object):
Скрипт необходимо грамотно оформить с выводом каких-либо сообщений в терминал. Объявляем стандартную функцию " __init__":
Python:
def __init__(self):
    print("Старт программы!")
Объявляем следующую немаловажную для нас функцию createQR, которая будет генерировать наш стегоконтейнер в виде QR-кода. Указываем параметры, которые будем передавать функции при ее вызове – containText и QRFileName.

Параметр containText содержит тот текст, на основе которого будет генерация QR-кода, в свою очередь параметр QRFileName отвечает за имя файла, в который будет записан QR-код.
Python:
def createQR(self, containText, QRFileName):
    self.a = containText
    self.b = QRFileName
Далее устанавливаем равенство двух внутренних переменных self.a, self.b и containText, QRFileName соответственно. Это обуславливается удобством чтения кода и переменных.

Обработка ошибок будет делаться через связку try-except:
Python:
try:
    qr = qrcode.QRCode(
        version=20, #версия QR, чем выше, тем больше код;
        error_correction=qrcode.constants.ERROR_CORRECT_H, #уровень коррекции ошибок. H - 30%
        box_size=4,
        border=4,
    )

    qr.add_data(self.a)
    qr.make(fit=True)

    img = qr.make_image()
    img.save(self.b)
    print("QR создан!")
    return 1     

except:
    print("Ошибка на стадии генерации QR-кода!")
    return 0
Следующие функции предназначены для кодирования и декодирования текста в двоичный код и обратно.
Python:
def __encode_binary_string(self, s):
    try:
        o = ''.join(format(ord(x), '08b') for x in s)
        print ("Текст преобразован в двоичный код успешно!")
        return o
    except:
        print("Ошибка на стадии преобразования текста в двоичный код!")
        return 0
     
def __decode_binary_string(self, s):
    return ''.join(chr(int(s[i*8:i*8+8],2)) for i in range(len(s)//8))
Разберем подробнее функцию, которая скрывает нужный текст методом LSB в QR-коде.
Параметр textToHide - это текст, который необходимо скрыть. QRFileName - имя исходного файла с QR-кодом, outNameFile - имя конечного файла с зашифрованными данными. Открываем сгенерированное изображение QR-кода и конвертируем в градацию серого, так как изначально оно у нас было в битовом представлении. Битовое изображение - бинарное изображение, для представления и хранения которого в цифровом виде используется битовая карта, где на каждый элемент изображения (пиксель) отводится 1 бит информации. Плюсы битовых изображений в том, что они хорошо сжимаются. Минусы - для представления информации отводится только ноль и единица.
Для конвертирования изображения используем функцию convert и выберем необходимым режимом.
  • 1 (Битовое изображение состоящее из черного и белого цвета)
  • L (черно-белое 8-ми битное изображение)
  • P (цветное 8-ми битное изображение )
  • RGB (24-х битное изображение, true color)
  • RGBA (32-х битное изображение, true color, с прозрачностью)
  • CMYK (32-битное изображение, true color, с разделением цветов)
  • YCbCr (32-битное изображение, видео формат)
  • I (32-разрядные целые пиксели со знаком)
  • F (32-битные пиксели с плавающей точкой)
Python:
def encode(self, textToHide, QRFileName, outNameFile): #,QRFileName,outNameFile):
        self.a = self.__encode_binary_string(textToHide)
        self.b = QRFileName
        self.c = outNameFile
 
        try:
            image = Image.open(self.b).convert('L') #Открываем и конвертируем изображение;
            pix = image.load() #Подгружаем его в переменную;
            draw = ImageDraw.Draw(image) #Инициализируем модуль рисования;

            i = 0 #Этот счетчик нужен нам для своевременного брейка;
     
            for y in range(image.size[1]):    #Вхождение первого цикла для отработки по оси координат Oy;
                for x in range(image.size[0]):   #Вхождение  второго цикла для отработки по оси координат Ox;
             
                    if i == len(self.a): #Наш брейк с условием если скрытое сообщение было отработано все, дабы не занимать процессорное время просто так, доводя цикл до конца;
                        break;
                    if pix[x,y][0] == 0: #Прячем информацию только в черные пиксели;

                        if int(self.a[i]) == 0:
                            draw.point((x,y), fill=(1,0,0))

                        elif int(self.a[i]) == 1:
                            draw.point((x,y), fill=(2,0,0))


                        i += 1
             
            image.save(self.c, "PNG") #Сохраняем результат;
            print("QR файл обработан! Текст сокрыт успешно!")
            return 1
     
        except:
            print("Ошибка на стадии внедрения информации!")
            return 0
Декодирование происходит с помощью функции decode, которой мы передаем лишь один параметр fileToDecode;
Python:
def decode(self, fileToDecode):

        self.a = fileToDecode
        self.lst = []

        try:
            image = Image.open(self.a)
            pix = image.load()
     
     
            for y in range(image.size[1]):
                    for x in range(image.size[0]):
                 
                        if int(pix[x,y]) == 1:
                            self.lst.append("0")
                        elif int(pix[x,y]) == 2:
                            self.lst.append("1")
                     
            text = self.__decode_binary_string(''.join(self.lst))
            print("Текст декодирован успешно!")
            print "Скрытое сообщение: ", text
            return text
     
        except:
            print("Ошибка на стадии декодирования!")
            return 0
    pass
Таким образом, мы рассмотрели один из возможных стеганографических приемов сокрытия данных методом LSB в QR-коде.
К преимуществам данного способа стоит отнести:
  1. отклонение на пикселях черного цвета трудно выявить специалисту (допустим, сотруднику правоохранительных органов), не знакомому с методом LSB;
  2. достаточно простой, но эффективный способ передачи скрытой информации с условием использования криптографических алгоритмов.
Из недостатков можно выделить следующее:
  1. невозможность применения в условиях компрессии, наличия посторонних шумов либо преобразования изображения;
  2. метод подвергается почти всем видам атак на стегографические алгоритмы.
P.S. во вложении полная версия скрипта.
 

Вложения

  • hide.zip
    hide.zip
    2,6 КБ · Просмотры: 268
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!

Курс AD