Communication Architecture

This document describes the two inter-process communication channels that connect the kernel driver, user-mode monitor, and scanner: the Filter Communication Port and the Named Pipe.

Related: System Overview ยท Driver Architecture ยท Kernel โ†” User Interface ยท Monitor Module


1. Communication Topology

flowchart LR
    subgraph Ring0["Kernel (Ring 0)"]
        Driver["WindowsFileSystemMinifilter.sys"]
        ServerPort["Server Port\n(\\FsMinifilterPort)"]
        Driver --> ServerPort
    end

    subgraph Ring3["User (Ring 3)"]
        Monitor["FsMinifilterMonitor.exe"]
        ClientPort["Client Port Handle"]
        Pipe["Named Pipe\n(\\\\.\\pipe\\ScannerPipe)"]
        Scanner["Scanner.exe"]
        
        Monitor --> ClientPort
        Monitor --> Pipe
        Pipe --> Scanner
    end

    ServerPort -.->|"FltSendMessage\n(MINIFILTER_MESSAGE)"| ClientPort
    ClientPort -.->|"FilterGetMessage"| Monitor

    style Ring0 fill:#7b2cbf,color:#fff
    style Ring3 fill:#2d6a4f,color:#fff

2. Channel 1: Filter Communication Port

Overview

The kernel driver creates a filter communication port during DriverEntry. The monitor connects to this port using FilterConnectCommunicationPort. The kernel sends unidirectional messages (driver โ†’ monitor); no replies are expected.

Lifecycle Sequence

sequenceDiagram
    participant D as Driver (Kernel)
    participant FM as Filter Manager
    participant M as Monitor (User)

    Note over D: DriverEntry()
    D->>FM: FltBuildDefaultSecurityDescriptor()
    D->>FM: FltCreateCommunicationPort("\\FsMinifilterPort")
    FM-->>D: g_serverPort handle

    Note over M: wmain()
    M->>FM: FilterConnectCommunicationPort("\\FsMinifilterPort")
    FM->>D: PortConnectCallback()
    D->>D: g_clientPort = ClientPort
    D->>D: g_clientProcessId = PsGetCurrentProcessId()
    FM-->>M: port handle

    loop File System Events
        D->>FM: FltSendMessage(MINIFILTER_MESSAGE)
        FM->>M: FilterGetMessage() returns
        M->>M: Process message
    end

    Note over M: Process exit or Ctrl+C
    M->>FM: CloseHandle(port)
    FM->>D: PortDisconnectCallback()
    D->>D: g_clientPort = NULL
    D->>D: g_clientProcessId = 0

Port Configuration

Parameter Value Rationale
Port Name \FsMinifilterPort NT object namespace path
Security FLT_PORT_ALL_ACCESS Default security descriptor
Max Connections 1 Single monitor instance at a time
Message Direction Driver โ†’ Monitor No MessageNotifyCallback registered
Timeout 100ms (-1000000 in 100ns units) Non-blocking; timeout errors are silently ignored

Message Format

classDiagram
    class MINIFILTER_MESSAGE {
        +ULONG MessageType
        +ULONG ProcessId
        +WCHAR FilePath[520]
    }

    class FILTER_MESSAGE {
        +FILTER_MESSAGE_HEADER Header
        +MINIFILTER_MESSAGE Message
    }

    FILTER_MESSAGE --> MINIFILTER_MESSAGE : contains

The kernel sends raw MINIFILTER_MESSAGE via FltSendMessage. The user-mode monitor receives it wrapped in FILTER_MESSAGE which includes the FILTER_MESSAGE_HEADER prepended by Filter Manager.

MessageType Constant Value Meaning
MSG_TYPE_FILE_CREATE 1 File/directory created or opened
MSG_TYPE_FILE_READ 2 File data was read
MSG_TYPE_FILE_MODIFY 3 File data was written/modified
MSG_TYPE_FILE_DELETE 4 File was deleted

See Data Types Reference for complete struct layouts.


3. Channel 2: Named Pipe (Monitor โ†’ Scanner)

Overview

The monitor forwards scan-worthy events to the scanner via a Win32 named pipe. The scanner creates the pipe server; the monitor connects as a client.

Lifecycle Sequence

sequenceDiagram
    participant S as Scanner.exe
    participant M as Monitor.exe

    Note over S: wmain() โ†’ InitializeScanner()
    S->>S: CreateNamedPipe("\\\\.\\pipe\\ScannerPipe")
    S->>S: ConnectNamedPipe() โ€” blocks

    Note over M: Receives first .exe/.dll event
    M->>S: CreateFile(PIPE_NAME) โ€” connects
    S-->>S: ConnectNamedPipe returns

    loop Scan Requests
        M->>S: WriteFile(SCAN_REQUEST)
        S->>S: ReadFile() โ†’ Enqueue โ†’ ScanWorker
    end

    Note over M: Process exits
    M->>S: Pipe disconnected
    S-->>S: ReadFile returns FALSE

Pipe Configuration

Parameter Value
Pipe Name \\.\pipe\ScannerPipe
Access PIPE_ACCESS_DUPLEX
Mode PIPE_TYPE_MESSAGE \| PIPE_READMODE_MESSAGE \| PIPE_WAIT
Max Instances 1
Buffer Sizes 1024 bytes (in/out)

Message Format

The pipe carries raw SCAN_REQUEST structs:

classDiagram
    class SCAN_REQUEST {
        +WCHAR filePath[260]
        +DWORD pid
        +FILETIME timestamp
    }

Connection Resilience

The monitor implements a retry loop for pipe connection (EnsurePipeConnection):

  • Up to 20 retries with 100ms intervals
  • If a pipe write fails, the handle is closed and re-established on the next request
  • This allows the scanner to be started before or after the monitor

4. Deduplication Layer

Between receiving kernel messages and forwarding to the scanner, the monitor applies deduplication to avoid flooding the scanner with repeated events for the same file.

flowchart TD
    MSG["Kernel Message Received"]
    
    MSG --> IsExe{"Is .exe or .dll?"}
    IsExe -->|No| Drop1["Drop"]
    IsExe -->|Yes| Dedup{"Seen within\nlast 5 seconds?"}
    
    Dedup -->|Yes| Drop2["Drop (Deduplicated)"]
    Dedup -->|No| Cache["Update Cache\n+ Forward to Scanner"]

    subgraph CacheMgmt["Cache Management"]
        direction TB
        Cleanup["Periodic Cleanup\n(every 30s)"]
        Evict["Evict entries\n> 5s old"]
        Cleanup --> Evict
    end

    style Drop1 fill:#6c757d,color:#fff
    style Drop2 fill:#6c757d,color:#fff
    style Cache fill:#2d6a4f,color:#fff
Parameter Value
Cooldown Window 5,000ms
Cleanup Interval 30,000ms
Data Structure std::unordered_map<wstring, ULONGLONG>

5. End-to-End Message Flow

flowchart LR
    A["App writes\nmalware.exe"] 
    --> B["NTFS I/O"]
    --> C["Filter Manager"]
    --> D["PreOperationWrite"]
    --> E["SendMessageToUserMode\n(MSG_TYPE_FILE_MODIFY)"]
    --> F["FilterGetMessage\n(Monitor)"]
    --> G["Dedup Check"]
    --> H["WriteFile\n(Named Pipe)"]
    --> I["ReadFile\n(Scanner)"]
    --> J["Enqueue"]
    --> K["ScanWorker\nDequeue + Analyze"]

    style A fill:#e63946,color:#fff
    style K fill:#2d6a4f,color:#fff

Next Steps