Доступ к пользовательским буферам в подпрограмме обратного вызова после операции
Подпрограмма обратного вызова драйвера минифильтра после операции ввода-вывода должна обрабатывать буфер в операции ввода-вывода на основе IRP следующим образом:
Проверьте, существует ли MDL для буфера. Указатель MDL можно найти в параметре MdlAddress или OutputMdlAddress в FLT_PARAMETERS операции. Драйверы минифильтра могут вызывать FltDecodeParameters для запроса указателя MDL.
Одним из способов получения допустимого MDL является поиск флага IRP_MN_MDL в элементе MinorFunction блока параметров ввода-вывода FLT_IO_PARAMETER_BLOCK в данных обратного вызова. В следующем примере показано, как проверка для флага IRP_MN_MDL.
NTSTATUS status; PMDL *ReadMdl = NULL; PVOID ReadAddress = NULL; if (FlagOn(CallbackData->Iopb->MinorFunction, IRP_MN_MDL)) { ReadMdl = &CallbackData->Iopb->Parameters.Read.MdlAddress; }
Однако флаг IRP_MN_MDL можно задать только для операций чтения и записи. Лучше всего использовать FltDecodeParameters для получения MDL, так как подпрограмма проверяет допустимое значение MDL для любой операции. В следующем примере возвращается только параметр MDL, если он действителен.
NTSTATUS status; PMDL *ReadMdl = NULL; PVOID ReadAddress = NULL; status = FltDecodeParameters(CallbackData, &ReadMdl, NULL, NULL, NULL);
Если для буфера существует MDL, вызовите MmGetSystemAddressForMdlSafe , чтобы получить системный адрес буфера, а затем используйте этот адрес для доступа к буферу. (MmGetSystemAddressForMdlSafe можно вызвать по адресу IRQL <= DISPATCH_LEVEL.)
Продолжая работу с предыдущим примером, следующий код получает системный адрес.
if (*ReadMdl != NULL) { ReadAddress = MmGetSystemAddressForMdlSafe(*ReadMdl, NormalPagePriority); if (ReadAddress == NULL) { CallbackData->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; CallbackData->IoStatus.Information = 0; } }
Если MDL для буфера отсутствует, проверка, установлен ли флаг системного буфера для операции с помощью макроса FLT_IS_SYSTEM_BUFFER.
Если макрос FLT_IS_SYSTEM_BUFFER возвращает значение TRUE, операция использует буферный ввод-вывод, и к буферу можно безопасно обращаться по адресу IRQL = DISPATCH_LEVEL.
Если макрос FLT_IS_SYSTEM_BUFFER возвращает значение FALSE, к буферу невозможно получить безопасный доступ по адресу IRQL = DISPATCH_LEVEL. Если подпрограмму обратного вызова после операции можно вызвать в DISPATCH_LEVEL, она должна вызвать FltDoCompletionProcessingWhenSafe , чтобы выполнить операцию, пока она не будет обработана по irQL <= APC_LEVEL. Подпрограмма обратного вызова, на которую указывает параметр SafePostCallbackобъекта FltDoCompletionProcessingWhenSafe , должна сначала вызвать FltLockUserBuffer для блокировки буфера, а затем вызвать MmGetSystemAddressForMdlSafe , чтобы получить системный адрес буфера.
Подпрограмма обратного вызова после операции должна обрабатывать буфер в быстрой операции ввода-вывода следующим образом:
Используйте адрес буфера для доступа к буферу (так как быстрая операция ввода-вывода не может иметь MDL).
Чтобы убедиться, что адрес буфера пользовательского пространства является допустимым, драйвер минифильтра должен использовать подпрограмму , например ProbeForRead или ProbeForWrite, заключая все ссылки на буфер в try/, кроме блоков.
Подпрограмма обратного вызова после операции после операции быстрого ввода-вывода гарантированно будет вызываться в правильном контексте потока.
Подпрограмма обратного вызова после операции после операции ввода-вывода гарантированно будет вызываться в IRQL <= APC_LEVEL, поэтому она может безопасно вызывать такие подпрограммы, как FltLockUserBuffer.
В следующем примере фрагмента кода проверяет системный буфер или флаги быстрого ввода-вывода для операции управления каталогом и при необходимости откладывает обработку завершения.
if (*DirectoryControlMdl == NULL)
{
if (FLT_IS_SYSTEM_BUFFER(CallbackData) || FLT_IS_FASTIO_OPERATION(CallbackData))
{
dirBuffer = CallbackData->Iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer;
}
else
{
// Defer processing until safe.
if (!FltDoCompletionProcessingWhenSafe(CallbackData, FltObjects, CompletionContext, Flags, ProcessPostDirCtrlWhenSafe, &retValue))
{
CallbackData->IoStatus.Status = STATUS_UNSUCCESSFUL;
CallbackData->IoStatus.Information = 0;
}
}
}
Для операций, которые могут быть либо быстрыми, либо на основе IRP, все ссылки на буфер должны быть заключены в try/, за исключением блоков. Хотя вам не нужно заключать эти ссылки для операций на основе IRP, использующих буферизованное ввод-вывод, блоки try/except являются безопасной мерой предосторожности.