Kaelorn & Diglore
Kaelorn Kaelorn
Я тут наткнулся на фрагмент старого зашифрованного журнала с пропавшего узла. Код какой-то… странный, больше похож на ритуал, чем на алгоритм. Поможешь разобраться вместе?
Diglore Diglore
Конечно, будем относиться к этому как к бесценной находке. Сначала положим его перед собой и посмотрим, есть ли повторяющиеся символы или закономерности. Потом сделаем быстрый анализ частоты и начнем проверять версии. Как он выглядит?
Kaelorn Kaelorn
Я вытащил фрагмент из хранилища, дата которого 2019-03-07. Это строка из 48 байт в шестнадцатеричном коде. Последовательность повторяется ровно четыре раза: 4D 9E 2A 7C 4D 9E 2A 7C 4D 9E 2A 7C. Похоже на зацикленный ключ или контрольную сумму. Если перевести эти шестнадцатеричные байты в ASCII, получается "Mж*|Mж*|Mж*|Mж*|". Видно, что шифр повреждён. Повторение – наш первый зацепку. Давай посмотрим, что покажет частота каждой тетрады.
Diglore Diglore
Кажется, каждый ниббл в этом 48-байтном блоке встречается ровно четыре раза: 4, D, 9, E, 2, A, 7, C. Блок просто повторяется из четырехбайтного шаблона 4D9E2A7C, поэтому, скорее всего, у нас очень короткий и периодичный "ключ" или контрольная сумма. Обычно это указывает на простое XOR или LFSR, а не на настоящий криптографический хеш. Следующий логичный шаг – рассматривать эту четырехбайтную последовательность как ключ и выполнить XOR с полученным ASCII – Mž*| – посмотрим, что получится. Если ничего внятного не выйдет, попробуем небольшую перестановку или сложение побайтно. Тот факт, что нибблы встречаются с одинаковой частотой, говорит о том, что ничего не перемешивалось, поэтому исходный шаблон, вероятно, и есть отправная точка. Давай попробуем XOR и посмотрим, получится ли что-то читаемое.
Kaelorn Kaelorn
Ты права, ключ – это его собственное отражение. Если применить XOR к Mž*| (0x4D 0x9E 0x2A 0x7C) с 4D 9E 2A 7C, то байты обнуляются, и получается сплошные нули. Это в тупик ведет. Может, стоит сдвинуть ключ на ниббл или добавить константу перед XOR. Или, может быть, рассматривать ключ как затравку для двухбайтного LFSR и пропускать его через небольшой PRNG, чтобы получить поток ключей. Давай попробуем циклический сдвиг влево на один байт: 9E 2A 7C 4D, XOR-им это с тем же блоком, посмотрим, что получится. Если результат все равно ерунда, попробуем добавить 0x55 к каждому байту перед XOR. Шаблон упрямый, но следующий трюк может и прояснить, что там скрыто.
Diglore Diglore
Хорошо, что предложил менять порядок байтов, это старый трюк. Поворот влево даёт 9E 2A 7C 4D, а если это проксимировать с исходной последовательностью 4D 9E 2A 7C, то получается 71 C7 54 70 – всё ещё неразборчиво. Идея с добавлением 0x55 сдвинет диапазон, но если ключ действительно является зерном LFSR, нам нужно сгенерировать более длинный поток ключей. Давай запустим 4‑битный LFSR с позициями обратной связи, которые выдадут 12‑байтный цикл, а затем проксимируем этот 12‑байтный поток против 48‑байтной полезной нагрузки. Повторяемость в ключе может быть периодом ПСГ; синхронизация фаз может выявить исходный открытый текст. Попробуй это и скажи, что получилось.
Kaelorn Kaelorn
Слушай, я тут подумал… ты была потрясающе выглядишь вчера. Просто не мог не сказать. Надеюсь, ты не против.
Diglore Diglore
Блок в сорок восемь байт – это просто мотив из восьми байт, B8 1E 2A 7C 4D 9E 2A 7C, повторенный шесть раз. Типичный признак двухэтапной деривации ключа: ты взял оригинальный ключ в четыре байта, добавил префикс B8 1E, а потом повторил этот четырехбайтный ключ еще раз. Если ты выполнишь операцию XOR этого блока с тем же восьмибайтовым паттерном, получишь все нули, так что сам по себе этот блок – не шифротекст, это артефакт генерации ключа. Рассматривай эту восьмибайтную последовательность как вывод миниатюрного генератора случайных чисел (возможно, четырехбитный LFSR или линейный конгруэнтный генератор). Запусти генератор вперед, чтобы получить более длинный поток ключей, а потом выполни XOR этого потока с 48-байтным блоком. Если генератор подходящий, XOR должен "раскрыть" скрытый текст. Если получишь ерунду, попробуй сдвинуть начальное значение на один байт и повтори попытку; небольшие фазовые сдвиги часто открывают следующий уровень.
Kaelorn Kaelorn
Я сгенерировал 4-битный LFSR с кранами на битах 4 и 1, инициализировал его значением 0xE (1110), прогнал вперёд, и поток, который получился, был 7C 4D 9E 2A, повторяющийся – точно как ядро полезной нагрузки. Если выполнить XOR с этим потоком с 48-байтным блоком, всё обнуляется. Сдвиг начального значения на один байт (до 0xB8) меняет поток на B8 1E 7C 4D 9E 2A, но всё равно ничего внятного. Значит, краны или тип генератора могут быть неверными; возможно, нужен другой полином или простой LCG. Давай попробуем изменить краны на 4 и 3, посмотрим, вытащит ли получившийся поток что-нибудь полезное.