• XSS.stack #1 – первый литературный журнал от юзеров форума

Bytecode malware

beecoder

floppy-диск
Пользователь
Регистрация
01.04.2023
Сообщения
7
Реакции
3
Приветствую. Скажите, какие есть перспективы у решений написания малварей в байткоде? Ну то есть использовать тот же lua для исполнения тела малвари. Ещё, как пример, есть очень легковестное решение - q3vm, в этом проекте реализован компилятор Си в байткод q3vm (через промежуточный three address code), причём сама ВМ очень простая, легко модифицировать под свои нужды (прокинуть вызов библиотечных функций и т.п.) Очевидные плюсы - легко написать упаковщик, легче проводить манипуляции с готовым байткодом. Очевидные минусы - очень много кодить, все малвари придётся писать самому, либо хорошо постараться, чтобы готовые сорсы перенести.
Что скажете?
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Ну, полно интересных встраиваемых языков, помимо Lua (сходу вспоминается Umka, Duktape, QuickJS, BerryScript, Wren и другие), чем менее распространенный ты возьмешь, тем меньше вероятность, что для его опкодов будет дизассемблер или декомпилятор. А так по хорошему нужно модифицировать язык так, чтобы байткод был полиморфным и менялся от семпла к семплу.

По поводу "много кодить", а как ты хотел? У некоторых таких языков (как у Lua) есть готовые байндинги для многих необходимых вещей (как, например, luasocket у Lua для передачи данных по сети), для некоторых еще и придется все необходимые байндинги сделать.
 
Приветствую. Скажите, какие есть перспективы у решений написания малварей в байткоде? Ну то есть использовать тот же lua для исполнения тела малвари. Ещё, как пример, есть очень легковестное решение - q3vm, в этом проекте реализован компилятор Си в байткод q3vm (через промежуточный three address code), причём сама ВМ очень простая, легко модифицировать под свои нужды (прокинуть вызов библиотечных функций и т.п.) Очевидные плюсы - легко написать упаковщик, легче проводить манипуляции с готовым байткодом. Очевидные минусы - очень много кодить, все малвари придётся писать самому, либо хорошо постараться, чтобы готовые сорсы перенести.
Что скажете?
Почему бы просто не сделать трансляцию асма в код вм? Это тебя избавит от писать на ц.
 
Ну, полно интересных встраиваемых языков, помимо Lua (сходу вспоминается Umka, Duktape, QuickJS, BerryScript, Wren и другие), чем менее распространенный ты возьмешь, тем меньше вероятность, что для его опкодов будет дизассемблер или декомпилятор. А так по хорошему нужно модифицировать язык так, чтобы байткод был полиморфным и менялся от семпла к семплу.

По поводу "много кодить", а как ты хотел? У некоторых таких языков (как у Lua) есть готовые байндинги для многих необходимых вещей (как, например, luasocket у Lua для передачи данных по сети), для некоторых еще и придется все необходимые байндинги сделать.
Да, тот же Lua как пример просто привёл. А вот по поводу встроенного дизассемблера интересно, не знал, что АВ такое делают. Спасибо.
По поводу биндов это понятно, что такие вещи придётся самому делать, особенно для редких языков.
 
Почему бы просто не сделать трансляцию асма в код вм? Это тебя избавит от писать на ц.
Идея хорошая, хотя много подводных. Если говорить о чистом исполняемом файле без зависимостей, то да, можно перевести почти без проблем. Код с зависимстями (то есть любой исполняемый файл) нужно, конечно, постараться.
Но идея и правда очень интересная, спасибо!
 
Идея хорошая, хотя много подводных. Если говорить о чистом исполняемом файле без зависимостей, то да, можно перевести почти без проблем. Код с зависимстями (то есть любой исполняемый файл) нужно, конечно, постараться.
Но идея и правда очень интересная, спасибо!
Я не говорил про исполняемый код. Я подразумевал делать вывод компилятора в асм листинг, а его уже транслировать в вм код. Там тоже будет не все просто, но решаемо.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Я не говорил про исполняемый код. Я подразумевал делать вывод компилятора в асм листинг, а его уже транслировать в вм код. Там тоже будет не все просто, но решаемо.
Слишком много опкодов будет для решения в общем случае (даже если думать, что твой код никогда не будет использовать всякие SSE, AVX и тд), а если транслировать в какую-то ужатую RISC архитектуру, то это сложные анализы над кодом нужно проводить. Понятно, что условные вмпротекты так и делают, но сколько человеко-часов в них вложено, готов ли ты будешь вложить столько же.
 
Слишком много опкодов будет для решения в общем случае (даже если думать, что твой код никогда не будет использовать всякие SSE, AVX и тд), а если транслировать в какую-то ужатую RISC архитектуру, то это сложные анализы над кодом нужно проводить. Понятно, что условные вмпротекты так и делают, но сколько человеко-часов в них вложено, готов ли ты будешь вложить столько же.
Сути такая, что 1 неудобная асм команда может быть транслирована в несколько удобных, то есть мы можем довольно сильно упрощать ценой увеличения количества команд. Я как то брал десятки тысяч исполнимых файлов юзер мода и составлял статистику по частоте использования разных асм команд, результаты там не такие страшные как это могло бы показатся и редкие команды можно превращать хоть в 10 простых.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Сути такая, что 1 неудобная асм команда может быть транслирована в несколько удобных, то есть мы можем довольно сильно упрощать ценой увеличения количества команд. Я как то брал десятки тысяч исполнимых файлов юзер мода и составлял статистику по частоте использования разных асм команд, результаты там не такие страшные как это могло бы показатся и редкие команды можно превращать хоть в 10 простых
Я не говорю, что это невозможно сделать, я говорю о том, что ты скорее всего заебешься в процессе и бросишь это делать. Сразу вспомнил картинку из статьи с хабра:
ph4k700mdvxwacaepm4uzqoq188.png


При этом считались только разные мнемоники (то есть по факту разных вариантов инструкций гораздо больше): https://habr.com/ru/articles/503486/

Конечно, можно там отключить компилятору оптимизации и всякими другими костылями (типа компиляции под i386) снизить общее количество инструкций, которые надо будет обрабатывать. Это поможет, но насколько это имеет смысл, я не знаю.
 
Я не говорю, что это невозможно сделать, я говорю о том, что ты скорее всего заебешься в процессе и бросишь это делать. Сразу вспомнил картинку из статьи с хабра:
Посмотреть вложение 54498

При этом считались только разные мнемоники (то есть по факту разных вариантов инструкций гораздо больше): https://habr.com/ru/articles/503486/

Конечно, можно там отключить компилятору оптимизации и всякими другими костылями (типа компиляции под i386) снизить общее количество инструкций, которые надо будет обрабатывать. Это поможет, но насколько это имеет смысл, я не знаю.
Давай плясать от задачи. Если мы пытаемся сделать что то универсальное для всего на свете это одно, а если мы в треде про малвару то это совсем другое. Да и чисто по логике брать и запирать оптимизацию(SSE, AVX) в виртуал машину само по себе немного странно. Да все не обязательное нам будем отключать. Очень сомневаюсь что например та же денува будет че то там делать с AVX кодом. Все просчитать, расписать проблемы и возможные пути их решения, противовзвесить с профитом и будет ясно. Вм может быть и mixed с коллами в натив процедуры, понадобилось нам хешировать, развернули в момент надобноси процедурку и ходим на нее через гейт, когда не надо потерли. Получаем биз логигу строго под вм, леер абстракции от ос тоже под вм, всякое критичное гибридно. Архитектура ясно что не простая, но задача интересная.
 
Почему бы просто не сделать трансляцию асма в код вм? Это тебя избавит от писать на ц.
Слишком много опкодов будет для решения в общем случае (даже если думать, что твой код никогда не будет использовать всякие SSE, AVX и тд), а если транслировать в какую-то ужатую RISC архитектуру, то это сложные анализы над кодом нужно проводить. Понятно, что условные вмпротекты так и делают, но сколько человеко-часов в них вложено, готов ли ты будешь вложить столько же.

Почему бы не строить вм еще на уровне промежуточного кода ? GCC и Clang это позволяют, и если с GCC все не так однозначно, то clang llvm прекрасно описан. Документация там на высшем уровне.
Гонять асм листинги выплюнутые компилятором это самоебство имхо. Гораздо практичнее написать компиляторный модуль, обрабатывающий код еще на его промежуточном представлении.
В итоге ты компилируешь любые сорцы, а на выходе получаешь exe в который уже встроена ВМ (которую к тому же на этом же промежуточном представлении можно морфить и каждый раз получать уникальный код). Не нужна никакая постобработка, код виртуализируется во время компиляции. И не надо думать ни о каких AVX SSE, да и вообще нет никаких головняков, созданием бинаря будет заниматься кодогенератор платформы, он переводит промежуточное представление в ассемблер/байткод реального процессора, наша задача находится уровнем выше в промежуточном представлении - заменить все виртуальные команды на команды делающие выборку VM-байткода, который тоже создается на промежуточном представлении, и далее передача этого байткода на хендлеры VM. Все достаточно просто и максимально надежно. И не нужны тысячи человеко-часов.
 
Последнее редактирование:
Я не говорил про исполняемый код. Я подразумевал делать вывод компилятора в асм листинг, а его уже транслировать в вм код. Там тоже будет не все просто, но реш
Я подумал, что идея была, чтобы покрыть существующие малвари. Если на уровне исходников, то можно взять язык, который умеет транслироваться в WebAssembly (вроде для rust есть что-то такое) и уже его попробовать перевести в тот же q3vm (или вообще какой-то приват вм).
 
Почему бы не строить вм еще на уровне промежуточного кода ? GCC и Clang это позволяют, и если с GCC все не так однозначно, то clang llvm прекрасно описан. Документация там на высшем уровне.
Гонять асм листинги выплюнутые компилятором это самоебство имхо. Гораздо практичнее написать компиляторный модуль, обрабатывающий код еще на его промежуточном представлении.
В итоге ты компилируешь любые сорцы, а на выходе получаешь exe в который уже встроена ВМ (которую к тому же на этом же промежуточном представлении можно морфить и каждый раз получать уникальный код). Не нужна никакая постобработка, код виртуализируется во время компиляции. И не надо думать ни о каких AVX SSE, да и вообще нет никаких головняков, созданием бинаря будет заниматься кодогенератор платформы, он переводит промежуточное представление в ассемблер, наша задача находится уровнем выше в промежуточном представлении - заменить все виртуальные команды на команды делающие выборку VM-байткода, который тоже создается на промежуточном представлении, и далее передача этого байткода на хендлеры VM. Все достаточно просто и максимально надежно. И не нужны тысячи человеко-часов.
Ну есть момент привязки к компилятору раз, и два - необходимость очень хорошо понимать как работают потроха этого компилятора. Я например с гцц и шлангом вообще дел не имел, просто не было надобности. Писал о том с чем дело имел и что понимаю, а какие там чудеса ждут внутри компилей я хз.
 
Ну есть момент привязки к компилятору раз, и два - необходимость очень хорошо понимать как работают потроха этого компилятора. Я например с гцц и шлангом вообще дел не имел, просто не было надобности. Писал о том с чем дело имел и что понимаю, а какие там чудеса ждут внутри компилей я хз.
Да согласен, привязка есть к компилятору, и таким образом получится генерировать вмки только если у тебя есть сорцы. Рандомные бинари так обработать не получится (вопрос спорный, где-то видел как перегоняют бинарик обратно в промежуточное представление, что-то вроде дизассемблера в промежуточный код, но насколько это эффективно и безошибочно сказать сложно)
Но в 9/10 случаев те кто за такое возьмется, скорее всего имеет свой приват софт и свои сорцы, которые ему надо будет защищать.
А паблик малвари такие технологии и не нужны. Это не тот уровень, разрабы паблика даже не задумываются об этом, чаще всего потому что либо им просто фиолетово, либо потому что это школьники и не хватает знаний.
Сам я ни разу не видел паблик малвари в которой была хоть какая-то виртуализация. Собственная, а не всякие VMP и прочие протекторы.
 
Последнее редактирование:
А еще иногда надо брать асм код из иды, править, билдить, тестить, снова править... Думаю все же трюк с конвертом асм листинга дает больше маневра, и легче наверное, не надо вникать кишки компиля(очень сомневась что это недельку почитать и потыкать) там можно и на годик увязнуть.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
а если мы в треде про малвару то это совсем другое
Ну ты вроде говорил, что собирал статистику по инструкциям, можешь ее выложить? Ну и ты измерял именно мнемоники, то есть без учета того, что, например, инструкция mov может иметь 100500 вариантов семантики?) Я писал виртуализатор для MSIL, с горем пополам и ограничениями на исходный MSIL сделал, но сильно заебался, хотя в MSIL всего то порядка 150 инструкций важных, и каждая делает ровно одну задачу. А мы тут говорим о сотнях инструкций, каждая из которых что-то разное делает в зависимости от аргументов.

это самоебство имхо
Это оно и есть, но не все всегда ищут легких путей.

И не надо думать ни о каких AVX SSE
Ну и да и нет. В целом озвученный тобой вариант гораздо лучше, имхо. Но технически ничего не мешает clang'у прямо в LLVM IR вхерачить ассемблерную вставку, если он, условно, решит векторизировать какой-то алгоритм. Не знаю насчет GCC и его GIMPLE представления, но компиляторы на базе LLVM умеют читерить, когда им это нужно.

Если на уровне исходников, то можно взять язык, который умеет транслироваться в WebAssembly (вроде для rust есть что-то такое) и уже его попробовать перевести в тот же q3vm (или вообще какой-то приват вм
Ну есть уже готовые встраиваемые движки под васм, типа wasm3 или как-то так назывался, можно взять уже готовый. Но FFI все равно придется докручивать самому скорее всего. Есть Binaryen и другие инфраструктуры для работы с васм кодом. В этом плане с васм будет куда проще, чем с x86 или LLVM IR работать. К тому же он стековый вроде, код стековой ВМ проще транслировать, чем регистровой.
 
очень сомневась что это недельку почитать и потыкать там можно и на годик увязнуть.
Когда я впервые разбирался с этим, первые результаты были уже через месяц наверное. Конечно это было что-то очень очень простое. Но за месяца 2-4 можно уже и простую вм уровня промежуточного кода сделать.
А еще иногда надо брать асм код из иды, править, билдить, тестить, снова править... Думаю все же трюк с конвертом асм листинга дает больше маневра, и легче наверное
Не понял правда для чего это может понадобиться при построении ВМ, если у нас есть сорцы, то однозначно лучшее решение это модуль для компилятора. иду даже открывать не придется. Я это пишу как человек, который занимался тем чем занимался ты, успел побаловаться с асм листингами, не понравилось. Если сорцев мы не имеем, тот тут в любом случае простых путей нет.
Но технически ничего не мешает clang'у прямо в LLVM IR вхерачить ассемблерную вставку, если он, условно, решит векторизировать какой-то алгоритм. Не знаю насчет GCC и его GIMPLE представления, но компиляторы на базе LLVM умеют читерить, когда им это нужно.
Тут вопрос в том, что бы не использовать готовый компилятор, а собрать свой на базе уже существующего, со всеми нужными модулями обработки, clang позволяет это сделать очень безболезненно. Его пайплайн сборки можно модифицировать как тебе захочется. Тогда без твоего ведома ни одна муха не пролетит. Это конечно требует некоторой подготовки и изучения компилятора. Но не так страшен черт как его малюют.
1681218286443.png
1681218328010.png
 
Последнее редактирование:
Ну ты вроде говорил, что собирал статистику по инструкциям, можешь ее выложить?


Статистики не сохранил, да брал по имени опкода, давно это было. Если интересно загони что нибудь на твой взгляд подходящее в иду и собери ее скриптом, это будет конечно в рамках одной прилы но все равно даст понять суть. А то что - mov rax, [rbx] и mov rcx, rdi как бы разное это не существенно вообще, у тебя ведь сущности такие command, op1, op2 (там где нужен третий операнд просто трансилруем в набор команд где достаточно двух), так для вмки все эти вариции того что может быть внутри op1\2 без разницы, и вся семантика сведется что мув это мув, а адд это адд.
 
Я если что про то что - op1, op2 это просто ссылки, каким бы они ни были, а комнды это просто ссылки на действия. Нет огромной вариабельности на уравне - устанешь писать интерпритатор.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх