Безопасность: Защита исполняемого кода шифрованием

В данной статье будет приведён пример защиты исполняемого кода от различных анализаторов и прочей бяки. Данный способ может использоваться как для защиты кода от крякеров, так и для написания зловредного ПО (чего вам крайне не рекомендую), которое делает неизвестно что… код то зашифрован. Исходники на C++.

Итак, для начала делаем заготовку для нашей программы. Выглядеть это будет примерно так:

В общем то эта вся основная «функциональная» часть нашей программы. Не много, но для того чтобы понять логику достаточно. Далее нам потребуется как-то найти в скомпилированном коде ту часть, которую требуется зашифровать/расшифровать. Для этого неплохо было бы пометить её. Вставим перед выводом сообщения и после него метки:

Ассемблерные вставки при дизасcемблировании не изменятся, поэтому их будет достаточно легко найти. Чтобы наша программа в своём конечном виде работала неплохо было бы расшифровать её код при записи. Сделаем заготовку определим пустую (пока что) функцию decrypt() типа void и вставим её вызов в самое начало нашего приложения.

Теперь скомпилируем всё это дело. Теперь открываем OllyDbg или любой другой debugger, запускаем в нём наше приложение в пошаговом режиме и ищем нужный код между нашими NOP’ами. Запоминаем виртуальные адреса в памяти и сколько байт кода между метками надо зашифровать. У меня начало искомого диапазона равно 0x00411648 (в Вашем приложение значение будет отличаться), конец — 0x0041165b. Пришло время дописать функцию расшифровки. Будет использовано три API-функции — ReadProcessMemory() и WriteProcessMemory(), их смысл, думаю, понятен, и функция OpenProcess() для доступа к виртуальной памяти процесса. Для шифрования выбран единственный шифр, для которого доказана абсолютная криптографическая стойкость — шифр Вернама. Если кто-то не понял, то шифровать будем с помощью XOR’а (операция «исключающего или», или, побитовое сложение по модулю 2). Ключ, правда, в этом примере будет примитивным — каждый байт будем XOR’ить с единичкой. Итак, наша функция должна считать нужную область памяти (19 байт), расшифровать код в ней и записать обратно. Вот как это выглядит:

Попробуем перекомпилировать нашу программу и запустить. С компилированием проблем возникнуть не должно, а вот при запуске — мы получим ошибку. Дело в том, что наша программа пытается расшифровать незашифрованный участок кода и в итоге получает полный бред. Исправить это не сложно. Открываем исполняемый файл в любом hex-редакторе (например, WinHex) и ищем шестнадцатеричное значение 9090909090 — это наша метка, после первого вхождения через несколько байт (в моём случае это было через 19 байт) встретится вторая метка, как и было в исходном коде. Как Вы наверное уже догадались команде ассемблера NOP соответствует машинный код длиной в 1 байт и равный 0x90. Теперь зашифровываем участок между меток и сохраняем изменения. Шифровать можно вручную (при ключе равным единице чётные значения байт надо увеличивать на 1, а нечётные — уменьшать) или написать программку которая за Вас будет это делать.

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

Полный код программы:

 

Понравилась статья? Поделиться с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: