NtAreMappedFilesTheSame
Détermine si deux vues mappées sont adossées au même fichier (test d'identité du file object).
Prototype
NTSTATUS NtAreMappedFilesTheSame( PVOID File1MappedAsAnImage, PVOID File2MappedAsFile );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| File1MappedAsAnImage | PVOID | in | Pointeur dans une vue mappée avec SEC_IMAGE (typiquement une base de DLL/EXE chargée). |
| File2MappedAsFile | PVOID | in | Pointeur dans une vue mappée depuis un mapping de fichier régulier (tout type de section). |
IDs de syscalls par version de Windows
| Version 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 |
Module noyau
APIs liées
Stub du 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
Notes non documentées
Un petit utilitaire étonnamment porteur : le noyau parcourt les `MM_SECTION_OBJECT_POINTERS` des deux adresses, compare le `FILE_OBJECT` sous-jacent (plus précisément les `SectionObjectPointer`) et retourne `STATUS_SUCCESS` lorsqu'ils renvoient au même fichier de backing — même si les deux vues diffèrent en attributs (image vs data), en protection, en taille ou en offset. Les deux arguments sont *nommés* de manière asymétrique (Image vs File) pour des raisons historiques ; en pratique, l'un comme l'autre peut être de n'importe quel type et la comparaison est symétrique.
Usage courant par les malwares
Joue dans les deux camps. **Usage défensif (et EDR)** : ouvrir `\KnownDlls\ntdll.dll`, mapper une copie fraîche en tant que fichier, puis appeler `NtAreMappedFilesTheSame(LoadedNtdllBase, FreshNtdllMap)`. Si la réponse est *même*, la ntdll chargée a le même fichier de backing — les défenseurs comparent alors octet-à-octet les sections `.text` pour découvrir les hooks inline EDR/AV et les écraser avec les octets vierges. C'est le fondement de toute primitive de désrochage moderne (Perun's Fart, FreshyCalls, SysWhispers3 +unhook, RefleXXion). **Usage offensif** : un implant **DLL-hollowed** (son image module écrasée en mémoire alors que le fichier sous-jacent reste intact) peut appeler ceci sur lui-même pour détecter la falsification — auto-défense symétrique contre les unhookers blue-team. Certains checks d'évasion sandbox utilisent aussi cet appel pour confirmer qu'une DLL chargée provient bien de `\System32\` plutôt que d'une copie attaquante redirigée.
Opportunités de détection
Quasi aucun signal de détection propre : le syscall est rare en logiciel bénin mais de plus en plus courant dans l'outillage de sécurité (Defender, CrowdStrike, SentinelOne y passent tous lors de leurs comparaisons d'unhooking). Le signal fort, c'est le *pairing* : un processus qui ouvre `\KnownDlls\ntdll.dll` (ou mappe toute DLL système depuis `\System32`) puis appelle immédiatement `NtAreMappedFilesTheSame` contre sa propre ntdll chargée effectue une danse d'unhooking. Les éditeurs EDR s'auto-protègent de plus en plus en supprimant les astuces d'identification par identité de section — si l'appel retourne same sur une DLL que l'EDR attend différente, l'EDR peut détecter *cela* et réagir.
Exemples de syscalls directs
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)
}Mappings MITRE ATT&CK
Last verified: 2026-05-20