NtAreMappedFilesTheSame
Determines whether two mapped views are backed by the same file (file object identity test).
Prototype
NTSTATUS NtAreMappedFilesTheSame( PVOID File1MappedAsAnImage, PVOID File2MappedAsFile );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| File1MappedAsAnImage | PVOID | in | Pointer inside a view that was mapped with SEC_IMAGE (typically a loaded DLL/EXE base). |
| File2MappedAsFile | PVOID | in | Pointer inside a view that was mapped from a regular file mapping (any section type). |
Syscall IDs by Windows version
| Windows version | Syscall ID | 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 |
Kernel module
Related APIs
Syscall stub
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
Undocumented notes
A small but surprisingly load-bearing helper: the kernel walks `MM_SECTION_OBJECT_POINTERS` for both addresses, compares the underlying `FILE_OBJECT` (or, more precisely, the `SectionObjectPointer`s), and returns `STATUS_SUCCESS` when they refer to the same backing file — even if the two views differ in attributes (one as image, the other as data), in protection, in size, or in offset. The two arguments are *named* asymmetrically (Image vs File) for historical reasons; in practice either may be either, and the comparison is symmetric.
Common malware usage
Cuts both ways. **Defensive (and EDR) use**: open `\KnownDlls\ntdll.dll`, map a fresh copy as a file, then call `NtAreMappedFilesTheSame(LoadedNtdllBase, FreshlyMappedNtdll)`. If the answer is *same*, the loaded ntdll has the same backing file — defenders then byte-compare the `.text` sections to discover inline EDR/AV hooks and overwrite them with the pristine bytes. This is the foundation of every modern unhooking primitive (Perun's Fart, FreshyCalls, SysWhispers3 +unhook, RefleXXion). **Offensive use**: an implant that has been **DLL-hollowed** (its module image overwritten in memory while the underlying file is unchanged) can call this against itself to detect tampering — symmetric self-defense against blue-team unhookers. Some sandbox-evasion checks also use this call to confirm a loaded DLL really comes from `\System32\` rather than a redirected attacker copy.
Detection opportunities
Almost no detection signal of its own: the syscall is rare in benign software but increasingly common in security tooling (Defender, CrowdStrike, SentinelOne all hit it during unhook-comparison). The strong signal is *pairing*: a process that opens `\KnownDlls\ntdll.dll` (or maps any system DLL from `\System32`) and then immediately calls `NtAreMappedFilesTheSame` against its own loaded ntdll is performing an unhooking dance. EDR vendors increasingly self-protect by removing identification-by-section-identity tricks — if the call returns same on a DLL that the EDR expects to differ, the EDR can detect *that* and respond.
Direct syscall examples
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)
}MITRE ATT&CK mappings
Last verified: 2026-05-20