LSB-стеганография: что это и как скрывают данные в изображениях
10
Что это вообще такое и с чем это едят? Сейчас будет максимально подробно и просто.
Я бы хотел начать с этого тяжёлого слова «стеганография» — что это? Если говорить по-простому, это способ передачи или хранения информации (любого вида), при котором скрывается сам факт её наличия.
Теперь ближе к нашей теме: LSB (Least Significant Bit) — наименьший значащий бит. Для того чтобы разобраться в этом, нам нужно вообще понять, как компьютер «видит» наши фотографии. Очевидно, он не может так же смотреть на них глазами, как и мы, поэтому он их оцифровывает:
В данном процессе фигурируют несколько других, а именно:
Дискретизация (sampling) — это разбиение изображения на сетку крошечных ячеек (пикселей). Также можно часто услышать слово «разрешение» — это те самые пиксели, обычно их записывают «1920 × 1080», то есть число пикселей по горизонтали и вертикали. 1920 столбцов и 1080 строк, что в сумме даёт 2 073 600 пикселей, где в каждом хранится своя информация.
Квантование (quantization) — это присвоение каждому пикселю конкретного цифрового значения. Если по-простому, то компьютер не знает оттенков, как мы: он назначает каждому каналу пикселя цифровое значение из доступного диапазона. Для примера возьмём изображение с 8 битами на канал: для каждого канала (RGB — RED, GREEN, BLUE) он присвоит одно из 256 значений.
Теперь поговорим про RGB (RED, GREEN, BLUE). Любое изображение RGB состоит из 3 отдельных каналов: RED — красный, GREEN — зелёный и BLUE — синий. Каждый пиксель несёт в себе значение каждого канала, то есть это буквально три числа (R, G, B), где каждый канал в нашем 8-битном примере принимает значение от 0 до 255.
Например, ярко-красный пиксель может быть закодирован как (255, 0, 0), белый — как (255, 255, 255), чёрный — как (0, 0, 0). В памяти компьютера такое изображение можно представить как трёхмерный массив (тензор). Тензор хранит информацию так: (строка, столбец, канал), где канал — это R, G или B.
Теперь, когда мы знаем про RGB, возникает вопрос, почему у некоторых картинок есть RGBA. Так вот, эта «A» — это alpha plane. Это отдельный канал, такой же, как каналы RGB, но он не хранит никакой информации о цвете пикселя. Зачем же он тогда нужен? Альфа-канал хранит информацию о прозрачности пикселя. Зачастую он всё так же записывается в пределах 0–255 и даёт возможность смешивать изображение с фоном.
Теперь мы плавно перешли к самой сути — стеганографии в LSB. Что вообще такое LSB? Как я уже упомянул ранее, это наименьший значащий бит. Как это относится к стеганографии? Всё предельно просто, сейчас покажу всё на наглядном примере:
Представим какой-нибудь пиксель из RGB-картинки, его значения будут (150, 0, 0). В двоичном коде красный канал будет выглядеть как набор нулей и единиц, а именно: 10010110. Последний 0 — это тот самый «незаметный бит». Если мы поменяем на 10010111, что произойдёт? Да, компьютер сразу поймёт, что в красном канале теперь 151, а не 150, как раньше. НО для человека увидеть изменение в 1 из 256 возможных значений обычно очень сложно. А почему именно так? Старшие биты менять гораздо заметнее: если мы, например, 10010110 поменяем на 11010110, наше число подскочит от 150 до 214. Тут будет разумно привести бытовой пример:
Игра «Дженга» наглядно демонстрирует, насколько заметнее становятся изменения в старших битах. Поэтому, если вы захотите что-то спрятать в LSB любой картинки, помните про это. Для начала я рекомендую использовать только самый младший, нулевой бит. Биты от 0 до 3 — не универсально безопасная зона: чем больше битов мы меняем, тем заметнее могут стать изменения.
Если мы вернёмся к количеству пикселей в фото с разрешением 1920 × 1080, то вспомним, что их там 2 073 600. А теперь представьте, что в каждом из них можно изменить LSB, и от этого ничего толком не поменяется: человеческому глазу такие изменения обычно незаметны.
Однако важно понимать две вещи:
Данный метод неустойчив к сжатию с потерями. То есть JPEG — плохой вариант для такой LSB-стеганографии. Лучше брать форматы с сохранением без потерь, по типу PNG, BMP, TIFF.
Его во многих случаях легко обнаружить: закинув фотографию в инструмент стегоанализа, можно заметить следы скрытых данных. Но мгновенное обнаружение и извлечение не гарантированы — всё зависит от способа записи и самого изображения.
Теперь мы поняли, как работает LSB и почему же его так часто применяют в CTF-заданиях, но ведь нужно добавить и практики:
Ниже представлен скрипт на Python для записи текста в младшие биты картинки:
from PIL import Image
img = Image.open("input.png").convert("RGB")
px = img.load()
w, h = img.size
text = "То самое соо, которое хотим спрятать в lsb"
bits = []
for byte in text.encode("utf-8") + b"\x00":
for i in range(7, -1, -1):
bits.append((byte >> i) & 1)
if len(bits) > w * h * 3:
raise ValueError("Изображение слишком маленькое для этого сообщения")
idx = 0
for y in range(h):
for x in range(w):
r, g, b = px[x, y]
vals = [r, g, b]
for c in range(3):
if idx < len(bits):
vals[c] = (vals[c] & 0xFE) | bits[idx]
idx += 1
px[x, y] = tuple(vals)
if idx >= len(bits):
break
if idx >= len(bits):
break
img.save("secret.png", "PNG")Теперь давайте пойдём в обратную сторону и извлечём наше послание.
from PIL import Image
img = Image.open("secret.png").convert("RGB")
px = img.load()
w, h = img.size
out = bytearray()
cur = 0
n = 0
for y in range(h):
for x in range(w):
for v in px[x, y]:
cur = (cur << 1) | (v & 1)
n += 1
if n == 8:
if cur == 0:
print(out.decode("utf-8"))
raise SystemExit
out.append(cur)
cur = 0
n = 0
raise ValueError("Нулевой байт конца сообщения не найден")Отлично! Также мы можем прочитать скрытые данные через утилиту zsteg:
zsteg -E "b1,rgb,lsb,xy" secret.pngА вот список утилит, которыми можно пользоваться для более комфортной работы с LSB в изображениях:
https://www.aperisolve.com/ — позволяет посмотреть битовые плоскости;
zsteg — весьма полезная утилита при анализе PNG или BMP, т. к. автоматически проверяет разные порядки обхода пикселей, каналы и комбинации младших битов. Также пытается найти известные сигнатуры файлов (по типу ZIP, PNG, PDF) и выводит читаемый текст.



