Всем доброго дня! Пишу свой собственный файловый фильтр Общая задача на будущее - на лету шифровать данные при записи и расшифровывать при чтении Написал каркас, стал отлаживать в Soft-Ice'e - падает с кодом 0x0000008E Убрал практически всё, оставил лишь процедуру, пропускающую IRP-пакеты дальше, все равно падает. Подскажите, где здесь кроется злосчастный баг? =) Код (Text): // 0x0000008E (0xC0000005, 0x8058DD6A, 0xF7791C14, 0x00000000) #include "ntddk.h" #define NT_DEVICE_NAME L"\\Device\\HarddiskVolume1" #define WIN32_DEVICE_NAME L"\\DosDevices\\HarddiskVolume1" #define MAXPATHLEN 1024 // GLOBALS PDEVICE_OBJECT DriveHookDevices[26]; ULONG DrivesToHook = 0; // typedef struct _FILE_FS_ATTRIBUTE_INFORMATION { ULONG FileSystemAttributes; LONG MaximumComponentNameLength; ULONG FileSystemNameLength; WCHAR FileSystemName[1]; } FILE_FS_ATTRIBUTE_INFORMATION, *PFILE_FS_ATTRIBUTE_INFORMATION; typedef struct { PDEVICE_OBJECT FileSystem; unsigned LogicalDrive; BOOLEAN Hooked; PFILE_FS_ATTRIBUTE_INFORMATION FsAttributes; IO_REMOVE_LOCK RemoveLock; } HOOK_EXTENSION, *PHOOK_EXTENSION; NTKERNELAPI NTSTATUS ZwQueryVolumeInformationFile( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FsInformation, IN ULONG Length, IN FS_INFORMATION_CLASS FsInformationClass ); BOOLEAN HookDrive(IN ULONG Drive, IN PDRIVER_OBJECT DriverObject); NTSTATUS DispatchAny(PDEVICE_OBJECT, PIRP); NTSTATUS CompleteRequest(PIRP, NTSTATUS, ULONG_PTR); VOID UnloadOperation(IN PDRIVER_OBJECT); VOID UnhookDrive(IN ULONG); ULONG HookDeviceSet(IN ULONG,IN PDRIVER_OBJECT); NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING RegistryPath) { NTSTATUS status; int i; for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) { pDriverObject->MajorFunction[i] = DispatchAny; } HookDrive(2, pDriverObject); pDriverObject->DriverUnload = UnloadOperation; return STATUS_SUCCESS; } BOOLEAN HookDrive(IN ULONG Drive, IN PDRIVER_OBJECT DriverObject) { IO_STATUS_BLOCK ioStatus; HANDLE ntFileHandle; OBJECT_ATTRIBUTES objectAttributes; PDEVICE_OBJECT fileSysDevice; PDEVICE_OBJECT hookDevice; UNICODE_STRING fileNameUnicodeString; ULONG fileFsAttributesSize; PFILE_FS_ATTRIBUTE_INFORMATION fileFsAttributes; NTSTATUS status; ULONG i; PFILE_OBJECT fileObject; WCHAR filename[]=L"\\DosDevices\\A:\\"; PHOOK_EXTENSION hookExtension; ULONG LockTag; LockTag='Neko'; if (Drive >=26) return FALSE; if (DriveHookDevices[Drive] == NULL) // если диск ещё не захвачен { filename[12]=(CHAR)('A'+Drive); // настраиваем имя диска RtlInitUnicodeString(&fileNameUnicodeString,filename); InitializeObjectAttributes(&objectAttributes, &fileNameUnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL); status=ZwCreateFile(&ntFileHandle, SYNCHRONIZE|FILE_ANY_ACCESS, &objectAttributes, &ioStatus, NULL, 0, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT|FILE_DIRECTORY_FILE, NULL, 0); if(!NT_SUCCESS(status)) { return FALSE; } status=ObReferenceObjectByHandle(ntFileHandle, FILE_READ_DATA, NULL, KernelMode, &fileObject, NULL); if(!NT_SUCCESS(status)) { ZwClose(ntFileHandle); return FALSE; } fileSysDevice=IoGetRelatedDeviceObject(fileObject); if(!fileSysDevice) { ObDereferenceObject(fileObject); ZwClose(ntFileHandle); return FALSE; } // проверяем, не захвачен ли for(i=0;i<26;i++) { if(DriveHookDevices[i]==fileSysDevice) { // Если уже захватили, ассоциируем с другими дисками, указывающими на тот же драйвер ObDereferenceObject(fileObject); ZwClose(ntFileHandle); DriveHookDevices[Drive]=fileSysDevice; return TRUE; } } status=IoCreateDevice(DriverObject, sizeof(HOOK_EXTENSION), NULL, fileSysDevice->DeviceType, fileSysDevice->Characteristics, FALSE, &hookDevice); if(!NT_SUCCESS(status)) { ObDereferenceObject(fileObject); ZwClose(ntFileHandle); return FALSE; } hookDevice->Flags &= ~DO_DEVICE_INITIALIZING; hookDevice->Flags |= (fileSysDevice->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO)); hookExtension=hookDevice->DeviceExtension; hookExtension->LogicalDrive='A'+Drive; hookExtension->FileSystem=fileSysDevice; hookExtension->Hooked=TRUE; status=IoAttachDeviceByPointer(hookDevice,fileSysDevice); if(!NT_SUCCESS(status)) { ObDereferenceObject(fileObject); ZwClose(ntFileHandle); return FALSE; } // проверяем, не NTFS ли это fileFsAttributesSize=sizeof(FILE_FS_ATTRIBUTE_INFORMATION)+MAXPATHLEN; hookExtension->FsAttributes=(PFILE_FS_ATTRIBUTE_INFORMATION)ExAllocatePool(NonPagedPool,fileFsAttributesSize); if(KeGetCurrentIrql()==PASSIVE_LEVEL) { IoInitializeRemoveLock(&hookExtension->RemoveLock,LockTag,0,0); } if(hookExtension->FsAttributes && !NT_SUCCESS( ZwQueryVolumeInformationFile(ntFileHandle, &ioStatus, hookExtension->FsAttributes, fileFsAttributesSize, FileFsAttributeInformation))) { // В случае провала мы не получаем аттрибуты ExFreePool(hookExtension->FsAttributes); hookExtension->FsAttributes=NULL; } // обновляем список захваченных устройств, добавляя этот ObDereferenceObject(fileObject); ZwClose(ntFileHandle); DriveHookDevices[Drive]=hookDevice; } else // если диск уже захвачен { hookExtension = DriveHookDevices[Drive]->DeviceExtension; hookExtension->Hooked=TRUE; } return TRUE; } NTSTATUS DispatchAny(PDEVICE_OBJECT fido, PIRP pIrp) { NTSTATUS status; PHOOK_EXTENSION pdx = (PHOOK_EXTENSION) fido->DeviceExtension; //status = IoAcquireRemoveLock(&pdx->RemoveLock, pIrp); //if (!NT_SUCCESS(status)) //return CompleteRequest(pIrp, status, 0); IoSkipCurrentIrpStackLocation(pIrp); status = IoCallDriver(pdx->FileSystem, pIrp); //IoReleaseRemoveLock(&pdx->RemoveLock, pIrp); return status; } NTSTATUS CompleteRequest(PIRP Irp, NTSTATUS status, ULONG_PTR info) { Irp->IoStatus.Status = status; Irp->IoStatus.Information = info; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } VOID UnloadOperation(IN PDRIVER_OBJECT pDriverObject) { PDEVICE_OBJECT deviceObject = pDriverObject->DeviceObject; PHOOK_EXTENSION pDiskDeviceExtension=(PHOOK_EXTENSION)pDriverObject->DeviceObject->DeviceExtension; IoDetachDevice(pDiskDeviceExtension->FileSystem); IoDeleteDevice( deviceObject ); return; } Заранее огромное спасибо всем откликнувшимся
Сразу ещё один вопрос - перед аттачем к диску я активирую флаг DO_BUFFERED_IO, поскольку в дальнейшем буду использовать данный метод работы с диском hookDevice->Flags |= (fileSysDevice->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO)); Но в утилите DeviceTree показано, что мои локальные имеют параметры Interpreted Device Flag: BUS_ENUMERATE_NAME DEVICE_HAS_NAME DIRECT_IO (на системном ещё SYSTEM_BOOT_PARTITION) Если я правильно понял, флаги на создаваемом устройстве и к которому подключаешься должны быть одинаковые. Где ошибка?
Первый вопрос уже разрешил - надо было использовать \\Device\\HarddiskVolume1 Второй до сих пор актуален
Ты пишешь, что хочешь себе флаги как у того устройства либо buffered i/o либо direct i/o. у того устройства direct очевидно. чтоты хочешь еще? юзай директ.
Спасибо. Скажите, правильно ли я всё понял: при перехвате IRP_MJ_READ: a) при BUFFERED_IO мы получаем указатель на данные через Buf = (PUCHAR)(pIrp->AssociatedIrp.SystemBuffer); b) при DIRECT_IO - через Buf = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, HighPagePriority); при этом в обоих случаях смещение в файле, в который осуществляется запись, хранится в p_IO_STK->Parameters.Read.ByteOffset.QuadPart? Таким образом, если драйвер перехватывает и чтение и запись с целью дописывания в начало каждого файла сигнатуры, скажем, из 4-х байт, достаточно увеличивать в процедурах перехвата данный параметр на 4, что при BUFFERED_IO, что при DIRECT_IO? (Разумеется, только для новых или уже обработанных файлов)
Небольшой вопрос по азам программирования драйверов: я правильно понял, что если вызываешь IoCompleteRequest, то Irp передается вышестоящим драйверам, и вниз уже не попадает? Также интересует, обязательно ли должен быть вызов данной функции в конце своей IoCompletion routine, если ты не самый высокий драйвер (для тех обязательно, как написано в DDK)?
в конце IoCompletion - зачем? IoCompleteRequest как раз сама дергает completion routines. если ты возвращаешь саксесс, тогда пакет и так завершится. есил ты возвращаешь море процессинг реквайред - тогда потом уже завершай. Вниз уже не попадает, но драйверв вышестоящие при чем тут? IoCompleteRequest в двух словах дергает IoCompletion Routines, копирует буффера там какие надо и освобождет память. Это если очень грубо. То есть если есть цепочка драйверов, то все фильтры должны вызывать IoCallDriver и передавать ниже, либо IoCompleteRequest и сами завершить пакет, а нижестоящий только complete может.
IoCompleteRequest indicates the caller has completed all processing for a given I/O request and is returning the given IRP to the I/O Manager.When a driver has finished all processing for a given IRP, it calls IoCompleteRequest. The I/O Manager checks the IRP to determine whether any higher-level drivers have set up an IoCompletion routine for the IRP. If so, each IoCompletion routine is called, in turn, until every layered driver in the chain has completed the IRP. (с) DDK Если судить по этому копипасту из DDK, IoCompleteRequest говорит, что данный пакет обработан, передавай его обратно наверх, проходя все completion routines других драйверов, которые там расставлены (c STATUS_SUCCESS естественно) STATUS_MORE_PROCESSING_REQUIRED возвращается, если с пакетом больше ничего не надо делать, т.е. вышестоящие и нижестоящие его не получат. Если вернуть STATUS_SUCCESS, пакет irp пойдёт дальше при том, что вызов процедуры IoCompleteRequest и возврат STATUS_SUCCESS передадут пакет вышестоящим. Единственное, что я не понял, STATUS_MORE_PROCESSING_REQUIRED надо возвращать процедуре, вызывающей IoCompleteRequest, или устанавливать его в Irp->IoStatus.Status перед вызовом IoCompleteRequest?
В нескольких примерах по написанию драйверов-фильтров видел, например, при перехвате IRP_MJ_CREATE завершение процедуры обработки с помощью return CompleteRequest(Irp, STATUS_SUCCESS, 0); ,где NTSTATUS CompleteRequest(PIRP Irp, NTSTATUS status, ULONG_PTR info) { Irp->IoStatus.Status = status; Irp->IoStatus.Information = info; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } Разве в этом случае пакет не остановит свое движение вниз по стеку драйверов и не начнет двигаться обратно вверх, проходя через все IoCompletion'ы, установленные ранее? Я думал, более логично использовать IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(((PHOOK_EXTENSION)DeviceObject->DeviceExtension)->FileSystem,Irp); чтобы пакет достиг самого нижнего драйвера носителя (в моём случае диска)
Прочитал И всё-таки можно узнать, почему в драйверах-фильтрах для обработки не своих пакетов повсеместно используют return CompleteRequest(pIrp, STATUS_SUCCESS, 0); где NTSTATUS CompleteRequest(PIRP Irp, NTSTATUS status, ULONG_PTR info) { Irp->IoStatus.Status = status; Irp->IoStatus.Information = info; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } Ведь это не позволит пройти пакету дальше, он вернётся по стеку драйверов обратно отправителю, пройдя все установленные ранее IoCompletion, и не достигнет устройства?
Фильтры могут обрабатывать данные, идущие как до устройства, так и от него Так я всё-таки прав или нет, в данном случае данные устройства не достигнут? IRP к нему ведь придёт до того, как дойдёт до устройства, а ему он дальше пакет получается не передаст? Кстати, как вообще регулировать, каким по счету в стеке драйверов идёт твой фильтр?