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

Статья Генерируем мусорный код на C++ на шаблонах

tenocyclidine

floppy-диск
Пользователь
Регистрация
05.11.2022
Сообщения
3
Реакции
6
При написании софта иногда необходимо скрыть его код от посторонних глаз, запутать реверсера (или антивирус), чтобы софт не крякнули (или чтобы софт не палился антивирусами).
Одним из способов это сделать является генератор мусорного кода, который создает бессмысленные инструкции с целью разбавить ими код, выполняющий реальную работу.
Хоть тема генерации мусора не раз упоминалась в статьях на этом форуме, но в основном мусор генерировался на уровне ассемблера, из-за чего итоговый бинарник не похож на работу реального компилятора,
так что реверсер с наметанным глазом, или хорошо обученная ИИшка, смогут раскусить этот код, но что если заставить генерировать мусор сам компилятор?
Основная идея заключается в том, чтобы вручную написать подобные блоки мусора (проявите фантазию):
C++:
inline unsigned Block(unsigned dummy) {
    unsigned result = dummy;
    for (int i = 0; i < dummy % 1024; i++) {
        result *= 2;
        result += i;
    }
    return result + dummy;
}
Важно: каждый блок должен быть inline (а лучше __forceinline, или, для gcc, always_inline), чтобы мусор был встроен в место вызова
И каким-то образом вызывать блоки в случайном порядке, причем порядок должен определяться во время компиляции, а сам мусор должен инлайниться в место вызова.
Так же должна быть возможность указывать количество мусора для генерации (не писать же подряд 50 вызовов Junk(); Junk(); Junk(); подряд).
Сначала решим проблему с указанием количества мусора, напишем вспомогательную функцию constexpr for (аналог constexpr if, честно позаимстовованный уже не помню откуда):
C++:
template <auto Start, auto End, auto Inc, class F>
constexpr void constexpr_for(F&& f) {
    if constexpr (Start < End) {
        f(std::integral_constant<decltype(Start), Start>());
        constexpr_for<Start + Inc, End, Inc>(f);
    }
}
Все просто, в шаблонные параметры запихиваем количество итераций и значения откуда и куда итерироваться, а в обычный параметр пихаем лямбду, и constexpr чтобы это все считалось в compile time.
Теперь надо как-то вызывать в цикле блоки мусора, выбирая блок случайно во время компиляции:
C++:
template <unsigned Seed, int Amount>
inline unsigned GenerateJunk(unsigned dummy) {
    unsigned result;
    constexpr auto kBlocksAmount = 3;
    constexpr_for<0, Amount, 1>([&](auto i) {
        if constexpr ((Seed * i) % kBlocksAmount == 0) {
            result *= Block1(dummy);
        } else if constexpr ((Seed * i) % kBlocksAmount == 1) {
            result *= Block2(dummy);
        } else if constexpr ((Seed * i) % kBlocksAmount == 2) {
            result *= Block3(dummy);
        }
    });
    return result;
}
Для работы этого кода надо где-то объявить функции Block1, Block2 и Block3,
C++:
Seed * i
- это и есть та самая часть, которая отвечает за случайность порядка вызова, Seed должен быть определен во время компиляции, а умножение на i для того, чтобы каждую итерацию цикла не генерировался одинаковый мусор, constexpr if гарантирует, что порядок будет вычислен не в рантайме.
C++:
result *= Block1...
чтобы компилятор не оптимизировал весь мусор и не выкинул его из результирующего бинаря.
Пример использования фунции:
C++:
GenerateJunk<__LINE__, x>
В качестве первого шаблонного параметра - любое рандомное число, известное в compile time (__LINE__, __COUNTER__, __TIME__), в качестве второго - любое число, которое ТОЧНО не будет известно во время компиляции,
ведь если компилятор будет знать это число он может выполнить весь мусор во время компиляции и подставить результат в место вызова, выкинув весь мусор из бинарника. Так же обязательно надо использовать число, которое вернул мусор для создания побочных эффектов (например указать это число как код возврата, или напечатать его в консоли), иначе компилятор вырежет весь этот код т.к. его результат не используется.
Итоговый результат: https://godbolt.org/z/311eKqjWc
15 тысяч строк ассемблера, но даже с __forceinline мусор заинлайнился не полностью (в коде main есть какие-то переходы, умножения но сложно понять что они значат даже с подсказками godbolt), так что практической ценности этот код имеет мало, но думаю если его допилить, поиграться с флагами компилятора, заставив его это все инлайнить, добавить побольше блоков, может получиться что-то годное для реального применения
 
статья интересная и была актуальна лет 5 назад, генерация мусорного рабочего кода фактически бесполезна, на первом этапе детектов конечно АВ не будут на нее сильно обращать внимания, НО потом будут детекты, как пример из моей жизни - использование XOR, системы анализа как видят это сразу ставят на софт уведомление - Подозрительное программное обеспечение, это зависит еще от пары факторов (размеры и названия некоторых секций к примеру). Лучшее применение подобного рабочего мусора - это разбавление выполняемой секции в начале и конце для создания смещений кода и изменения размера секции.
 


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