Привет бандиты!
Я почитал конкурсный топик и похоже просек рецепт идеальной статьи:
- Минимум теории, максимум практики
- Пиши про морфинг кода, это больная тема у всех
- Не выдавай рабочий софт, выдавай концепт
Оно и понятно. Сейчас у большинства серьезных ребят своя приватная малварь (есть доступ к сорцам) + как я понял крипт на уровне бинарников уже не торт.
Ну хуль, морфим значит морфим. Погнали.
Что морфим
- Числа
- Строки
- Функции
Числа
Начнем с простого - числа. Тут все просто, как дважды два. Есть глобальная мысль и есть 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 остановимся поподробнее. Что это значит?
А это значит, что можно любое число представить вот так:
где x - любое случайное число.
Круто, правда? Получаем в рантайме абсолютно левое число и серией неоптимизируемых операций всегда получаем нужное.
Кстати про левое число, пара методов:
1) Дернуть рандом, очевидно
2) Узнать время
3) Узнать размер файла
4) Просто указателем в рандомную ячейку памяти ткнуть
5) Запросить содержимое сайта, взять ord() от какого-нибудь символа
6) Взять длину левой строки
И так далее, бесконечное количество информации, которую мы можем взять и преобразовать в рантайме в нужную нам.
Да, детка, гений господствует над хаосом.
Строки
На строках я не буду так подробно останавливаться, потому что если поняли с числами - допрете и до строк.
Лайфхаки:
1) Собрать список чисел (уже умеем), преобразовать с помощью char() в строку.
2) Обратный хэш. Это когда мы от нужной строки заранее берем хэш, а потом в рантайме его брутим (чтобы найти ключ - нашу исходную строку). Тут мы убиваем двух зайцев - прячем строку и обходим эмуляцию.
3) Составляем строку, используя известный нам символ и математику.
Тут методика основана на том, что мы человеки знаем больше, чем бездушные машины. Например, все мы знаем с чего начинается любой html файл - это можно использовать.
4) Не все знают, но в большинстве языков программирования можно воткнуть рандомную строку в качестве условия в if. Если строка не пустая - вернет True, иначе False. А тру и фолс можно преобразовать в строку и получить "True"/"False" - пердсказуемый стартовый кусок данных.
Я думаю со строками все понятно, поэтому перейдем к самому соку - функциям.
Функции
Ну блин, вы же не думали, что я просто по фану писал предыдущую статью про Функциональное программирование?
Суть вот в чем... Да фиг с ней с сутью, практика-практика!
Сразу покажу пример, а потом уже все объясню.
1) У нас есть функция вида:
2) Переводим ее в лямбда функцию (поддерживается со стандарта C++11, а в более новых версиях вижуал студио еще проще - через auto)
3) Фигачим каррирование (кто не знает - это когда мы функцию от нескольких аргументов сводим к цепочке функций от одного аргумента.)
Ну а теперь теория. Нафиг надо все это делать?
Это достаточно сложно понять и еще сложнее объяснить, но я попытаюсь.
В примере мы сделали вот что:
1) Есть наша рабочая функция, допустим от 2 аргументов
2) Мы превращаем ее в лямбда-функцию, а потом каррируем - получаем 2 новых функции, каждая с новой сигнатурой и новым набором аргументов, но результат работы тот же.
Идея для морфинга функций:
1) Есть наша рабочая функция от 2 аргументов
2) Добавляем в нее трэш-аргументы, получаем функцию от 30 аргументов
3) Превращаем в лямбда функцию и каррируем как черти, но при этом на каждом уровне с трэш-аргументами добавляем еще и трэш-код.
Таким образом мы получаем бесконечное количество вариантов flow нашего софта (потому что мы движемся не сверху вниз по коду, как в императивном программировании, а по функциям-коллбэкам, как в ФП), и функционал при этом остается тот же. Более того, на каждом из этапов движения мы можем втыкать абсолютно любой код, который использует наши трэш-аргументы.
Я надеюсь никто не обиделся, что я расписал все на примере C++.
То же самое можно сделать и в шарпе, и в делфях, и практически в любом другом языке.
Я очень хотел приложить PoC рабочего морфера, а еще очень хотел написать про макросы и препроцессор, но как обычно отложил все на последний день, а до 1 марта остается всего несколько часов.
Короче, итоговые мысли:
1) Морфить сорцы можно, причем делать это можно в автоматическом режиме, бесконечным числом вариантов.
2) Для обфускации констант мы применяем метод "неизвестно что на входе - предсказуемый результат на выходе" и неупрощаемые математические операции.
3) Для морфинга функции - мы переводим ее в лямбда-функцию, расширяем число аргументов и делаем трэш-каррирование, изменяя тем самым поток выплонения и сигнатуры функций.
Спасибо Администрации XSS и Unknown за конкурс - я на площадке недавно, но мне уже нравится.
Буду рад вопросам в комментариях.
Буду еще больше рад поработать с командой REvil
Я почитал конкурсный топик и похоже просек рецепт идеальной статьи:
- Минимум теории, максимум практики
- Пиши про морфинг кода, это больная тема у всех
- Не выдавай рабочий софт, выдавай концепт
Оно и понятно. Сейчас у большинства серьезных ребят своя приватная малварь (есть доступ к сорцам) + как я понял крипт на уровне бинарников уже не торт.
Ну хуль, морфим значит морфим. Погнали.
Что морфим
- Числа
- Строки
- Функции
Числа
Начнем с простого - числа. Тут все просто, как дважды два. Есть глобальная мысль и есть 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
Круто, правда? Получаем в рантайме абсолютно левое число и серией неоптимизируемых операций всегда получаем нужное.
Кстати про левое число, пара методов:
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