Difficulty: Advanced

Module 7: AV/EDR Scan Timing Window

Understanding exactly when antivirus and EDR products scan files during process creation — and why Process Ghosting bypasses all three inspection points.

Module Objective

Analyze the three primary inspection points where AV/EDR attempts to scan a process’s backing file (file creation/write, section mapping, process creation callback), understand the kernel mechanisms behind each (minifilter callbacks, PsSetCreateProcessNotifyRoutineEx, image load callbacks), and demonstrate why Process Ghosting defeats all three by ensuring the file is either inaccessible or non-existent at each point.

1. The Three Inspection Points

AV and EDR products have three primary opportunities to scan a process’s backing executable. Each occurs at a different stage of the file → section → process pipeline:

AV/EDR Inspection Points

Inspection 1
File Create/Write
Minifilter IRP
Inspection 2
Image Load
PsSetLoadImageNotify
Inspection 3
First Thread Insert
PsSetCreateProcess
NotifyRoutineEx
Inspection PointKernel MechanismWhen It FiresWhat AV Typically Does
1. File I/OMinifilter driver (IRP callbacks)On file create, write, closeScan file content on write/close
2. Image LoadPsSetLoadImageNotifyRoutineWhen an image section is mappedVerify image integrity, check known signatures
3. Process CreatePsSetCreateProcessNotifyRoutineExWhen the first thread is inserted via NtCreateThreadEx (not at NtCreateProcessEx time)Open and scan the image file, apply policies

2. Inspection Point 1: File I/O Minifilter

The file system minifilter is the first line of defense. AV products register minifilter drivers that intercept file I/O operations (IRPs). The relevant callbacks are:

Minifilter IRP Callbacks for AV Scanning

IRPWhenAV Action
IRP_MJ_CREATEFile is opened/createdCheck file path, apply policies, may deny access
IRP_MJ_WRITEData is written to fileMay buffer writes for later scanning
IRP_MJ_CLEANUPLast user handle closedTrigger on-close scan of file content
IRP_MJ_SET_INFORMATIONFile metadata changed (incl. disposition)May detect delete-pending, but typically does not trigger a scan

Why Ghosting Bypasses Minifilter Scanning

During the ghosting sequence:

Some AV minifilters can scan data from the write IRP buffer directly (without opening a separate handle). However, most on-access scanners are designed to scan on IRP_MJ_CLEANUP (handle close) and rely on being able to re-open the file for scanning. The delete-pending state defeats this model.

3. Inspection Point 2: Image Load Notification

The kernel provides PsSetLoadImageNotifyRoutine for drivers to register callbacks that fire when an image (EXE or DLL) is mapped into a process. This callback provides the image’s FILE_OBJECT and full image name:

// Kernel callback prototype
typedef VOID (*PLOAD_IMAGE_NOTIFY_ROUTINE)(
    PUNICODE_STRING FullImageName,
    HANDLE ProcessId,
    PIMAGE_INFO ImageInfo
);

// IMAGE_INFO structure
typedef struct _IMAGE_INFO {
    union {
        ULONG Properties;
        struct {
            ULONG ImageAddressingMode : 8;
            ULONG SystemModeImage : 1;
            ULONG ImageMappedToAllPids : 1;
            ULONG ExtendedInfoPresent : 1;
            // ...
        };
    };
    PVOID ImageBase;
    ULONG ImageSelector;
    SIZE_T ImageSize;
    ULONG ImageSectionNumber;
} IMAGE_INFO, *PIMAGE_INFO;

Ghosting vs Image Load Callback

The image load callback fires when the image section is mapped into the process’s address space (during NtCreateProcessEx or when DLLs are loaded). At this point, the ghost file has already been closed and deleted. The FullImageName parameter points to the path of the deleted file. If the AV driver tries to open this path for scanning, it fails with STATUS_OBJECT_NAME_NOT_FOUND because the file no longer exists.

The FILE_OBJECT referenced by the section’s control area may still exist in kernel memory, but it is not directly provided to the image load callback in a way that allows easy scanning. The callback gets the image name (a path), not a handle or file object.

4. Inspection Point 3: Process Creation Notification

The most important inspection point is PsSetCreateProcessNotifyRoutineEx. Despite its name, this callback does not fire when the process object is created (NtCreateProcessEx). It fires when the first thread is inserted into the process via NtCreateThreadEx. This timing gap — between process object creation and first thread insertion — is exactly what Process Ghosting exploits. The file can be deleted after creating the process object but before the notify routine fires. The callback provides the AV driver with the information needed to make a scan/block decision:

// Extended process notification callback
typedef VOID (*PCREATE_PROCESS_NOTIFY_ROUTINE_EX)(
    PEPROCESS Process,
    HANDLE ProcessId,
    PPS_CREATE_NOTIFY_INFO CreateInfo  // NULL for process exit
);

// PS_CREATE_NOTIFY_INFO (on process creation)
typedef struct _PS_CREATE_NOTIFY_INFO {
    SIZE_T Size;
    union {
        ULONG Flags;
        struct {
            ULONG FileOpenNameAvailable : 1;
            ULONG IsSubsystemProcess : 1;
            // ...
        };
    };
    HANDLE ParentProcessId;
    CLIENT_ID CreatingThreadId;
    struct _FILE_OBJECT *FileObject;  // THE IMAGE FILE OBJECT
    PUNICODE_STRING ImageFileName;    // image file path
    PUNICODE_STRING CommandLine;
    NTSTATUS CreationStatus;          // can set to block!
} PS_CREATE_NOTIFY_INFO, *PPS_CREATE_NOTIFY_INFO;

The Critical Field: FileObject

The FileObject field in PS_CREATE_NOTIFY_INFO points to the file object of the image that backs the new process. For a normal process, this is a valid, open file object that the AV can use to read and scan the file. For a ghosted process, this file object points to a file that is deleted. The AV driver receives a reference to a file object whose backing file no longer exists in the filesystem namespace.

What happens when the AV tries to use this file object to scan the image?

AV ActionResult with Ghost File
Open file by name (ImageFileName)STATUS_OBJECT_NAME_NOT_FOUND — file deleted
Read from FileObject directlyMay succeed or fail depending on implementation. The file object exists in kernel memory but has no directory entry. Some drivers cannot handle this state.
Query file informationMay return stale or incomplete information since the file is deleted
Set CreationStatus to blockCould block the process, but only if the AV has a reason to block (requires successful scan first)

5. The Complete Timing Analysis

Here is a detailed timeline showing the state of the file at each inspection point during the ghosting sequence:

Ghosting Timeline vs AV Inspection Points

Ghosting StepFile StateActive InspectionAV Can Scan?Why Not?
NtCreateFileEmpty, normalMinifilter IRP_MJ_CREATEYes, but file is emptyNothing malicious to find
NtSetInformationFileDelete-pending, emptyMinifilter IRP_MJ_SET_INFONo content to scanFile still empty
NtWriteFileDelete-pending, has PEMinifilter IRP_MJ_WRITECannot re-open fileSTATUS_DELETE_PENDING blocks new opens
NtCreateSectionDelete-pending, has PENone (no callback for section creation from files)NoNo relevant callback fires
NtClose(hFile)DeletedMinifilter IRP_MJ_CLEANUPFile goneSTATUS_OBJECT_NAME_NOT_FOUND
NtCreateProcessExDeletedNone (callback does not fire here)NoPsSetCreateProcessNotifyRoutineEx fires later at thread insertion
Image mappingDeletedPsSetLoadImageNotifyRoutineCannot open by nameFile does not exist
NtCreateThreadExDeletedPsSetCreateProcessNotifyRoutineExCannot open by nameFile does not exist

At every single inspection point, the AV either sees an empty file, cannot open the file because it is delete-pending, or cannot find the file because it has been deleted. There is no point in the sequence where the AV can both access the file and see malicious content.

6. Comparison: How Each Technique Handles the Scan Points

Inspection PointHollowingDoppelgängingHerpaderpingGhosting
File I/O scanClean file (legitimate EXE)Transacted (visible only in TxF context)Malicious content visible during writeEmpty during create; delete-pending during write
Image loadMismatch detectableClean (transaction rolled back)File now has benign contentFile deleted
Process createFile is clean; memory is notFile reverted to cleanFile has benign contentFile does not exist
Overall evasionWeak (memory/file mismatch)Good (but TxF is flagged)Good (but file still on disk)Strong (no file at all)

7. Kernel Callback Limitations

Understanding why the kernel’s callback architecture has this gap reveals a fundamental design assumption:

Design Assumptions the Kernel Makes

Why Microsoft Has Not Fully Fixed This

Microsoft could add a callback that fires at NtCreateSection time, providing the file object and content at the moment the section captures the image. However, this would be a significant kernel change with performance implications (every DLL load would trigger the callback). As of Windows 11, PsSetCreateProcessNotifyRoutineEx2 provides improved information but the fundamental timing gap remains exploitable. The kernel would need to prevent section creation from delete-pending files entirely, or provide a mandatory scan window, to fully close this gap.

8. ETW (Event Tracing for Windows) Visibility

ETW provides additional visibility but has its own limitations in detecting ghosting:

ETW ProviderEvents AvailableGhosting Detection Capability
Microsoft-Windows-Kernel-FileFile create, write, delete, renameCan see the create → set disposition → write → close sequence, but requires correlation
Microsoft-Windows-Kernel-ProcessProcess create, thread createCan see process creation but file is already gone
Microsoft-Windows-Kernel-Audit-API-CallsNtCreateSection, NtCreateProcessExCan see section and process creation, but requires correlating with file events

ETW-based detection is possible but requires event correlation: linking the file creation event, the disposition change, the write, the section creation, and the process creation into a single chain. This is non-trivial and most EDR products do not correlate these events in real time.

Detection via ETW Correlation

A detection strategy based on ETW would look for: (1) NtCreateFile for a new file, (2) NtSetInformationFile with FileDispositionInformation shortly after, (3) NtWriteFile to the same handle, (4) NtCreateSection from the same handle, (5) NtCreateProcessEx with the section. Finding all five events in sequence on the same handle is a strong indicator of ghosting. Module 8 covers detection in detail.

Knowledge Check

Q1: At which point in the ghosting sequence does the AV first have the opportunity to scan malicious content in the file?

A) During NtCreateFile (file creation)
B) During NtSetInformationFile (marking delete-pending)
C) Never — at every point the file is either empty, inaccessible (delete-pending), or deleted
D) During NtCreateProcessEx (process creation)

Q2: What does PsSetCreateProcessNotifyRoutineEx provide to the AV driver in its PS_CREATE_NOTIFY_INFO structure?

A) A copy of the PE file in a kernel buffer
B) A FILE_OBJECT pointer and the image file path, which for ghosted processes points to a deleted file
C) The process's virtual memory contents for direct scanning
D) A hash of the PE file content

Q3: Why can an AV minifilter not scan the payload when NtWriteFile writes it to the delete-pending file?

A) The file is in the delete-pending state, so the AV cannot open a second handle to read it (STATUS_DELETE_PENDING blocks all new opens)
B) NtWriteFile encrypts the data before writing
C) The minifilter callback does not fire for NtWriteFile
D) Write operations to delete-pending files are invisible to minifilters