8 min read

Analysis of Evolving Evasion Tradecraft in Commodity Malware and Command-and-Control Frameworks

Analysis of Evolving Evasion Tradecraft in Commodity Malware and Command-and-Control Frameworks

In early September 2024, RevEng.AI conducted a brief analysis of the evasion techniques leveraged by modern malware and command-and-control (C2) frameworks. This analysis underscores the methods employed by adversaries to bypass traditional detection mechanisms and security solutions, enabling their malicious activities to remain concealed. The techniques described reflect the increasing tendency of malware and C2 developers to leverage low-level system features and obscure functionality within Windows operating systems, which are less understood or monitored by conventional security tools.

Some of the techniques discussed in this report are far from being "novel", a term frequently used by certain vendors to describe them. In reality, many of these methods have been in use for years, particularly within popular English-speaking game cheat development communities. The adoption of these tactics by malware authors often follows a delayed timeline, with techniques pioneered by cheat developers eventually being integrated into broader malicious frameworks.

The research focuses on advanced evasion strategies that include direct driver access, manipulation of DLL loading notifications, abuse of NTFS streams for locked file deletion, and Vectored Exception Handler (VEH) abuse, among others. These tactics demonstrate an adversarial shift toward circumventing high-level constructs in favor of deeper interaction with the Windows kernel, enabling stealthy operations that are harder to detect, prevent, or analyse.

This short report delves into these Tactics, Techniques & Procedures (TTPs), providing baseline implementations in some cases.

Techniques

Direct Driver Access

In a typical Windows environment, applications interact with the operating system through a series of high-level APIs. These APIs abstract the complexity of interaction, allowing developers to write code without needing to understand the intricacies of further low-level constructs. However, for certain advanced operations, especially evasion of security mechanisms, it may be beneficial—or even necessary—to bypass these APIs and interact directly with the system's drivers.

Drivers in Windows operate at a lower level within the system architecture, are generally managed by the Windows kernel and are accessible via IOCTL (I/O control) commands (subject to implementation), which provide a method for user-mode applications to send instructions directly to a driver.

Direct driver access, therefore, refers to the practice of bypassing the typical Windows API layer and interacting directly with system drivers via these IOCTLs. This approach can provide significant advantages, such as greater control and the ability to perform operations that might be restricted or monitored if done through higher-level APIs.

Examples of Direct Driver Access

  • The tcpip.sys driver is responsible for handling low-level network communications in Windows. By directly interacting with this driver, a process can perform network operations such as sending and receiving packets without going through the standard Windows Sockets API (Winsock). This can be used to create stealthy communication channels that are harder for network monitoring tools to detect, as the process bypasses common networking APIs that are typically logged or inspected.

  • The ntfs.sys driver manages the NTFS file system, handling all file and directory operations. By directly accessing this driver, an application can manipulate files and directories at a lower level, potentially bypassing security mechanisms like file system filters used by security solutions (depending on specific implementations). For example, malware could use direct driver access to hide files, create stealthy file streams, or delete locked files without triggering standard file monitoring systems.

  • The http.sys driver is used by the Windows HTTP Server API to handle HTTP requests at the kernel level. By directly interacting with http.sys, an application can create an HTTP server that operates entirely in kernel mode, bypassing user-mode components like IIS (Internet Information Services). This technique can be used to establish a covert C2 server that is less visible to traditional monitoring tools, as the HTTP traffic is handled directly by the kernel driver.

  • The portcls.sys driver manages audio operations in Windows. Direct access to this driver allows an application to manipulate audio streams at a lower level, potentially bypassing Windows-wide audio effects or filters. For example, malware could use this capability to intercept or inject audio data, enabling stealthy eavesdropping or data exfiltration via audio channels.

Direct driver access eliminates the overhead associated with higher-level API calls, which can be particularly beneficial in evading detection. By interacting directly with drivers, applications can bypass security mechanisms or API restrictions that might otherwise prevent certain operations. It also provides greater control over hardware resources, allowing applications to perform operations that are not exposed through standard APIs.

Thread Pool Abuse (Tp*)

Using the example of NtWriteVirtualMemory, a pointer to a structure containing the necessary arguments is passed to the TpAllocWork API, which handles thread pool work items, and the callback function WorkCallback is implemented in assembly.

The WorkCallback callback-function extracts the required arguments from the structure and assigns them to the appropriate registers (in the case of x64: RCX, RDX, R8, R9), while managing the stack to accommodate for the remaining arguments. This approach allows for precise control over the call to NtAllocateVirtualMemory, ensuring that the stack appears clean and legitimate to an outside observer - i.e. a security solution.

By carefully adjusting the stack without altering the stack frame, the call stack remains clean and can be properly unwound. This method maintains a legitimate-looking stack, upon inspection by security solutions, and avoids common pitfalls associated with stack manipulation

The function definitions can be found in Process Hacker's Windows definitions here.

VEH Abuse

Vectored Exception Handlers (VEHs) are an extended feature of Structured Exception Handlers (SEH) in Windows, and can be abused for a variety of scenarios, primarily function hooking and process injection.

Benefits of using VEH For Hooking

  • No Code Modification: VEHs do not require changes to the actual function code. Traditional hooking methods often involve modifying function prologues or inserting trampolines, which can be detected by security tools. VEHs work by triggering exceptions and redirecting execution at runtime without altering the code in memory.

  • Temporary and Context-Sensitive: VEHs can be registered and unregistered dynamically, allowing hooks to be applied only when needed and removed afterwards. This dynamic application minimizes the hook's footprint and reduces the likelihood of detection by security solutions.

Practical Approach Of Abusing VEH For Hooking

  • Use AddVectoredExceptionHandler to register a vectored exception handler. This handler will be invoked when an exception occurs in the process, providing an opportunity to manage control flow redirection.

  • Applying Page Protection: Protect the target function or memory page with PAGE_GUARD using VirtualProtect. This protection causes an exception to be triggered when the guarded page is accessed, creating an opportunity for the VEH handler to intercept and redirect execution.

  • Exception Handling: When a guarded page is accessed, a STATUS_GUARD_PAGE_VIOLATION exception is raised. The VEH handler catches this exception and checks if it occurred at the target function's address. If so, it modifies the instruction pointer (i.e. EIP or RIP) to redirect execution to the hook function.

  • Single-Step Exception: To ensure that the guard page protection is reapplied, the VEH handler sets the single-step flag in the EFlags register. This triggers a STATUS_SINGLE_STEP exception after the redirected instruction is executed, allowing the handler to reapply PAGE_GUARD protection and maintain stealth.

Disabling Instrumentation Callbacks (Nirvana)

Instrumentation Callbacks (also called Nirvana internally by Microsoft) allows the tracing of system calls from inside a process - given that the required permissions are set, and was first present in the Windows 7 SDK. This is used by a few security solutions, however is not widely adopted.

In the context of the current process, the callback can be set to NULL:

#define ProcessInstrumentationCallback  40

typedef struct _PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION
{
    ULONG Version;
    ULONG Reserved;
    PVOID Callback;
} PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION, *PPROCESS_INSTRUMENTATION_CALLBACK_INFORMATION;

ic.Version = 0;
ic.Reserved = 0;
ic.Callback = NULL;
NtSetInformationProcess(GetCurrentProcess(), ProcessInstrumentationCallback, &ic, sizeof(PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION));

Figure 1 - Disabling Nirvana Hooks

Etw Event Neutralisation

Event Tracing for Windows (ETW) is a feature that enables the collection of system-wide events from both user-mode and kernel-mode applications. ETW is often used by security solutions (however, in the form of EtwTi) to monitor system activity, providing visibility into various events, such as process creation, network activity, and DLL loads.

For attackers, ETW presents both a challenge and an opportunity. While ETW can be used to detect malicious activity, it can also be manipulated to hide or obscure such activity. By carefully controlling which events are generated or by tampering with the ETW infrastructure, attackers can reduce their visibility to security tools.

The most common approach is to patch the first byte of the target function with a ret (x64 instruction encoding:0xC3), on the following candidates:

  • Taking the approach of the lowest-call API, the function NtTraceControl can be patched within kernel32.dll when a consumer attempts to gather a HANDLE to Etw
  • Again, the function NtTraceEvent is used for writing Etw events and can be patched.

NTFS Stream Self-Deletion

NTFS (New Technology File System) streams are lesser-known features of the Windows file system that allow for alternative data streams (ADS) to be associated with a file. These streams are not immediately visible in standard file listings, making them useful for various purposes—both legitimate and malicious.

One interesting and practical use of NTFS streams is in deleting locked executables or files currently in use by the operating system. This technique can be particularly useful in scenarios where a file cannot be deleted through conventional means because it is locked by a running process. The method, initially discovered by Jonas Lykkegaard, has been demonstrated through a proof of concept (POC) that showcases how NTFS streams can be manipulated to bypass such restrictions.

LdrRegisterDllNotification

Commonly, anti-virus solutions leverage a notification mechanism provided by Microsoft Windows to detect when a Dynamic Link Library (DLL) is loaded into a process. This functionality can effectively identify foreign DLLs that may be malicious, depending on the detection logic implemented by the anti-virus software.

The notification is registered using the LdrRegisterDllNotification function, which allows a program to specify a callback function that will be invoked whenever a DLL is loaded or unloaded.

NTSTATUS NTAPI LdrRegisterDllNotification(
  _In_     ULONG                          Flags,
  _In_     PLDR_DLL_NOTIFICATION_FUNCTION NotificationFunction,
  _In_opt_ PVOID                          Context,
  _Out_    PVOID                          *Cookie
);

The developer must provide a callback function that adheres to the following prototype:

VOID CALLBACK LdrDllNotification(
  _In_     ULONG                       NotificationReason,
  _In_     PCLDR_DLL_NOTIFICATION_DATA NotificationData,
  _In_opt_ PVOID                       Context
);

Figure 2 - Unregistering DLL notifications

This callback function is notified whenever a DLL is loaded or unloaded, with the NotificationReason parameter indicating the action. This method is widely used by security tools to monitor DLLs within a process, adding a layer of protection by identifying potentially malicious libraries as soon as they are loaded.

While LdrUnregisterDllNotification is provided to remove the notification, it requires the cookie that was originally provided during registration. However, an attacker could potentially retrieve or nullify the notification list manually by manipulating the LdrpDllNotificationList. This list is a linked list (LIST_ENTRY) containing all registered notifications, and an attacker could walk through the list to remove entries as shown in the code snippet:

// NOTE: enumerate the list
PLIST_ENTRY tmp_node_blink = dll_notification_head->Blink;
PLIST_ENTRY tmp_node_flink = dll_notification_head->Flink;

tmp_node_blink = NULL;
tmp_node_flink = NULL;

Figure 3 - Setting list nodes to NULL

This code effectively empties the LdrpDllNotificationList, thereby removing all registered DLL notifications. By modifying the Blink and Flink pointers of each entry, an attacker can bypass DLL loading notifications, leaving subscribed consumers blind to new DLLs being loaded.

Conclusion

The evolving landscape of malware and command-and-control (C2) frameworks continues to present significant challenges to modern security solutions.

By leveraging obscure and poorly documented (or, completely undocumented) low-level system features and bypassing traditional API and security mechanisms, attackers can create stealthy operations that are harder to detect and analyse.

The findings in this analysis reflect the need for continuous adaptation in defensive strategies, particularly in expanding visibility into low-level system activities that have traditionally been overlooked or deprioritised. Security solutions must evolve to better monitor and defend against these techniques, ensuring that adversaries can no longer rely on obscure system behaviors to mask their malicious activity to achieve their operational objectives.

For assistance analysing the latest malware or binary executables, join our community of researchers and security engineers using the AI Binary Analysis Platform today.