ShadowFight2: байты прямо в RGBA

стеганография · задача 14   pngrgbaальфа-канал

Картинка 9 × 9 пикселей, 195 байт. Структура чистая: только IHDR, pHYs, IDAT, IEND, без ошибок CRC и хвостов.

Платформа
CTF (флаги вида DUCKERZ{…})
Носитель
PNG RGBA
Приём
коды символов прямо в каналах, включая альфа
Инструменты
Python (Pillow), ImageMagick + xxd
Статус
решено

Метод исключения

Структура чистая, метаданных нет, strings пусто. А находки zsteg — «файлы» Atari и PlayStation с абсурдными размерами: на 81 пикселе слишком мало бит, и случайные куски легко похожи на чьи-то заголовки. Остаётся одно место: сами значения пикселей. 81 пиксель × 4 канала = 324 байта, на флаг хватает с запасом.

Название ShadowFight («бой с тенью»): тень намекает на альфа-канал, прозрачность, которую просмотрщик не показывает как цвет.

Вывести все числа

import sys
from PIL import Image
im = Image.open(sys.argv[1]).convert('RGBA')
raw = im.tobytes()                                  # R,G,B,A,R,G,B,A,...
asc = lambda vals: ''.join(chr(v) if 32 <= v < 127 else '.' for v in vals)

for y in range(im.height):                          # таблица, чтобы увидеть глазами
    print(' '.join('%3d,%3d,%3d,%3d' % tuple(raw[(y*im.width + x)*4:][:4]) for x in range(im.width)))
for i, name in enumerate('RGBA'):                   # каждый канал отдельно
    print(name, asc(raw[i::4]))
print('RGBA подряд:', asc(raw))

Или вообще без Python: ImageMagick распаковывает пиксели в голые байты, а xxd их показывает.

convert ShadowFight2.png RGBA:raw.bin && xxd raw.bin

Как это выглядит

Я сделал такую же картинку 3 × 3 со своей строкой. На шахматке видно, что пиксели полупрозрачные: альфа тоже несёт буквы.

Пиксели 3×3 с данными
Мой пример 3 × 3, увеличено. Нижний ряд — нули (полностью прозрачные)
ПиксельR G B AКак ASCII
0 68 69 77 79DEMO
1123 52 108 112{4lp
2104 52 95 99h4_c
3 52 114 114 494rr1
4 51 115 95 1003s_d
5 52 116 52 1254t4}

Без альфа-канала теряется каждая четвёртая буква. Именно поэтому важно .convert('RGBA'), а не 'RGB'.

показать флаг (спойлер)DUCKERZ{Sh4d3OfC0l0r2_4nsw3r}

Сколько букв помещается в пиксель

Формат пикселяБукв в пикселеГде встречалось
палитра / серый, 8 бит1BMP с Гамлетом
RGB3Три в одном
RGBA4эта задача
LSB в RGB3 бита, то есть ~⅜ буквыЗачарованный лес

Прямая запись вмещает в 8–10 раз больше, чем LSB, но картинка сразу выглядит странно. LSB глазу незаметен.