Задача:
Формат: “RUCTF_.*”
Решение:
Так, у нас есть гифка. Первая идея, которая пришла всем в голову это посмотреть, а может в ней несколько кадров (фреймов). И действительно. Их оказалось восемь. Леша Лобанов грубо проверил их на равенство с помощью md5sum. Они были разные, но картинки были на них идентичны. Я думал, что ответ будет спрятам где-то среди недр gif формата. Нашел на питоне исходник, который читает gif формат. Немножко printов (дебажного вывода) и я представлял что и где читается и как обрабатывается. Но вот где искать ответ. И тут на помощь пришел Борис Чуприн. Сейчас найду точный его комментарий:
есть мнение что различия скрыты палитрой
т.е. номера пикселей разные, а цвет в палитре на них стоит одинаковый
Быстро проанализировав палитру получил, что некоторые цвета действительно в палитре используются несколько раз:
(0, 0, 0) 7 (0, 18, 17) 3 (0, 10, 10) 3 (8, 0, 0) 2 (183, 0, 0) 2 (0, 197, 0) 2 (0, 255, 0) 2 (21, 0, 0) 2 (0, 9, 0) 2 (0, 10, 0) 2
А значит один и от же цвет может показываться одинаково, но нам самом деле он должен отличаться. Создаем свою палитру и подгружаем её в каждый фрейм:
palette = [] for i in range(256): palette.extend((i, i, i)) assert len(palette) == 768 im = Image.open("task.gif") images = [] try: ind = 0 while True: original = im converted = Image.new('P', original.size) converted.putpalette(palette) converted.paste(original, (0, 0)) images.append(converted) im.seek(im.tell() + 1) except EOFError: pass
Ну а дальше надо было проанализировать отличия. Первая идея была от Андрея Малевич:
Мне вот интересно, а что эти штуки рассмотреть как биты
топовый бит я бы за 0 принял
На выходе получалось совсем уж не то, что требовалось. Пробовалось даже как-то менять порядок этих битов, но это совсем было неправильно и глупо. Но играясь с числами я заметил, что каждый символ в таком преобразовании является степенью двойки. То есть получается, что какой-то пиксель изменен максимум в одном из восьми фреймов. А значит можно их друг на друга наложить, т.е. они как бы не мешают друг друга. И все тот же Андрей предложил то, над чем я уже был в процессе реализации:
А если попробовать разбить эту штук на 8ки последовательных битов?
И на выходе получаем:
RUCTF_e4dd9f5cee307b322c3a27abe66e3df9
Исходный код целиком:
from PIL import Image, ImageChops import Image def main(): palette = [] for i in range(256): palette.extend((i, i, i)) assert len(palette) == 768 im = Image.open("task.gif") images = [] try: ind = 0 while True: original = im converted = Image.new('P', original.size) converted.putpalette(palette) converted.paste(original, (0, 0)) images.append(converted) im.seek(im.tell() + 1) except EOFError: pass assert len(images) == 8 for i in range(0, len(images)): images[i].save("images/task_%d.gif" % i) target = Image.new('P', original.size) trgt = target.load() sm = 0 for ind in range(0, len(images)): diff = ImageChops.difference(images[0], images[ind]) pix = diff.load() count = 0 with open("data/data_%d.txt" % ind, "w") as fo: for i in range(diff.size[0]): for j in range(diff.size[1]): if pix[i, j]: fo.write("%d %d\n" % (i, j)) pix[i, j] = 255 trgt[i, j] = 255 count += 1 sm += count diff.save("images/diff_%d.gif" % ind) count = 0 a = [] for j in range(target.size[1]): for i in range(target.size[0]): a.append(1 if trgt[i, j] else 0) if trgt[i, j]: count += 1 target.save("images/result.gif") s = "" while len(a) > 7: x = 0 for i in a[:8]: x = x * 2 + i if x == 0: break s += chr(x) a = a[8:] print s if __name__ == "__main__": main()
Leave a Reply