> Windows Syscalls
ntoskrnl.exeT1027.011T1106

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

NameTypeDirDescription
ProcessHandleHANDLEinHandle auf den Prozess, dessen Write-Watch-Zustand zurückgesetzt wird. Üblicherweise NtCurrentProcess().
BaseAddressPVOIDinBasisadresse der MEM_WRITE_WATCH-Region, deren Dirty-Bits gelöscht werden sollen.
RegionSizeSIZE_TinGröße der zurückzusetzenden Region in Bytes. Muss innerhalb der ursprünglichen Allokation liegen.

Syscall-IDs pro Windows-Version

Windows-VersionSyscall-IDBuild
Win10 15070x15Fwin10-1507
Win10 16070x166win10-1607
Win10 17030x16Cwin10-1703
Win10 17090x16Fwin10-1709
Win10 18030x171win10-1803
Win10 18090x172win10-1809
Win10 19030x173win10-1903
Win10 19090x173win10-1909
Win10 20040x179win10-2004
Win10 20H20x179win10-20h2
Win10 21H10x179win10-21h1
Win10 21H20x17Bwin10-21h2
Win10 22H20x17Bwin10-22h2
Win11 21H20x183win11-21h2
Win11 22H20x186win11-22h2
Win11 23H20x186win11-23h2
Win11 24H20x188win11-24h2
Server 20160x166winserver-2016
Server 20190x172winserver-2019
Server 20220x181winserver-2022
Server 20250x188winserver-2025

Kernel-Modul

ntoskrnl.exeNtResetWriteWatch

Verwandte APIs

ResetWriteWatchGetWriteWatchNtGetWriteWatchVirtualAllocNtAllocateVirtualMemory

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.

Erkennungs­mö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 ENDP

cTwo-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