-
Notifications
You must be signed in to change notification settings - Fork 96
Request Monitoring
As stated in the Introduction chapter, one of the project goals is compatibility with 64-bit Windows, especially with Patchguard. Since Patchguard does not like modifications of kernel code, the IRPMon driver needs to make its hooks in more clever way. This chapter describes how the driver accomplishes the task.
Each request captured by the IRPMon driver is represented by one request record. A request record consists of a header and body. The header contains data common to multiple request types whereas the body ha specific structure for each type. When a request record is fully initialized, it is inserted to a request queue. The request queue is visible to user applications – an application can connect to it and retrieve requests from there. At most one entity can be connected to the queue at any time moment. By default, when no one is connected, no request records are stored in the queue; they are freed instead.
Each request record header may contain the following information:
-
request ID,
-
request type,
-
device object that is the target of the request,
-
driver object associated in any way with the request (e.g. the driver to which the target device belongs)
-
request result,
-
TID of the thread processing the request (together with its PID and IRQL).
The IRPMon driver gets control over IRPs targeted at a given device object by redirecting IRP all dispatch callbacks of device's driver. These callbacks are stored in the MajorFunction
array and all of them are always defined which makes the redirecting task quite straightforward.
The newly IRP dispatch callback has the following semantics:
- capture information about the incoming IRP into a request record,
- if IRP completion is also being monitored, hook the IRPMon driver into the chain of completion routines,
- pass the IRP to the original dispatch routine,
- record the result of the original routine and insert the request record to the request queue,
- if there already is a request record representing completion of the IRP, insert it to the request queue.
As explained in the Device Stacks chapter, an IRP dispatch callback receives arguments of the incoming IRP in "its own" stack location that is named current. Except the arguments, each stack location contains space for storing completion routine – a callback that is invoked when the IRP is completed. The current stack locations contains storage for the completion routine registered by the driver owning the next highest device in the stack. That completion routine is invoked after the IRP completion notification is processed by the current driver.
To be notified about IRP completion, the IRP dispatch callback of the IRPMon driver sets the completion routine stored in the current stack location to its own. So, the IRPMon driver gets notified just before the next higher driver. The IRPMon driver registers its completion routine even when the higher driver does not – there is just no original completion routine to invoke.
Semantics of the hooking IRP completion routine is the following:
- record all necessary information (mainly the IO status block of the completing IRP) to a request record,
- call the original IRP completion routine if such exists,
- associate the request record with the record reporting the IRP being completed. If the corresponding IRP request had already been inserted to the request queue, insert the completion request to it too.
When the IRP dispatch callback of the IRPMon driver invokes the original callback, the IRP may be completed before control returns to IRPMon which means, that the request record describing completion of the IRP is ready before the one describing the IRP. Such an order is not acceptable since the completion record should always follow the IRP one. For this reason, the IRPMon driver ensures that the request records are inserted to the request queue in correct order by postponing the insertion of the completion record until the IRP one is inserted.
For the IRP record case, the result field in the header part is filled with the return value of the original IRP dispatch callback, since the IO status block usually does not not contain reliable results. The record body includes:
- major and minor function codes,
- file object (aka handle),
- IRP address,
- all four operation arguments stored in the current stack location.
IRP completion records contain fills the result field with the return value of the original completion routine, or with STATUS_CONTINUE_COMPLETION if no such routine is defined. Their body stores address of the IRP and the IO status block captured after the original completion routine is executed.
If the monitored driver has a valid FastIoDispatch filled in its DRIVER_OBJECT structure and the IRPMon driver is configured to monitor fast I/O operation of that driver, the IRPMon driver redirects all fast I/O callbacks defined by the driver to its own. The semantics of IRPMon's fast I/O callbacks is quite straightforward: collect the information about the current call, invoke the original callback, record its result and insert the newly created fast I/O record into the request queue. Fast I/O is much simpler mechanism, so there is nothing similar to IRP completion.
Body of a fast I/O record is very specific to the type of the fast I/O request. The only field common to all fast I/O records is just the fast I/O request type.
Records of AddDevice type are generated when a driver is informed about a PnP device newly connected to the computer. The system notifies the driver about this by invoking its AddDevice
callback defined in the extension of its driver object. The newly connected device is passed to the callback as an argument and the driver usually creates its own device object and attaches it above the new device, thus forming a device stack.
To receive these notifications, the IRPMon driver redirects the AddDevice callback of the target driver to its own. That callback do the standard IRPMon job:
- records the PDO of the stack to a newly created AddDevice record,
- invokes the original AddDevice callback,
- records its result and inserts the just-finished AddDevice record to the request queue.
To become dynamically unloadable, a driver needs to set the DriverUnload
field in its DRIVER_OBJECT to point to a callback that is invoked when the system, or the user, decides that the driver is no longer needed. The IRPMon driver always redirects this callback, regardless of whether it is configured to monitor driver unloads or not. The reason is simple – this callbacks tells IRPMon when a driver is unloaded which also means destruction of its driver object. IRPMon must remove such driver objects from its data structures.
In case the IRPMon driver is configured to report driver unloads, its driver unload callback creates a driver unload record, fills it with information about the driver being unloaded, and inserts it to the request queue.
The IRPMon driver also monitors activities that, although are not directly related to app-to-device or driver-to-device communication, the user would like to know about. There are four types of request record defined for such cases, describing the monitored events:
- Driver detected. The IRPMon driver noticed that a new driver had been located into the kernel. Requests of this type are generated during processing of the AddDevice callback and inform the user about all drivers that attached their devices into the new device stack. It may happen that the IRPMon driver informs about presence of one driver more than once.
- Device detected. Presence of a new device object has been detected. This record is generated in three cases: when processing IRP, fast I/O and AddDevice callbacks.
- Process created. A new process has been created. This event is detected via a callback registered through PsSetCreateProcessNotifyRoutineEx.
- Process terminated. An existing process just exited. This event is detected by the same means as process creation.
- IRPMonDllClassWatchEnum
- IRPMonDllClassWatchEnumFree
- IRPMonDllClassWatchRegister
- IRPMonDllClassWatchUnregister
- IRPMonDllCloseHookedDeviceHandle
- IRPMonDllCloseHookedDriverHandle
- IRPMonDllConnect
- IRPMonDllDisconnect
- IRPMonDllDriverHooksEnumerate
- IRPMonDllDriverHooksFree
- IRPMonDllDriverNameWatchEnum
- IRPMonDllDriverNameWatchEnumFree
- IRPMonDllDriverNameWatchRegister
- IRPMonDllDriverNameWatchUnregister
- IRPMonDllDriverSetInfo
- IRPMonDllDriverStartMonitoring
- IRPMonDllDriverStopMonitoring
- IRPMonDllEmulateDriverDevices
- IRPMonDllEmulateProcesses
- IRPMonDllFinalize
- IRPMonDllGetRequest
- IRPMonDllHookDeviceByAddress
- IRPMonDllHookDeviceByName
- IRPMonDllHookDriver
- IRPMonDllHookedDeviceGetInfo
- IRPMonDllHookedDeviceSetInfo
- IRPMonDllHookedDriverGetInfo
- IRPMonDllInitialize
- IRPMonDllInitialized
- IRPMonDllOpenHookedDevice
- IRPMonDllOpenHookedDriver
- IRPMonDllQueueClear
- IRPMonDllSettingsQuery
- IRPMonDllSettingsSet
- IRPMonDllSnapshotFree
- IRPMonDllSnapshotRetrieve
- IRPMonDllUnhookDevice
- IRPMonDllUnhookDriver
- CLASS_WATCH_RECORD
- DRIVER_MONITOR_SETTINGS
- DRIVER_NAME_WATCH_RECORD
- EFastIoOperationType
- EIRPMonConnectorType
- ERequestHeaderFlags
- ERequestResultType
- ERequestType
- HOOKED_DEVICE_INFO
- HOOKED_DEVICE_UMINFO
- HOOKED_DRIVER_INFO
- HOOKED_DRIVER_UMINFO
- HOOKED_OBJECTS_INFO
- IRPMNDRV_SETTINGS
- IRPMON_DEVICE_INFO
- IRPMON_DEVICE_INIT_INFO
- IRPMON_DRIVER_INFO
- IRPMON_INIT_INFO
- IRPMON_INIT_INFO_DATA
- IRPMON_NETWORK_INIT_INFO
- PCLASS_WATCH_RECORD
- PDRIVER_MONITOR_SETTINGS
- PDRIVER_NAME_WATCH_RECORD
- PEFastIoOperationType
- PEIRPMonConnectorType
- PERequestHeaderFlags
- PERequestResultType
- PERequestType
- PHOOKED_DEVICE_INFO
- PHOOKED_DEVICE_UMINFO
- PHOOKED_DRIVER_INFO
- PHOOKED_DRIVER_UMINFO
- PHOOKED_OBJECTS_INFO
- PIRPMNDRV_SETTINGS
- PIRPMON_DEVICE_INFO
- PIRPMON_DEVICE_INIT_INFO
- PIRPMON_DRIVER_INFO
- PIRPMON_INIT_INFO
- PIRPMON_INIT_INFO_DATA
- PIRPMON_NETWORK_INIT_INFO
- PREQUEST_ADDDEVICE
- PREQUEST_FASTIO
- PREQUEST_HEADER
- PREQUEST_IRP
- PREQUEST_STARTIO
- PREQUEST_UNLOAD
- REQUEST_ADDDEVICE
- REQUEST_FASTIO
- REQUEST_HEADER
- REQUEST_IRP
- REQUEST_STARTIO
- REQUEST_UNLOAD
- _CLASS_WATCH_RECORD
- _DRIVER_MONITOR_SETTINGS
- _DRIVER_NAME_WATCH_RECORD
- _EFastIoOperationType
- _EIRPMonConnectorType
- _ERequestHeaderFlags
- _ERequestResultType
- _ERequestType
- _HOOKED_DEVICE_INFO
- _HOOKED_DEVICE_UMINFO
- _HOOKED_DRIVER_INFO
- _HOOKED_DRIVER_UMINFO
- _HOOKED_OBJECTS_INFO
- _IRPMNDRV_SETTINGS
- _IRPMON_DEVICE_INFO
- _IRPMON_DEVICE_INIT_INFO
- _IRPMON_DRIVER_INFO
- _IRPMON_INIT_INFO
- _IRPMON_INIT_INFO_DATA
- _IRPMON_NETWORK_INIT_INFO
- _REQUEST_ADDDEVICE
- _REQUEST_FASTIO
- _REQUEST_HEADER
- _REQUEST_IRP
- _REQUEST_STARTIO
- _REQUEST_UNLOAD