Найти в Дзене

Генерируем мусорный код на 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(); подр

При написании софта иногда необходимо скрыть его код от посторонних глаз, запутать реверсера, чтобы софт не крякнули.
Одним из способов это сделать является генератор мусорного кода, который создает бессмысленные инструкции с целью разбавить ими код, выполняющий реальную работу.

Основная идея заключается в том, чтобы вручную написать подобные блоки мусора (проявите фантазию):

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, честно позаимстовованный уже не помню откуда):

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.

Теперь надо как-то вызывать в цикле блоки мусора, выбирая блок случайно во время компиляции:

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.

Seed * i

- это и есть та самая часть, которая отвечает за случайность порядка вызова, Seed должен быть определен во время компиляции, а умножение на i для того, чтобы каждую итерацию цикла не генерировался одинаковый мусор, constexpr if гарантирует, что порядок будет вычислен не в рантайме.

result *= Block1...

- чтобы компилятор не оптимизировал весь мусор и не выкинул его из результирующего бинаря.

Пример использования функции:

GenerateJunk<__LINE__, x>

В качестве первого шаблонного параметра - любое рандомное число, известное в compile time (__LINE__, __COUNTER__, __TIME__).

В качестве второго - любое число, которое ТОЧНО не будет известно во время компиляции, ведь если компилятор будет знать это число он может выполнить весь мусор во время компиляции и подставить результат в место вызова, выкинув весь мусор из бинарника.

Также обязательно надо использовать число, которое вернул мусор для создания побочных эффектов (например указать это число как код возврата, или напечатать его в консоли), иначе компилятор вырежет весь этот код, т.к. его результат не используется.

Итоговый результат: https://godbolt.org/z/311eKqjWc

15 тысяч строк ассемблера, но даже с __forceinline мусор заинлайнился не полностью (в коде main есть какие-то переходы, умножения, но сложно понять что они значат даже с подсказками godbolt), так что практической ценности этот код имеет мало, но думаю если его допилить, поиграться с флагами компилятора, заставив его это все инлайнить, добавить побольше блоков, может получиться что-то годное для реального применения.

Понравился материал? - С вас лайк и подписка!)
Вся представленная информация носит исключительно ознакомительный характер и не призывает приступать к действиям!