Поиск  Пользователи  Правила 
Закрыть
Логин:
Пароль:
Забыли свой пароль?
Войти
 
Страницы: 1
RSS
Фильтр файловой системы (legacy driver): запись в файл.
 
Разрабатывается фильтр файловой системы (не минифильтр!). Появилась необходимость писать в свой лог-файл информацию о перехватываемых IRP_MJ_xxx (в частности: IRP_MJ_CREATE, IRP_MJ_WRITE, IRP_MJ_READ, IRP_MJ_GET_INFORMATION). Чтобы было наглядно:
Код
NTSTATUS IrpHandler_Write(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
   PUCHAR log = ExAllocPool(NonPagedPool, LOG_SIZE);

   // парсим содержимое запроса   
   ParseRequest(log, pDevObj, pIrp);

   // записываем в лог-файл
   WriteLog(log);
   
   // передача пакета нижележащему DO
   ...   
}


Проблема в том, что обычные методы записи (ZwWriteFile) здесь не помогают, поскольку если WriteLog(PUCHAR log) вызывает ZwWriteFile, то получится рекурсия, поскольку ZwWriteFile для записи лога инициирует новый IRP_MJ_WRITE в контексте текущей обработки IrpHandler_Write. По этому данный способ не приемлим.

Покурив документацию DDK, в голову пришла мысль использовать IoBuildSynchronousFsdRequest.
Теперь WriteLog(log) выглядит примерно так:
Код
NTSTATUS WriteLog(PUCHAR log, ULONG size)
{
  PIRP                pIrp;
  PIO_STACK_LOCATION  pIrpSp;
  KEVENT              evt;
  IO_STATUS_BLOCK     ioStatus;
  NTSTATUS            ns;
  LARGE_INTEGER       offset;
  ULONG               allignedSize;
  PFILE_OBJECT        pFileObjectIntern;
  PDEVICE_OBJECT      pDeviceObjectIntern;
  PUCHAR              pLockedBuff;

  // global_pFileObjectLog - глобальная переменная, содержащая указатель на объект файла лога
  // у него (объекта файла) должен быть установлен флаг FILE_OPENED (достаточно не вызывать 
  // ZwClose() после создания файла лога).
  pFileObjectIntern = global_pFileObjectLog;

  // получаем указатель на объект нашего фильтрующего DO
  pDeviceObjectIntern = IoGetRelatedDeviceObject(pFileObjectIntern);

  // извлекаем указатель на нижележащий DO в стеке девайсов
  pDeviceObjectIntern = IoGetLowerDeviceObject(pDeviceObjectIntern);

  // инициализируем евент для ожидания завершения операции
  KeInitializeEvent(&evt, NotificationEvent, FALSE);

  // создаем буфер с учетом выравнивания  
  allignedSize = ALLIGN_UP(size, 512);

  pLockedBuff = ExAllocatePool(NonPagedPool, allignedSize);

  memcpy(pLockedBuff, log, allignedSize);

  offset = 0i64;

  // создаем IRP
  pIrp = IoBuildSynchronousFsdRequest(
      IRP_MJ_WRITE,
      pDeviceObjectIntern,
      pLockedBuff,
      allignedSize,
      &offset,
      &evt,
      &ioStatus
   );

  pIrp->Tail.Overlay.Thread = KeGetCurrentThread();
  pIrp->RequestorMode = KernelMode;
  pIrp->Tail.Overlay.OriginalFileObject = pFileObjectIntern;
  
  pIrpSp = IoGetNextIrpStackLocation(pIrp);
  pIrpSp->FileObject = pFileObjectIntern;
  
  pIrpSp->Parameters.Write.ByteOffset.LowPart = offset.LowPart;
  pIrpSp->Parameters.Write.ByteOffset.HighPart = offset.HighPart;
  pIrpSp->Parameters.Write.Key =0;
  pIrpSp->Parameters.Write.Length = size;

  // пишем в файл
  ns = IoCallDriver( pDeviceObjectIntern, pIrp);
  if (ns == STATUS_PENDING)
  {
    KeWaitForSingleObject(&evt,Executive,KernelMode,FALSE, NULL);
  }
  
  ExFreePool(pLockedBuff);
  
  return ioStaus.Status;
}


Собственно, здесь запрос на запись завершается нормально (STATUS_SUCCESS), если параметры верны.
Дополнительных запосов для моего фильтра при этом не строится.

Теперь, собственно проблема: если операция записи завершена успешно, а при закрытии файла лога данные на диск не сбрасываются. У кого есть какие мысли? Может быть нужно отправить еще какие-то IRP в догонку IRP_MJ_WRITE (к стати, попробовал IRP_MJ_FLUSH_BUFFERS: в результате он иницировал еще IRP_MJ_WRITE для файла лога)
 
Есть следующая мысль: для FILE_OBJECT - структуры файла лога переопределить каким-либо образом связанный с ним DeviceObject.
Объясню: когда мы (в нашем примере) вызываем IoGetRelatedDeviceObject, в результате возвращается наш фильтрующий DO, и I/O Manager при отправке запросов этот момент учитвает (то есть формирует IRP с учетом наличия нашего DO).
 
Самый простой выход из положения это сделать "програмный цикличекский буфер" и скидывать этот буфер на диск, в отдельном потоке, каждые несколько десятков секунд, а то и больше.
В основном перехватчике проверять id треда, и если он равен id пишущего потока, то не писать в лог.

И никакой рекурсии не будет!
Страницы: 1
Читают тему