NtAreMappedFilesTheSame
Prüft, ob zwei gemappte Views durch dieselbe Datei gestützt werden (Identitätstest des File-Objekts).
Prototyp
NTSTATUS NtAreMappedFilesTheSame( PVOID File1MappedAsAnImage, PVOID File2MappedAsFile );
Argumente
| Name | Type | Dir | Description |
|---|---|---|---|
| File1MappedAsAnImage | PVOID | in | Zeiger innerhalb einer mit SEC_IMAGE gemappten View (typischerweise Basis einer geladenen DLL/EXE). |
| File2MappedAsFile | PVOID | in | Zeiger innerhalb einer aus einem regulären Datei-Mapping gemappten View (beliebiger Section-Typ). |
Syscall-IDs pro 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-Modul
Verwandte 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
Undokumentierte Hinweise
Ein kleiner, aber überraschend tragender Helper: der Kernel läuft `MM_SECTION_OBJECT_POINTERS` für beide Adressen ab, vergleicht das zugrundeliegende `FILE_OBJECT` (genauer die `SectionObjectPointer`s) und liefert `STATUS_SUCCESS`, wenn beide auf dieselbe Backing-Datei verweisen — selbst wenn die zwei Views in Attributen (eine als Image, die andere als Data), in Schutz, Größe oder Offset abweichen. Die beiden Argumente sind aus historischen Gründen asymmetrisch *benannt* (Image vs File); in der Praxis darf jedes von jeder Sorte sein, und der Vergleich ist symmetrisch.
Häufige Malware-Nutzung
Spielt in beiden Lagern. **Defensive (und EDR-) Nutzung**: `\KnownDlls\ntdll.dll` öffnen, frische Kopie als Datei mappen, dann `NtAreMappedFilesTheSame(LoadedNtdllBase, FreshNtdllMap)` aufrufen. Lautet die Antwort *gleich*, hat die geladene ntdll dieselbe Backing-Datei — Verteidiger vergleichen dann die `.text`-Sektionen byteweise, um Inline-EDR/AV-Hooks zu finden und mit den unberührten Bytes zu überschreiben. Das ist das Fundament jeder modernen Unhooking-Primitive (Perun's Fart, FreshyCalls, SysWhispers3 +unhook, RefleXXion). **Offensive Nutzung**: ein **DLL-hollowed** Implant (sein Modul-Image im Speicher überschrieben, während die Datei unverändert ist) kann den Aufruf gegen sich selbst tätigen, um Manipulation zu erkennen — symmetrische Selbstverteidigung gegen Blue-Team-Unhooker. Einige Sandbox-Evasion-Checks bestätigen damit, dass eine geladene DLL tatsächlich aus `\System32\` stammt und nicht aus einer umgeleiteten Angreiferkopie.
Erkennungsmöglichkeiten
Praktisch kein eigenes Detection-Signal: der Syscall ist in gutartiger Software selten, in Security-Tooling aber zunehmend gängig (Defender, CrowdStrike, SentinelOne treffen ihn alle beim Unhook-Vergleich). Das starke Signal ist das *Pairing*: ein Prozess, der `\KnownDlls\ntdll.dll` öffnet (oder eine beliebige System-DLL aus `\System32` mappt) und dann sofort `NtAreMappedFilesTheSame` gegen seine eigene geladene ntdll aufruft, vollzieht einen Unhooking-Tanz. EDR-Anbieter schützen sich zunehmend selbst, indem sie Identifikation-per-Section-Identity-Tricks entfernen — liefert der Aufruf *gleich* für eine DLL, von der der EDR Unterschied erwartet, kann der EDR genau *das* erkennen und reagieren.
Direkte Syscall-Beispiele
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