4.0 KiB
В глазах всё написано
На старте есть RGB-изображение kot1_v_glazah.png размером 1254 × 1254. Начинаем с текстовых чанков PNG. Запись EditorNote описывает работу редактора с блоками 2 × 2.
Решение
Читаем метаданные через Pillow:
from PIL import Image
img = Image.open("public/kot1_v_glazah.png")
for key, value in img.info.items():
print(key, value, sep="\n")
Из заметки узнаём, что редактор только переставляет четыре пикселя внутри блока, не меняя их цвета. Пригодны блоки, в которых все цвета различны, а разброс каждого канала не превышает 3. После перестановки блок остаётся пригодным по тем же признакам.
Обходим сетку от (0, 0) по строкам, слева направо. Для каждого пригодного блока сортируем цвета по ключу:
(77 * R + 150 * G + 29 * B, R, G, B)
Так получаем канонические цвета C0, C1, C2, C3. Позиции в блоке нумеруем отдельно: 0 — сверху слева, 1 — сверху справа, 2 — снизу слева, 3 — снизу справа. Перестановку записываем как позиции канонических цветов: где находится C0, затем C1, C2 и C3.
Например, если в порядке чтения блока стоят C2, C0, C3, C1, нужный кортеж — (1, 3, 0, 2). Кортеж номеров цветов (2, 0, 3, 1) задаёт обратную перестановку и даст другой полубайт.
У четырёх элементов 4! = 24 перестановки. Нумеруем их лексикографически. Первые 16 номеров кодируют значения полубайта от 0 до 15.
from itertools import permutations
catalog = list(permutations(range(4)))
# px — четыре RGB-пикселя в порядке TL, TR, BL, BR.
positions = tuple(sorted(
range(4),
key=lambda i: (77 * int(px[i][0]) + 150 * int(px[i][1]) + 29 * int(px[i][2]),
*map(int, px[i])),
))
nibble = catalog.index(positions)
Первые 16 пригодных блоков должны дать контрольный ряд 0, 1, …, 15. Сверяем его перед чтением сообщения: так проверяем отбор блоков, сортировку и направление обхода сразу.
Пропускаем 16 калибровочных блоков. Дальше объединяем полубайты, старший первым, byte = (hi << 4) | lo. В начале потока появляется конверт:
| Поле | Формат |
|---|---|
| Сигнатура | CTP1 |
| Версия | 1 |
| Длина payload | u32 LE |
| Payload | Текст указанной длины |
| Контрольная сумма | CRC32 payload, u32 LE |
После заголовка читаем только указанное число байт payload и следующие четыре байта CRC32: остальные пригодные блоки к сообщению уже не относятся. Сверяем контрольную сумму. В тексте лежит флаг, а за ним авторская приписка: «Он ничего не потерял. Просто всё переставил. Даже взгляд.»
Флаг
caplag{p3r3st4n0vk1_v_gl4z4h_k0t4}