Поделиться через


Доступ к пользовательским буферам в подпрограмме обратного вызова после операции

Подпрограмма обратного вызова драйвера минифильтра после операции ввода-вывода должна обрабатывать буфер в операции ввода-вывода на основе 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 являются безопасной мерой предосторожности.