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

Обновлено: 02.07.2024

Фреймворк криптора/протектора с антиэмуляцией

Что это за проект и зачем он нужен ?

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

Так-как неизвестно-кто будет этим проектом пользоваться и кто прочитает эту статью, начнем с небольшего ликбеза.)

Что-же такое крипторы, их будущее и зачем они нужны ?

Очень нравится, что про это дело говорил Вазонез (До ухода в реал):

Криптор (aka cryptor) — это тулза, которая предназначена для скрытия троянов, ботов и прочей нечисти от детектирования антивирусами.

Крипторы можно разделить на 2 вида: хорошие и дерьмовые:

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

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

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

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

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

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

Думаю с этим всё понятно, каково-же будущее крипторов ?

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

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

Что делать ?

Менять технологию скрытия детекта, вот следующие решения:

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

Морфить код, если вкратце, это самомодификация кода на лету (Данный метод реализован в этом крипторе).

3.Первые два способа, это если у вас есть исходник, что-же делать если исходника нет. Неужели крипторы умерли ?

Честно я так и думал, до недавнего времени, но это несовсем так:

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

Во вторых не все антивирусы детектят код в памяти, например виндовый дефендер как-то криво с этим работает.

Некоторые антивирусы дают запустить процесс на пару секунд, а этого и может хватит кстати.)))

С лекбезом закончили, на форуме есть темы для глубокого изучения вопроса:

Теперь про конкретный проект, что он делает:

Думал я что-бы такое сделать, просто стабовые крипторы, как описано выше смысла делать нет, через какое-то время будет детект стаба и смысл ?

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

Далее уже второй проект (Сам криптор), работает с этим массивом данных, т.е. расшифровывает этот массив, делает антиэмуляцию, запускает.

Для достижения метаморфизма, shell_gen генерирует заголовочный файл, со следующими дефайнами:

На основе этих значений при сборки, генерируется "мусорный код" (Математичесие операции и т.д.), который будет случайным, после каждого вызова shell_gen и сборки.

Тем самым код будет разный, как по коду, так и по данным.

Сам криптор (x86_pe_cryptor) имеет модульную структуру:

1)modules/lazy_importer/

Модуль скрытия API из таблицы импорта. Пример использования:

auto base = reinterpret_caststd::uintptr_t(LI_FIND(LoadLibraryA)(kernel32));

LI_GET(base, VirtualFree)(pFile, 0, MEM_RELEASE);

2)modules/murmurhash/

Реализация вычисления хеша murmurhash на ассемблере (FASM, приложен к проекту).

3)modules/trash_gen_module/

4)modules/xtea/

Реализация алгоритма шифрование xtea на ассемблере (FASM, приложен к проекту).

5)modules/run_pe/

Реализация функции запуска, расшифрованного массива PE-файла в памяти, путем создания процесса.

6)modules/antiemul/

7)modules/metamorph_code/

Функции метаморфинга. Смысл, что при сборки, генерируются функции в случайном порядке, функции берутся в modules/metamorph_code/morph.cpp.

8)modules/simple_mutate_pe/

Функции мутации PE перед запуском.

Характеристики получившегося криптора:

1)Отстутствуют "опасные API" в таблице импорта (Такие-как CreateProcess и т.д.).

2)Имеет антиэмуляцию, направленную на привышение лимитов эмулятора антивирусов:

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

Получение ключа для расшифровки, следующим образом:

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

  • Все строки зашифрованы, перед расшифровкой строк,происходит задержка 1 секунду, далее по меткам времени происходит вычисление, действительно-ли была задержка секунду. Если да, то на основе этого вычисляется размер ключа:size_key = (mesure2.wSecond - mesure1.wSecond) + 3. Если sleep был пропущен, то размер ключа будет неверный и расшифровка будет неправильна.

3)При сборки, в main и в модулях:modules/run_pe/, modules/trash_gen_module/ генерируются функции в случайном порядке, функции берутся в modules/metamorph_code/morph.cpp (На данный момент там функции арифметических операций). Тем самым достигается полиморфность не только данных, но и кода каждого образца.)))

4)Перед запуском происходит мутация запускаемого PE, что позволяет обойти детект в памяти у некоторых антивирусов.

Web мы спасли от антивирусов несколько месяцев назад. Это было нетрудно — область относительно новая, не освоенная. С исполнимыми же файлами антивирусы борются уже десятилетиями. Побороть EXE-модуль будет сложнее, но… мы справимся :).

Выпуск 1. Ознакомительный

Ты уже знаешь, что я считаю антивирусы абсолютно бесполезными — хотя бы по той причине, что они помогают только от самых примитивных зверьков, которые в условиях современного денежного малварьбизнеса встречаются не так часто. Современные злокодеры, подогретые денежными вливаниями, научились программировать довольно жестко, но есть у них одна маленькая проблема — криптовка — достаточно сложная штука, для написания которой нужны глубокие знания PE-формата, ассемблера и системного программирования. Из-за высокого «входного барьера» в этой области мало профессионалов.

И найти хорошего криптора ой как сложно.

Но решение проблемы есть! Как мы знаем, антивирусные компании обмениваются технической информацией и создают специальные ресурсы, посредством которых мы сами отсылаем им сэмплы (типа VirusTotal’а). Но ведь и вирмейкеры тоже могут обмениваться информацией! Необязательно палить приватные мазы — публичные технологии тоже сгодятся. Например, было бы круто, если бы в каком-то одном месте лежали функции для генерации PE-файла, генерации импорта, шифрования ресурсов, рабочие функции определения SandBox’ов, тогда мы могли бы создавать крипторы так же непринужденно, как домики из кубиков Лего.

Кроме того, в решении проблемы здорово помогло бы использование высокоуровневых языков программирования. В паблике сейчас валяются исходники крипторов на С++ или VisualBasic’е, но ведь от этого проще не становится, поскольку разобраться в написанном коде — ой как непросто. На Python’е все выглядит в разы лучше, поэтому именно его мы сегодня и будем использовать. В общем, заложим фундамент этой благородной миссии. Присоединяйся!


Выпуск 2. PE-файл

Структура PE-файла довольно сложная, поэтому подробная документация будет ждать тебя на диске, а здесь я представлю твоему вниманию лишь избранные моменты.

PE-файл представляет набор разных служебных структур, связанных между собой, и набор данных, которые размещены в секторах. Загрузчик Windows’a читает структуры, обрабатывает их (например, импортирует DLL’ки) и потом передает управление на инструкцию, указанную в поле «Entry Point».

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

Выпуск 3. Теоретический криптор

Для начала выберем файл, который будет у нас исполнять функции лабораторной мыши. Чтобы сделать приятное Андрюшку :), мы, пожалуй, будем издеваться над Putty.exe. Упрощенно его структура будет выглядеть так:

  1. Служебные данные
  2. Первая кодовая секция
  3. Другие секции с данными

Алгоритм криптора следующий. Создать две ассемблерные программы. Первая будет косить под обычную прогу и проверять, что мы не в эмуляторе, а потом передаст управление на вторую программу. Вторая же восстановит оригинальную структуру файла и передаст управление на оригинальную точку входа Putty. И записать эти программы в файл.

В результате получится следующая структура:

  1. Служебные данные
  2. Первая кодовая секция
    1. Наша первая программа, которая передаст управление на 4.2
    2. Шифрованный код первой секции
    1. Часть кодовой секции, перезаписанной программой 2.1
    2. Вторая программа, которая оригинальный код из 4.1 поместит на 2.1, а потом расшифрует кодовую секцию и передаст на нее управление.

    Выпуск 4. Практический криптор

    Ну наконец-то мы добрались до сердца нашей статьи. Для работы криптора нам понадобится модуль pefile (будем использовать несколько модифицированную версию), и с помощью либы откроем Putty:

    import pefile
    pe = pefile.PE("putty.exe")

    Теперь, если ты напишешь «print pe», то увидишь подробную инфу обо всех характеристиках файла, по этой инфе я советую искать нужные для изменения поля в файле. А о внутренней работе модуля обязательно прочитай во врезке. Теперь немного математики. У нас будут две программы, которые нужно внедрить в файл. Они будут занимать где-то по 512 байт каждая максимум. Поэтому для размещения добавим новую секцию в 1024 килобайт вызовом:

    Закриптуем первую секцию XOR’ом с ключом «1»:

    Магия, правда? :). А теперь прикинь, что все это пришлось бы писать на С++!

    Поскольку в начале программы будет наш код, то сохраним оригинальный код, скопировав его в последнюю секцию. Адрес первой секции в файле находится в переменной — pe.sections[0]. PointerToRawData, а последней, соответственно — в pe.sections[-1].PointerToRawData:

    pe.data_copy(pe.sections[0].PointerToRawData, pe.sections[-1].PointerToRawData, 512)

    Оригинальный код сохранен, и мы приступим к написанию первой программы. Конечно же, писать мы ее будем на ассемблере, используя FASM для компиляции. Создадим файлик pack.tpl.asm с содержанием:

    use32
    mov eax, >
    jmp eax

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

    asm = Template(open("pack.tpl.asm", "r").read()).generate(
    go=pe.OPTIONAL_HEADER.ImageBase + pe.sections[-1].VirtualAddress+512,
    )
    with open("pack.asm", "w") as f:
    f.write(asm)
    os.system(r"c:fasmwFASM.EXE pack.asm")

    В переменной go мы передаем адрес в памяти, где будет наша вторая программа — то есть, в последней секции, начиная с 512 байта. А в последней строчке компилим результат на FASM’е. Теперь запишем получившийся код в начало первой секции:

    new_pack = open("pack.bin", "rb").read()
    pe.data_replace(offset=pe.sections[0].PointerToRawData, new_data=new_pack)

    Вторую программу запишем в файл copy.tpl.asm. Размер у нее более внушительный, поэтому полный код смотри на диске. Там содержится два цикла, один скопирует 512 байт оригинальной программы с последней секции в первую, а второй цикл расшифрует всю первую секцию. После этого передается управление на оригинальную программу.

    При компиляции темплейта нужно передать туда параметры для циклов копирования и расшифровки:

    copy_from = pe.OPTIONAL_HEADER.ImageBase+pe.sections[-1].VirtualAddress
    copy_to = pe.OPTIONAL_HEADER.ImageBase+pe.sections[0].VirtualAddress
    oep = pe.OPTIONAL_HEADER.ImageBase+pe.OPTIONAL_HEADER.AddressOfEntryPoint
    asm = Template(open("copy.tpl.asm", "r").read()).generate( copy_from=copy_from, copy_to=copy_to, copy_len=512, xor_len=pe.sections[0].Misc_VirtualSize, key_encode=1, original_oep=oep,)

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

    new_copy = open("copy.bin", "rb").read()
    pe.data_replace(offset=pe.sections[-1].PointerToRawData+512, new_data=new_copy)
    pe.sections[0].Characteristics |= pefi le.SECTION_CHARACTERISTICS["IMAGE_SCN_MEM_WRITE"]
    pe.OPTIONAL_HEADER.AddressOfEntryPoint = pe.sections[0].VirtualAddress
    pe.write(fi lename="result.exe")

    Выпуск 5. Завершающий

    Если собрать кусочки кода вместе, то будет у нас всего 50 строк. Всего лишь 50 — и криптор готов! А теперь прикинь, сколько строк содержала бы программа на С? Конечно, это еще далеко не готовый продукт, над ним нужно работать и работать. Чтобы довести систему до реального криптора, нужно добавить как минимум шифрование ресурсов и импорта, а также антиэмуляцию. О том как теоретически эти проблемы решить, смотри во врезках. Удачи!

    Желательный функционал 1. Обход песочниц

    В крипторе нужно делать проверки на запуск в виртуальной машине, SandBox’е или анализаторе типа анубиса. Чтобы их зедетектить, нужно провести небольшое исследование и написать программу, которая будет на экран выводить разные внутренние параметры системы, а дальше — проверить этот файл на том же анубисе и в скриншоте посмотреть параметры, которые показала наша прога. Дальше все просто — при запуске на системе с подобными параметрами — просто уходим в цикл.

    Обязательный функционал 2. Шифрование ресурсов и импорта

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

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

    Обязательный функционал 1. АнтиЭмуляция

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

    Вот тебе такая идейка для реализации:

    Внутренности Антивирусов

    В упрощенном виде, антивирус — это набор правил (сигнатур) и система, которая проверяет файл по этим правилам.

    К примеру, пусть в антивирусе будут такие сигнатуры:

    • секция с кодом, записываемая +10;
    • после запуска прописывается в авторан +30;
    • вторая секция с именем Zeus +30;
    • меньше 4 энтропия кодовой секции +20;
    • есть сертификат от майкрософта -10.

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

    Как работает pefile

    При загрузке в pefile экзэхи, библиотека сохраняет сам файл в pe.data, а потом обрабатывает его и создает массив структур pe.structures. Структура — это объект, у которого есть адрес. Адрес, по которому она находится в файле, и есть набор полей.

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