A copy on write detector for Windows APIs across processes.
Released as open source by NCC Group Plc - http://www.nccgroup.com/
Developed by Ollie Whitehouse, ollie dot whitehouse at nccgroup dot com
https://github.com/nccgroup/DetectWindowsCopyOnWriteForAPI
Released under AGPL see LICENSE for more information
TBC
By default Microsoft Windows will back copies of the same DLL against the same physical memory to save space. When a patch occurs a copy on write operation will happen.
From the Microsoft documentation: https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualquery
If a shared copy-on-write page is modified, it becomes private to the process that modified the page. However, the VirtualQuery function will continue to report such pages as MEM_MAPPED (for data views) or MEM_IMAGE (for executable image views) rather than MEM_PRIVATE. To detect whether copy-on-write has occurred for a specific page, either access the page or lock it using the VirtualLock function to make sure the page is resident in memory, then use the QueryWorkingSetEx function to check the Shared bit in the extended working set information for the page. If the Shared bit is clear, the page is private.
Due to this bahaviour we can:
- Open processes
- Search for the address of EtwEventWrite
- Use QueryWorkingSetEx to check the page is shared OR not
- If not then it is an indication a patch has occurred
This should be a performant way to detect any memory patches to the .text section of DLLs.
Only Windows 10/11 tested
- GetProcAddress of EtwEventWrite
- Open processes
- Validate that NTDLL.dll is loaded and that EtwEventWrite is within the .text segement
- Use QueryWorkingSetEx to check the page is shared OR not
- If not then it is an indication a patch has occurred and alert
The below is an example where we have patched the EtwEventWrite function
x64\Release>d-cow.exe
[i] Running..
[i] [11960][Calculator.exe] EtwEventWrite is located in NONE shared memory - indication of copy of write
thanks for Peter Winter-Smith for pointing out this technique is implemented in Moneta by Forrest Orr
https://github.com/forrest-orr/moneta/blob/master/Source/Subregions.cpp