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

Viva la Морфер

x3Rx

HDD-drive
Пользователь
Регистрация
13.12.2019
Сообщения
32
Реакции
59
Привет бандиты!

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

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

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

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

Числа
Начнем с простого - числа. Тут все просто, как дважды два. Есть глобальная мысль и есть 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 :D
 
Пожалуйста, обратите внимание, что пользователь заблокирован
По поводу того, что оптимизатор преобразовывает весь ваш мат. треш на этапе компиляции:
Работайте не напрямую с числами, а с переменными.
Т. Е есть у нас такой примерчик:
(2+2*2)
Оптимизатор преобразует сразу же в ответ: 6
Чтобы этого не произошло загоняете числа из примера в переменные:
int n2 = 2;
И уже работайте с ними:
(n2+n2*n2)
 
Последнее редактирование:
По поводу того, что оптимизатор преобразовывает весь ваш мат. треш на этапе компиляции:
Работайте не напрямую с числами, а с переменными.
Т. Е есть у нас такой примерчик:
(2+2*2)
Оптимизатор преобразует сразу же в ответ: 6
Чтобы этого не произошло загоняете числа из примера в переменные:
int n2 = 2;
И уже работайте с ними:
(n2+n2*n2)
если n2 дальше не будет использовать например с ВинАПИ то оптимизатор посчитает и воткнет туда 6 всеравно :) плохой совет короче
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Для обфускации строк можно использовать след. алгоритм:
Есть у нас алфавит (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;
}
Использую данный метод своем морфере, только каждая строчка кода в отдельном блоке с мусором
 
Пожалуйста, обратите внимание, что пользователь заблокирован
если n2 дальше не будет использовать например с ВинАПИ то оптимизатор посчитает и воткнет туда 6 всеравно :) плохой совет короче
Тем не менее, в моем случае помог
 
Где доказательства? Написать можно все что угодно, где ФАКТЫ я автора спрашиваю!!!
Лозунг конкурса должен быт таким: Меньше соплей! Даешь исходники! Ленин жив!
 
Я очень хотел приложить PoC рабочего морфера, а еще очень хотел написать про макросы и препроцессор, но как обычно отложил все на последний день, а до 1 марта остается всего несколько часов.
Разве конкурс закрывается не 2 марта? Просто голосование начинается именно тогда.
 
Тем не менее, в моем случае помог
ну настройки разные бваю, в дэбаг режиме например оптимизатор отключается, или у тебя он отключен, да и настрокйи оптимизатора надо смотреть.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Ну так если ты отключил оптимизацию, то очевидно она происходить не будет.
Забыл что отключал оптимизацию, когда кое-какие фишки тестил))
Сейчас проверил, действительно оптимизатор все сьедает
 
К стати, слишком "длинным" этот тип морфинга делать не стоит, тк руками он снимается очень легко, те защищает только от автоматического детекта, а веса программе может прибавить.
 
Для меня лично, смысл разбития в мат выражение - не в наличии мат выражения как такового в сорцах, как бы странно это не звучало. Мы порождаем таким образом «почву» для дальнейших морф операций.
Само по себе мат выражение это слабый предикат. Но, благодаря магии математики, мы можем представить результат в виде почвы бесконечным количеством способов. Что значит почва?
10 -> opaque_predicate(5+5)
Смысл в том, чтобы
1)породить новые данные в сорцах при каждой генерации предиката.
2)чтобы они были предикатом для оригинала.
Потому, что дальше происходит следующее:
10->opaque_predicate(opaque_predicate(5)+opaque_predicate(5))
То есть мы уходим в глубину морфинга. Изначальной 10 больше нет. Она представлена в виде сложения двух 5. И пятерки, как таковой, больше не существует в явном виде в сорцах. Вместо пятерки - любой предикат. Например, мы знаем что в алфавите на 5 позиции находится символ e(если считаем с 1) Значит можем вставить вместо пятерки в сорцах предикат, который итерирует алфавит, до тех пор, пока не наткнётся на символ e. А потом мы берём значение счетчика, и, о магия, в счетчике находим 5. Но 5 в сорцах то уже нет в явном виде.
А вот в чем глубина заключается. Допустим мы все же вставили кусок кода, о котором я только что выше сказал. А ведь у этого куска есть какие то свои данные и внутреннее состояние, нужное для поиска в алфавите. И для его данных применяем такие же правила с набора подготовленных полиморфных предикатов. Почему они полиморфны? С шага, где мы заложили «почву» бесконечным математическим представлением данных. То есть если сам предикат зависим в своей реализации от входа - то имея хаотичный вход, имеем хаотичный предикат(например на число 4, итерация по алфавиту уже будет то символа d, если считаем с 1). Чем глубже уходить в рекурсию, тем больше магии этой хаотичной системы, которая является по сути полностью детерминированной. Незначительный рандом на входе, такой как мат выражение и незначительный рандом в каждом последующем предикате - порождает сеть предикатов, дающих полностью хаотичный сорц если смотреть в общем, без возможности подобрать сигнатуру к конкретной его части. И эта сеть больше не повторит своего состояния за всю историю ваших генераций. Если вход уникальный, то и вся сеть уже будет уникальна. Если каждый элемент сети (предикат) вносит свою порцию уникальности, то, каждый шаг привносит уникальности во всю цепочку уже в геометрической прогрессии. Эффект бабочки в действии. Чтобы проникнуться всей идеей, советую изучить хотя бы базово теорию хаоса и такие вещи как задача трех тел, почитать о двойном маятнике и посмотреть в действии.
 
Последнее редактирование:
Мой фаворит в конкурсе
Странный выбор, одна теория, нет исходников, нет подтверждений его идей, да и методы не уникальны. Самое сложно - это универсальное ядро парсинга исходников и изменение их не нарушив работы. Но в этой статье ровным счетом ничего нету. Вот если бы выложил исходники с парсингом и рефакторингом кода - это да.
 
Парни, всем спасибо за комментарии, очень рад что статья зашла!

Хочу добавить еще две крутые штуки, которые не очевидны, но могут сильно вам помочь:

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

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

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

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

Ну и приложу простенький скрипт на питоне, который берет случайное число и формирует вам шаблон для функции:
Python:
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) и так далее.
 
Забей, на экспе тоже победила статья-концепт, типа вот вам идея, а с реализацией ебитесь сами.
Реализация стоит больше 5к(которые ещё не гарантированно, что получишь, ибо конкурс). Мб в одни руки да. Но не всем же)
 
Реализация стоит больше 5к(которые ещё не гарантированно, что получишь, ибо конкурс). Мб в одни руки да. Но не всем же)
Разве что, если 5к рублей.
 


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