Он немного изменился

Stego

На старте есть изображение `kot4_izmenilsya.png`. В метаданных указаны встроенный слепок и движение внутри клеток. Размер плитки и таблица направлений не указаны. Сначала ищем исходное изображение внутри PNG. ## Решение В приватном ancillary-чанке `caTs` лежит конверт `CREF`: сигнатура, версия `1`, длина `u32 LE`, SHA-256 и байты чистого PNG. Проверяем CRC чанка, затем длину и SHA-256 конверта. Получаем эталонную картинку для сравнения. Разность изображений показывает изменения в клетках, выровненных от `(0, 0)`. Пробуем размеры `4 × 4`, `8 × 8`, `16 × 16`. Для каждого сравниваем изменённую плитку с четырьмя циклическими сдвигами эталонной на один пиксель. ```python import numpy as np candidates = [ np.roll(tile, -1, axis=0), # вверх np.roll(tile, 1, axis=1), # вправо np.roll(tile, 1, axis=0), # вниз np.roll(tile, -1, axis=1), # влево ] ``` Подходит только сетка **8 × 8**, в которой каждая изменённая плитка точно совпадает с одним из сдвигов. При других размерах движение пересекает предполагаемые границы плиток, и такого совпадения нет. Однозначное направление можно прочитать только у плиток, которые действительно меняются при сдвиге. Поэтому пригодность проверяем по оригиналу, ещё до чтения битов. Все четыре сдвига должны различаться между собой и с исходной плиткой, а максимальная абсолютная разница по каналам между каждой сдвинутой плиткой и оригиналом не должна превышать `4`. Разность считаем в знаковом типе, чтобы вычитание `uint8` не переполнилось. Обходим пригодные плитки по строкам. По подсказке первые использованные клетки служат для калибровки. В первых четырёх видим сдвиги вверх, вправо, вниз и влево. Пробуем сопоставить им пары `00`, `01`, `10`, `11` по порядку, а затем проверяем гипотезу по сигнатуре и CRC полученного потока: | Направление | Пара бит | |---|---| | Вверх | `00` | | Вправо | `01` | | Вниз | `10` | | Влево | `11` | Пропускаем четыре калибровочные плитки и собираем байты по четыре пары, старшая первой. Сначала читаем девятибайтный заголовок с сигнатурой `CTM4`, версией `1` и длиной payload в `u32 LE`. Затем читаем payload указанной длины и ещё четыре байта CRC32 в little-endian. Полная длина конверта равна `9 + payload_length + 4` байтам. Совпадение сигнатуры, версии и CRC подтверждает выбранную карту направлений. В payload читаем флаг и фразу «Это точно та же самая картинка. Мы почти уверены.» Встроенного эталона и самих различий хватило, чтобы восстановить схему без готовой карты в метаданных. [Солвер](solve/solve.py). ## Флаг `caplag{sdv1g_n4_0d1n_p1ks3l}`