Skip to content

A demonstration of remote process injection using direct & indirect syscalls with syswhispers

Notifications You must be signed in to change notification settings

wizardy0ga/ProcessInjectSyscall

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Process Inject Syscall (x64)

About

This repository contains a solution file that implements classic process injection via normal Win32 API calls & SysWhispers. The purpose of this code is to demonstrate normal system calls vs direct & indirect system calls and the usage of SysWhispers 1 through 3.

Disclaimer

I did not discover these techniques. This is simply a repo that i put together for my own learning / understanding... And for fun :). Some of the information may or not be accurate. Feel free to leave an issue on the repo if any information is incorrect.

Solution Information

  • Built with Visual Studio 2022 (v143) on Windows 10.0.19045 (22H2)

Important

The SysWhispers configuration within the solution will only work on windows 10.0.19045. This is due to how the first iteration of SysWhispers works. The SSN is hardcoded into the assembly stub within the malware. SysWhispers doesn't have support for 10.0.19045 so i wrote in the assembly for it and removed the redundant code for other build versions from the files generated by SysWhispers.

What are Syscalls?

Syscalls are used by the cpu to transition from user mode to kernel mode. This allows ring 3 applications to temporarily perform procedures stored in the kernel (ring 0) before returning to user land. These procedures include operations such as writing to a file, creating a process or allocating memory. In the Windows architecture, an API call transitions between the address space of multiple DLL's before arriving in the address space of the NTDLL which acts as a middleman between user & kernel land. The last function call in userland prior to passing data to the kernel occurrs in NTDLL.

NTDLL exports a set of functions that are prefixed with 'Nt' or 'Zw'. Nt functions interface with user land while Zw functions interface with the kernel. Both Zw & Nt functions can be called from user land applications. All functions with a syscall with have this prefix however not all functions with this prefix are a syscall. To be classified as a syscall, the function must declare a system service number that is passed to the syscall instruction in NTDLL. Each function within the kernel can be identified by a unique system service number. This system service number can change between each build of windows, therefore it is not static. The system service number is passed to the 'syscall' instruction on x64 systems. This instruction will pass the system service number to the kernels system service descriptor table which will resolve the corresponding function address within the kernel. The function will then be executed and return to the calling application back through the address space that was initially traversed before arriving at NTDLL and the kernel.

Standard system call control flow

Syscall x64 ASM example
mov r10, rcx                    ; Save the parameters for the function in the r10 register
mov eax, 26                     ; Move the SSN into eax register
test byte ptr ds:[7FFE0308], 1  ; Check if the caller is an x86 application
jne ntdll.7FFAB654D4C5          ; Jump to 'int 2E' syscall if caller is x86
syscall                         ; Execute the x64 syscall instruction
ret                             ; Return to calling function
int 2E                          ; Execute the x86 syscall instruction
ret                             ; Return to calling function
Syscall opcodes
Architecture Instruction Opcode
x64 syscall 0F 05
x86 int 2E CD 2E

Note

Syscalls can be identified by the signature of their opcodes. Since these instructions will always be followed by a 'ret' instruction, a syscall on x64 systems can be identified by the bytes 0F 05 C3 and CD 2E C3. The 'int 2E' instruction allows x86 applications to access system calls when running on x64 systems.

Why Direct / Indirect Syscalls?

The usage of syscalls in malware development exists due to EDR hooking. An EDR will hook various API calls that have a high potential for abuse. These hooks allow the EDR to redirect the control flow of a program that calls the hooked function to the EDR's own code where it can analyze the parameters that were passed to the function. If the EDR determines that the parameters are suspicious, it can block the function call and terminate the calling application. An example would be a program that calls WriteProcessMemory with a pointer to a shellcode buffer. The EDR will intercept this function and analyze the code in the buffer that is being pointed to.

Syscalls allow malware developers to evade EDR hooks by interacting directly with the syscall instruction of a function rather than calling a function that traverses the memory of multiple dlls which could contain a hook from the EDR. By interacting with the syscall outside of the normal Win32 API control flow, malware can evade the execution of an EDR hook and therefore analysis by an EDR.

Hooked system call control flow

Direct Syscalls?

Direct syscalls are a form of syscall where the malware calls the syscall instruction directly from the memory space of the malware. The return statement following the syscall is also executed from the malwares memory space. In a benign or normal application, the syscall instruction and return statement is always executed from the memory space of NTDLL. Although this method will bypass EDR hooks, it opens up a vulnerability on the part of the malware developer where the EDR can look for syscall / return instructions that were executed outside of NTDLL memory space. Since syscalls are only supposed to be executed from NTDLL this means that syscall instructions should always return to the address space of NTDLL. A direct syscall is performed from the address space of the malware meaning the syscall will return to the address space of the malware which is a definitive indicator of compromise.

Direct system call control flow

Benign system call

This photo shows the syscall for NtOpenProcess as it exists in NTDLL on Windows 10.0.19045. This is how a normal syscall should appear, where the syscall & return statement are being executed from the NTDLL which means that the syscall should return to the address space of the NTDLL and the following return statement should be executed from the same NTDLL address space.

Malicious direct system call

This photo shows a malicious system call. The syscall / return instructions are executed directly from the memory of the malware. In this case, the syscall will return directly to the malwares memory rather than NTDLL which is an indicator of compromise. The return statement is also executed from the memory of the malware while pointing to the memory of the malware. EDR can use this to detect the direct syscalls.

Indirect Syscalls?

Indirect Syscalls are the next progressive step for malicious syscalls. The indirect syscall improves upon its direct counterpart by executing the syscall / return instructions from the address space of NTDLL while also bypassing any EDR hooks. To achieve an indirect syscall, the malware must retrieve the system service number for the desired function, locate any address that points to the syscall instruction within NTDLL and execute an unconditional jump to that address. This will instruct the CPU to execute the syscall / return instructions inside of the NTDLLs memory space. Since the EDR's hook exists at a lower address than the syscall instruction, the hook will not be executed.

Indirect system call control flow

Indirect system call assembly

This image depicts an indirect system call implemented with syswhispers3. An address pointing to any system call instruction within NTDLL is retrieved and stored in the r11 register. Then, the system service number for the desired function is retrieved and stored within the eax register by default. Parameters are moved into place and an unconditional jump to the syscall address is performed. The syscall instruction will execute inside of and return to NTDLLs memory space. The return instruction will also execute inside of the NTDLL and return to the calling application. This is where the indirect syscall opens up for detection by EDR as the return instruction still points to the memory space of the malware.

Normal callstack

This image displays the callstack a normal windows API call. The OpenProcess function is called from ProcessInjectSyscall.exe!main which then traverses through the memory space of kernelbase.dll & finally ntdll.dll. The return instruction after the syscall instruction within ntdll will point to kernelbase and the return instruction inside of kernelbase will point to the application. This is the expected behavior for a normal win32 api call.

Indirect system call callstack

This image displays the callstack of an indirect syscall. Notice that kernelbase is missing. This is due to jumping directly from the memory space of the malware to the memory space of NTDLL when executing the syscall instruction. As there was no call to functions within kernelbase, no functions from kernelbase will be shown within the callstack. This means that the return instruction inside of NTDLL is pointing to the memory space of the malicious application rather than kernelbase. Going from the memory of NTDLL back to the memory of a third party application is an indicator that can be used by EDR for detection.

An EDR can not detect an indirect syscall based on the memory context surrounding the execution & return of the syscall, but the EDR can detect this by analyzing the callstack of the suspicious thread in the application or the memory space that the return instruction within NTDLL points to. Since the malware jumps from it's memory space to the memory space of the NTDLL, the return instruction will still point to the address of the calling function in the memory space of the malware. This leaves a gap in the callstack of the suspicious thread where the return addresses of the normal functions used to reach NTDLL should exist.

Summary: Direct vs Indirect System Calls

Direct and indirect syscalls are used by malware authors to bypass hooks placed in win32 api functions by EDR. Each technique presents an opportunity for detection. A direct syscall will implement the required code for the syscall within its own code meaning that any syscall instructions will execute within and return to the memory space of the malware itself. An EDR can detect this through the use of kernel callbacks to determine if the syscall returned to the memory space of NTDLL or another application. Indirect syscalls build upon this by executing the syscall instruction within the memory space of NTDLL, therefore the syscall will return to the memory space of NTDLL which will appear normal to the EDR. The indirect syscall opens itself up for detection since the return address inside of ntdll points to the memory space of the malware which is also reflected in the callstack.

Resources

Direct Syscalls: A journey from high to low

Direct Syscalls vs Indirect Syscalls

SysWhispers is dead, long live SysWhispers!

SysWhispers

SysWhispers2

SysWhispers3

About

A demonstration of remote process injection using direct & indirect syscalls with syswhispers

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages