NtResetWriteWatch
Efface l'état de suivi d'écriture d'une région MEM_WRITE_WATCH sans récupérer les pages sales.
Prototype
NTSTATUS NtResetWriteWatch( HANDLE ProcessHandle, PVOID BaseAddress, SIZE_T RegionSize );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| ProcessHandle | HANDLE | in | Handle vers le processus dont l'état write-watch est réinitialisé. Généralement NtCurrentProcess(). |
| BaseAddress | PVOID | in | Adresse de base de la région MEM_WRITE_WATCH dont les bits dirty doivent être effacés. |
| RegionSize | SIZE_T | in | Taille en octets de la région à réinitialiser. Doit être dans l'allocation d'origine. |
IDs de syscalls par version de Windows
| Version de Windows | ID de syscall | 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 |
Module noyau
APIs liées
Stub du syscall
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
Notes non documentées
NtResetWriteWatch est le compagnon de reset autonome de NtGetWriteWatch. La plupart des appelants préfèrent la forme atomique `NtGetWriteWatch(WRITE_WATCH_FLAG_RESET, ...)` car elle élimine la fenêtre de course entre requête et reset. Le syscall séparé est utile quand l'appelant veut écarter entièrement l'information dirty (par ex. après une passe de re-chiffrement complète) sans payer la copie de tableau de NtGetWriteWatch. L'implémentation noyau parcourt les mêmes bits dirty au niveau PTE qu'efface `MiResetWriteWatch`.
Usage courant par les malwares
Utilisé par les implants à sleep-mask qui effectuent un re-chiffrement complet de la mémoire au démarrage ou après une mise à jour de config — l'implant se moque de savoir quelles pages étaient sales, il les chiffre toutes, mais il doit quand même réinitialiser l'état de suivi pour que la *prochaine* passe incrémentale ne voie que les pages vraiment sales de la fenêtre éveillée suivante. Moins utilisé isolément que NtGetWriteWatch puisque le flag reset atomique existe, mais apparaît dans certains sleep-masks de première génération (pré-Ekko) qui découplaient les deux phases. L'analyse forensique d'un snapshot d'implant en pause peut parfois saisir l'appel reset en plein cycle quand les bits dirty sont momentanément propres.
Opportunités de détection
Même signal noyau que NtGetWriteWatch — le drapeau `VadWriteWatch` du VAD est prérequis pour que l'un ou l'autre syscall fasse quoi que ce soit de significatif. NtResetWriteWatch sans un NtGetWriteWatch précédent (ou en attente) sur la même plage est inhabituel hors du chemin reset de début de cycle du GC .NET. Les EDR avec capacité de parcours VAD peuvent signaler tout processus user-mode détenant une région MEM_WRITE_WATCH conséquente avec protection PAGE_EXECUTE_READ ou PAGE_EXECUTE_READWRITE — cette combinaison ne se voit essentiellement jamais dans du logiciel bénin et constitue une signature des sleep-masks dirty-page-aware.
Exemples de syscalls directs
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
}Mappings MITRE ATT&CK
Last verified: 2026-05-20