NtResetWriteWatch
Setzt den Schreibverfolgungszustand einer MEM_WRITE_WATCH-Region zurück, ohne Dirty Pages abzurufen.
Prototyp
NTSTATUS NtResetWriteWatch( HANDLE ProcessHandle, PVOID BaseAddress, SIZE_T RegionSize );
Argumente
| Name | Type | Dir | Description |
|---|---|---|---|
| ProcessHandle | HANDLE | in | Handle auf den Prozess, dessen Write-Watch-Zustand zurückgesetzt wird. Üblicherweise NtCurrentProcess(). |
| BaseAddress | PVOID | in | Basisadresse der MEM_WRITE_WATCH-Region, deren Dirty-Bits gelöscht werden sollen. |
| RegionSize | SIZE_T | in | Größe der zurückzusetzenden Region in Bytes. Muss innerhalb der ursprünglichen Allokation liegen. |
Syscall-IDs pro Windows-Version
| Windows-Version | Syscall-ID | Build |
|---|---|---|
| Win10 1507 | 0x15F | win10-1507 |
| Win10 1607 | 0x166 | win10-1607 |
| Win10 1703 | 0x16C | win10-1703 |
| Win10 1709 | 0x16F | win10-1709 |
| Win10 1803 | 0x171 | win10-1803 |
| Win10 1809 | 0x172 | win10-1809 |
| Win10 1903 | 0x173 | win10-1903 |
| Win10 1909 | 0x173 | win10-1909 |
| Win10 2004 | 0x179 | win10-2004 |
| Win10 20H2 | 0x179 | win10-20h2 |
| Win10 21H1 | 0x179 | win10-21h1 |
| Win10 21H2 | 0x17B | win10-21h2 |
| Win10 22H2 | 0x17B | win10-22h2 |
| Win11 21H2 | 0x183 | win11-21h2 |
| Win11 22H2 | 0x186 | win11-22h2 |
| Win11 23H2 | 0x186 | win11-23h2 |
| Win11 24H2 | 0x188 | win11-24h2 |
| Server 2016 | 0x166 | winserver-2016 |
| Server 2019 | 0x172 | winserver-2019 |
| Server 2022 | 0x181 | winserver-2022 |
| Server 2025 | 0x188 | winserver-2025 |
Kernel-Modul
Verwandte APIs
Syscall-Stub
4C 8B D1 mov r10, rcx B8 88 01 00 00 mov eax, 0x188 F6 04 25 08 03 FE 7F 01 test byte ptr [0x7FFE0308], 1 75 03 jne short +3 0F 05 syscall C3 ret CD 2E int 2Eh C3 ret
Undokumentierte Hinweise
NtResetWriteWatch ist das eigenständige Reset-Pendant zu NtGetWriteWatch. Die meisten Aufrufer bevorzugen die atomare Form `NtGetWriteWatch(WRITE_WATCH_FLAG_RESET, ...)`, weil sie das Race-Fenster zwischen Abfrage und Reset eliminiert. Der separate Syscall ist nützlich, wenn der Aufrufer die Dirty-Information komplett verwerfen will (z. B. nach einem vollständigen Re-Encryption-Durchlauf), ohne den Array-Copy-Out von NtGetWriteWatch zu bezahlen. Die Kernel-Implementierung läuft über dieselben PTE-Level-Dirty-Bits, die `MiResetWriteWatch` löscht.
Häufige Malware-Nutzung
Wird von Sleep-Mask-Implants verwendet, die beim Start oder nach einem Konfigurations-Update eine vollständige Speicher-Neuverschlüsselung durchführen — den Implant interessiert nicht, welche Seiten dirty waren, er verschlüsselt ohnehin alle, muss aber den Tracking-Zustand zurücksetzen, damit der *nächste* inkrementelle Durchlauf nur die wirklich verschmutzten Seiten der nächsten Wachphase sieht. Allein seltener verwendet als NtGetWriteWatch, da das atomare Reset-Flag existiert, taucht aber in einigen Sleep-Masks der ersten Generation (vor Ekko) auf, die beide Phasen entkoppelten. Forensische Analyse eines pausierten Implant-Snapshots kann den Reset-Aufruf gelegentlich mitten im Zyklus erwischen, wenn die Dirty-Bits kurzzeitig sauber sind.
Erkennungsmöglichkeiten
Gleiches Kernel-Signal wie bei NtGetWriteWatch — das `VadWriteWatch`-Flag des VAD ist Voraussetzung, damit einer der beiden Syscalls überhaupt etwas Sinnvolles tut. NtResetWriteWatch ohne vorhergehenden (oder ausstehenden) NtGetWriteWatch auf demselben Bereich ist außerhalb des Zyklus-Start-Reset-Pfads des .NET-GC ungewöhnlich. EDRs mit VAD-Walk-Fähigkeit können jeden User-Mode-Prozess markieren, der eine größere MEM_WRITE_WATCH-Region mit PAGE_EXECUTE_READ- oder PAGE_EXECUTE_READWRITE-Schutz hält — diese Kombination kommt in gutartiger Software praktisch nie vor und ist ein Markenzeichen Dirty-Page-bewusster Sleep-Masks.
Direkte Syscall-Beispiele
cStandalone reset after a full pass
// After a full memory rescan / full re-encrypt, drop the dirty bits without
// the cost of copying out the dirty-page array.
#include <windows.h>
void reset_tracking(PVOID region, SIZE_T size) {
// ResetWriteWatch is the documented Win32 wrapper around NtResetWriteWatch.
if (ResetWriteWatch(region, size) != 0) {
// Region was not allocated with MEM_WRITE_WATCH.
}
}asmx64 direct stub (Win11 24H2, SSN 0x188)
NtResetWriteWatch PROC
mov r10, rcx
mov eax, 188h
syscall
ret
NtResetWriteWatch ENDPcTwo-phase sleep mask (pre-Ekko style)
// Phase 1: encrypt every page (full pass), then reset the watch state.
// Phase 2 (next cycle): NtGetWriteWatch reports only newly dirtied pages.
void full_encrypt_and_reset(PVOID region, SIZE_T size) {
encrypt_full(region, size);
ResetWriteWatch(region, size); // ntdll!NtResetWriteWatch under the hood
}MITRE ATT&CK-Mappings
Last verified: 2026-05-20