Giter Site home page Giter Site logo

syswhispers2's Introduction

SysWhispers2

SysWhispers helps with evasion by generating header/ASM files implants can use to make direct system calls.

All core syscalls are supported and example generated files available in the example-output/ folder.

Difference Between SysWhispers 1 and 2

The usage is almost identical to SysWhispers1 but you don't have to specify which versions of Windows to support. Most of the changes are under the hood. It no longer relies on @j00ru's syscall tables, and instead uses the "sorting by system call address" technique popularized by @modexpblog. This significantly reduces the size of the syscall stubs.

The specific implementation in SysWhispers2 is a variation of @modexpblog's code. One difference is that the function name hashes are randomized on each generation. @ElephantSe4l, who had published this technique earlier, has another implementation based in C++17 which is also worth checking out.

The original SysWhispers repository is still up but may be deprecated in the future.

Introduction

Various security products place hooks in user-mode API functions which allow them to redirect execution flow to their engines and detect for suspicious behaviour. The functions in ntdll.dll that make the syscalls consist of just a few assembly instructions, so re-implementing them in your own implant can bypass the triggering of those security product hooks. This technique was popularized by @Cn33liz and his blog post has more technical details worth reading.

SysWhispers provides red teamers the ability to generate header/ASM pairs for any system call in the core kernel image (ntoskrnl.exe). The headers will also include the necessary type definitions.

Installation

> git clone https://github.com/jthuraisamy/SysWhispers2.git
> cd SysWhispers2
> py .\syswhispers.py --help

Usage and Examples

Command Lines

# 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

Script Output

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

Before-and-After Example of Classic CreateRemoteThread DLL Injection

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" // 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);
}

Common Functions

Using the --preset common switch will create a header/ASM pair with the following functions:

Click to expand function list.
  • NtCreateProcess (CreateProcess)
  • NtCreateThreadEx (CreateRemoteThread)
  • NtOpenProcess (OpenProcess)
  • NtOpenThread (OpenThread)
  • NtSuspendProcess
  • NtSuspendThread (SuspendThread)
  • NtResumeProcess
  • NtResumeThread (ResumeThread)
  • NtGetContextThread (GetThreadContext)
  • NtSetContextThread (SetThreadContext)
  • NtClose (CloseHandle)
  • NtReadVirtualMemory (ReadProcessMemory)
  • NtWriteVirtualMemory (WriteProcessMemory)
  • NtAllocateVirtualMemory (VirtualAllocEx)
  • NtProtectVirtualMemory (VirtualProtectEx)
  • NtFreeVirtualMemory (VirtualFreeEx)
  • NtQuerySystemInformation (GetSystemInfo)
  • NtQueryDirectoryFile
  • NtQueryInformationFile
  • NtQueryInformationProcess
  • NtQueryInformationThread
  • NtCreateSection (CreateFileMapping)
  • NtOpenSection
  • NtMapViewOfSection
  • NtUnmapViewOfSection
  • NtAdjustPrivilegesToken (AdjustTokenPrivileges)
  • NtDeviceIoControlFile (DeviceIoControl)
  • NtQueueApcThread (QueueUserAPC)
  • NtWaitForMultipleObjects (WaitForMultipleObjectsEx)

Importing into Visual Studio

  1. Copy the generated H/C/ASM files into the project folder.
  2. In Visual Studio, go to ProjectBuild Customizations... and enable MASM.
  3. In the Solution Explorer, add the .h and .c/.asm files to the project as header and source files, respectively.
  4. Go to the properties of the x86 ASM file.
  5. Select All Configurations from the Configurations drop-down.
  6. Select Win32 from the Platform drop-down.
  7. Set the following options:
    • Excluded From Build = No
    • Content = Yes
    • Item Type = Microsoft Macro Assembler
  8. Click Apply
  9. Select x64 from the Platform drop-down.
  10. Set the following options:
    • Excluded From Build = Yes
    • Content = Yes
    • Item Type = Microsoft Macro Assembler
  11. Click Apply, then OK.
  12. Go to the properties of the x64 ASM file.
  13. Select All Configurations from the Configurations drop-down.
  14. Select Win32 from the Platform drop-down.
  15. Set the following options:
    • Excluded From Build = Yes
    • Content = Yes
    • Item Type = Microsoft Macro Assembler
  16. Click Apply
  17. Select x64 from the Platform drop-down.
  18. Set the following options:
    • Excluded From Build = No
    • Content = Yes
    • Item Type = Microsoft Macro Assembler
  19. Click Apply, then OK.

Compiling with MinGW and NASM

The following examples demonstrate how to compile the above example programs as EXE and DLLs using MinGW and the NASM assembler:

x86 Example EXE

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

x86 Example DLL with Exports

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

x64 Example EXE

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

x64 Example DLL with Exports

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

Compiling with MingGW and GNU Assembler (GAS)

x86 Example EXE

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

x86 Example DLL with Exports

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

x64 Example EXE

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

x64 Example DLL with Exports

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

Using with LLVM/Clang

SysWhispers2 outputs a clang compatible .s file which contains the ASM stubs. This can be used with llvm to compile your code. For example, using the CreateRemoteThread DLL injection example above:

clang -D nullptr=NULL main.c syscall.c syscallstubs.std.x64.s -o test.exe

Inline Header Only

The inlinegas output option will generate a header only version of Syswhispers2 that can be used with the compilation of BOFs. Simply include the header in your project.

Random Syscall Jumps

By using the random syscall jump routine it is possible to avoid "mark of the syscall". The assembly stub calls a new function SW__GetRandomSyscallAddress which searches for and selects a clean syscall instruction in ntdll.dll to use. By doing this, it is possible to avoid triggering userland sycall instructions as well.

To use random syscall jumps, you will need to define RANDSYSCALL when compiling your program and use the rnd version of SysWhispers2's output. The following examples demonstrate using the GNU Assembler stubs.

x86 Example EXE - Using Random Syscall Jumps

i686-w64-mingw32-gcc main.c syscalls.c syscallsstubs.rnd.x86.s -DRANDSYSCALL -Wall -o example.exe

x64 Example EXE - Using Random Syscall Jumps

x86_64-w64-mingw32-gcc main.c syscalls.c syscallsstubs.rnd.x64.s -DRANDSYSCALL -Wall -o example.exe

Caveats and Limitations

  • System calls from the graphical subsystem (win32k.sys) are not supported.
  • Tested on Visual Studio 2019 (v142) with Windows 10 SDK.

Troubleshooting

  • Type redefinitions errors: a project may not compile if typedefs in syscalls.h have already been defined.
    • Ensure that only required functions are included (i.e. --preset all is rarely necessary).
    • If a typedef is already defined in another used header, then it could be removed from syscalls.h.

Credits

Developed by @Jackson_T and @modexpblog, but builds upon the work of many others:

Related Articles and Projects

References to SysWhispers

Licence

This project is licensed under the Apache License 2.0.

syswhispers2's People

Contributors

ajpc500 avatar b0rnh4ck avatar bugproof avatar denandz avatar jfmaes avatar jthuraisamy avatar odzhan avatar xenoscr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

syswhispers2's Issues

can't compile project - cannot convert argument 4 from "PULONG" to "PSIZE_T"

Hi,

I take example code from your description (how inject DLL) but get 2 errors during compilation- both on line

NtAllocateVirtualMemory(hProcess, &lpAllocationStart, 0, (PULONG)&szAllocationSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

Errors :
1 ) argument of type "PULONG" is incompatible with parameter of type "PSIZE_T"
2 ) cannot convert argument 4 from "PULONG" to "PSIZE_T"

All code and the errors can be seen on the link https://ibb.co/JzrBx4y

NtProtectVirtualMemory issue

How to solve symbol redefinition : NtProtectVirtualMemory and unmatched block nesting : NtProtectVirtualMemory issue

RANDSYSCALL doesn't work with BOFs

I'm able to compile BOFs using the random syscall output with -DRANDSYSCALL; however, the BOF doesn't execute. No error is thrown by Cobalt Strike, it simply does nothing.

Using the embedded syscalls works absolutely fine. It's only the random jumps that fail.

inlinegas rnd.h error

1.got the inline head file
python3 syswhispers.py --functions test,test -l inlinegas -o syscalls
2.include the file
syscallsinline.rnd.x64.h
3.complie
x86_64-w64-mingw32-gcc -w -o test.x64.o -c testc -DRANDSYSCALL -masm=intel

when i compile,i got this error

syscalls.h:260:9: error: missing terminating " character
260 | asm(".intel_syntax noprefix
| ^~~~~~~~~~~~~~~~~~~~~~~
syscalls.h:261:5: error: expected string literal before '.' token
261 | .global WhisperMain
| ^
syscalls.h:287:5: error: missing terminating " character

image

unable to compile in VS 2019 - 16.9.2

I have tried both SysWhisper and SysWhisper2. VS is throwing the following error messages. I have enabled the MASM in build customization and also the asm file is set to Macro Assembler.

1 . The first error on the line for NtAllocateVirtualMemory.
Error (active) | E0167 | argument of type "PULONG" is incompatible with parameter of type "PSIZE_T" | NewMetaPlayerLow | main.cpp | 127 |
status = NtAllocateVirtualMemory(process_handle, &pointer_after_allocated, 0, (PULONG)&allocation_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

The second error is on the ASM file.
Error | A2088 | END directive required at end of file | NewMetaPlayerLow |
c:\project\folder\syscalls_common.asm | 2872 |

The third error is
Error | MSB3721 | The command "ml64.exe /c /nologo /Zi /Fo"x64\Release\syscalls_common.obj" /W3 /errorReport:prompt /Tasyscalls_common.asm" exited with code 1. | NewMetaPlayerLow | C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\BuildCustomizations\masm.targets | 70 |

Any help would be great or if you have a working visual studio project, that I can use to compare against my environment, would be big help too.

Missing prototypes of ONE Structure and SOME Functions.

I found some new functions were added in the newest Windows 10 build. But I can't look for their document. Will write the undocumented functions and a structure.

Functions:
NtPssCaptureVaSpaceBulk, NtAllocateUserPhysicalPagesEx, NtAcquireCrossVmMutant, NtCreateCrossVmMutant, NtDirectGraphicsCall, NtWriteErrorLogEntry, NtCreateWinStation, NtOpenWinStation, NtSetWinStationInformation, NtQueryWinStationInformation

Type:
CHANNEL_MESSAGE

Cannot compile

Hi,
Got link error LNK 2005 for WhisperMain and any other sys call the was added.
already defined in syscallsstubs.md.x86/x64.obj

Improvements/Suggestions (function names, x86, wow64)

First of all, great idea and work. Kudos on that! I discovered too late your work and unfortunately for me I had to create something similar years ago with lots of headaches...

Second, I would like to propose a few suggestions:

  • Add option to create or just create function prefixes (this is because if someone is using a library like phnt, or already has functions Nt/Zw defined it will horribly conflict)
  • Try to introduce x86 support as well (you need to create a compile time template and resolve the function arguments size)
  • Have a look at rewolf's wow64 gate, it may be possible to introduce wow64 support

Small help for x86:

0xB8, 0x0, 0x0, 0x0, 0x0, // mov eax, SYS INDEX
0xE8, 0x3, 0x0, 0x0, 0x0, // call sysentry
0xC2, 0x00, 0x0,  // ret ARGUMENTS LENGTH SIZE
// sysenter:
0x8B, 0xD4, // mov edx,esp
0x0F, 0x34, // sysenter
0xC3 // retn

These are just suggestions of course so please take it or leave it. I`m happy with just the x64 version for some future projects :)

NtCreateThreadEx crashes the process

I have spent quite a few hours in debugging and finally nailed down why process injection is failing. The call to NtCreateThreadEX using SysWhispers2 isn't really working for me. The process crashes as soon as the thread is injected.

My code is very simple that I open the process using Pid, create a virtual memory and then create RemoteThreadEX. I have ported all the calls to Syswhispers from High level APis but when I call NtCreateThreadEX, the process is crashing. When I just call CreateRemoteThread Directly, it works fine.

This is how I am calling the function
NtCreateThreadEx(&hThread, GENERIC_EXECUTE, NULL, process_handle, pointer_after_allocated, pointer_after_allocated, FALSE, NULL, NULL, NULL, NULL);

I am trying to follow this tutorial but in my code, I take PID to inject.
https://sevrosecurity.com/2020/04/08/process-injection-part-1-createremotethread/

Moving from SysWhispers1 to SysWhispers2

Correct me if im wrong but from what i understand to move from 1 -> 2 all i should have to do is replace .h file on the #include part of my code if the code using SysWhispers1 was working with that old .h syscalls file?
I ask because im getting a error when using x86_64-w64-mingw32. Error is "Undefined reference to '{INSERT syscall NAME}'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.