Уроки Разработка вирусов-33.Уменьшение вероятности детекта зверька


X-Shar

:)
Администрация
Регистрация
03.06.2012
Сообщения
6 094
Репутация
8 211
В общем в рамках этого цикла думаю осталось две статьи, эта статья закончит теорию.

А две другие будут практика, где мы попробуем использовать полученные знания для обхода Windows defender.

Далее если у меня будет силы и время, попробую оформить эти статьи в pdf книге.

Итак теперь по теме...

Уменьшение бинарной энтропии

Введение


Энтропия относится к степени случайности в предоставленном наборе данных. Существует различные типы мер энтропии, такие как энтропия Гиббса, энтропия Больцмана и энтропия Реньи. Однако в контексте кибербезопасности термин "энтропия" обычно относится к Энтропии Шеннона, которая выдает значение от 0 до 8. С увеличением уровня случайности в наборе данных увеличивается и значение энтропии.

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

На рисунке ниже сравнивается энтропия легитимного программного обеспечения и образцов вредоносных программ. Обратите внимание, как у большинства файлов с вредоносными программами значение энтропии находится в диапазоне от 7,2 до 8, в то время как безвредные файлы в основном находятся в диапазоне от 5,6 до 6,8. Изображение взято из статьи , которая показывает, как использовать энтропию файла для поиска угроз.

1696750415196.png


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

Измерение энтропии файла

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

Однако ради простоты, в коде, предоставленном в этом разделе, есть файл на Python, EntropyCalc.py, который вычисляет энтропию файла. Кроме того, с помощью этого скрипта на Python можно вычислить энтропию разделов PE-файла с помощью флага -pe.

Следующее изображение показывает файл EntropyCalc.py в действии.

1696750491541.png


EntropyCalc.py

EntropyCalc.py использует функцию calc_entropy для вычисления энтропии указанных данных в буфере. Эта функция использует формулу энтропии Шеннона для вычисления значения энтропии.

Код:
def calc_entropy(buffer):
    if isinstance(buffer, str):
        buffer = buffer.encode()
    entropy = 0
    for x in range(256):
        p = (float(buffer.count(bytes([x])))) / len(buffer)
        if p > 0:
            entropy += - p * math.log(p, 2)
    return entropy

Уменьшение энтропии

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

Например, использование шифрования XOR с одним байтом не изменяет общей энтропии выходных данных. Недостатком этого алгоритма является то, что он считается слабым алгоритмом шифрования.

Другим эффективным методом для поддержания низкой энтропии является использование алгоритмов обфускации, объясненных в начальных статьях, таких как IPv4fuscation, IPv6fuscation, Macfuscation и UUIDfuscation, вместо использования алгоритмов шифрования. Эти методы обфускации выводят данные, которые имеют степень организации и порядка. Поэтому похожие байтовые узоры в наборе данных будут иметь более низкие значения энтропии по сравнению с набором данных с полностью случайными байтами.

Вставка английских строк

Другим методом для уменьшения энтропии является вставка английских строк в код окончательной реализации. Эта техника была замечена в различных образцах вредоносных программ, где в код вставляется случайный набор английских строк. Это работает потому, что английский алфавит состоит всего из 26 символов, что означает, что есть всего 26 * 2 (верхний и нижний регистр букв) разных возможностей для каждого байта. Это меньше, чем количество возможностей, которые выдают алгоритмы шифрования (255 возможностей). Если вы хотите использовать эту технику, рекомендуется использовать либо только строчные, либо только прописные буквы, чтобы уменьшить количество возможностей для каждого байта.

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

Дополнение одинаковыми байтами


Более простым способом уменьшения энтропии является дополнение шифротекста полезной нагрузки одинаковым байтом. Это работает потому, что добавленные байты будут иметь энтропию 0,00, так как они все одинаковы.

Например, на следующем изображении показано, как энтропия шелл-кода Msfvenom резко снижается с 5,88325 до 3,77597 после добавления к нему 285 байтов 0xEA.

1696750770333.png


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

Независимость от библиотеки CRT Библиотека CRT, или C Runtime library, представляет собой стандартный интерфейс для языка программирования C, который содержит набор функций и макросов. Эти функции обычно связаны с управлением памятью (например, memcpy), открытием и закрытием файлов (например, fopen) и манипулированием строками (например, strcpy).

Удаление библиотеки CRT

Может значительно уменьшить энтропию окончательной реализации. На следующем изображении сравниваются два файла, Hello World.exe и Hello World - No CRT.exe, которые имеют одинаковый код, но скомпилированы с и без библиотеки CRT. Hello World - No CRT.exe имеет значительно более низкое значение энтропии по сравнению с Hello World.exe.

1696750861234.png


Инструмент Maldev Academy - EntropyReducer

Также можно уменьшить энтропию полезной нагрузки с помощью инструмента , разработанного командой MalDev Academy.
EntropyReducer использует собственный алгоритм, который использует связанные списки для вставки нулевых байтов между каждым блоком полезной нагрузки размером BUFF_SIZE.

Объяснение связанных списков выходит за рамки данной статьи, однако хорошо документированный readme и хорошо прокомментированный код в репозитории должны быть достаточными для понимания алгоритма инструмента.

Брут ключа дешифровки полезной нагрузки

Введение


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

В этом модуле будет продемонстрирован алгоритм дешифрования XOR, при котором программа должна угадать ключ методом "грубой силы".

Процесс шифрования ключа

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

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

Функция шифрования ключа

Функция GenerateProtectedKey принимает байт-подсказку и добавляет его в начало ключа в виде первого байта. Затем она использует алгоритм XOR для шифрования ключа с использованием случайно сгенерированного ключа во время выполнения.

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

C:
/*

HintByte: байт-подсказка, который будет сохранен в качестве первого байта ключа
sKey: размер ключа для генерации
ppProtectedKey: указатель на буфер PBYTE, который получит зашифрованный ключ
*/
VOID GenerateProtectedKey(IN BYTE HintByte, IN SIZE_T sKey, OUT PBYTE* ppProtectedKey) {

// Генерация начального значения
srand(time(NULL));

// 'b' используется как ключ алгоритма шифрования ключа
BYTE        b                = rand() % 0xFF;

// 'pKey' - это буфер для генерации исходного ключа
PBYTE       pKey             = (PBYTE)malloc(sKey);

// 'pProtectedKey' - это зашифрованная версия 'pKey', использующая 'b'
PBYTE       pProtectedKey    = (PBYTE)malloc(sKey);

if (!pKey || !pProtectedKey)
    return;

// Генерация еще одного начального значения
srand(time(NULL) * 2);

// Ключ начинается с байта-подсказки
pKey[0] = HintByte;
// Генерация остальной части ключа
for (int i = 1; i < sKey; i++){
    pKey[i] = (BYTE)rand() % 0xFF;
}

printf("[+] Сгенерированный байт ключа: 0x%0.2X \n\n", b);
printf("[+] Исходный ключ: ");
PrintHex(pKey, sKey);

// Шифрование ключа с использованием алгоритма XOR
// Используется 'b' в качестве ключа
for (int i = 0; i < sKey; i++){
    pProtectedKey[i] = (BYTE)((pKey[i] + i) ^ b);
}

// Сохранение зашифрованного ключа через указатель
*ppProtectedKey = pProtectedKey;

// Освобождение буфера с исходным ключом
free(pKey);
}

Процесс дешифрования ключа

Поскольку ключ шифрования, используемый для шифрования ключа, не сохраняется нигде, функция дешифрования должна угадать значение b, показанное в функции GenerateProtectedKey. Для этого функция дешифрования будет выполнять операцию XOR с первым байтом ключа, который является байтом-подсказкой, с разными ключами до тех пор, пока полученный байт не станет равным байту-подсказке исходного ключа. Когда это происходит, функция узнает, что было выбрано правильное значение b. Фрагмент кода ниже демонстрирует эту логику.

C:
if (((EncryptedKey[0] ^ b) - 0) == HintByte)
// Тогда значение 'b' - это ключ шифрования XOR
else
// Тогда значение 'b' не является ключом шифрования XOR, попробуйте с другим значением 'b'

Продолжая пример, когда 71 становится равным BA, это означает, что правильное значение b было угадано.

Функция дешифрования ключа

Функция BruteForceDecryption требует тот же байт-подсказку, которая передавалась в функцию шифрования.

C:
/* - HintByte : тот же байт-подсказка, что и в функции генерации ключа
    - pProtectedKey : зашифрованный ключ - sKey : размер ключа
    - ppRealKey : указатель на буфер PBYTE, который получит расшифрованный ключ */

BYTE BruteForceDecryption(IN BYTE HintByte, IN PBYTE pProtectedKey, IN SIZE_T sKey, OUT PBYTE* ppRealKey) {

BYTE      b         = 0;
PBYTE     pRealKey  = (PBYTE)malloc(sKey);

if (!pRealKey)
    return NULL;

while (1){

// Используя байт-подсказку, если он равен, то мы найдем значение 'b', необходимое для дешифрования ключа
if (((pProtectedKey[0] ^ b) - 0) == HintByte)
        break;
// иначе увеличьте 'b' и попробуйте снова
    else
 b++;
}

// Обратный алгоритм XOR-шифрования, так как 'b' теперь известен
for (int i = 0; i < sKey; i++){
pRealKey[i] = (BYTE)((pProtectedKey[i] ^ b) - i);
}

    // Сохранение расшифрованного ключа через указатель
*ppRealKey = pRealKey;

return b;

}

Заключение

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

Удаление библиотеки CRT

Введение


До этого раздела все проекты кода компилировались с использованием опции "Выпуск" или "Отладка" в Visual Studio.
Для разработчиков вредоносных программ важно понимать разницу между опциями компиляции "Выпуск" и "Отладка" в Visual Studio, а также последствия изменения настроек компилятора по умолчанию. Изменение настроек компилятора Visual Studio может повлиять на созданный двоичный файл, такое как уменьшение его размера или снижение энтропии.

Опция "Выпуск" против "Отладка"

Конфигурации сборки "Выпуск" и "Отладка" определяют, как программа компилируется и выполняется, и каждая из них выполняет свою задачу и предоставляет разные возможности. Наиболее важные различия между этими двумя опциями приведены ниже.

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

Отладка - Отладка приложений, созданных с использованием конфигурации сборки "Отладка", упрощается, потому что в этом режиме отключены оптимизации компиляции, что делает код более доступным для отладки. Кроме того, конфигурация "Отладка" генерирует файлы символов отладки (.pdb), которые содержат информацию о скомпилированном исходном коде. Это позволяет отладчикам отображать дополнительную информацию, такую как переменные, функции и номера строк.

Развертывание - Версия "Выпуск" приложения используется из-за увеличенной совместимости с их компьютерами, в отличие от версии "Отладка", которая обычно требует дополнительных динамических библиотек (DLL), доступных только в Visual Studio, что делает отладочные приложения совместимыми только с компьютерами, на которых установлена Visual Studio.

Обработка исключений
- В конфигурации сборки "Отладка" Visual Studio может приостанавливать выполнение и отображать сообщение об ошибке в виде окна сообщения, когда возникает исключение, указывая имя переменной или номер строки, вызвавших повреждение стека, например. Такие исключения могут привести к зависанию программы, если она скомпилирована в режиме "Выпуск".

Настройки компилятора по умолчанию

Исходя из предыдущих аспектов, опция "Выпуск" предпочтительнее опции "Отладка". Однако опция "Выпуск" все равно имеет несколько проблем.

Совместимость - Некоторые приложения, использующие опцию "Выпуск", всё равно могут вызывать ошибки, аналогичные приведенной ниже, если целевой компьютер не имеет установленной Visual Studio.

1696752594631.png


Импортируемые функции CRT - В таблице импорта (IAT) присутствуют несколько неразрешенных функций, которые невозможно разрешить с использованием методов, таких как API-хэширование. Эти функции импортируются из библиотеки CRT, что будет объяснено позже. Пока достаточно понимать, что в любом приложении, созданном настройками компилятора по умолчанию Visual Studio, есть несколько неиспользуемых импортированных функций. В качестве примера, IAT программы 'Hello World' должна импортировать информацию только о функции printf, однако она импортирует следующие функции (вывод сокращен из-за размера).

1696752677756.png


Размер - Сгенерированные файлы часто больше, чем они должны быть из-за оптимизаций компилятора по умолчанию. Например, следующая программа "Hello World" имеет размер около 11 килобайт.

1696752735386.png


Информация для отладки - Использование опции "Выпуск" все равно может включать информацию, связанную с отладкой, и другие строки, которые могут быть использованы средствами безопасности для создания статических сигнатур. На изображениях ниже показан результат выполнения программы Strings.exe для программы "Hello World" (вывод сокращен из-за размера).

1696752797224.png


Библиотека CRT

Библиотека CRT, также известная как Microsoft C Run-Time Library, представляет собой набор низкоуровневых функций и макросов, обеспечивающих базовую функциональность для стандартных программ на C и C++. Она включает функции управления памятью (например, malloc, memset и free), манипулирования строками (например, strcpy и strlen) и функции ввода-вывода (например, printf, wprintf и scanf).

DLL-файлы библиотеки CRT названы vcruntimeXXX.dll, где XXX - это номер версии используемой библиотеки CRT. Также существуют DLL-файлы, такие как api-ms-win-crt-stdio-l1-1-0.dll, api-ms-win-crt-runtime-l1-1-0.dll и api-ms-win-crt-locale-l1-1-0.dll, которые также связаны с библиотекой CRT. Каждая из этих DLL выполняет конкретные функции и экспортирует несколько функций. Эти DLL-файлы связываются компилятором на этапе компиляции и, следовательно, находятся в таблице импорта (IAT) созданных программ.

Решение проблем совместимости

По умолчанию, при компиляции приложения в Visual Studio, опция Runtime Library установлена на "Multi-threaded DLL (/MD)". С этой опцией DLL-файлы библиотеки CRT связываются динамически, что означает их загрузку во время выполнения. Это вызывает проблемы совместимости, о которых упоминалось ранее. Для их решения установите опцию Runtime Library на "Multi-threaded (/MT)", как показано ниже.

1696752948780.png


Multi-threaded (/MT)

Компилятор Visual Studio может быть настроен на статическую связь функций CRT, выбрав опцию "Multi-threaded (/MT)". Это приводит к тому, что функции, такие как printf, будут представлены напрямую в созданной программе, а не импортируются из DLL-файлов библиотеки CRT. Обратите внимание, что это увеличит размер конечного бинарного файла и добавит больше функций WinAPI в таблицу импорта, но удалит DLL-файлы библиотеки CRT.

Использование опции "Multi-threaded (/MT)" для компиляции программы "Hello World" приводит к следующей таблице импорта.

1696753030714.png


Размер бинарного файла также значительно увеличивается, как показано ниже.

1696753042745.png


Библиотека CRT и отладка

После удаления библиотеки CRT программа может быть скомпилирована только в режиме "Выпуск". Это делает отладку кода более сложной. Поэтому рекомендуется удалять библиотеку CRT только после завершения отладки и разработки.

Дополнительные изменения компилятора

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

Отключение исключений C++

Опция Enable C++ Exceptions используется для генерации кода, который правильно обрабатывает исключения, выбрасываемые кодом. Однако, поскольку библиотека CRT больше не связана, эта опция не требуется и должна быть отключена.

1696753165176.png


Отключение оптимизации всей программы

Опция Whole Program Optimization должна быть отключена, чтобы предотвратить выполнение компилятором оптимизаций, которые могут повлиять на стек. Отключение этой опции предоставляет полный контроль над скомпилированным кодом.

1696753201829.png


Отключение отладочной информации

Отключите опции Generate Debug Info и Generate Manifest, чтобы удалить добавленную отладочную информацию.

1696753231084.png


1696753241254.png


Игнорирование всех библиотек по умолчанию

Установите опцию Ignore All Default Libraries на "Yes (/NODEFAULTLIB)", чтобы исключить связывание системных библиотек по умолчанию компилятором с программой. Это приведет к исключению связывания библиотеки CRT, а также других библиотек. В этом случае пользователь должен предоставить необходимые функции, которые обычно предоставляются этими библиотеками по умолчанию. На изображении ниже показано установление опции "Yes (/NODEFAULTLIB)".

1696753294690.png


К сожалению, компиляция с этой опцией приводит к ошибкам, как показано ниже.

1696753305642.png


Установка символа точки входа

Первая ошибка "LNK2001 - unresolved external symbol mainCRTStartup" указывает на то, что компилятор не смог найти определение символа "mainCRTStartup". Это ожидаемо, так как "mainCRTStartup" является точкой входа для программы, связанной с библиотекой CRT, что не имеет места в данном случае. Чтобы решить эту проблему, следует установить новый символ точки входа, как показано ниже.

1696753352010.png


1696753364818.png


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

1696753386259.png


Отключение проверки безопасности

Следующая ошибка, "LNK2001 - unresolved external symbol __security_check_cookie", означает, что компилятор не нашел символ "__security_check_cookie". Этот символ используется для выполнения проверки стековой куки, которая является функцией безопасности, предотвращающей переполнение буфера стека. Чтобы решить эту проблему, установите опцию Security Check на "Disable Security Check (/Gs-)", как показано ниже.

1696753425445.png


Отключение проверок SDL

После отключения проверки безопасности ошибка исчезает, но появляется новое предупреждение.

1696753479449.png


Предупреждение "D9025 - overriding '/sdl' with '/GS-'" можно решить, отключив проверки Security Development Lifecycle (SDL).

1696753463252.png


Остаются две неразрешенные ошибки символов, которые решаются в разделе Замена функций библиотеки CRT ниже.

1696753519805.png


Замена функций библиотеки CRT

Из-за удаления библиотеки CRT остались две неразрешенные ошибки из-за использования функции printf для вывода на консоль, хотя библиотека CRT была удалена из программы.

При удалении библиотеки CRT необходимо написать собственные версии функций, такие как printf, strlen, strcat, memcpy. Для этой цели можно использовать библиотеки, например, . Например, заменяет функцию strcmp для сравнения строк.

Замена функции Printf

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

C:
#define PRINTA( STR, ... )                                                                  \
    if (1) {                                                                                \
        LPSTR buf = (LPSTR)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 1024 );           \
        if ( buf != NULL ) {                                                                \
            int len = wsprintfA( buf, STR, __VA_ARGS__ );                                   \
            WriteConsoleA( GetStdHandle( STD_OUTPUT_HANDLE ), buf, len, NULL, NULL );       \
            HeapFree( GetProcessHeap(), 0, buf );                                           \
        }                                                                                   \
    }

Макроинструкция PRINTA принимает два аргумента:
  • STR - Строка формата, которая определяет, как выводить результат.
  • VA_ARGS или ... - Аргументы, которые нужно вывести. Макроинструкция PRINTA выделяет кучевой буфер размером 1024 байта, затем использует функцию wsprintfA для записи отформатированных данных из переменных аргументов (VA_ARGS) в буфер с использованием строки формата (STR). Затем используется функция WriteConsoleA WinAPI для записи полученной строки на консоль, которая получается через функцию GetStdHandle WinAPI.
Замена printf на PRINTA приводит к тому, что программа Hello World становится независимой от библиотеки CRT. Этот код устраняет оставшиеся ошибки и теперь может успешно компилироваться.

C:
#include <Windows.h>
#include <stdio.h>
#define PRINTA( STR, ... )                                                                  \
    if (1) {                                                                                \
        LPSTR buf = (LPSTR)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 1024 );           \
        if ( buf != NULL ) {                                                                \
            int len = wsprintfA( buf, STR, __VA_ARGS__ );                                   \
            WriteConsoleA( GetStdHandle( STD_OUTPUT_HANDLE ), buf, len, NULL, NULL );       \
            HeapFree( GetProcessHeap(), 0, buf );                                           \
        }                                                                                   \
    }
  
int main() {
    PRINTA("Hello World ! \n");
    return 0;
}

Создание вредоносного ПО, независимого от библиотеки CRT

При создании вредоносного программного обеспечения, которое не использует библиотеку CRT, следует учесть несколько моментов.

Использование внутренних функций

Некоторые функции и макросы в Visual Studio используют функции CRT для выполнения своих задач. Например, макрос ZeroMemory использует функцию CRT memset для заполнения указанного буфера нулями. Это требует от разработчика поиска альтернативы этому макросу, так как его нельзя использовать. В этом случае может использоваться функция из в качестве замены.

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

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

C:
#include <Windows.h>

// Ключевое слово `extern` задает функцию `memset` как внешнюю функцию.
extern void* __cdecl memset(void*, int, size_t);

// Макросы #pragma intrinsic(memset) и #pragma function(memset) - это инструкции компилятора, специфичные для Microsoft.
// Они заставляют компилятор генерировать код для функции memset с использованием встроенной интригирующей функции.
#pragma intrinsic(memset)
#pragma function(memset)

void* __cdecl memset(void* Destination, int Value, size_t Size) {
    // логика, аналогичная memset
    unsigned char* p = (unsigned char*)Destination;
    while (Size > 0) {
        *p = (unsigned char)Value;
        p++;
        Size--;
    }
    return Destination;
}

int main() {
    PVOID pBuff = HeapAlloc(GetProcessHeap(), 0, 0x100);
    if (pBuff == NULL)
        return -1;

    // это будет использовать нашу версию 'memset' вместо версии CRT Library
    ZeroMemory(pBuff, 0x100);

    HeapFree(GetProcessHeap(), 0, pBuff);

    return 0;
}

Скрытие окна консоли

Вредоносное программное обеспечение не должно создавать окно консоли при выполнении, так как это вызывает подозрение и позволяет пользователю завершить программу, закрыв окно. Для предотвращения этого можно использовать функцию ShowWindow(NULL, SW_HIDE) в начале функции точки входа, хотя это требует времени (в миллисекундах) и может вызвать заметное мерцание.

Лучшим решением является установка программы для компиляции как GUI-программы, установив опцию SubSystem Visual Studio в "Windows (/SUBSYSTEM:WINDOWS)".

1696753939202.png


Демонстрация

После выполнения всех описанных в этом разделешагов, получаются следующие результаты.

Во-первых, размер исполняемого файла уменьшается с 112,5 килобайт до примерно 3 килобайт.

1696753995373.png


Затем в IAT не обнаружено неиспользуемых функций.

1696754025640.png


В бинарном файле обнаружено меньше строк без отладочной информации.

1696754060498.png


Наконец, удаление библиотеки CRT приводит к меньшему детекту.

Файл загружается на VirusTotal дважды: первый раз с использованием опции "Multi-threaded (/MT)", чтобы статически связать библиотеку CRT, а второй раз после полного удаления библиотеки CRT.

1696754106509.png


1696754213329.png


IAT Camouflage

Введение


Удаление библиотеки C Runtime из конечного бинарного файла позволяет очистить IAT от неиспользуемых функций WinAPI. Однако это может вызвать подозрение, если бинарный файл импортирует очень мало функций WinAPI, особенно если это совмещается с хэшированием API, что может даже привести к отсутствию импортированных функций.

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

Давайте начнем с бинарного файла с именем IatCamouflage.exe, который не использует библиотеку CRT и был скомпилирован аналогично тому, как показанно выше.

C:
#include <Windows.h>
int main() {

      // бесконечное ожидание
    WaitForSingleObject((HANDLE)-1, INFINITE);
    return 0;
}

Когда бинарный файл выполняется, Process Hacker выделит процесс розовым цветом и отобразит заметку, когда мышь наводится на процесс. Process Hacker предполагает, что бинарный файл упакован из-за отсутствия импорта в IAT.

1696754401470.png


Убедитесь, что IatCamouflage.exe импортирует одну функцию с помощью dumpbin.exe.

1696754417090.png


Манипуляция IAT

Манипуляция IAT может быть легко выполнена с использованием доброжелательных WinAPI, которые не изменяют поведение программы. Это можно сделать, вызывая WinAPI с параметрами NULL или используя WinAPI на фиктивных данных, которые не повлияют на программу. Кроме того, эти функции могут размещаться в if-условиях, которые никогда не будут выполняться.
Тем не менее, некоторые компиляторы могут изменять ход выполнения кода с использованием оптимизации "удаление мертвого кода". Это свойство оптимизации компилятора для удаления кода, который не влияет на работу программы.

Пример удаления мертвого кода

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

C:
int z = 4;

// Невозможное if-условие, которое никогда не выполнится
if (z > 5) {

    // Случайные доброжелательные WinAPI
    unsigned __int64 i = MessageBoxA(NULL, NULL, NULL, NULL);
    i = GetLastError();
    i = SetCriticalSectionSpinCount(NULL, NULL);
    i = GetWindowContextHelpId(NULL);
    i = GetWindowLongPtrW(NULL, NULL);
    i = RegisterClassW(NULL);
    i = IsWindowVisible(NULL);
    i = ConvertDefaultLocale(NULL);
    i = MultiByteToWideChar(NULL, NULL, NULL, NULL, NULL, NULL);
    i = IsDialogMessageW(NULL, NULL);
}

Если проект Visual Studio не имеет зависимости от библиотеки CRT и компилирует вышеуказанный код, то функции WinAPI не будут видны в IAT бинарного файла. Компилятор знает, что if-условие невозможно выполнить, и, следовательно, весь код if-условия не включается в скомпилированный бинарный файл, что приводит к тому, что функции WinAPI не будут в IAT бинарного файла. Существуют два способа решения этой проблемы:
  1. Отключение оптимизации кода.
  2. Обман компилятора, чтобы он думал, что этот код используется.
Отключение оптимизации кода

Для этого метода необходимо просто отключить опцию оптимизации Visual Studio, как показано на изображении ниже. Это отключит свойство оптимизации компилятора "удаление мертвого кода", что приведет к тому, что функции WinAPI будут видны в IAT. Однако отключение оптимизации на более крупных программах может негативно сказаться на производительности, поскольку компилятор больше не оптимизирует эффективность и скорость кода. Поэтому программа может потреблять больше памяти или работать медленнее.

1696754616831.png



Обман компилятора

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

C:
// Генерация случайного seed во время компиляции
int RandomCompileTimeSeed(void)
{
    return '0' * -40271
        __TIME__[7] * 1
        __TIME__[6] * 10
        __TIME__[4] * 60
        __TIME__[3] * 600
        __TIME__[1] * 3600
        __TIME__[0] * 36000;
}

// Функция-пустышка, делающая if-условие в 'IatCamouflage' интересным
PVOID Helper(PVOID *ppAddress) {

    PVOID pAddress = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0xFF);
    if (!pAddress)
        return NULL;

    // Установка первых 4 байтов в pAddress равными случайному числу (меньше 255)
    *(int*)pAddress = RandomCompileTimeSeed() % 0xFF;

    // Сохранение базового адреса по указателю и возврат его
    *ppAddress = pAddress;
    return pAddress;
}

// Функция, импортирующая WinAPI, но никогда их не использующая
VOID IatCamouflage() {

    PVOID        pAddress    = NULL;
    int*        A            = (int*)Helper(&pAddress);

    // Невозможное if-условие, которое никогда не выполнится
    if (*A > 350) {

        // Несколько случайных функций WinAPI
        unsigned __int64 i = MessageBoxA(NULL, NULL, NULL, NULL);
        i = GetLastError();
        i = SetCriticalSectionSpinCount(NULL, NULL);
        i = GetWindowContextHelpId(NULL);
        i = GetWindowLongPtrW(NULL, NULL);
        i = RegisterClassW(NULL);
        i = IsWindowVisible(NULL);
        i = ConvertDefaultLocale(NULL);
        i = MultiByteToWideChar(NULL, NULL, NULL, NULL, NULL, NULL);
        i = IsDialogMessageW(NULL, NULL);
    }

    // Освобождение выделенного в 'Helper' буфера
    HeapFree(GetProcessHeap(), 0, pAddress);
}

Ниже приведены несколько моментов, чтобы облегчить понимание этого фрагмента кода.

Функция RandomCompileTimeSeed используется для генерации случайного значения во время компиляции с использованием макроса TIME.

Функция Helper выделяет кучу и устанавливает первые 4 байта равными значению RandomCompileTimeSeed() % 0xFF, что ограничивает значение числа, чтобы оно было меньше 0xFF (в шестнадцатеричной системе) или 255 (в десятичной системе).

Функция IatCamouflage содержит переменную A, которая является указателем на целое число и устанавливается равной первым четырем байтам буфера, возвращенного функцией Helper.

Поскольку функция Helper всегда будет возвращать значение меньше 255, if-условие if (*A > 350) всегда будет ложным. Здесь интересно то, что компилятор не знает об этом и, следовательно, включит эту логику в скомпилированный бинарный файл.

Результаты

Скомпилируйте приведенный выше фрагмент кода и проверьте IAT бинарного файла. Как и ожидалось, доброжелательные функции WinAPI внутри if-условия теперь видны.

1696754889130.png


Эти импортированные функции достаточно, чтобы сделать бинарный файл незаразным при статическом анализе. С другой стороны, вредоносные функции WinAPI должны быть удалены из IAT с использованием хэширования API.
 

Вложения

  • EntropyReducer-main.zip
    22.2 КБ · Просмотры: 10
Последнее редактирование:
Автор темы Похожие темы Форум Ответы Дата
X-Shar Введение в разработку вредоносных программ 1
X-Shar Введение в разработку вредоносных программ 6
X-Shar Введение в разработку вредоносных программ 2
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 5
X-Shar Введение в разработку вредоносных программ 1
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 2
X-Shar Введение в разработку вредоносных программ 2
X-Shar Введение в разработку вредоносных программ 0
X-Shar Введение в разработку вредоносных программ 3
X-Shar Введение в разработку вредоносных программ 2
X-Shar Введение в разработку вредоносных программ 0
Похожие темы
Уроки Разработка вирусов-35.Обход EDRs.Последняя тема цикла
Уроки Разработка вирусов-34.Обход Windows defender
Уроки Разработка вирусов-32.Открываем врата ада
Уроки Разработка вирусов-31.Обход виртуальных машин
Уроки Разработка вирусов-30.Черпаем силы в антиотладке
Уроки Разработка вирусов-29. Предельная техника-2. Практика. Реализуем техники инъекции через сисколы
Уроки Разработка вирусов-28. Предельная техника. Разборка с сисколами
Уроки Разработка вирусов-27.Кунгфу-2.Изучаем API Hooking
Уроки Разработка вирусов-26. Изучаем кунгфу-1. Скрытие таблицы импорта
Уроки Разработка вирусов-25. Скрытие строк
Уроки Разработка вирусов-24. Изучаем технику Spoofing
Уроки Разработка вирусов-23. Контроль выполнения полезной нагрузки
Уроки Разработка вирусов-22.Изучаем технику Stomping Injection
Уроки Разработка вирусов-21.Инъекция отображаемой памяти
Уроки Разработка вирусов-20.Вызов кода через функции обратного вызова
Уроки Разработка вирусов-19.Изучаем технику APC Injection
Уроки Разработка малвари-18.Определение PID нужного процесса, или перечисления процессов
Уроки Разработка вирусов-17.Изучаем технику Thread Hijacking
Уроки Разработка вирусов-16.Разборка с цифровой подписью зверька
Уроки Разработка вирусов-15. Прячем Payload в реестре
Верх Низ