Два года назад взялся писать программу, что помогает выставлять счета за аренду ковров. Прежде бухгалтер держал текст договоров в Word, расписание в Excel, а три дня каждого месяца убивал на подсчеты. Теперь программа сама рассчитает суммы и загрузит счета в 1С.

Компания такая не одна: защищу программу - продам ключи.

Защита программы - трудное дело. Исследую защитные алгоритмы на практике: вытащу из готовых программ, а чтобы никому не вредить, исследую программы, что специально написаны для взлома - crackme или keygenme.

Жребий пал на q_keygenme_1.0 by quetz.

Поймать защиту за хвост

Ищу код, что выводит сообщение "Invalid serial". Загружаю подопытного в IDA, по единственной ссылке на строку "Invalid serial" нахожу код вызова MessageBox. Отладчик подтверждает, что этот код выполняется при вводе неверного ключа. Условие перехода к MessageBox("Invalid serial") определить трудно: зависимости между флагами запутаны. Выясню, что делает процедура sub_4330E0 - возможно, удастся понять логику.

Дизассемблировать

sub_4330E0 обрабатывает сообщения окну: будь то нажатие кнопки Check или ввод символов name и serial. Возможно, программа проверяет name и serial после нажатия Check, а может и при вводе символа или перемещении курсора мыши, а после нажатия кнопки проверять флаг и показывать сообщение.

Например, на начало цикла похожа конструкция по адресу loc_433137: на этот адрес указывает ссылка снизу, то есть выполнение кода от старших адресов переходит назад к младшим. Это либо цикл, либо goto.

jz loc_433137 похоже на условие продолжение цикла, но если условие не выполняется, следует не выход из цикла, а прыжок в его середину. Конструкция больше похожа на if-else, вложенный в бесконечный цикл while(true).

Подробнее об идентификации циклов читайте в статье Идентификация циклов или в книге Криса Касперски "Искусство дизассемблирования".

Убил три часа на попытки понять код, нарисовал блок-схему, но схема оказалась ничем не проще запутанного графа в IDA. Нашел два вызова GetDlgItemText - чтение name и serial - два вызова MessageBox("Congratulations"), но ничего не выяснил об алгоритме вычисления serial.

Трассировать в отладчике

Трассирую программу в отладчике - остановлюсь на MessageBox("Invalid serial") и узнаю, какие условные переходы привели к этому коду.

Остановлюсь на последнем условном переходе 433F40 jz INVALIDSERIAL и сброшу ZF, чтобы переход не выполнился. Программа снова показывает "Invalid serial", значит, флаг var_B30 не влияет на результат.

Остановлюсь на предпоследнем переходе 4349F4 jz loc_433F16 и сброшу ZF - программа показывает "Yep, this serial is valid", значит, var_B49 - флаг, что отвечает за успех проверки.

Построить граф вычислений

var_B49 = (var_B44 ^ var_B3C) | (var_B40 ^ var_B38)

Построим граф вычислений для var_B49. Код запутывает вычисления, скрывая одни переменные в других, добавляя и вычитая из них константы. Отбросим лишнее и увидим, что var_B40 и var_B44 - результаты вызовов sub_430DB0, а var_B3C и var_B38 - это упакованный serial.

Код содержит два вызова sub_430DB0: с помощью трассировки выясним, что оба вызова - в цикле, который выполняется 100 раз. var_B40 и var_B44 - результаты двух последних вызовов sub_430DB0.

Код упаковывает serial длиной 16 символов в два dword так: каждый символ преобразуется в шестнадцатеричную цифру - полубайт. Символы, кроме [0-9A-Fa-f], обнуляются. Например:

Serial: 9FEYD6ZSRP9XEVG7
Packed: 90 E0 D6 00 00 90 E0 07

x86 CPU байты читает в обратном порядке, поэтому

var_B3C = 0x00D6E09F, var_B38 = 0x07E09000

Serial верный, когда var_B49 = 0.

(var_B44 ^ var_B3C) | (var_B40 ^ var_B38) = 0

Известно, что 0 | 0 = 0 и A ^ A = 0, значит

var_B44 ^ var_B3C = 0
var_B40 ^ var_B38 = 0

var_B44 = var_B3C
var_B40 = var_B38

Обратим порядок байт у var_B44 и var_B40 и преобразуем полубайты в 16 символов - получим верный serial.

Два способа получить keygen:

  • изучить sub_430DB0 и восстановить исходный код алгоритма

  • пропатчить код так, чтобы программа показывала верный serial

Исследуем алгоритм защиты

Восстанавливать исходный код трудно. Процедура sub_430DB0 вызывает sub_401360, в которой автор реализовал хитрый генератор псевдослучайных чисел:

sub_401360(int* pCase, int* pResult) {
	while (true) {
		var = 0x4FB;
		switch (var) {
		case 0x4FB:
			//...
			var = *pCase;
		// 2187 cases ...
		default:
			/* ... */
			return;
		}
	}
}

Каждый case вычисляет следующее значение var, некоторые case вычисляют и записывают result. Потребуется исследовать 2188 case, чтобы в точности воспроизвести алгоритм.

Патчим программу

Проще использовать sub_430DB0. Пусть программа запрашивает только name, а serial генерирует. Сделаем так:

  • вместо вызова GetDlgItemText для поля serial подставим memset, чтобы заполнить буфер 16 символами

  • Вместо вычисления var_B49 = (var_B44 ^ var_B3C) | (var_B40 ^ var_B38) распакуем верный serial из var_B44 и var_B40

  • вместо MessageBox("Invalid serial") выполним SetDlgItemText - запишем верный serial в поле ввода.

Внедрим процедуру распаковки в конец секции .text - там нашлось свободное место. Писать будем по смещению в файле 00034850.

Необходимо отсчитать по меньшей мере 10h байт от последнего ненулевого символа, оставляя этот участок нетронутым (в конце некоторых структур присутствует до 10h нулей, искажение которых ни к чему хорошему не приведет).
https://samag.ru/archive/article/315

Код процедуры для распаковки верного serial:

#include <stdio.h>

typedef unsigned int uint;

void unpack_value(uint value, char* dest) {
  for (int i = 0; i < 4; i++) {
    char a = (value & 0xF0) >> 4;
    a = (a <= 9) ? a + '0' : a - 0xA + 'A';
    *dest++ = a;

    unsigned char b = value & 0x0F;
    b = (b <= 9) ? b + '0' : b - 0xA + 'A';
    *dest++ = b;

    value >>= 8;
  }
}

char* unpack_serial(uint part1, uint part2, char* dest) {
  dest[16] = '\0';
  unpack_value(part1, dest);
  unpack_value(part2, dest + 8);
  return dest;
}

Правил код в отладчике и hex-редакторе: для страховки от ошибок написал автоматический patcher и выполнил такой патч .

Программа не импортирует SetDlgItemText, поэтому верный serial передаю MessageBox.

Заменю надпись Check на Generate serial и скрою поле ввода serial в редакторе ресурсов. Теперь это keygen.

Делаем выводы

Защита трудна там, где требует аккуратно распутать вычисление var_B49. Обфускация и оптимизация кода мешают дизассемблировать программу, но отладчик поможет выяснить, что и как выполняется. Недостаточно запутать код: противодействуйте отладке.

Объем кода не мешает сломать защиту, если программа не следит за целостностью кода. Пусть процедура занимает хоть 100 Мб - изучим входные и выходные параметры и подчиним себе код.

Комментарии (22)


  1. forthuse
    12.08.2023 13:39

    Сталкивались ли вы с crackMe программами реализованными в рамках использования какой то модели вычислителя (вне родного нативного кода), виртуальной машины и насколько это сложнее/проще ломать в сравнении с представленным в статье крякми?


    1. sa2304 Автор
      12.08.2023 13:39
      +1

      Да, вот пример такого crackme на основе виртуальной машины https://habr.com/ru/articles/712290/ .

      Байт-код виртуальной машины - не сложнее x86 ассемблера. ВМ шагает по строке байтов, интерпретирует команды из одного или нескольких байтов и выполняет действия.

      string program = "...";
      for (char code: program) {
      	switch (code) {
      		case 0x1: f1(); break;
      		case 0x2: f2(); break;
      		/* ... */
      		default: /* invalid opcode */
      	}
      }

      Изучаем систему команд ВМ и дальше изучаем алгоритм защиты.


      1. splatt
        12.08.2023 13:39

        А что если внутри ВМ написана еще одна ВМ?


        1. 1dNDN
          12.08.2023 13:39

          Повторять, пока ВМ не закончатся?


        1. sa2304 Автор
          12.08.2023 13:39

          А вот это уже интересней :)

          Нашел решение crackme c France Cybersecurity Challenge 2021: ВМ в 5 слоев виртуализации.
          https://mrt4ntr4.github.io/FCSC21-CTF-VMV/
          Жаль, не нашел сам бинарник.

          Еще по теме: Tuts 4 You - Learn to devirtualize x86 code


  1. dyadyaSerezha
    12.08.2023 13:39

    В начале статьи чётко поставлена задача - защитить свою программу. И не написано в конце, она была решена или нет.


    1. sa2304 Автор
      12.08.2023 13:39
      -1

      Статья озаглавлена "Исследуем защиту программ на практике" и задача поставлена "Исследую защитные алгоритмы на практике". Статья решила маленькую часть большой проблемы. Предстоит еще много работы.


      1. dyadyaSerezha
        12.08.2023 13:39

        А почему не купить готовую защиту или даже взять из open source, если есть?

        Любую защиту можно сломать. Где ваш порог трудоёмкости на создание защиты?


        1. sa2304 Автор
          12.08.2023 13:39
          +1

          Мой порог трудоемкости - "столько, сколько потребуется" :) Чем тщательней защищаю секреты, тем крепче сплю. Хочешь сделать хорошо - делай сам.

          Можно купить иллюзию безопасности. Когда ломают свежую версию условного AutoMegaSuperProtect 3.5, ломают и ПО, которое им защитили. Для навесных протекторов выходит AutoMegaSuperProtectUnpack 3.5 или руководство по распаковке, для аппаратных защит - эмулятор. Разработчик защиты пустит ваши деньги на разработку AutoMegaSuperProtect 3.6, продаст вам ее снова и... новую версию ждет та же судьба.

          Open-source - открытое и бесплатное ПО. Например, UPX - упаковщик, но его задача - не защита ПО, а сжатие файла.

          Хорошая защита вплетена в функции программы и неотделима от них. Если хакер пропустит хотя бы одно, сломанная программа будет работать нестабильно или откажет вовсе. Каждую следующую версию программы придется ломать снова.

          Программа защищена, когда ломать защиту дороже, чем купить программу.


          1. Daddy_Cool
            12.08.2023 13:39

            Программа защищена, когда ломать защиту дороже, чем купить программу.
            Ну это если программа уникальная и дорогая.
            А есть же программы которые нужны многим. Взять тот же Cubase, нормально взломана была только 5-я версия 2010 года. Потом на много лет - тишина. Главный хакер группы которая занималась Кубейсом разбился в аварии, и... всё. Ну там кстати был аппаратный ключ. Вроде последние версии всё же взломали.
            Вопрос кто получает прибыль за взлом такого софта? Приходит в голову, что только владельцы трекеров зарабатывающих на рекламе.


            1. sa2304 Автор
              12.08.2023 13:39
              +2

              Борьбе капиталистов с пиратами вечна :)

              Капиталисты видят счетчик загрузок своей программы на торрентах, умножают число загрузок на цену программы и театрально хватаются за сердце, мол, ах, мы недополучили прибыль. Торрент-трекеры расстрелять без права на помилование.

              Лоуренс Лессиг в книге «Свободная Культура» поясняет, что качают люди все подряд, потому что могут, потому что любопытно. Закроют торрент - человек найдет другое занятие, а капиталист так и не дождется ни копейки.

              Мотив взлома - необязательно прибыль: хакер может проучить жадных капиталистов, у которых нет бесплатной, учебной или дешевой персональной версии программы. Владельцу ПО стоит задуматься.

              программа уникальная и дорогая

              Корпорации скупают патенты на алгоритмы, душат конкуренцию и творчество - оттого их программы "уникальны".


              1. uuger
                12.08.2023 13:39
                -1

                Лоуренс Лессиг в книге «Свободная Культура» поясняет, что качают люди все подряд, потому что могут, потому что любопытно. Закроют торрент - человек найдет другое занятие, а капиталист так и не дождется ни копейки.

                Мотив взлома - необязательно прибыль: хакер может проучить жадных капиталистов, у которых нет бесплатной, учебной или дешевой персональной версии программы. 

                Слушайте, а мне прям очень любопытно, как управляется 911 GTS3, может ли кто-нибудь для меня проучить клятого капиталиста и угнать для меня Порше у какого-нибудь миллиардера? Или это не так работает?


                1. sa2304 Автор
                  12.08.2023 13:39

                  Любопытство - это здорово, осваивайте тему :)

                  Вы подменяете понятия: машина - не программа.

                  Вы потратили ресурсы на сборку одной машины. Вы продаете одну машину. Сборка второй машины требует столько же ресурсов.

                  Вы потратили деньги и время - написали программу. Вы не несете затрат на копирование программы.

                  Жадность - когда вы хотите бесконечно тиражировать копии программы и каждую продавать. Жадность - когда вы запрещаете создавать аналоги своей программы, прикрываясь патентами. Людям нужна программа, но правообладатель задирает цену. Что мешает ему раздавать программу за скромную цену или бесплатно, продавая услуги по технической поддержке?


                  1. uuger
                    12.08.2023 13:39

                    Любопытство - это здорово, осваивайте тему :)

                    Вы подменяете понятия: машина - не программа.

                    Вы потратили ресурсы на сборку одной машины. Вы продаете одну машину. Сборка второй машины требует столько же ресурсов.

                    Какой прекрасный образец ничем не прикрытой демагогии.

                    Как было бы прекрасно жить в мире, в котором RnD случается бесплатно, а все проекты заканчиваются успехом. Интересно, каким образом наш маленький коммунистический друг планирует покрывать издержки на неудачные проекты? Валютные риски? Инвестиции в собственное развитие? За каждым экономическим гением Маркса где-то должен стоять условный Энгельс, снабжающий "творца" невозвратными кредитами. Попробуйте почитать что-нибудь про ваших кумиров, откроете массу нового.


                    1. sa2304 Автор
                      12.08.2023 13:39

                      Обратите внимание, где я завершил мысль и о чем говорю. Я пояснил разницу между материальной вещью - машиной - и интеллектуальным произведением - программой.

                      Вы продали машину - у вас ее больше нет. Вы продали одну копию программы - у вас еще бесконечное число ее копий.

                      Выпустили одну машину - осчастливили одного потребителя. Написали программу - осчастливили нескольких.

                      издержки на неудачные проекты

                      Не это ли называется венчурные инвестиции?;)

                      Инвестиции в собственное развитие?

                      А насколько велики ваши потребности? Они адекватны способностям покупателей? Это ведь предприниматель планирует, сколько он хочет заработать и в какие сроки. Что вами движет - личные амбиции, жажда сесть в 911 GTS3 или вам по кайфу оттого, что вы смогли решить сложную задачу и решить проблему других людей, которые вам за это даже заплатили?

                      наш маленький коммунистический друг

                      Авторитетом давите?:) Если у вас есть успешные программные проекты, это здорово - я ваш опыт не оспариваю. Между нами нет спора: мир не идеален и у бизнеса масса проблем.

                      Каково ваше личное мнение - где грань между прибылью и сверхприбылью?:)


          1. dyadyaSerezha
            12.08.2023 13:39

            Хочешь сделать хорошо - делай сам.

            Машину, компьютер, телевизор, клавиатуру, мышку, ботинки, часы, трусы и всё остальное - тоже сам? ????

            В целом ваше рассуждение предполагает, что мир (или только РФ) населен сплошь сволочами, причём сволочами, которым кровь из носу нужна эта программа и каждый попытается индивидуально её хакнуть. Но мир не состоит из сволочей и хакают всегда не те, кто пользуется. В общем, совсем не убедили.


            1. sa2304 Автор
              12.08.2023 13:39

              Машину, компьютер, телевизор, клавиатуру, мышку, ботинки, часы, трусы и всё остальное - тоже сам?

              Не перегибайте :)

              Машины собирают на заводе, код пишут, сидя на стуле пятой точкой.

              Выбор за вами. Знаете, как устроена машина, мышка, телевизор - не купите хлам, вас не обманет наклейка "Трусы успеха!". Знаете, как устроена защита - не отдадите деньги за кота в мешке.

              Вы спросили, почему не готовые защиты и не open-source, я ответил. Убеждать - не моя работа :)

              Если вам интересно, погрузитесь в тему, попробуйте что-то сломать или защитить, почитайте книги по ассемблеру, реверсу, C/C++, компиляторам, статьи на WASM. А, может, вам интересней право - возьмите курс на законодательство в IT. А, может, вам интересны мотивы людей - добро пожаловать в психологию и психопатологию :) Вокруг столько всего интересного. Успехов вам во всех начинаниях :)


              1. dyadyaSerezha
                12.08.2023 13:39

                Не перегибайте :)

                Хорошо, тогда сузим - написать самому защиту, текстовый редактор, IDE программисте, базу данных, операционку - тоже сам?

                Предположу, что защиту для игр пишут многие человеко-месяцы множество профессионалов по защите. И игры взламывают практически в день публикации. Так что это такое "столько, сколько потребуется"?

                Насчёт же ассемблера, реверса и взлома, я все это делал еще в конце 80-х начале 90-х.

                Удачи в написании своей 1С ;)


                1. sa2304 Автор
                  12.08.2023 13:39

                  Да, если это важно для результата, напишу и операционку. Защита программы - ответственность самого разработчика.

                  Предположу, что защиту для игр пишут многие человеко-месяцы множество профессионалов по защите. И игры взламывают практически в день публикации.

                  Давайте на конкретных примерах - какие именно игры, на каких платформах, от какой студии и реально ли у них потеют над защитой. Не потому ли, что люди полагаются на защиту из коробки - protect.dll или steam_api.dll?

                  Так вы же опытный реверсер, вам ли не знать о недостатках коробочных защит.

                  Удачи в написании своей 1С ;)

                  Напишу и 2C и 3C, если потребуется :)


                  1. dyadyaSerezha
                    12.08.2023 13:39

                    Прекрасный выбор. Потратьте пару лет на защиту и профукайте весь рынок и бабки)


                    1. sa2304 Автор
                      12.08.2023 13:39

                      Так не поручайте защиту программы Unity-программисту - конечно, он будет год и 11 месяцев учиться ;)


  1. sa2304 Автор
    12.08.2023 13:39

    (здесь был лишний комментарий)