Как зашифровать exe файл

Обновлено: 06.07.2024

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

И если есть проблемы с выбором, ниже представлены некоторые из программ для этих целей.

1. VeraCrypt

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

Преимущества: открытая программа, стремится быть «идеальной»; бесплатно; возможность шифрования целых дисков.

2. BoxCryptor

Программа создает контейнер для файлов и папок, которые вы хотите зашифровать. Через эту форму файлы остаются на диске в зашифрованном виде. Интересной и беспрецедентной функцией является отправка файлов в облако, например, в Dropbox или SugarSync. В случае утечки данных никто не сможет их прочитать. Их структура будет зашифрована с использованием популярного и надежного алгоритма AES-256. Процесс шифрования и дешифрования данных происходит «на лету».

После запуска программы каждый раз вам будет задан пароль, который разблокирует все зашифрованные папки. Бесплатная версия позволяет хранить только 2 ГБ данных в контейнере на диске. Этот контейнер представляет собой папку, описанную ранее.

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

Преимущества: синхронизация с облаками.
Недостатки: ограничения бесплатной версии.

3. AxCrypt

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

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

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

4. 7-Zip

7-Zip - это программа для сжатия и декомпрессии данных. Она работает по тому же принципу, что и популярный WinRAR, однако она бесплатная. Мы можем упаковать в него папки, разделить их на части, создать самораспаковывающиеся файлы.

Как это подходит для шифрования?

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

Как зашифровать EXE файл, что б его трудно было изминять всякими дебагерами, к примером EXEscopom.

Ну что же вы, не чем мне помочь не можите.
може хоть что то посоветуете. ну самый простой и примитивный это использовать упаковщик ASPack например
остальное это дело посложнее Kashuk
Помочь не могу, ибо вопрос дюже сложный.
Но могу посоветовать почитать Криса Касперски.

Originally posted by Kashuk
Помогите хелпом.

Как зашифровать EXE файл, что б его трудно было изминять всякими дебагерами, к примером EXEscopom.

Можно попробовать сделать следующее.
Прогу разбить на 2 блока (файла)
В первом поместить загрузчик, который будет загружать второй блок, дешифрировать его а потом запускать.

Можно не скромный вопрос, а зачем столько гемора?

действительно, самый простой способ - это упаковать экзешник, размер меньше станет и зашифруется. ASPack - вещь! сам им пользуюсь :)
только как показывает практика - все это фигня! - потом его все равно можно распаковать с помощью спец. утилит и сломать. Originally posted by Relax
действительно, самый простой способ - это упаковать экзешник, размер меньше станет и зашифруется. ASPack - вещь! сам им пользуюсь :)
только как показывает практика - все это фигня! - потом его все равно можно распаковать с помощью спец. утилит и сломать.

За совет про ASPack спасибо, но это дествительно не поможет от вздома, а вот против взлома что посоветуете?

ответ такой - от взлома нет лекарства :), посмотри на софт на своей тачке. почему-то я на 99% уверен, что он весь ломаный, кроме бесплатного, конечно :) - и это правильно - я вообще за Open Source :)
эта тема уже обсуждалась. и в этом форуме тоже и результат всегда один - любая защита сводится к переходу на какой-то адрес в памяти по какому-то условию. задача взлома состоит в том, чтобы найти это место. так что можно привязывать свои проги к винтам, процам, матерям, можно изобретать всякие мудреные ключи, но опытный кракер все равно это все обойдет, так что, я считаю - не надо на этом заморачиваться, а наоборот - надо гордиться тем, что твои проги ломают - значит они представляют ценность ;) Originally posted by Kashuk
Против взлома что посоветуете?


Использовать голову т.е. для написания хорошей защиты нужно знать ассемблер, а так же способы и принципы взлома программ. А для шифрования своей проги лучше использовать HASP или пароль(рег номер) но его можно сказать остальным пользователям.
P.S. Если программа для продажи на нашем рынке то можешь и не дергаться - ни кто не купит, а сломать сломают.
:)

Originally posted by Non_prog

Использовать голову т.е. для написания хорошей защиты нужно знать ассемблер, а так же способы и принципы взлома программ. А для шифрования своей проги лучше использовать HASP или пароль(рег номер) но его можно сказать остальным пользователям.
P.S. Если программа для продажи на нашем рынке то можешь и не дергаться - ни кто не купит, а сломать сломают.
:)

50% "ненужного" кода, который намного усложняет процедуру взлома.

Но опять же. Нельзя сделать защиту совершенной, но можно попытаться усложнить влзом до максимума. 8)

50% "ненужного" кода, который намного усложняет процедуру взлома.

Но опять же. Нельзя сделать защиту совершенной, но можно попытаться усложнить влзом до максимума. 8)

Это меня заинтересоваль, но вот в инете я не где не могу её найти, може подкинеш ссылку, буду очень благодарен

Как можно скрыть код exe-файла (написаного, например, на Delphi) от расшифровки под дизассемблером и, впоследствии, от реверса.

Строки, например, можно просто XOR-ить или смещать каждый символ на k позиций по таблице ASCII (первое лучше, ИМХО). А вот как скрыть имена функций или даже целые блоки кода? Ведь многие разработчики (особенно вирусописатели) любят это делать, тем самым скрывая свои «творения» от исследования посторонними лицами. Таким же образом скрывается от антивирусов всем известный SpyEye. А как этого добиться мне?


11.3k 7 7 золотых знаков 36 36 серебряных знаков 69 69 бронзовых знаков


13.4k 12 12 золотых знаков 55 55 серебряных знаков 121 121 бронзовый знак вот мне всегда было интересно как приходят в голову такие вопросы? от нечего делать? провел бы сначала сам какуето исследовательскую работу, может бы и нам рассказал, а может просто вопросы такие отпали сами собой. Сейчас как раз занимаюсь исследованием одного вредоносного приложения, наткнулся на то, что описал в вопросе. P.S "какуето" через дефис =) @Asen, если исходник скомпилирован в режиме Release, то имена функций в экзешнике не сохраняются. Также для сокрытия кода можно воспользоваться "навесной" защитой, типа ASProtect. @megacoder, как Вы считаете цель форума вопросов в чем заключается ? Мне кажется - в быстрой и квалифицированной реальной помощи, а вовсе не в высказывании автору вопроса пожеланий поучиться или поискать ответы в сети. Иначе, зачем ХэшКод вообще нужен ?

(Некоторое время не занимался реверсингом, поэтому информация может быть outdated )

Если говорить о серьезной обфускации, то имеет смысл рассматривать только языки, которые компилируются в машинный код ( C++ , например). Языки с промежуточным слоем байт-кода обычно легко поддаются реверсингу и, судя по всему, пока нет более-менее адекватных способов запротектить написанные на них приложения.

Адекватные протекторы совершают некоторую последовательность действий для защиты готового приложения от реверсинга:

  • Вставляют в готовый бинарник готовые антиотладочные фрагменты кода (например, дешифрующий его в рантайме), путают секции, совершают хитрые джампы, в общем, совершают атомарные изменения (не меняющие поведение программы!) над исполняемым кодом, которые затрудняют отладку человеку.

  • Человек - это reverse engineer , который открывает ваше приложение в IDA , зрительно выцепляет знакомые паттерны из дизассемблерного листинга, трейсит приложение, подменяя содержимое стека и патча это самое приложение в его рантайме.

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

  • Более серьезная защита, иногда останавливающая даже крутых и опытных реверсеров - это виртуализация кода приложения для того, чтобы исполнять его на собственной виртуальной машине (обычно сочетается со стандартным антиотладочными приемами, описанными выше). При этом виртуальная машина внедряется в бинарник вместе с некоторым IL -кодом, который может выглядеть абсолютно произвольно. Параметры создаваемой машины также можно варьировать в некоторых пределах - при желании можно и виртуалку с троичной логикой написать.

  • Бонус такого подхода в том, что для полноценного реверсинга такого приложения необходимо каким-либо образом воспроизвести эту самую виртуальную машину. В такой ситуации (если виртуалка оказывается достаточно хитрой), большая часть стандартных тулзов и приемов реверсера перестает работать "из коробки". Что, опять же, приближает этого самого потенциального реверсера к состоянию "Спасибо, с меня хватит".

  • С точностью до деталей таким образом протектит Themida.

В соответствии с написанным выше - если есть желание запротектить свою программу более-менее достойно, то напишите свою виртуалку и сгенерируйте для нее IL -код из имеющихся объектных файлов вашего приложения. Это действие поможет отсеять большую часть реверсеров, которым недавно рассказали и показали, что такое IDA и как применять patch .

Quick test - для того, чтобы понять, сможете ли вы создать более-менее адекватную защиту с виртуализацией, попробуйте сломать CrackMe от ESET. Если поймете, причем здесь SSE и общую механику проверки, то можно браться за дело.

Всегда есть набор готовых public и private пакеров, которыми можно воспользоваться. Themida, ASProtect, FSG. Понятно также, что всегда найдется набор людей, которые эти или иные протекторы в состоянии снять.

В этой статье я постараюсь подробно рассказать о типах крипторов и их устройстве.

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

Криптография?!
Криптография определяется, как наука о методах обеспечения конфиденциальности и целостности данных.

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

Вы наверно уже разглядели в криптографии потенциал защиты со злым умыслом, те разработку вредоносного по, которое использует преимущества, предоставляемые криптографией. Сейчас подобные вредоносные программы весьма распространены.

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

Обнаружение на основе сигнатур

Как следует из названия, обнаружение на основе сигнатур - это метод, при котором во входящем пакете просматривается байт за байтом и сравнивается с сигнатурой (подписью) – характерной строкой программы, указывающей на характеристику вредного трафика.

Хотя обнаружение на основе сигнатур может предотвратить большинство ранее известных вредоносных программ, оно имеет свои недостатки, поскольку авторы вредоносных программ могут применять уровень защиты от такого подхода, такие как полиморфный(наличие разных ассемблерных форм, для одного и того же кода) и/или метаморфический код (способный мутировать целиком, пересобирая себя по внутренним “чертежам”). Эвристическое обнаружение пытается контролировать поведение и характеристики приложения и ссылаться на него с известным злонамеренным поведением.

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

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

Криптор отвечает за шифрование целевого объекта. После шифрования на выходе получается наш зашифрованный объект и дополнительный сектор зашифровнаного объекта (так называемый stub - огрызок), который обеспечивает извлечение и выполнение указанного объекта.

Типы крипторов
Scantime
Этот тип крипторов получил свое название благодаря способности шифрования данных и дабавлении части на диск, чтобы расшифровать его в дальнейшем на диске при открытии.
Runtime
Он расшифровывает программу в памяти (ОЗУ) и зашифровывает ее снова при закрытии, те дешифровка происходит только по мере необходимости.

Реализация Scantime криптора (c++)
Scantime криптор проще, поскольку он не требует знания виртуальной памяти. По сути, stub деобфусцирует файл, вытаскивая его на диск и выполняя затем.

Начнем. Нам нужно будет определить основные и два условия, которые определяют выполнение криптера или стаба.

Поскольку мы определяем приложение как оконное приложение, мы не можем получить argc и argv аргументы, как обычно в консольном приложении, но Microsoft предоставила решение для этого с __argc и __argv. Если аргумент командной строки __argс[1] существует, приложение будет пытаться шифровать указанный файл, иначе он попытается расшифровать существующий файл, зашифрованный криптором.

Перейдя на ветку криптора, мы будем запрашивать декриптор указанного файла __argс[1] и его размер, чтобы мы могли скопировать его в буфер для шифрования.

Функция Crypt будет считывать содержимое файла в буфер, затем склеивать их и возвращать указатель на буфер с зашифрованными байтами.

Теперь, когда у нас есть зашифрованные байты, нам нужно будет создать новый файл и записать в него эти байты.

Обратите внимание, что мы использовали простой XOR для шифрования содержимого файла и если задача стоит на повышении безопасными, мы можем использовать другие алгоритмы шифрования, такие как RC4 или TEA.

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

Затем мы прочитаем содержимое файла и получим дешифрованные данные. Поскольку операция XOR восстанавливает значения, мы можем просто повторно использовать функцию Crypt. После этого нам нужно будет создать временный файл и записать в него дешифрованные данные.

Наконец, пишем строчку для выполнить расшифрованной программы.

Реализация Runtime криптора
Для экономии времени я опишу только стаб, так как это включает более сложный материал, поэтому допустим, что программа уже зашифрована.

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

Приступим. Давайте настроим две функции: одну для дешифрования зашифрованной программы, а другую - для загрузки её в память для выполнения.

Функция Decrypt будет полностью зависеть от используемого алгоритма шифрования. Вот пример кода с использованием XOR:

Далее мы проверим, является ли программа действительным PE-файлом, проверяя сигнатуры DOS и PE.

Создадим приостановленный процесс.

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

Теперь освободим область виртуальной памяти процесса, чтобы мы могли выделить пространство для нашей программы.

Поскольку приостановленный процесс имеет собственное содержимое внутри своего пространства виртуальной памяти, нам нужно убрать его, а затем выделить это место в памяти для себя. Мы сделаем это с помощью функции WriteProcessMemory.

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

image

Автор: Кристиан Амманн (Christian Ammann)

Динамический шифратор (runtime-cryptor) шифрует двоичные исполняемые файлы с сохранением их функциональности. При запуске сначала расшифровывается тело файла, а затем исполняется код. Такой подход позволяет запускать вредоносные программы в защищенных средах и затрудняет детектирование по сигнатуре и блокирование подозрительных файлов антивирусными средствами. Зашифрованная копия содержит неизвестную сигнатуру и не может быть исследована при помощи эвристического анализа, тем самым оставаясь незаметной для антивируса.

Наименование

MSDOS-заголовок, MSDOS-заглушка, указатель на Image FileHeader

Image File Header

Размер опционального заголовка, количество секций

Image Optional Header

Адрес точки входа, база образа, размер образа

Указатели на таблицы импорта и экспорта

Список заголовков секций

Секции .code, .data и т. д.

Как и стандартные PE-файлы, NET-приложения содержат MZ-заглушку, Image File Header, Image Optional Header, Dada Directory и соответствующие секции. Детальное описание этих терминов выходит за рамки данной статьи. С более подробным объяснением этих понятий вы можете ознакомиться в документах [1] и [2]. При открытии NET-приложения в PE-редакторе (например, вLord PE) [8], можно увидеть следующую структуру:

  • Секция .text, содержащую таблицу импорта (import table), таблицу импортируемых адресов (import address table) и CIL-код.
  • Секция .rsrc, содержащую иконку файла (file icon).
  • Секцию .reloc, содержащую таблицу релокаций (relocation table). В этой таблице хранится только одна запись, инструкция точки входа (entry point instruction).

При запуске NET-приложение загружается в память как обычный исполняемый файл. Когда процесс загрузки окончен, адреса секций преобразуются к виртуальным адресам, и PE-загрузчик определяет, содержит ли файл собственный машинный код или CIL-код. Если файл содержит собственный машинный код, тогда загрузчик переходит к точке входа и далее приложение исполняется процессором. В случае с CIL-кодом управление передается среде CLR. Таким образом, массив указателей (data directory) каждого PE-файла содержит запись заголовка среды исполнения CLR (CLR Runtime Header), при помощи которого можно отличить стандартный исполняемый файл от NET-приложения. Если файл содержит байт-код CIL, тогда в секции CLRRuntime Header присутствует указатель на CIL-заголовок. В случае стандартного исполняемого файла этот указатель равен 0.

  1. CIL-заголовок.
  2. CIL-код и ресурсы.
  3. Заголовок метаданных (MetaData header).
  4. Потоки (Streams).
  5. Таблицы метаданных (MetaData tables).

CIL-заголовок содержит базовое описание NET-файла. В двух записях представлены предыдущая/последующая версии среды выполнения (runtime environment version), которые необходимы для запуска CIL-кода. Также CIL-заголовок содержит указатели на секцию с NET-ресурсами и заголовок метаданных.

Заголовок метаданных содержит магическое число и, также как CIL-заголовок, хранит информацию о предыдущей/последующей версии среды выполнения (runtime version), которые, согласно [11], игнорируются загрузчиком. Важными элементами заголовка метаданных являются заголовки потоков (stream headers). Каждый такой заголовок содержит имя, размер и смещение (offset). По умолчанию в каждом NET-файле присутствую потоки Strings или Blob. Поток Strings содержит информацию о строках, а Blob – двоичные данные.

. Таким образом, формат NET-файлов, о котором рассказано выше, нуждается в корректировке: таблицы метаданных являются частью секции потоков. Хочется подчеркнуть, что семантика этого потока отличается от семантики остальных потоков и вынесена в дополнительный раздел. Таблицы метаданных содержат следующие элементы:

  • Имена классов, подклассов и т. п.
  • Имена переменных, типов и их начальные значения.
  • Имена констант и их значения.
  • Имена методов, параметры, тело метода, возвращаемые значения и т. п.

Следующий пример демонстрирует взаимосвязь CIL-кода, таблиц метаданных и потоков. Допустим, в NET-приложение объявлена константа со значением 1. Кроме того, там же присутствует класс B, в котором реализован метод c(). Идентификаторы a, B и c хранятся в потоке Strings, а значение константы в потоке Blob. В таблице метаданных хранятся записи класса, константы и метода. По сути, эти записи являются ссылками на соответствующие элементы потока. К тому же, в таблице присутствует запись-ссылка на CIL-код, который запускается при вызове функции. Еще раз обращаем внимание на то, что это лишь краткое описание формата, и за более подробной информацией рекомендуем обратиться к соответствующим источникам.

Теперь вам знакома структура NET-приложения, которая встроена в структуру PE-файла. Остался еще один аспект, который не описан – запуск CIL-кода по его загрузке в оперативную память. Для этого загрузчик сначала проверяет содержание ссылки на CIL-заголовок в data directory файла. Когда корректная ссылка найдена, в память загружается MsCorEE.dll, а затем вызывается функция _CorValidateImage(). Эта функция модифицирует PE-заголовок, перезаписывая точку входа. Новая точка входа является адресом функции _CorExeMain(), которая передает управление среде CLR.

3 Шифрование NET-файлов в реальном времени

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

Для запуска процесса Windows-приложения используют функцию CreateProcess(). Один из параметров этой функции имя запускаемого файла. С точки зрения шифратора такой механизм запуска имеет недостаток, поскольку файл должен быть расшифрован и сохранен на диске перед вызовом функции CreateProcess(). К тому же, в Гиперионе и ему подобных динамических шифраторах реализован собственный PE-загрузчик, который работает с оперативной памятью, а не образом диска.

При помощи reflection API шифрование NET-приложений в реальном времени может быть реализовано следующим образом: динамический шифратор считывает входной файл, шифрует его, оставляя незашифрованную часть в виде исполняемого файла загрузчика (dropper executable) (например, в качестве ресурса). При запуске загрузчик расшифровывает основную программу в памяти и запускает ее при помощи функции Assembly.Load(). Данный механизм прост и понятен, но содержит следующие недостатки:

  • Assembly.Load() связана с CIL-кодом.
  • Антивирус может легко обнаружить вызов функции Assembly.Load() и затем запустить эвристический анализ.

Для устранения этих недостатков, используется еще один подход при реализации динамического шифратора. Согласно разделу 2, NET-файлы загружаются в память подобно обычным PE-файлам, а затем запускается функция _CorExeMain(). В соответствии с этим, Гиперион можно модифицировать так: когда файл находится в памяти, записи data directory могут быть проверены. В случае обнаружения CIL-заголовка вызывается функция _CorValidateImage(). В конце концов, загрузчик переходит к функции _CorExeMain() и выполняется CIL-код. Функции _CorExeMain() и _CorValidateImage() нужно динамически подгрузить, используя функцию GetProcAddress() и другие методы обфускации. Все это позволит создать эффективную защиту от детектирования исполняемого файла антивирусными средствами.

В предыдущем разделе было рассмотрено и проанализировано два подхода к созданию динамического шифратора. На основе сделанных выводов, мы выбрали второй алгоритм, который будет реализован в Гиперионе. Базовая версия шифратора работает так: сначала расшифровывается основная часть программы путем подбора шифр-ключа, после чего расшифрованный файл загружается в память согласно содержанию PE-заголовков и заголовков секций. В конце происходит переход к точке входа, и управление передается распакованному файлу.


Следующий участок кода показывает, как Гиперион переходит к точке входа после расшифровки файла:

01. ;.
02. mov edx,[image_base]
03. mov eax,[edx+IMAGE_DOS_HEADER.e_lfanew]
04. add eax,edx
05. add eax,4
06. ;image file header now in eax
07. add eax,sizeof.IMAGE_FILE_HEADER
08. mov eax,[eax+IMAGE_OPTIONAL_HEADER32.AddressOfEntryPoint]
09. add eax,[image_base]
10. ;entry point of original exe is now in eax
11. jmp eax

Рассмотрим листинг подробнее. Базовый образ (base image) расшифрованного файла хранится в регистре edx. Далее вычисляется и сохраняется в регистре eax адрес PE-заголовка. PE-заголовок используется для получения опционального заголовка, который содержит соответствующую точку входа. После перехода к точке входа происходит запуск исполняемого файла. Для NET-файлов вышеприведенный листинг добавляется небольшая модификация: в строке 1 появляется функция, которая получает в качестве параметра адрес загрузки расшифрованного файла и разбирает PE-заголовки для получения массива указателей (data directory). Если data directory содержит указатель на CIL-заголовок, вызывается функция _CorValidateImage(), которая изменяет адрес точки входа, и Гиперион переходит к функции _CorExeMain().

Первое, что приходит на ум – в Гиперионе произошла ошибка, и файл некорректно загрузился/расшифровался в памяти. После анализа образа расшифрованного файла был сделан вывод, что загрузка и расшифровка произошла успешно. Таким образом, текущая ситуация такова:

  • Поскольку файл был расшифрован, значит, NET-приложение работает корректно, и правильная рабочая версия доступна в системе.
  • После расшифровки файла, среда исполнения не может прочитать CIL-заголовок, даже в случае корректной загрузки файла в память.

Далее была запущена отладка зашифрованного файла с использованием Immunity [12] и точкой останова на функции _CorExeMain() (которая является точкой входа среды CLR) и пошагово пройден файл. Результаты отладки показаны на рис. 2 (выделено красным прямоугольником).


Рис. 2. Процесс отладки NET-приложения.

CIL framework открывает файл при помощи CreateFileW(), обращаясь к образу диска. Такое поведение странно, поскольку в нем нет необходимости, так как файл уже загружен в память PE-загрузчиком. Дальнейшие исследования выявили следующую картину:

  • Вызываются функции CreateFileW, CreateFileMapping and MapViewOfFile() для мапирования образа диска в память
  • Далее происходит разбор и обработка PE-заголовка файла образа диска

3.2 Частичное шифрование NET-файлов

Исследование в предыдущем разделе показало, что CIL framework обращается к файлу образа диска даже в том случае, когда файл уже загружен в память PE-загрузчиком. Следовательно, при загрузке исполняемых файлов NET, зашифрованных Гиперионом, возникает ошибка, поскольку среда CLR не может считать CIL-заголовок. В этом разделе будет представлена пилотная реализация шифрования в режиме реального времени для NET-приложений, где CLI-заголовок остается незашифрованным. Новый метод шифрования разделен на две части:

  • Шифровщик: шифруется только CIL-код; PE-заголовок, CIL-заголовок и метаданные остаются нетронутыми.
  • Загрузчик: стартует зашифрованный файл при помощи функции CreateProcess(). Главный поток находится в состоянии ожидания. Загрузчик расшифровывает CIL-код и возвращается в главный поток.

Само собой, при такой реализации приложение не защищено от статического анализа антивирусами, поскольку остаются незашифрованные места. В разделе 4 будет реализовано расширение для Гипериона, которое будет шифровать все NET-файлы.

3.2.1 Шифровщик

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

Каждый PE-файл начинается с MZ-заголовка, содержащего указатель на заголовок COFF. Размер заголовка COFF является постоянным, за ним следуют опциональные стандартные заголовки (optional standard headers) и windows-заголовки (optional windows headers), которые также фиксированного размера. После опциональных windows-заголовков находится data directory, размер которого определяется переменной NumberOfRvaAndSizes, являющейся элементом опционального windows-заголовка. Поскольку размер data directory может быть равным нулю, рекомендуется высчитывать размер опционального заголовка через переменную SizeOfOptionalHeader, а не обращаться к переменной NumberOfRvaAndSizes. Data directory представляет из себя массив структурImageDataDirectory:

01. struct ImageDataDirectory f
02. uint32_t VirtualAddress;
03. uint32_t Size;
04. g;

Каждая запись содержит относительный виртуальный адрес (relative virtual address, rva) и относительный адрес. Если rva равен нулю, то эта запись не используется в PE-файле. Следующий код проверяет присутствие в файле CIL-кода:

01. ImageCor20Header* rva_cil_header = (ImageCor20Header*)
02. data_directory [CLR_RUNTIME_HEADER]).VirtualAddress;
03.
04. if(rva_cil_header==0)f
05. printf("no clr runtime header found");
06. return false;
07. g

CIL-заголовок представлен структурой ImageCor20Header, которая имеет такой формат:

01. struct ImageCor20Header
02. f
03. uint32_t cb;
04. uint16_t MajorRuntimeVersion;
05. uint16_t MinorRuntimeVersion;
06. struct ImageDataDirectory MetaData;
07. uint32_t Flags;
08. uint32_t EntryPoint;
09. struct ImageDataDirectory Resources;
10. struct ImageDataDirectory StrongNameSignature;
11. struct ImageDataDirectory CodeManagerTable;
12. struct ImageDataDirectory VTableFixups;
13. struct ImageDataDirectory ExportAddressTableJumps;
14. struct ImageDataDirectory ManagedNativeHeader;
15. g;

Для реализации шифровщика важным элементом является указатель на метаданные, представленный структурой ImageDataDirectory. Как только получен CIL-заголовок, шифровщик считывает следующую информацию:

  • Размер и относительный адрес CIL-заголовка.
  • Размер и относительный адрес заголовка метаданных.

Здесь упущен один аспект: входной файл смапирован в память по определенному адресу загрузки (image base). Заголовки PE-файлов содержат указатели, являющие относительными виртуальными адресами (rva). Rva должен быть трансформирован в соответствующий физический адрес (raw address), после чего будет доступна запись и чтение данных. Для преобразования адреса используется функция:

01. unsigned char* rvaToOffset(unsigned char* base,

02. struct SectionHeader* sections,
03. uint16_t sections_size, uint32_t rva)
04. f
05. unsigned char* ret = 0;
06. for(int i=0;i<sections_size;i++)f
07. if(rva >= sections[i].VirtualAddress &&
08. rva < sections[i].VirtualAddress + sections[i].VirtualSize)f
09. ret = base + sections[i].PointerToRawData +
10. (rva - sections[i].VirtualAddress);
11. g
12. g
13. return ret;
14. g

Метод rvaToOffset() на входе получает следующие параметры: адрес загрузки файла в память, указатель на секцию заголовков, число секций заголовков и относительный адрес, который необходимо преобразовать. В начале работы происходит поиск секции, содержащей относительный виртуальный адрес. Далее к адресу загрузки файла добавляется смещение секцииPointerToRawData и разность между относительным виртуальным адресом и виртуальным адресом секции VirtualAddress, а затем возвращается итоговое значение преобразованного адреса.

3.2.2 Загрузчик

В предыдущем разделе был рассмотрен процесс шифровки CIL-кода NET-приложения. Загрузчик запускает зашифрованный файл, используя метод CreateProcess(). При этом главный поток приостанавливается. После дешифровки CIL-кода загрузчик вновь возвращается в главный поток.

01. STARTUPINFOA startupinfo;
02. memset(&startupinfo, 0, sizeof(startupinfo));
03. startupinfo.cb = sizeof(STARTUPINFOA);
04. PROCESS_INFORMATION process_information;
05. memset(&process_information, 0, sizeof(process_information));
06. CreateProcessA(application_name_and_path, 0, 0, 0,
07. false, NORMAL_PRIORITY_CLASS | CREATE_SUSPENDED, 0, 0, &startupinfo,
08. &process_information);

Метод CreateProcess() использует две структуры: STARTUPINFO и PROCESS INFORMATION. В вышеприведенном листинге сначала обе структуры инициализированы нулевыми байтами, а затем CreateProcess() запускает приложение. Информация о созданном процессе хранится в структуре process_information. CIL-код расшифровывается при помощи следующего алгоритма:

  • Метод VirtualProtectEx() делает участок памяти процесса доступной для записи
  • Метод ReadProcessMemory() копирует участок память процесса в буфер. Туда же попадает выше заполненная структура process_information
  • Дешифруется CIL-код
  • Обновленный буфер копируется обратно в память процесса с использованием метода WriteProcessMemory()
  • VirtualProtectEx() восстанавливает прежние атрибуты участка памяти процесса

После отработки алгоритма загрузчик возвращается в потом, используя конструкцию ResumeThread(process information.hThread);.

4 Заключение и направление дальнейшей работы

  • Используется простое XOR-шифрование, в то время как Гиперион может использовать алгоритм AES-128.
  • Шифруется только CIL-код, а заголовок и метаданные остаются уязвимыми для статического анализа антивирусными средствами.

В дальнейшем функционал шифровщика будет доработан, и Гиперион сможет шифровать все NET-файлы по следующему алгоритму: сначала происходит перехват (hook) методов CreateFileW(),CreateFileMapping() and MapViewOfFile() в модуле kernel32.dll. Перехватчик проверяет, пытается ли среда исполнения обратиться к файлу образа диска. Если такое происходит, тогда возвращается указатель образа ранее дешифрованного файла и среда CLR получает доступ к CIL-заголовку. Это решает проблему, описанную в разделе 3.1, что позволяет полностью зашифровать NET-файлы.

5 Благодарности

Мы хотим выразить благодарность Крису Каулингу (Chris Cowling), Яну Квисту (Ian Qvist) и Томасу Педерсену (Thomas Pedersen) за помощь при работе над данной статьей. Кроме того, мы хотим сказать спасибо всей команде Nullsecurity за плодотворные беседы и (что более важно) за прекрасную вечеринку на Berlin-Sides 2012.

[3] Information Technology Laboratory (National Institute of Standards and Technology). Announcing

the Advanced Encryption Standard (AES) [electronic resource]. Computer Security Division, Information Technology Laboratory, National Institute of Standards and Technology, Gaithersburg, MD :, 2001.

[9] Microsoft Cooperation. Standard ECMA-335 - Common Language Infrastructure (CLI).

Читайте также: