Viva la Морфер

  • Автор темы Admin

Admin

#1
Администратор
Регистрация
31.12.2019
Сообщения
6,649
Реакции
23
Привет бандиты!

Я почитал конкурсный топик и похоже просек рецепт идеальной статьи:
- Минимум теории, максимум практики
- Пиши про морфинг кода, это больная тема у всех
- Не выдавай рабочий софт, выдавай концепт

Оно и понятно. Сейчас у большинства серьезных ребят своя приватная малварь (есть доступ к сорцам) + как я понял крипт на уровне бинарников уже не торт.

Ну хуль, морфим значит морфим. Погнали.

Что морфим
- Числа
- Строки
- Функции

Числа
Начнем с простого - числа. Тут все просто, как дважды два. Есть глобальная мысль и есть 1001 метод.

Глобальная мысль: вычисляем нужное число, но так, чтобы наши операции не оптимизировал компилятор и чтобы вычислить можно было большим количеством способов.

Короче, идея супер простая - мы своим человеческим мозгом приходим к алгоритму, который даст нам фиксированное число. А из этого числа мы уже получим все остальные нужные нам числа.
Легко же получить 5, если у тебя уже есть в программе 3?
Конечно, 3 + 2 = 5, да?
А фиг там, оптимизация. И на месте твоего выражения уже сияет цифра 5, а твой софт на динчеке горит аки новогодняя елка.

Что делать? Придумать вычисления, которые компилятор не умеет оптимизировать. Например, с рандомом.

Метод 1:
Получаем число 3 или 9 из любого числа на входе.

Довольно забавная магия, следи за руками:
1) Загадай любое число
2) Возьми его и два следующих за ним числа и сложи (то есть если загадал число 21 - берешь 21 22 и 23 и складываешь - у меня получилось 66)
3) Возьми эту сумму и сложи все цифры между собой (у меня 66, значит я складываю 6+6=12)
4) Опять разбиваешь на цифры, опять складываешь и так повторяешь пока не состаришься пока не получишь число из одной цифры
5) Ахалай-махалай, у тебя получилось 3 или 9!

Как это работает? Вспоминаем школу, признак деления на 3 и 9 - если сумма цифр делится, то и число делится. А также сумма любых трех подряд идущих чисел делится на 3 (ну или 9, если повезет).
Совмещаем эти два простых факта и получаем 3 или 9 из абсолютно любого числа на входе.

Как получить из 3 или 9 любое другое число думаю объяснять излишне.

Метод 2:
Играем с остатками.

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

1) x + x%y (остаток от деления) всегда делится на y
2) если x делится на y, то x%y = 0
3) если перебирать подряд числа и if'ом проверять делится ли без остатка на допустим 123, а потом поделить, то компилятор такое хрен оптимизирует, а мы на выходе всегда получим 0
4) Остаток суммы всегда равен сумме остатков. То же самое с разностью и произведением.

Давай на пункте 4 остановимся поподробнее. Что это значит?
А это значит, что можно любое число представить вот так:


Код:
 		38 = x*((15 + 13 + 10)//x) + (12 + 16 + 10)%x    = x*(15//x + 13//x + 10//x) + 12%x + 16%x + 10%x
где x - любое случайное число.
Круто, правда? Получаем в рантайме абсолютно левое число и серией неоптимизируемых операций всегда получаем нужное.

Кстати про левое число, пара методов:
1) Дернуть рандом, очевидно
2) Узнать время
3) Узнать размер файла
4) Просто указателем в рандомную ячейку памяти ткнуть
5) Запросить содержимое сайта, взять ord() от какого-нибудь символа
6) Взять длину левой строки

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

Да, детка, гений господствует над хаосом.

Строки
На строках я не буду так подробно останавливаться, потому что если поняли с числами - допрете и до строк.

Лайфхаки:
1) Собрать список чисел (уже умеем), преобразовать с помощью char() в строку.
2) Обратный хэш. Это когда мы от нужной строки заранее берем хэш, а потом в рантайме его брутим (чтобы найти ключ - нашу исходную строку). Тут мы убиваем двух зайцев - прячем строку и обходим эмуляцию.
3) Составляем строку, используя известный нам символ и математику.
Тут методика основана на том, что мы человеки знаем больше, чем бездушные машины. Например, все мы знаем с чего начинается любой html файл - это можно использовать.
4) Не все знают, но в большинстве языков программирования можно воткнуть рандомную строку в качестве условия в if. Если строка не пустая - вернет True, иначе False. А тру и фолс можно преобразовать в строку и получить "True"/"False" - пердсказуемый стартовый кусок данных.

Я думаю со строками все понятно, поэтому перейдем к самому соку - функциям.

Функции
Ну блин, вы же не думали, что я просто по фану писал предыдущую статью про Функциональное программирование?

Суть вот в чем... Да фиг с ней с сутью, практика-практика!
Сразу покажу пример, а потом уже все объясню.

1) У нас есть функция вида:

Код:
int max(int num1, int num2) {    // local variable declaration    int result;      if (num1 > num2)       result = num1;    else       result = num2;      return result;  }

2) Переводим ее в лямбда функцию (поддерживается со стандарта C++11, а в более новых версиях вижуал студио еще проще - через auto)

Код:
function<int (int) (int)> max = [] (int num1, int num2) -> int {     int result;      if (num1 > num2)         result = num2;     else         result = num1;          return result; }

3) Фигачим каррирование (кто не знает - это когда мы функцию от нескольких аргументов сводим к цепочке функций от одного аргумента.)


Код:
function<function<int (int)> (int)> max =     [] (int num1) -> function<int (int)> {         return [num1] (int num2) -> int {             int result;             if (num1 > num2)                 result = num2;             else                 result = num1;             return result;         } }

Ну а теперь теория. Нафиг надо все это делать?
Это достаточно сложно понять и еще сложнее объяснить, но я попытаюсь.

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

Идея для морфинга функций:
1) Есть наша рабочая функция от 2 аргументов
2) Добавляем в нее трэш-аргументы, получаем функцию от 30 аргументов
3) Превращаем в лямбда функцию и каррируем как черти, но при этом на каждом уровне с трэш-аргументами добавляем еще и трэш-код.

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

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

Я очень хотел приложить PoC рабочего морфера, а еще очень хотел написать про макросы и препроцессор, но как обычно отложил все на последний день, а до 1 марта остается всего несколько часов.

Короче, итоговые мысли:
1) Морфить сорцы можно, причем делать это можно в автоматическом режиме, бесконечным числом вариантов.
2) Для обфускации констант мы применяем метод "неизвестно что на входе - предсказуемый результат на выходе" и неупрощаемые математические операции.
3) Для морфинга функции - мы переводим ее в лямбда-функцию, расширяем число аргументов и делаем трэш-каррирование, изменяя тем самым поток выплонения и сигнатуры функций.

Спасибо Администрации XSS и Unknown за конкурс - я на площадке недавно, но мне уже нравится.
Буду рад вопросам в комментариях.
Буду еще больше рад поработать с командой REvil



Дополнительно:


1) Язык, на котором вы пишете морфер, не обязан совпадать с языком, на котором написана малварь!
Это реально многие упускают и потом ебутся с препроцессором С++, пытаясь молотком починить микросхему.
Идея очень простая: при морфинге код - это всего лишь данные. Морферу не надо знать, что делает код, ему нужно тупо работать с текстом. Парсить и изменять.

Если бы я писал морфер для цппшного кода, я бы взял язык с мощной системой парсинга - Perl, что-то из семейства Lisp или может даже Rebol, но никак не C++.

2) Очень понравился комментарий Haunt'а о том, что все эти выражения - это всего лишь фундамент, с которым мы дальше работаем.
Так понравился, что я решил показать вам еще один необычный метод получения нуля из любого числа, который не оптимизируется компиляторами.

Идея:
Снова окунемся в математику (вообще там кладезь инфы для морфинга, рекомендую), а именно - будем использовать следствие из Теоремы Безу.
Теорема Безу гласит, что для любого многочлена P(x) остаток от деления на (x-a) равен значению многочлена в этой точке - P(a).
Следствием из Теоремы Безу является тот факт, что если число a - корень многочлена P(x), то остаток от деления равен нулю.

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


Код:
         import random  def bezu():     x = round(random.random()*100)     for a in range(-1000,1000):         for b in range(-1000,1000):             if x**2 - (a+b)*x + a*b == 0:                 print("(x^2 + "+str(a+b)+"x + "+str(a*b)+") % (x - "+str(x)+")")
Запускаем и на выходе получаем:


Код:
... (x^2 - 1059x + 78320) % (x - 80) (x^2 - 1060x + 78400) % (x - 80) (x^2 - 1061x + 78480) % (x - 80) (x^2 - 1062x + 78560) % (x - 80) (x^2 - 1063x + 78640) % (x - 80) (x^2 - 1064x + 78720) % (x - 80) (x^2 - 1065x + 78800) % (x - 80) (x^2 - 1066x + 78880) % (x - 80) (x^2 - 1067x + 78960) % (x - 80) (x^2 - 1068x + 79040) % (x - 80) (x^2 - 1069x + 79120) % (x - 80) (x^2 - 1070x + 79200) % (x - 80) (x^2 - 1071x + 79280) % (x - 80) (x^2 - 1072x + 79360) % (x - 80) (x^2 - 1073x + 79440) % (x - 80) (x^2 - 1074x + 79520) % (x - 80) (x^2 - 1075x + 79600) % (x - 80) (x^2 - 1076x + 79680) % (x - 80) (x^2 - 1077x + 79760) % (x - 80) ...
Создаем функцию g(x), записываем в нее любое из понравившихся выражений.
Наша новая функция будет возвращать ноль для любого x.

Ну а дальше уже используем этот кирпичик по своему усмотрению - как условие в if, как базис для получения единицы (перевести в bool, применить not, перевести обратно в int) и так далее.
Так же:


Для обфускации строк можно использовать след. алгоритм:
Есть у нас алфавит (a-zA-Z0-9)
Есть строка:
"xss"
Мы знаем, что у нас в алфавите символ 'x' лежит по индексу 12, 's' лежит по индексу 19.
Соответственно можем преобразовать строку в след.код:


Код:
const char* alpha = "a-zA-Z0-9"; char* str_xss()  {     char ret[размер_строки];     ret[0] = alpha[12];     ret[1] = alpha[19];     ret[2] = alpha[19];     return ret; }
Использую данный метод своем морфере, только каждая строчка кода в отдельном блоке с мусором


Автор: x3Rx
 

Members, viewing this thread

Сейчас на форуме нет ни одного пользователя.