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

Полиморфная рекурсия? (Rust, C/C++)

Encommerce

(L3) cache
Пользователь
Регистрация
25.11.2022
Сообщения
235
Реакции
97
Хотелось бы вынести на обозрение интересную идею для морфинга кода.
Уверен, что где-то в недрах гитхаба что-то подобное имеется, но найти не получилось.

В общем к сути:

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

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

Условно:
[fn(bool) -> i32] => [fn(i32) -> str] => [fn(str) -> u8]

Конечно у меня получилось это сделать, но только в рантайме через enum'ы, что не имеет никакого смысла.

Как думаете, реально ли реализовать на раст или хотя бы C/C++ (или уже реализованно где-то?)
 
Вы можете добавить больше случайных функций с различными типами входных и выходных данных, чтобы сделать код еще более запутанным. Чтобы сделать это менее "палевным", вы также можете использовать указатели на функции и динамически определить порядок вызовов этих функций. Таким образом, статический анализ будет затруднен из-за непредсказуемости порядка вызова функций.

C:
typedef int (*fn_bool_to_i32_t)(bool);
typedef const char* (*fn_i32_to_str_t)(int);
typedef unsigned char (*fn_str_to_u8_t)(const char*);

int main() {
    fn_bool_to_i32_t ptr_fn_bool_to_i32 = fn_bool_to_i32;
    fn_i32_to_str_t ptr_fn_i32_to_str = fn_i32_to_str;
    fn_str_to_u8_t ptr_fn_str_to_u8 = fn_str_to_u8;

    bool bool_value = true;

    // Создаем цепочку вызовов функций с использованием указателей на функции
    int i32_value = ptr_fn_bool_to_i32(bool_value);
    const char* str_value = ptr_fn_i32_to_str(i32_value);
    unsigned char u8_value = ptr_fn_str_to_u8(str_value);

    printf("u8_value: %u\n", u8_value);
    return 0;
}
 
Вы можете добавить больше случайных функций с различными типами входных и выходных данных, чтобы сделать код еще более запутанным. Чтобы сделать это менее "палевным", вы также можете использовать указатели на функции и динамически определить порядок вызовов этих функций. Таким образом, статический анализ будет затруднен из-за непредсказуемости порядка вызова функций.

C:
typedef int (*fn_bool_to_i32_t)(bool);
typedef const char* (*fn_i32_to_str_t)(int);
typedef unsigned char (*fn_str_to_u8_t)(const char*);

int main() {
    fn_bool_to_i32_t ptr_fn_bool_to_i32 = fn_bool_to_i32;
    fn_i32_to_str_t ptr_fn_i32_to_str = fn_i32_to_str;
    fn_str_to_u8_t ptr_fn_str_to_u8 = fn_str_to_u8;

    bool bool_value = true;

    // Создаем цепочку вызовов функций с использованием указателей на функции
    int i32_value = ptr_fn_bool_to_i32(bool_value);
    const char* str_value = ptr_fn_i32_to_str(i32_value);
    unsigned char u8_value = ptr_fn_str_to_u8(str_value);

    printf("u8_value: %u\n", u8_value);
    return 0;
}
Да, я какое-то время рассуждал над этим. Короче, пришел к выводу, что большую часть функций нужно аннотировать как [inline] (в расте это означает, что тело функции будет встроенно непосредственно в тот участок кода, откуда она вызывалась) и свести действия, которые они выполняют к простейшим математическим примитивам / встроенным методам. Последовательность вызовов как раз должна быть определенна статически, но генерироваться случайным образом во время компиляции. Тогда можно будет генерировать уникальные блоки кода, которые не будут поддаваться сигнатурному распознаванию (теоретически). Сами последовательности можно встраивать с помощью процедурных макросов. Работаю над этим.
 
You can add more random functions with different types of inputs and outputs to make the code even more confusing. To make this less "fawny", you can also use function pointers and dynamically determine the order in which these functions are called. Thus, static analysis will be difficult due to the unpredictability of the order of function calls.

C:
typedef int (*fn_bool_to_i32_t)(bool);
typedef const char* (*fn_i32_to_str_t)(int);
typedef unsigned char (*fn_str_to_u8_t)(const char*);

int main() {
    fn_bool_to_i32_t ptr_fn_bool_to_i32 = fn_bool_to_i32;
    fn_i32_to_str_t ptr_fn_i32_to_str = fn_i32_to_str;
    fn_str_to_u8_t ptr_fn_str_to_u8 = fn_str_to_u8;

    bool bool_value = true;

    // Create a chain of function calls using function pointers
    int i32_value = ptr_fn_bool_to_i32(bool_value);
    const char* str_value = ptr_fn_i32_to_str(i32_value);
    unsigned char u8_value = ptr_fn_str_to_u8(str_value);

    printf("u8_value: %u\n", u8_value);
    return 0;
}

Against heuristics and basic emulators from AV, this may help. However, signature detections are still trivial.

Code like this is better applied when you already have a morpher in place. Nonetheless, good luck.
 


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