AV/EDR Evasion Using Direct System Calls (User-Mode vs kernel-Mode)

Usman Sikander
9 min readMar 11, 2022

--

Modern AVs and EDRs use a variety of approaches to accomplish both static and dynamic analysis. They can examine many signatures, such as recognized strings, hashes, and keys, to see if a file on disk is malicious. Attackers, on the other hand, have created a large number of obfuscation methods, rendering static analysis nearly worthless.

Modern EDRs are primarily concerned with dynamic/heuristic analysis, which means they can track the behavior of every process on the system in search of suspicious activity. As a result, we can download dangerous files that our EDR will not be able to identify if they have been obfuscated. The EDR, on the other hand, will detect and prevent malware once it has been started. Most AVs, EDRs, and sandboxes employ user-landed hooks, allowing them to monitor and intercept every user-land API request. They won’t be able to follow us if we run a system call and enter kernel mode!

The issue arises when we learn that system call numbers differ across OS versions and even amongst service build numbers… There is, however, a library called inline syscall that may be used to scrape the in-memory NTDLL module and retrieve the syscall numbers.

The issue here is that this module would scrape the syscall number via Windows API calls. We will be unable to get the right number if an AV/EDR hooks these functionality.

Another solution which I’ll explain in this blog is the use of Syswhispers. SysWhispers helps with evasion by generating header/ASM files implants can use to make direct system calls.

SysWhispers1 vs SysWhispers2

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.

API Hooks and Windows Architecture

Hooking is a method used by AV/EDRs to intercept a function call and redirect the code flow to a controlled environment where they can analyze the call and decide whether or not it is malicious. Looking at the Windows Architecture, we can see that the interaction between the OS’s surface and depths is managed by a library named NTDLL.DLL. The Native API (NTDLL.DLL) serves as the primary interface between user-mode applications and the operating system. As a result, every program will use it to interface with the operating system. NTDLL.DLL, for example, contains widely used Native APIs such as ZwWriteFile. When a process starts, it loads a number of DLLs into its memory address space. AV/EDRs can change the assembly instructions of a function within a loaded DLL and insert an in conditional jump at the start that leads to the EDR’s code.

WINDOWS OPERATING SYSTEM

USER AND KERNEL MODE

Virtual memory and privilege levels are used in modern operating systems to segregate executing processes from one another. For operating processes, Windows OS has two privilege levels: kernel-mode and user-mode. Using this method, Windows ensures that apps are segregated and that they cannot directly access crucial memory portions or system resources, which is extremely insecure and may result in system crashes. When the programme wants to perform a privileged action, the CPU enters kernel mode. Syscalls enable any software to enter kernel mode and conduct privileged activities such as file writing. We’ll utilise the previously described Win32 API method WriteFile as an example.

When a process, attempts to write a file, it will invoke WriteFile, a user-land API function.

Shellcode Injection using Windows API’s

As a malware developer, everybody know common ways to inject a shellcode into process. Windows API calls VirtualAllocEx, WriteProcessMemory, CreateRemoteThread are commonly invoked by attacker to perform shellcode injection. This will allocate a memory space in which we will write our shellcode. After that, we will create a remote thread and wait for it to finish its execution.

Firstly, I created a shellcode using msfvenom to inject into remote process. I am injecting shellcode into NOTEPAD.EXE. Shellcodes is just a message box which is displaying “Hi, From Red Team Operator”

msfvenom -p windows/x64/messagebox TEXT=”Hi, From Red Team Operator” -f csharp > output.txt

Msfvenom Generated x64 ShellCode

I am using windows API’s to inject shellcode into process. I want to show that AV/EDR hooked these API’s and are able to detect it. When a program allocate a memory in process and make it executable and writeable a same time it looks suspicious. For creating memory, writing shellcode and executing it into memory we are using Windows API’s so it pretty sure that AV/EDR’s will detect it.

Windows API Calls

I am injecting generating shellcode into notepad.exe. For this purpose, We need process name or process id. So I am getting pid of notepad.exe.

Process to inject shellcode

After successfully compiling, When I executed my program it caught by Windows Defender.

Windows Defender Result

This is caught by Windows Defender, because this time I am using Windows API’ s and all AV/EDR are hooked on user-land API’s, So it is very easy to detect such malicious program which is using windows API calls to perform this type of malicious activity.

Shellcode Injection using syscalls

I used same generated shellcode into a program which is using direct syscalls to allocate memory, writing shellcode into process. I used SysWhispers2 which is dynamically resolving syscalls number. SysWhispers1 is relying on windows version that’s why SysWhispers2 came into ground.

Common Functions

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

  • 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)

I worked mostly on ubuntu, so I was facing problem related to ASM/Header pair generated by SysWhispers2. Because Assemble format for MASM in different but to compile it with Mingw-w64 we need different assembly format. So I really thanks to Conor Richard who added x86 (Wow64 & Native) support, NASM ASM, and refactored the existing assembly. It’s now possible to compile using MinGW and NASM from the command line.

I developed a malware which is using direct syscalls to inject msfvenom generated shellcode into process. This time I am using direct syscalls to perform all step such as creating memory, writing shellcode into remote process.

Each of the mentioned Win32 API calls has an equivalent syscall:

  • VirtualAlloc -> NtAllocateVirtualMemory
  • WriteMemoryProcess -> NtWriteVirtualMemory
  • CreateRemoteThread -> NtCreateThreadEx

I used SysWhispers2 for generating ASM/Header pair for my above mentioned syscalls. This will generate nasm file which will be compiled using mingw-64 and NASM assembler.

x86_64-w64-mingw32-gcc -m64 -c implant.cpp syscalls.c -Wall -shared
nasm -f win64 -o syscallsx64stubs.o syscallsx64stubs.nasm
x86_64-w64-mingw32-gcc *.o -o temp.exe

you just need to copy the syscalls.c, syscalls.h and syscallsstubs.nasm file into your projcet directory and include “syscalls.h” into your project. Because I am using Mingw to compile it that’s why I am using NASM assembler. if you want MASM, you need to copy syscallsstubs.asm file and change customized setting of your project in visual studio. All steps are explained by Conor Richard.

Direct Syscalls Example

This time I am using syscalls to bypas AV/EDR. I am using direct syscalls numbers and switching into kernel to bypass user-land hooking.

Assembly Instructions
Malware compilation using Mingw-w64

After Successfully compilation, When I executed my malware in the presence of Windows defender I was able to bypass static and dynamic detection. I am using random variable and function names in my project. This is because, earlier when I was developing malware, I used Unsigned Char Shellcode[] to initialize my shellcode. My malware was caught by Windows defender. I encrypted my shellcode, obfuscated API calls but still after touching disk it caught by MDE. After some work, I came to know that it is catching my malware on this keyword ShellCode[]. So Antiviruses sometimes can do this type of shits. So to statically change the signature, I always change the variable and function names of my malware dynamically.

Windows Defender result after syscalls

This time windows defender didn’t caught my malware. Because I am using direct syscalls. So by using direct syscalls, you can bypass AV/EDR user-land hooking.

AntiScan.me Results

I uploaded my binary on AntiScan.me and it is not flagged by any antivirus. This result is maybe by using direct syscalls or anti-sandbox techniques used by me in my malware such as processor speed, ram size and processor numbers. But I ran this malware against different AV/EDR and I was able to bypass static and dynamic analysis.

Importance of encryption in shellcode Injection

As a red teamer, I cannot rely on open-source tools and shellcode generator. Let’s take an example of msfvenom shellcodes. The shellcodes generated by msfvenom are highly detected by AV/EDR. if you are using plain shellcode into your malware, it will be caught by antivirus in static analysis. So At least to bypass static analysis of your msfvenom generated shellcodes you need strong encryption. I mostly used AES-256 encryption and I was able to bypass MetaSploit generated shellcode.

CONCLUSION

We will now be able to run Meterpreter without being stopped by the AV/EDR using direct syscalls. As is customary, this approach will most likely be rendered obsolete in the future as AV/EDR detection techniques improve. This piece, on the other hand, has taught me some intriguing things about how Windows APIs and current AV/EDRs function.

Related Links:

--

--