warning
Статья имеет ознакомительный характер и написана в образовательных целях. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации.
Мы уже рассматривали в предыдущих статьях стандартные криптоалгоритмы или их модификации, однако бывают случаи, когда и самописный алгоритм становится хорошей разминкой для мозгов, даже если он реализован на заурядном дотнетовском бейсике. В своей статье «Золотой ключик. Разбираем на примере принцип создания кейгенов» я уже рассказывал, как разработчики защищают программы при помощи генерации лицензионных ключей, а также о том, как самому создавать подобные генераторы.
Мы изучали и совсем простой случай, и вариант не особо продуманной модификации стандартного криптоалгоритма. Однако недавно я столкнулся с хитрой реализацией проверки кода продукта вообще без использования стандартных алгоритмов.
Итак, условие задачи: имеется некая программа, и для ее активации необходим код, который зависит от имени пользователя. При вводе правильного кода активация программы переходит на следующий этап, если ввести неправильный — прерывается.

Программа вполне заурядная, написана на VB.NET, не использует никаких серьезных протекторов, весь код на виду, хотя control flow слегка обфусцирован, вероятно каким‑то простым самописным обфускатором:
PE32
Operation system: Windows(95)[I386, 32-bit, GUI]
Linker: Microsoft Linker
Compiler: VB.NET
Language: VB.NET
Library: .NET Framework(CLR 4.0.30319)
Tool: Microsoft Visual Studio
(Heur)Protection: Anti analysis[Anti-debug]
(Heur)Debug data: Contains[Absolute PDB path]
_debug_data.5.sg: PE/_debug_data.5.sg: 10: TypeError: Result of expression 'PE.getNumberOfDebugDataRecords' [undefined] is not a function.
Поэтому на первых шагах все просто: программа легко загружается в отладчик dnSpy и позволяет себя отлаживать, благодаря чему мы быстро находим место валидации product code по имени пользователя.

Конечно, было бы просто замечательно, если бы на месте выделенного выражения оказалось равенство типа f(. Тогда бы мы просто реверсировали функцию и получили готовый алгоритм преобразования имени в ключ. Но не тут‑то было: если внимательно всмотреться в код, то мы получаем хитрое криптоуравнение упрощенного вида:
Lenstr("Unregistered7777766666","11111-22222-33333-44444-55555") == Cval("11111-22222-33333-44444-55555")Как видишь, основная беда заключается в том, что первые пять групп ProductCode входят в обе части уравнения, а значит, его придется решать. Еще сильнее нагоняет жути, что результаты функций справа и слева — это длиннющие строки из десятичных цифр, вдобавок разные по длине. Код этих функций под обфускацией выглядит страшновато, особенно функция Lenstr, в которой при ближайшем рассмотрении каким‑то образом участвует еще и текущее время.

Продолжение доступно только участникам
Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».
Присоединяйся к сообществу «Xakep.ru»!
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
