NtAreMappedFilesTheSame
Determina si dos vistas mapeadas están respaldadas por el mismo fichero (prueba de identidad de file object).
Prototipo
NTSTATUS NtAreMappedFilesTheSame( PVOID File1MappedAsAnImage, PVOID File2MappedAsFile );
Argumentos
| Name | Type | Dir | Description |
|---|---|---|---|
| File1MappedAsAnImage | PVOID | in | Puntero dentro de una vista mapeada con SEC_IMAGE (típicamente una base de DLL/EXE cargada). |
| File2MappedAsFile | PVOID | in | Puntero dentro de una vista mapeada desde un mapping de fichero regular (cualquier tipo de sección). |
IDs de syscalls por versión de Windows
| Versión de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0x8A | win10-1507 |
| Win10 1607 | 0x8A | win10-1607 |
| Win10 1703 | 0x8B | win10-1703 |
| Win10 1709 | 0x8B | win10-1709 |
| Win10 1803 | 0x8C | win10-1803 |
| Win10 1809 | 0x8C | win10-1809 |
| Win10 1903 | 0x8C | win10-1903 |
| Win10 1909 | 0x8C | win10-1909 |
| Win10 2004 | 0x8E | win10-2004 |
| Win10 20H2 | 0x8E | win10-20h2 |
| Win10 21H1 | 0x8E | win10-21h1 |
| Win10 21H2 | 0x8E | win10-21h2 |
| Win10 22H2 | 0x8E | win10-22h2 |
| Win11 21H2 | 0x8E | win11-21h2 |
| Win11 22H2 | 0x8E | win11-22h2 |
| Win11 23H2 | 0x8E | win11-23h2 |
| Win11 24H2 | 0x90 | win11-24h2 |
| Server 2016 | 0x8A | winserver-2016 |
| Server 2019 | 0x8C | winserver-2019 |
| Server 2022 | 0x8E | winserver-2022 |
| Server 2025 | 0x90 | winserver-2025 |
Módulo del kernel
APIs relacionadas
Stub del syscall
4C 8B D1 mov r10, rcx B8 90 00 00 00 mov eax, 0x90 ; Win11 24H2 SSN 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
Notas no documentadas
Una pequeña ayuda sorprendentemente importante: el kernel recorre los `MM_SECTION_OBJECT_POINTERS` de ambas direcciones, compara el `FILE_OBJECT` subyacente (más exactamente los `SectionObjectPointer`) y devuelve `STATUS_SUCCESS` cuando se refieren al mismo fichero de backing — incluso si las dos vistas difieren en atributos (una como imagen, otra como datos), en protección, en tamaño o en offset. Los dos argumentos están *nombrados* de forma asimétrica (Image vs File) por motivos históricos; en la práctica cualquiera puede ser de cualquier tipo y la comparación es simétrica.
Uso común por malware
Funciona en ambos bandos. **Uso defensivo (y de EDR)**: abrir `\KnownDlls\ntdll.dll`, mapear una copia fresca como fichero, luego llamar `NtAreMappedFilesTheSame(LoadedNtdllBase, FreshNtdllMap)`. Si la respuesta es *misma*, la ntdll cargada tiene el mismo fichero de backing — los defensores comparan entonces byte a byte las secciones `.text` para descubrir hooks inline de EDR/AV y sobrescribirlos con los bytes prístinos. Es la base de toda primitiva de unhooking moderna (Perun's Fart, FreshyCalls, SysWhispers3 +unhook, RefleXXion). **Uso ofensivo**: un implante **DLL-hollowed** (su imagen de módulo sobrescrita en memoria mientras el fichero permanece intacto) puede llamarse contra sí mismo para detectar manipulación — autodefensa simétrica contra los unhookers blue-team. Algunas verificaciones de evasión de sandbox también usan esta llamada para confirmar que una DLL cargada realmente proviene de `\System32\` y no de una copia redirigida del atacante.
Oportunidades de detección
Casi sin señal de detección propia: el syscall es raro en software benigno pero cada vez más común en herramientas de seguridad (Defender, CrowdStrike, SentinelOne lo invocan todos durante comparaciones de unhooking). La señal fuerte es el *pairing*: un proceso que abre `\KnownDlls\ntdll.dll` (o mapea cualquier DLL del sistema desde `\System32`) e inmediatamente llama a `NtAreMappedFilesTheSame` contra su propia ntdll cargada está realizando una danza de unhooking. Los proveedores EDR se autoprotegen cada vez más eliminando trucos de identificación por identidad de sección — si la llamada devuelve same sobre una DLL que el EDR espera distinta, el EDR puede detectar *eso* y responder.
Ejemplos de syscalls directos
cVerify a fresh ntdll maps the same file as the loaded one
// Defender / red-team unhooking helper.
HANDLE hSection = OpenSectionByName(L"\\KnownDlls\\ntdll.dll");
PVOID freshBase = NULL; SIZE_T viewSize = 0;
NtMapViewOfSection(hSection, NtCurrentProcess(),
&freshBase, 0, 0, NULL, &viewSize,
ViewShare, 0, PAGE_READONLY);
PVOID loadedNtdll = GetModuleHandleW(L"ntdll.dll");
if (NtAreMappedFilesTheSame(loadedNtdll, freshBase) == STATUS_SUCCESS) {
// Same backing file: safe to byte-compare and unhook .text.
PatchTextSectionFromFresh(loadedNtdll, freshBase);
}asmx64 direct stub (Win11 24H2)
; NtAreMappedFilesTheSame direct stub — SSN 0x90 on Win11 24H2
NtAreMappedFilesTheSame PROC
mov r10, rcx
mov eax, 90h
syscall
ret
NtAreMappedFilesTheSame ENDPrustSelf-check for DLL hollowing
// An implant uses this call to detect that its own module image was
// overwritten in memory (DLL hollowing) — the section identity persists
// even if the bytes changed.
use ntapi::ntmmapi::NtAreMappedFilesTheSame;
use winapi::shared::ntdef::PVOID;
unsafe fn module_was_hollowed(loaded: PVOID, fresh: PVOID) -> bool {
let s = NtAreMappedFilesTheSame(loaded, fresh);
if s != 0 {
// Section identity differs — module image has been replaced.
return true;
}
// Same section: byte-compare to decide if .text was rewritten.
text_section_differs(loaded, fresh)
}Mapeos MITRE ATT&CK
Last verified: 2026-05-20