

Executive Summary
What began as a routine look at a “Cobalt Strike Beacon” sample turned out to be something far more dangerous: a full Windows kernel-mode rootkit that loads as a driver. Internally named SakDriver, the sample operates from Ring 0, where it blinds Event Tracing for Windows (ETW), hides and unhides processes through Direct Kernel Object Manipulation (DKOM), conceals its command-and-control (C2) ports by hooking the NSI driver, and manipulates the Windows Filtering Platform (WFP) to block security products from reaching the attacker’s infrastructure. It even probes the hypervisor time-stamp counter to sense when it is running inside a sandbox.
This walkthrough follows the driver from DriverEntry through its installation, persistence, self-defense and C2 logic. The most notable design decision is its covert control channel: instead of opening a socket, a user-mode agent writes attacker commands into an ordinary registry value, and the driver’s registry callback picks them up — a technique that sails past most endpoint telemetry. A recovered command dispatch table exposes more than two dozen kernel primitives, ranging from arbitrary physical-memory read/write and process injection to hardware-input spoofing and reflective PE loading. The relevant indicators of compromise and a matching YARA figure are included at the end.
Technical Analysis
Cobalt Strike is a commercial penetration-testing platform whose “Beacon” agent gives an operator command execution, keylogging, file transfer, SOCKS proxying, privilege escalation, credential theft, port scanning and lateral movement. Beacon is largely in-memory and file-less: it is stageless or multi-stage shellcode that, once loaded, reflectively maps itself into a process without touching disk, and it supports C2 over HTTP, HTTPS, DNS, SMB named pipes and forward/reverse TCP. Because it is stable, well written and highly customisable, Beacon is popular with both targeted attackers and criminal crews — which is exactly why a sample that looked like Beacon was worth a second look.
Initial triage told a different story. The usual first-pass tooling revealed that this was not a user-land Beacon at all but a kernel rootkit that loads as a driver. Recovered debug strings alone give away much of the design — references to k_hook::EventTraceControl, the syscall table, GetCpuClock and HvlGetQpcBias point straight at ETW/syscall hooking and hypervisor time manipulation.

HvlGetQpcBias) tampering. Source: original article.At a high level the driver actively hooks and patches the ETW (Event Tracing for Windows) structures in the kernel, blinding the defensive stack before an event is ever logged. It hides and reveals processes through DKOM, conceals its C2 port with an NSI_HIDE_PORT routine, and unblocks its own traffic with WFP_UNBLOCK_IP by driving the Windows Filtering Platform — the architecture that underpins the Windows firewall.

There is also logic tied to the hypervisor time-stamp counter, most likely a sandbox check that fires when the rootkit suspects it is being detonated in an analysis environment. The driver identifies itself as SakDriver, and a leftover PDB path points at the author’s build tree: E:\tools\oldfilelaoda\x64\Release\CrackerDrv.pdb.

CrackerDrv. Source: original article.
Execution starts, as with every driver, in DriverEntry — the routine the I/O manager calls for each installed and loaded driver. It receives a DriverObject, the structure the I/O manager creates to hold entry points for driver routines such as DriverEntry, Unload and the MajorFunction table, along with details like the driver name.

DriverEntry code working with the DriverObject. Source: original article.
Further in, the driver walks the DriverSection member — a pointer to a _KLDR_DATA_TABLE_ENTRY that describes the loaded module in kernel space — sets its unload routine to DriverUnload, and then reads the DLL name from offset 0x48.

_KLDR_DATA_TABLE_ENTRY and wiring up DriverUnload. Source: original article.
0x48 of the loader entry. Source: original article.If the driver is already present on the system, it iterates over FullDllName->Buffer looking for the string \SystemRoot\System32\Drivers using a hand-rolled loop rather than the documented kernel APIs, and searches for its own driver name — a small but deliberate step to avoid the API calls a monitoring product would expect to see.

FullDllName->Buffer for \SystemRoot\System32\Drivers instead of calling kernel string APIs. Source: original article.It then calls a routine that takes the driver name and opens a handle to \Registry\Machine\System\CurrentControlSet\Services (which stores per-service configuration). It allocates a non-paged, no-execute pool region of 0x218 bytes with the tag CvrD, iterates over every subkey under Services, and fills that pool region with KEY_BASIC_INFORMATION records.

CurrentControlSet\Services into a non-paged pool allocation tagged CvrD. Source: original article.Next it opens a handle to the intended driver, queries its ImagePath (the on-disk location of the driver file) and stashes the path in a global. It then registers as a minifilter driver — the driver class used to monitor, intercept and modify file-system I/O — and installs IRP_MJ_DIRECTORY_CONTROL as a major-function handler so it can police directory-enumeration requests and hide its files from anti-virus scans.

IRP_MJ_DIRECTORY_CONTROL to intercept directory I/O. Source: original article.
The driver then fingerprints the victim’s operating system and prepares an HTTP POST to its C2 server at 43.160.247.24, hitting the /api/event.php endpoint with the distinctive user-agent SakDriver 1.0.

sak_XXX.sys file name. Source: original article.
On a fresh installation it drops the driver to one of the paths \Device\HarddiskVolume%lu\Windows\System32\drivers\DRIVER_NAME or \??\C:\Windows\System32\drivers\DRIVER_NAME, naming the file sak_XXX.sys where XXX is a random seed derived from XORs of a memory region, the performance-counter frequency, the PID and the TID. It registers the service as msXXXX under \Registry\Machine\System\CurrentControlSet\Services\SERVICE_NAME, using the display name Microsoft Player Service and the description Provides support for %04X operations to blend in with legitimate Windows services. If something goes wrong it falls back to registering itself as SakDriver (not exactly OPSEC-friendly). When everything succeeds it reports an install_success marker to the C2 and deletes the dropper.


ntoskrnl.exe and constructing a private import table in memory. Source: original article.To call kernel APIs without leaving an obvious import table, the driver queries the base address of ntoskrnl.exe and builds its own Import Address Table in memory. API names are de-obfuscated and resolved at runtime by sub_14000E34C, which takes the kernel base and a decrypted API name and manually parses the PE header to find the Export Directory, resolving each address by hand. Besides hiding the driver’s intentions, this defeats an EDR’s static import scanning.

ntoskrnl.exe to resolve API addresses. Source: original article.
CmRegisterCallbackEx. Source: original article.The driver then calls CmRegisterCallbackEx to install a registry callback that runs on every registry operation performed by any thread. Named Function, this callback does double duty. First, it is a self-defense mechanism: if an anti-virus product tries to modify the rootkit’s registry keys, the callback intercepts the operation and returns STATUS_ACCESS_DENIED, aborting the change.

STATUS_ACCESS_DENIED to block tampering with the rootkit’s keys. Source: original article.Here is the clever part: the author repurposes that same registry callback as a covert C2 channel. A user-mode agent simply writes a command into a benign registry value using ordinary user-land APIs — an action that generates no suspicious kernel calls and slips past EDR — and the driver reads it back inside the callback. For every write, the driver checks for the magic bytes 0x2625B7146B and, when they are present, extracts the embedded command. The recovered dispatch table is reproduced below.
| Command ID | What it does |
|---|---|
22661 | Arbitrary read/write of process memory (attaches a kernel thread to the target’s address space, locks and maps the memory into the kernel, then reads or writes it). |
13592 | Performs a KVA-shadowing bypass to inject into and steal information from a user-land process. |
13129 | Allocates memory in the victim process to stage process injection. |
13128 | Frees the previously allocated memory after the injected thread finishes. |
12937 | Copies data between two different processes (i.e. the rootkit and a user-land process). |
10246 | Attaches to a user-land process and parses a DLL to locate exported API addresses (e.g. VirtualAlloc). |
9072 | Forcefully deletes protected or currently-executing files. |
8784 | Kernel-to-user-mode DLL injection via kernel APC queuing (parses the PEB to find kernel32.dll and resolves LoadLibrary). |
8306 | Defensive routine: sets a callback on OpenProcess/OpenThread and strips rights such as PROCESS_TERMINATE and PROCESS_QUERY_INFORMATION from handles opened against the rootkit; it can also elevate low-privilege access to PROCESS_ALL_ACCESS (specifically for v8 — purpose still to be determined). |
16964 | Queries memory for basic information about a region. |
22323 | Reads/writes any physical memory address by resolving its virtual pointer — building a custom page table with RW permissions and injecting it into a free PML4 slot — and can copy data directly from a physical address. |
13831 | Resolves a PID from a process name. |
13664 | Spoofs hardware input, forging mouse or PS/2 keyboard/mouse packets and invoking the callback with the fabricated input (\Driver\mouhid and \Driver\i8042prt). |
37720 | Reads a process’s memory by querying the PFN database and mapping the addresses. |
39029 | Changes the protection of a page. |
38536 | Returns a process’s section base address from its EPROCESS structure. |
38435 | Walks a process PEB to loop through loaded DLLs, matches an attacker-supplied name (e.g. kernel32.dll) and returns the DLL base address. |
37973 | Extracts a driver name. |
29557 | De-obfuscates APIs and resolves their addresses, then applies system-wide hooks on CKCL, ETWP, the syscall table, HvlGetQpcBias and GetCpuClock; otherwise it scans win32kfull.sys for a specific pattern in the text section to locate an undocumented function. |
29556 | Spoofs hardware input as keyboard or PS/2 keyboard/mouse packets and invokes the callback (\Driver\kbdhid and \Driver\i8042prt). |
25736 | Performs hardware spoofing for the disk, NVIDIA GPU and SMBIOS. |
25897 | Executes a user thread running the attacker’s DLL / malicious function. |
29462 | Reflective PE loading. |

Type, Start, ImagePath, …) as part of the registry stealth layer. Source: original article.Beyond the command handlers, the callback applies a stealth layer over the registry so that any read or write against the driver’s service key by an AV/EDR is intercepted. Returning to the main entry routine, the driver then performs network filtering, blocking requests made by security solutions to its C2 IP and port, and hides a set of ports — 6891, 12341, 12342, 12343, 6543, 7543 and 9199 — by hooking the NSI driver.

just-do-it.icu and 91.99.165.207 to the block table. Source: original article.Finally it attempts to create a WFP device object so it can observe the network stack, adding just-do-it.icu and 91.99.165.207 to its blocking table — ensuring defensive tooling cannot reach the attacker’s infrastructure while the rootkit’s own traffic flows freely.
Indicators of Compromise (IOCs)
- Domains / IPs:
just-do-it.icu,91.99.165.207,43.160.247.24— at the time of the original analysis these were not yet flagged on VirusTotal and the infrastructure was still live. - Hidden ports:
6891,12341,12342,12343,6543,7543,9199

43.160.247.24 (Tencent, SG) shown with a clean community score at analysis time. Source: original article.

- Endpoint:
/api/event.php - SHA-256:
4e95aba17c1a423cda5cc9f9f04f7cf8db17e294eb31ed1aa85063601b82fe8d - Driver / service names:
SakDriver,sak_xxx.sys,ms_xxxx - User-agent:
SakDriver 1.0
YARA Rule
A YARA rule keyed to the sample matches the analysed hash, confirming detection of the SakDriver rootkit.

cobalt_strike / SakDriver_Rootkit) matching the sample hash 4e95aba1…b82fe8d. Source: original article.Key Takeaways
- A sample that fingerprints as “Cobalt Strike” can actually be a kernel-mode rootkit — triage the load type, not just the strings.
- SakDriver blinds defenses at the source: it patches ETW, hooks the syscall table, and tampers with hypervisor QPC timing to frustrate sandboxes.
- Its registry-callback C2 channel is the standout technique — user-mode writes a benign registry value guarded by the magic bytes
0x2625B7146B, and the driver executes the command with zero network syscalls from the agent. - It hides on disk as a minifilter (
IRP_MJ_DIRECTORY_CONTROL), hides its ports by hooking NSI, and blocks defensive traffic through WFP. - API resolution is done by hand-parsing
ntoskrnl.exeexports, defeating static import-based detection. - The command table exposes powerful primitives: arbitrary physical memory R/W via a rogue PML4 entry, DKOM, process injection, hardware-input spoofing, hardware ID spoofing, and reflective PE loading.
- OPSEC slips — the
CrackerDrv.pdbpath and the SakDriver fallback service name — give defenders concrete hunting artifacts.
Defensive Recommendations
- Enforce driver-signing and blocklists: enable HVCI/Memory Integrity and Microsoft’s vulnerable-driver blocklist so unsigned or known-abusable drivers cannot load in the first place.
- Don’t rely solely on ETW: because this rootkit patches ETW in kernel, corroborate with sensors it cannot easily reach — hypervisor-based telemetry, network egress monitoring and periodic integrity scans of ETW structures and the syscall table.
- Watch registry-callback and minifilter registration: alert on unexpected
CmRegisterCallbackExand minifilter altitudes, and on newCurrentControlSet\Servicesentries with generic display names such as Microsoft Player Service. - Hunt the IOCs: block and alert on
43.160.247.24,91.99.165.207andjust-do-it.icu; look for the/api/event.phpendpoint and theSakDriver 1.0user-agent; hash-hunt4e95aba1…b82fe8d. - Flag hidden ports: reconcile kernel-reported connections against an out-of-band view; discrepancies around ports
6891/12341-12343/6543/7543/9199can reveal NSI hooking. - Deploy the YARA rule across endpoints and your sample pipeline to catch the driver on disk and in memory.
- Monitor for physical-memory abuse: unexpected PML4 modifications or raw physical R/W are strong signals of a kernel implant.
- Baseline loaded drivers: alert on new
sak_*.sys/ms_*drivers and on drivers carrying leftover PDB paths likeCrackerDrv.pdb.
Conclusion
SakDriver is a compact but capable Windows kernel rootkit that trades noisy user-land tradecraft for quiet, Ring-0 subversion: it silences ETW, hides itself as a minifilter and a fake Microsoft service, resolves its APIs by hand, defends its own registry keys, and — most memorably — takes its orders through registry writes rather than the network. Its de-obfuscated command table reads like a toolkit for total host control. For defenders, the lesson is layered visibility: assume kernel-level telemetry can be blinded, watch the callback and filesystem-filter surfaces the rootkit depends on, and turn the author’s OPSEC mistakes — the PDB path, the fallback service name, the live infrastructure — into detections.
Original text: “SakDriver: Reversing a Kernel Driver Rootkit” by 0xSec at 0xsec.gitbook.io.


