СОДЕРЖАНИЕ 1. Краткое описание .............................................................................................. 2 2. Реализация драйвер фильтров.......................................................................... 3 2.1. Драйвер фильтр клавиатуры ............................................................... 3 2.2. Подключение к функциональному драйверу .................................... 5 2.3. Механизм работы стека клавиатуры с установленным фильтром ......................................................................... 7 2.4. Алгоритм работы KbFilter_ServiceCallback ...................................... 9 2.5. Драйвер фильтр мыши......................................................................... 10 3. Управляющее приложение. .............................................................................. 11 4. Анализ клавиатурного почерка........................................................................ 12 5. Текст программы ............................................................................................... 14 5.1 Драйвер фильтр клавиатуры ................................................................ 14 5.2 Управляющее приложение ................................................................... 36 5.2 Драйвер фильтр мыши.......................................................................... 42 6. Литература ......................................................................................................... 43 1. КРАТКОЕ ОПИСАНИЕ Система позволяет выявить подмену идентифицированного пользователя, проводя непрерывный мониторинг клавиатурного почерка. В её состав входят два драйвер фильтра клавиатуры и мыши, а так же управляющее приложение. Система может работать в трёх режимах: обучение, анализ, блокировка: Режим обучение – определяются эталонные характеристики клавиатурного почерка Режим анализ – система сравнивает эталонные характеристиками с вновь введёнными, после чего переходит либо остаётся в режиме анализа, либо переходит в режим блокировки. Режим блокировки – в этом режиме блокированы клавиатура и мышь, система ожидает ввода пароля на разблокировку. Управляющее приложение предназначено для просмотра временных характеристик, их сохранения и перевода системы в режим анализ. Рис. 1. Переход между режимами работы системы 2 2. РЕАЛИЗАЦИЯ ДРАЙВЕР ФИЛЬТРОВ 2.1. Драйвер фильтр клавиатуры Cвязь клавиатуры с шиной осуществляет микроконтроллер клавиатуры Intel 8042 (или совместимый с ним). На современных компьютерах он интегрирован в чипсет материнской платы. Все клавиатуры являются PS/2-совместимыми или клавиатурами, подключаемыми через интерфейс USB. В PS/2-совместимом режиме микроконтроллер клавиатуры также связывает с шиной и PS/2-совместимую мышь. Всем этим управляет функциональный драйвер i8042prt (Intel 8042 Port Driver). Драйвер i8042prt создает два безымянных объекта "устройство" и подключает один к стеку клавиатуры. Поверх драйвера i8042prt, точнее поверх его устройств, располагаются именованные объекты "устройство" драйверов Kbdclass и Mouclass. Имя "KeyboardClass" является базовым, и к нему добавляются индексы (0, 1 и т.д.). Базовое имя хранится в параметре реестра HKLM\SYSTEM\CurrentControlSet\Services\Kbdclass\Parameters\KeyboardDeviceBaseName Драйверы Kbdclass и Mouclass являются так называемыми драйверами класса (class drivers) и реализуют общую функциональность для всех типов клавиатур и мышей, т.е. для всего класса этих устройств. Оба эти драйвера устанавливаются как высокоуровневые драйверы. Стек клавиатуры обрабатывает несколько типов. P Запросы типа IRP_MJ_READ несут в себе коды клавиш. Генератором этих IRP является поток необработанного ввода RawInputThread системного процесса csrcc.exe. Этот поток открывает объект "устройство" драйвера класса клавиатуры для эксклюзивного использования и с помощью функции ZwReadFile направляет ему IRP типа IRP_MJ_READ. Получив IRP, драйвер Kbdclass, используя макрос IoMarkIrpPending, отмечает его как ожидающий завершения (pending), ставит в очередь и возвращает STATUS_PENDING. Потоку необработанного ввода придется ждать завершения IRP. То есть RawInputThread получает клавиатурные события как вызов асинхронной процедуры. Рис. 2. Схема стека драйверов клавиатуры и мыши 3 Рис. 3. Схема стека драйверов клавиатуры и мыши после установки системы 4 2.2. Подключение к функциональному драйверу Для подключения к функциональному драйверу клавиатуры (I8042ptr), высокоуровневый драйвер фильтр kbdclass посылает внутренний запрос IRP_MJ_INTERNAL_CONTROL . Все нижележащие драйверы имеют процедуру обработки таких внутренних запросов. Внутренний запрос посылается с параметром IO_CTL_INTERNAL_KEYBOARD_CONNECT. (заносится в IrpStack– >Parametrs.DeviceIoControl.IoControlCode). С этим запросом kbdclass посылает структуру CONNECT_DATA . (заносится в IrpStack– >Parametrs.DeviceIoControl.Type3bufer) typedef struct _CONNECT_DATA { IN PDEVICE_OBJECT ClassDeviceObject; IN PVOID ClassService; } CONNECT_DATA, *PCONNECT_DATA; Так как инициатором этого запроса является kbdclass, то он соответственно и будет заполнять первым эту структуру. В ClassDeviceObject занесётся адрес объекта, который создается в драйвере kbdclass. В ClassService занесётся адрес функции KeyboardClassServiceCallback, которую будут вызывать IRP пакеты по возвращению. (когда встречается функция IoCompileIrp). Получив такой запрос на соеденение kbfilter (если установлен) или функциональный драйвер I8042ptr выполняет следующее: 1. Сохраняет полученную структуру CONNECT_DATA 2. Переопределяет структуру: заносит в ClassDeviceObject указатель на свой объект устройство, в ClassService указатель на свою процедуру обратного вызова. 3. Передаёт IRP дальше по стеку или завешает запрос. 5 Рис. 4. Схема подключения к функциональному драйверу 6 2.3. Механизм работы стека клавиатуры с установленным фильтром Когда будет нажата или отпущена клавиша, контроллер клавиатуры выработает аппаратное прерывание. Его обработчик вызовет I8042KeyboardInterruptService, которая прочитает из внутренней очереди контроллера клавиатуры необходимые данные. Так как обработка аппаратного прерывания происходит на повышенном IRQL, ISR делает только самую неотложную работу и ставит в очередь вызов отложенной процедуры (Deferred Procedure Call, DPC). DPC работает при IRQL = DISPATCH_LEVEL. Когда IRQL понизится до DISPATCH_LEVEL, система вызовет процедуру I8042KeyboardIsrDpc, которая вызовет зарегистрированную драйвером фильтром kbfilter процедуру обратного вызова KbFilter_ServiceCallback. (также выполняется на IRQL = DISPATCH_LEVEL). В этой процедуре произойдёт заполнение структуры KEY_INFO: typedef struct _KEY_INFO{ ULONG MakeCode; // код клавиши ULONG CountUp; // количество отжатий ULONGLONG LastDown; // последнее нажатие ULONGLONG LastUp; // последнее отжатие ULONGLONG LastLastUp; // предпоследнее отжатие ULONG LastFromDownToUp; // посленее нажатие-отпуск ULONG MeanFromDownToUp; // среднее нажатие отпуск ULONGLONG LastBetweenTwoUp; // последне между двумя отжатиями ULONG MeanBetweenTwoUp; // среднее между двумя отжатиями ULONG ToOtherBreak; // время до другой ULONG MeanToOtherBreak; // среднее время до другой ULONG KeyVector[NUMBER_OF_KEYS]; // среднее до определённой } KEY_INFO,*PKEY_INFO; Затем произойдёт вызов процедуры обратного вызова KeyboardClassServiceCallback в драйвере Kbdclas. KeyboardClassServiceCallback извлечет из своей очереди ожидающий завершения IRP, заполнит структуру KEYBOARD_INPUT_DATA, несущую всю необходимую информацию о нажатиях/отпусканиях клавиш и завершит IRP. Поток необработанного ввода пробуждается, обрабатывает полученную информацию и вновь посылает IRP типа IRP_MJ_READ драйверу класса, который опять ставится в очередь до следующего нажатия/отпускания клавиши. Таким образом, у стека клавиатуры всегда есть, по крайней мере, один, ожидающий завершения IRP и находится он в очереди драйвера Kbdclass. "Мышиный" стек ведет себя подобным же образом. 7 Рис.5. Схема работы стека клавиатуры с установленным фильтром 8 2.4. Алгоритм работы KbFilter_ServiceCallback Процедура KbFilter_ServiceCallback вызывается всякий раз когда происходят события, связанные с клавиатурой имеет следующий алгоритм: Рис. 6. Схема алгоритма работы KbFilter_ServiceCallback 9 2.5. Драйвер фильтр мыши Драйвер фильтр мыши полностью аналогичен описанному выше драйвер фильтру клавиатуры. Взаимодействие между драйверами осуществляется, как показано на рисунке. Рис. 7.Схема взаимодействия м между драйвер фильтрами 10 3. УПРАВЛЯЮЩЕЕ ПРИЛОЖЕНИЕ. Управляющее приложение предназначено для управления драйвер фильтром клавиатуры. Оно отображает все временные характеристики по каждой клавише на экране. Сохраняет сформированный клавиатурный почерк. Так же оно предназначено для запуска самой системы защиты. Рис. 8. Вид управляющего приложения Рис. 9. Вид окна «О программе» 11 4. АНАЛИЗ КЛАВИАТУРНОГО ПОЧЕРКА Для анализа клавиатурного почерка используется матрица межклавишных взаимодействий, которая имеет вид: t11 t21 ... t n1 t12 t22 ... tn2 ... ... ... ... t1n t2n ... tnn где n – количество клавиш на клавиатуре, tij – время нажатия от i-й клавиши на j-ю tij = keyinf[i].keyvector[j] Каждые 30 нажатий клавиш, составляется вектор времён, имеющий 29 элементов. Если система работает в режиме анализа, то для этого вектора составляется эталонный вектор из эталонной матрицы межклавишных взаимодействий. Для эталонного вектора определяется доверительный интервал. Если все значения проверяемого вектора входят в доверительный интервал с определённой точностью, то клавиатурный почерки совпадают. Получены при помощи программы результаты представлены в таблице 1. Таблица 1. Клавиша а н а л и з пробел к л а в и а т у р н о г о пробел п о ч е р к а Фраза введённая 50 раз 1676042 1798288 1364853 2439249 3214778 1787140 1605824 3153753 2030067 1495901 1876526 2339301 1848360 3289886 1947722 2886572 881932 1933248 2712689 1049556 1812371 2182239 2095982 1966695 2162288 1148330 1989579 Выборка по вектору межклавишного взаимодействия Нижняя граница Верхняя граница Фраза доверительного доверительного введённая интервала интервала 1 раз 1176042 1298288 864853 1939249 2714778 1287140 1105824 2653753 1530067 995901 1376526 1839301 1348360 2789886 1447722 2386572 381932 1433248 2212689 549556 1312371 1682239 1595982 1466695 1662288 648330 1489579 2176042 2298288 1864853 2939249 3714778 2287140 2105824 3653753 2530067 1995901 2376526 2839301 2348360 3789886 2447722 3386572 1381932 2433248 3212689 1549556 2312371 2682239 2595982 2466695 2662288 1648330 2489579 801152 2103024 1101584 2002880 4907056 1101584 1502160 1802592 1902736 1402016 1802592 2002880 1602304 4005760 1902736 2804032 801152 1902736 2403456 801152 1802592 2002880 2203168 1902736 1702448 901296 2002880 Фраза введенная другим человеком 2203168 2603744 3304752 1502160 2203168 2103024 3705328 2303312 2203168 2002880 4005760 1602304 1902736 2203168 1802592 1802592 1502160 2103024 1502160 1702448 2603744 2403456 3905616 1402016 1702448 2002880 3104464 12 Рис. 10. Фраза введённая 50 раз Рис. 11. Фраза введённая 1 раз Рис. 12. Фраза введённая другим человеком 13 5. ТЕКСТ ПРОГРАММЫ 5.1 Драйвер фильтр клавиатуры #include "kbfiltr.h" #pragma #pragma #pragma #pragma #pragma #pragma #pragma #pragma alloc_text alloc_text alloc_text alloc_text alloc_text alloc_text alloc_text alloc_text (INIT, (PAGE, (PAGE, (PAGE, (PAGE, (PAGE, (PAGE, (PAGE, DriverEntry) FilterAddDevice) FilterDispatchPnp) FilterUnload) FilterInternIoCtl) FilterCreateControlObject) FilterDeleteControlObject) FilterDispatchIo) NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /* Точка входа инициализации драйвера Вызывается непосредстенно системой в/в Аргументы: DriverObject - указатель на объект драйвер RegistryPath - указатель на unicode string пути в регистре к подразделу драйвера Возвращаемое значение: STATUS_SUCCESS если успешно, STATUS_UNSUCCESSFUL в противном случае. */ { NTSTATUS ULONG PDRIVER_DISPATCH status = STATUS_SUCCESS; ulIndex; * dispatch; UNREFERENCED_PARAMETER (RegistryPath); //все запросы будут приводить к вызову функции FilterPass, //которая переадресует запросы нижним драйверам в стеке. for (ulIndex = 0, dispatch = DriverObject->MajorFunction; ulIndex <= IRP_MJ_MAXIMUM_FUNCTION; ulIndex++, dispatch++) { *dispatch = FilterPass; } //исключение составят функции, зарегестрированные ниже //регестрация функций, которые будет обрабатывать объект в стеке DriverObject->MajorFunction[IRP_MJ_PNP] DriverObject->MajorFunction[IRP_MJ_POWER] DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] DriverObject->DriverExtension->AddDevice DriverObject->DriverUnload = = = = = FilterDispatchPnp; FilterDispatchPower; FilterInternIoCtl; FilterAddDevice; FilterUnload; //регестрация функций, которые будет обрабатывать объект устройство управленя DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverObject->MajorFunction[IRP_MJ_CLEANUP] = DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = FilterDispatchIo; // Инициализация FastMutex для понижения уровня IRQL при // создании и удалении объекта устройство управления ExInitializeFastMutex(&ControlMutex); return status; } 14 NTSTATUS FilterAddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject ) /* Созданию объекта устройства, подготовка устройства к работе Аргументы: DeviceObject - указатель на объект данного драйвера PhysicalDeviceObject - указатель на объект физического устройства, созданного родительским (шинным) драйвером Возвращаемое значение: STATUS_SUCCESS или код ошибки */ { NTSTATUS PDEVICE_OBJECT PDEVICE_EXTENSION ULONG PKEY_INFO USHORT i; PAGED_CODE (); status = STATUS_SUCCESS; deviceObject = NULL; deviceExtension; deviceType = FILE_DEVICE_UNKNOWN; KeyInfo; // Создание объекта устройство фильтр status = IoCreateDevice (DriverObject, sizeof (DEVICE_EXTENSION), NULL, // без имени deviceType, FILE_DEVICE_SECURE_OPEN, FALSE, &deviceObject); if (!NT_SUCCESS (status)) { return status; } deviceExtension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension; deviceExtension->Type = DEVICE_TYPE_FIDO; deviceExtension->NextLowerDriver = IoAttachDeviceToDeviceStack ( deviceObject, PhysicalDeviceObject); // при не удачном присоеденении к стеку, возвращаем STATUS_UNSUCCESSFUL if(NULL == deviceExtension->NextLowerDriver) { IoDeleteDevice(deviceObject); return STATUS_UNSUCCESSFUL; } deviceObject->Flags |= deviceExtension->NextLowerDriver->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE ); deviceObject->DeviceType = deviceExtension->NextLowerDriver->DeviceType; deviceObject->Characteristics = deviceExtension->NextLowerDriver->Characteristics; deviceExtension->Self = deviceObject; //Выделяем память под массив структур KEY_INFO, хранящих информацию о каждой клавише //обнуляем все её поля и сохраняем указатель в поле deviceExtension->KeyInfo KeyInfo=(PKEY_INFO)ExAllocatePool(PagedPool,NUMBER_OF_KEYS*sizeof(KEY_INFO)); ResetKeyInfo(KeyInfo); deviceExtension->KeyInfo=KeyInfo; for (i=0;i<NUMBER_OF_CHEK_KEYS;i++) { KeyList[i]=0; } 15 //инициализируем remove lock для веденя учёта IRP, чтобы не удалить //объект устройство, когда запрос в/в в стеке еще не завершён IoInitializeRemoveLock ( &deviceExtension->RemoveLock , //указатель на структуру IO_REMOVE_LOCK POOL_TAG, //#define POOL_TAG 'arty' 1, //максимальное количество минут удержки 100); //макс число незавершённых захватов блокировки // Устанавливаем статус Filter DO INITIALIZE_PNP_STATE(deviceExtension); deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; return STATUS_SUCCESS; } NTSTATUS FilterPass ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /* Стандартная функция обработки Если драйвер не обрабатывает запрс IRP, то просто посылает его вниз по стеку Аргументы: DeviceObject - указатель на объект данного драйвера Irp - указатель на пакет запроса ввода/вывода Возвращаемое значение: NT status code */ { PDEVICE_EXTENSION NTSTATUS status; deviceExtension; deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; //увеличиваем счётчик захватов Remove Lock status = IoAcquireRemoveLock (&deviceExtension->RemoveLock, Irp); //завершаем IRP запрос если ошибка при увелечении счётчика if (!NT_SUCCESS (status)) { Irp->IoStatus.Status = status; IoCompleteRequest (Irp, IO_NO_INCREMENT); return status; } //иначе передаем запрос нижележащему драйверу по стеку IoSkipCurrentIrpStackLocation (Irp); status = IoCallDriver (deviceExtension->NextLowerDriver, Irp); //уменьшаем счётчик захватов Remove Lock IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); return status; } NTSTATUS FilterDispatchPnp ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /* Функция диспетчеризации PnP. Аргументы: DeviceObject - указатель на объект данного драйвера Irp - указатель на пакет запроса ввода/вывода Возвращаемое значение: 16 NT status code */ { PDEVICE_EXTENSION PIO_STACK_LOCATION NTSTATUS KEVENT deviceExtension; irpStack; status; event; PAGED_CODE(); deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; irpStack = IoGetCurrentIrpStackLocation(Irp); //Стандартная проверка счётчика Remove Lock status = IoAcquireRemoveLock (&deviceExtension->RemoveLock, Irp); if (!NT_SUCCESS (status)) { Irp->IoStatus.Status = status; IoCompleteRequest (Irp, IO_NO_INCREMENT); return status; } switch (irpStack->MinorFunction) { case IRP_MN_START_DEVICE: // Устройство запущено // Неможем трогать устройство пока не // перенаправим запрос нижележащим драйверам KeInitializeEvent(&event, NotificationEvent, FALSE); //Копируем содержимое ячейки стека IRP для текущего //драйвера в ячейку стека для нижестоящего драйвера IoCopyCurrentIrpStackLocationToNext(Irp); //Регестрируем процедуру завершения IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) FilterStartCompletionRoutine, &event, TRUE, TRUE, TRUE); status = IoCallDriver(deviceExtension->NextLowerDriver, Irp); //Перед таем как запустить устройство нужно дождаться пока //стартуют низкоуровневые драйверы if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = Irp->IoStatus.Status; } if (NT_SUCCESS (status)) { //если NT_SUCCESS значит внизу всё нормально, устанавливаем статус SET_NEW_PNP_STATE(deviceExtension, Started); if (deviceExtension->NextLowerDriver->Characteristics & FILE_REMOVABLE_MEDIA) { DeviceObject->Characteristics |= FILE_REMOVABLE_MEDIA; } if(Stopped != deviceExtension->PreviousPnPState) { // запущено первый раз FilterCreateControlObject(DeviceObject); } } Irp->IoStatus.Status = status; IoCompleteRequest (Irp, IO_NO_INCREMENT); IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); return status; case IRP_MN_REMOVE_DEVICE: //Если существует KeyInfoTemp (переходили в режим анализ), отчищаем if (deviceExtension->KeyInfoTemp)ExFreePool(deviceExtension->KeyInfoTemp); //Освобождаем память под KeyInfo ExFreePool(deviceExtension->KeyInfo); 17 //Ждём завершения всех незавершенных IRP IoReleaseRemoveLockAndWait(&deviceExtension->RemoveLock, Irp); IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(deviceExtension->NextLowerDriver, Irp); SET_NEW_PNP_STATE(deviceExtension, Deleted); FilterDeleteControlObject(); IoDetachDevice(deviceExtension->NextLowerDriver); IoDeleteDevice(DeviceObject); return status; case IRP_MN_QUERY_STOP_DEVICE: SET_NEW_PNP_STATE(deviceExtension, StopPending); status = STATUS_SUCCESS; break; case IRP_MN_CANCEL_STOP_DEVICE: if(StopPending == deviceExtension->DevicePnPState) { RESTORE_PREVIOUS_PNP_STATE(deviceExtension); } status = STATUS_SUCCESS; // We must not fail this IRP. break; case IRP_MN_STOP_DEVICE: SET_NEW_PNP_STATE(deviceExtension, Stopped); status = STATUS_SUCCESS; break; case IRP_MN_QUERY_REMOVE_DEVICE: SET_NEW_PNP_STATE(deviceExtension, RemovePending); status = STATUS_SUCCESS; break; case IRP_MN_SURPRISE_REMOVAL: SET_NEW_PNP_STATE(deviceExtension, SurpriseRemovePending); status = STATUS_SUCCESS; break; case IRP_MN_CANCEL_REMOVE_DEVICE: if(RemovePending == deviceExtension->DevicePnPState) { RESTORE_PREVIOUS_PNP_STATE(deviceExtension); } status = STATUS_SUCCESS; break; default: status = Irp->IoStatus.Status; break; } // Посылаем IRP дальше и забываем про него Irp->IoStatus.Status = status; IoSkipCurrentIrpStackLocation (Irp); status = IoCallDriver (deviceExtension->NextLowerDriver, Irp); IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); return status; } 18 NTSTATUS FilterStartCompletionRoutine( IN PDEVICE_OBJECT IN PIRP IN PVOID DeviceObject, Irp, Context ) /* Функция завершения, используется когда вызываются нижележащие объекты устройства Аргументы: DeviceObject - указательна объект устройство Irp - указатель на PnP Irp. Context - NULL Возвращаемое значение: NT status code */ { PKEVENT event = (PKEVENT)Context; UNREFERENCED_PARAMETER (DeviceObject); // если нижележащий драйвер не вернул STATUS_PENDING // мы не должны отмечать событие if (Irp->PendingReturned == TRUE) { KeSetEvent (event, IO_NO_INCREMENT, FALSE); } // функция диспетчеризации вызовит IoCompleteRequest return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS FilterDispatchPower( IN PDEVICE_OBJECT IN PIRP DeviceObject, Irp ) /* Функция обработки IRP электропитания. Аргументы: DeviceObject - указательна объект устройство Irp - указатель на пакет запроса. Возвращаемое значение: NT status code */ { PDEVICE_EXTENSION deviceExtension; NTSTATUS status; //стандартная проверка RemoveLock deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; status = IoAcquireRemoveLock (&deviceExtension->RemoveLock, Irp); if (!NT_SUCCESS (status)) { Irp->IoStatus.Status = status; PoStartNextPowerIrp(Irp); IoCompleteRequest (Irp, IO_NO_INCREMENT); return status; } //просто передаём запрос вниз PoStartNextPowerIrp(Irp); IoSkipCurrentIrpStackLocation(Irp); status = PoCallDriver(deviceExtension->NextLowerDriver, Irp); IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); return status; } 19 VOID FilterUnload( IN PDRIVER_OBJECT DriverObject ) /* Функция выгрузки драйвера освобаждаем все ресурсы, выделенные в DriverEntry Аргументы: DeviceObject - указательна объект устройство Возвращаемое значение: VOID. */ { PAGED_CODE (); // устанавливаем DeviceObject в NULL ASSERT(DriverObject->DeviceObject == NULL); return; } NTSTATUS FilterCreateControlObject( IN PDEVICE_OBJECT ) /* DeviceObject Функция создания устройства управления Аргументы: DeviceObject - указательна объект устройство Возвращаемое значение: NT status code */ { UNICODE_STRING ntDeviceName; UNICODE_STRING symbolicLinkName; PCONTROL_DEVICE_EXTENSION deviceExtension; NTSTATUS status = STATUS_UNSUCCESSFUL; PAGED_CODE(); // понижаем уровень IRQL, IoCreateDeviceSecure и IoCreateSymbolicLink // должны быть вызваны на уровне PASSIVE_LEVEL ExAcquireFastMutexUnsafe(&ControlMutex); // если это первый экземпляр устройства, то созжаём объект управления if(1 == ++InstanceCount) { RtlInitUnicodeString(&ntDeviceName, NTDEVICE_NAME_STRING); RtlInitUnicodeString(&symbolicLinkName, SYMBOLIC_NAME_STRING); status = IoCreateDevice(DeviceObject->DriverObject, sizeof(CONTROL_DEVICE_EXTENSION), &ntDeviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &ControlDeviceObject); if (NT_SUCCESS( status )) { ControlDeviceObject->Flags |= DO_BUFFERED_IO; status = IoCreateSymbolicLink( &symbolicLinkName, &ntDeviceName ); if ( !NT_SUCCESS( status )) { IoDeleteDevice(ControlDeviceObject); 20 goto End; } deviceExtension = deviceExtension->Type = deviceExtension->ControlData= deviceExtension->Deleted = deviceExtension->StackObject= deviceExtension->MouseLock= ControlDeviceObject->DeviceExtension; DEVICE_TYPE_CDO; NULL; FALSE; DeviceObject; FALSE; //в начале мышь работает deviceExtension->ModeFlag= MODE_EDUCATION; режиме обучение ControlDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; //начинаем работу в }else { DbgPrint("IoCreateDevice failed %x\n", status); } } End: ExReleaseFastMutexUnsafe(&ControlMutex); return status; } VOID FilterDeleteControlObject( ) /* Функция удаления устройства управления Возвращаемое значение: VOID */ { UNICODE_STRING PCONTROL_DEVICE_EXTENSION symbolicLinkName; deviceExtension; PAGED_CODE(); //захватываем быстрый мьютекс для понижения irql ExAcquireFastMutexUnsafe (&ControlMutex); // если это последний экземпляр устройства то удаляем controlobject if(!(--InstanceCount) && ControlDeviceObject) { RtlInitUnicodeString(&symbolicLinkName, SYMBOLIC_NAME_STRING); deviceExtension = ControlDeviceObject->DeviceExtension; deviceExtension->Deleted = TRUE; IoDeleteSymbolicLink(&symbolicLinkName); IoDeleteDevice(ControlDeviceObject); ControlDeviceObject = NULL; } ExReleaseFastMutexUnsafe (&ControlMutex); } NTSTATUS FilterDispatchIo( IN PDEVICE_OBJECT IN PIRP ) /* DeviceObject, Irp Routine Description: Функция обаботки IRP не проходящих через стек определяем входне значение объекта устройство если это устройство управления, обрабатываем IRP и завершаем если это устройство в стеке, передаем IRP вниз Аргументы: 21 DeviceObject - указательна объект устройство Irp - указатель на пакет запроса. Возвращаемое значение: NT status code */ { PIO_STACK_LOCATION NTSTATUS PCOMMON_DEVICE_DATA ULONG irpStack; status; commonData; outBufLength; PCONTROL_DEVICE_EXTENSION PKEY_INFO PDEVICE_EXTENSION CtrlDevExtension; keyinfo; MainDevExt; PCONTROL_MOUSE_DEVICE_EXTENSION MouCtrlDevExt; UNICODE_STRING PFILE_OBJECT PDEVICE_OBJECT ObjectName; FileObject=NULL; DeviceMouCtrlObject=NULL; PKEY_INFO KeyInfoTemp; USHORT i; PAGED_CODE(); commonData = (PCOMMON_DEVICE_DATA)DeviceObject->DeviceExtension; // определяем тип устройства if(commonData->Type == DEVICE_TYPE_FIDO) { // Если IRP адресован устройству в стеке // просто перенаправляем вниз по стеку return FilterPass(DeviceObject, Irp); } ASSERT(commonData->Type == DEVICE_TYPE_CDO); // Иначе это устройство для контроля // для открытия устройства управленя мышью RtlInitUnicodeString(&ObjectName, L"\\Device\\MouCtrlFilter"); //получаем указатель на объект Расширение устройства управления CtrlDevExtension = (PCONTROL_DEVICE_EXTENSION)DeviceObject->DeviceExtension; // из Расширения Устройства Управления получаем указатель // на Расширение устройства в стеке MainDevExt=(PDEVICE_EXTENSION)CtrlDevExtension->StackObject->DeviceExtension; // из Расширения устройства в стеке получаем указатель на KeyInfo keyinfo=MainDevExt->KeyInfo; if(! CtrlDevExtension->Deleted) { status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; irpStack = IoGetCurrentIrpStackLocation (Irp); switch (irpStack->MajorFunction) { case IRP_MJ_CREATE: DbgPrint("Create in kbdfiltr"); break; case IRP_MJ_CLOSE: DbgPrint("Close break; in kbdfiltr"); case IRP_MJ_CLEANUP: DbgPrint("Cleanup break; in kbdfiltr"); case IRP_MJ_DEVICE_CONTROL: // получаем длину буфера переданного с запросом outBufLength = irpStack>Parameters.DeviceIoControl.OutputBufferLength; switch (irpStack->Parameters.DeviceIoControl.IoControlCode) { case GET_KEY_INFO: 22 RtlCopyMemory(Irp>AssociatedIrp.SystemBuffer,keyinfo,outBufLength); Irp->IoStatus.Information=outBufLength; status=STATUS_SUCCESS; break; case GET_KEY_LIST: RtlCopyMemory(Irp>AssociatedIrp.SystemBuffer,&KeyList,outBufLength); Irp->IoStatus.Information=outBufLength; status=STATUS_SUCCESS; break; case WRITE_FILE: ReadWriteKeyInfoToFile(keyinfo,FLAG_WRITE); status=STATUS_SUCCESS; break; case READ_FILE: ReadWriteKeyInfoToFile(keyinfo,FLAG_READ); status=STATUS_SUCCESS; break; case RESET_KEY_INFO: ResetKeyInfo(MainDevExt->KeyInfo); status=STATUS_SUCCESS; break; case START_KBD_SCAN: //Получаем указатель на объект устройство управление мыши status=IoGetDeviceObjectPointer (&ObjectName, FILE_ALL_ACCESS, &FileObject, &DeviceMouCtrlObject); if( NT_SUCCESS(status) ) { // из полученного указателя получем указатель на УУ мыши MouCtrlDevExt=(PCONTROL_MOUSE_DEVICE_EXTENSION) DeviceMouCtrlObject->DeviceExtension; //захват управления над мышью CtrlDevExtension->MouseLock=&MouCtrlDevExt>MouseLock; } //выделение памяти под структру почерка KeyInfoTemp=(PKEY_INFO)ExAllocatePool(PagedPool, NUMBER_OF_KEYS*sizeof(KEY_INFO)); // записываем в KeyInfoTemp клавиатурный почерк status=ReadWriteKeyInfoToFile(KeyInfoTemp,FLAG_READ); // ели операция чтения успешна if( NT_SUCCESS(status) ) { // записываем указатель MainDevExt->KeyInfoTemp=KeyInfoTemp; // обнуляем KeyInfo для режима анализ ResetKeyInfo(MainDevExt->KeyInfo); // обнуляем список, зопиманаемых клавиш CountKeyList=0; // переходим в режим анализ CtrlDevExtension->ModeFlag=MODE_ANALYSIS; } status=STATUS_SUCCESS; break; default: status = STATUS_INVALID_PARAMETER; break; } default: break; 23 } } else { ASSERTMSG(FALSE, "Requests being sent to a dead device\n"); status = STATUS_DEVICE_REMOVED; } // и завершаем запрос Irp->IoStatus.Status = status; IoCompleteRequest (Irp, IO_NO_INCREMENT); return status;} NTSTATUS ReadWriteKeyInfoToFile( IN PKEY_INFO keyinfo, ULONG flag ) /* Routine Description: Функция записи/чтения структуры почерка в фаило Аргументы: keyinfo - указательна структуру почерка flag - режим работы FLAG_READ/FLAG_WRITE Возвращаемое значение: NT status code */ { ULONG Length; IO_STATUS_BLOCK IoStatus; OBJECT_ATTRIBUTES objectAttributes; NTSTATUS status; HANDLE FileHandle; UNICODE_STRING fileName; // инициализируем имя фаила fileName.Buffer = NULL; fileName.Length = 0; fileName.MaximumLength = sizeof(DEFAULT_LOG_FILE_NAME) + sizeof(UNICODE_NULL); fileName.Buffer = ExAllocatePool(PagedPool, fileName.MaximumLength); if (!fileName.Buffer) { DbgPrint("LogMessage: FAIL. ExAllocatePool Failed"); return FALSE; } RtlZeroMemory(fileName.Buffer, fileName.MaximumLength); status = RtlAppendUnicodeToString(&fileName, (PWSTR)DEFAULT_LOG_FILE_NAME); InitializeObjectAttributes (&objectAttributes, (PUNICODE_STRING)&fileName, OBJ_CASE_INSENSITIVE, NULL, NULL ); switch(flag) { // если операция на чтение case FLAG_WRITE: status = ZwCreateFile( &FileHandle, FILE_WRITE_DATA, &objectAttributes, &IoStatus, 0, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_WRITE, FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); ZwWriteFile( FileHandle, NULL,NULL, NULL,&IoStatus, keyinfo, 24 NUMBER_OF_KEYS*sizeof(KEY_INFO), NULL, NULL ); break; // если операция на запись case FLAG_READ: status = ZwCreateFile( &FileHandle, FILE_READ_DATA, &objectAttributes, &IoStatus, 0, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); ZwReadFile( FileHandle, NULL,NULL, NULL,&IoStatus, keyinfo, NUMBER_OF_KEYS*sizeof(KEY_INFO), NULL, NULL ); break; } if( NT_SUCCESS(status) ) { DbgPrint("ZwCreateFile SUCCESS"); ZwClose( FileHandle ); } if( fileName.Buffer ) { ExFreePool (fileName.Buffer); } return status;; } NTSTATUS FilterInternIoCtl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /* Функция обработки фнутренних запросов IOCTL_INTERNAL_KEYBOARD_CONNECT: сохраняем старый контекст и указатель функции и заменяем на свой для потключения к стеку клавиатуры Аргументы: DeviceObject - указательна объект устройство Irp - указатель на пакет запроса. Возвращаемое значение: NT status code */ { PIO_STACK_LOCATION PDEVICE_EXTENSION KEVENT PCONNECT_DATA NTSTATUS irpStack; devExt; event; connectData; status = STATUS_SUCCESS; devExt = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; Irp->IoStatus.Information = 0; irpStack = IoGetCurrentIrpStackLocation(Irp); switch (irpStack->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_INTERNAL_KEYBOARD_CONNECT: if (devExt->UpperConnectData.ClassService != NULL) { status = STATUS_SHARING_VIOLATION; 25 break; } else if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(CONNECT_DATA)) { // неверный буфер status = STATUS_INVALID_PARAMETER; break; } // копируем параметры соеденения в device extension. connectData = ((PCONNECT_DATA) (irpStack->Parameters.DeviceIoControl.Type3InputBuffer)); devExt->UpperConnectData = *connectData; // заменяем данные на свои, утанавливаем свю функцию connectData->ClassDeviceObject = devExt->Self; connectData->ClassService = KbFilter_ServiceCallback; break; case IOCTL_INTERNAL_KEYBOARD_DISCONNECT: // не потдерживается status = STATUS_NOT_IMPLEMENTED; break; case case case case case case IOCTL_KEYBOARD_QUERY_ATTRIBUTES: IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION: IOCTL_KEYBOARD_QUERY_INDICATORS: IOCTL_KEYBOARD_SET_INDICATORS: IOCTL_KEYBOARD_QUERY_TYPEMATIC: IOCTL_KEYBOARD_SET_TYPEMATIC: break; } if (!NT_SUCCESS(status)) { Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } // передаем запрос дальше по стеку return FilterPass(DeviceObject, Irp); } VOID KbFilter_ServiceCallback( IN IN IN IN ) /* PDEVICE_OBJECT DeviceObject, PKEYBOARD_INPUT_DATA InputDataStart, PKEYBOARD_INPUT_DATA InputDataEnd, OUT PULONG InputDataConsumed Функция, которую вызывает i4082ptr после генерирования прерывания от клавиатуры Аргументы: DeviceObject - указательна объект устройство InputDataStart - указатель на первый пакет InputDataEnd - указатель на последний пакет InputDataConsumed - количество обработаных сообщений Возвращаемое значение: VOID */ { int jk; USHORT PDEVICE_EXTENSION PKEY_INFO PKEY_INFO PCONTROL_DEVICE_EXTENSION CHAR buf [300]; i,t,NumberOfKeys,ik,r; devExt; keyInf; keyEtalon; ControlDevExt; 26 // получаем указатель на устройство расширения устройства стека devExt = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; // получаем указатель на устройство расширения утсройства управления ControlDevExt=(PCONTROL_DEVICE_EXTENSION)ControlDeviceObject->DeviceExtension; // вытаскиваем указатель на KEY_INFO keyInf=devExt->KeyInfo; // определяем количество структур KEYBOARD_INPUT_DATA NumberOfKeys=(USHORT)(InputDataEnd-InputDataStart); // если режим анализ, существует указатель на почерк if(devExt->KeyInfoTemp) keyEtalon=devExt->KeyInfoTemp; //по всем PKEYBOARD_INPUT_DATA for (i=0;i<NumberOfKeys;i++) { // предварительная обработка нажатия клавиши if (InputDataStart[i].Flags==KEY_MAKE){ // определяем интервал от предидущего нажатия gLastLastUp=KeQueryInterruptTime(); // фильтр на слишком долгие паузы, если вермя между нажатием 2х клавиш // более 3 сек, то эту паузу заменяем на паузу в 1 секунду if ((gLastLastUp-gLastUp)>30000000) gDelay=gDelay+(gLastLastUpgLastUp)-10000000; gLastUp=gLastLastUp; // проверка на ввод пароля --------------if ((PasswLock[PasswLock[6]])==(InputDataStart[i].MakeCode)){ PasswLock[6]++; } else {PasswLock[6]=0;} if ((PasswUnLock[PasswUnLock[6]])==(InputDataStart[i].MakeCode)){ PasswUnLock[6]++; } else {PasswUnLock[6]=0;} if (PasswLock[6]==6){ if(ControlDevExt->MouseLock) {*(ControlDevExt>MouseLock)=TRUE;} // в режиме обученя пароли не работают if(ControlDevExt>ModeFlag!=MODE_EDUCATION){ControlDevExt>ModeFlag=MODE_BLOCK;} } if (PasswUnLock[6]==6){ if(ControlDevExt->MouseLock) *(ControlDevExt>MouseLock)=FALSE; // в режиме обученя пароли не работают if(ControlDevExt>ModeFlag!=MODE_EDUCATION){ControlDevExt>ModeFlag=MODE_ANALYSIS ;} } // --------------------------------------} // если в режиме анализа или обучения просчтываем статистику if((ControlDevExt->ModeFlag==MODE_ANALYSIS)||(ControlDevExt>ModeFlag==MODE_EDUCATION)){ //по всем клавишам (104 штуки) for (t=0;t<NUMBER_OF_KEYS;t++) { //определяем в контексте какой keyinf будем работать if ((InputDataStart[i].MakeCode==keyInf[t].MakeCode)){ switch (InputDataStart[i].Flags) { case KEY_MAKE: 27 // определяем время последнего нажатия keyInf[t].LastDown=KeQueryInterruptTime()gDelay; break; case KEY_BREAK: case KEY_E0: case KEY_E1: // подсчёт остальных верменных характеристик AnalizKeyInfo(keyInf,t); // если режим анализ, и нажато 30 клавиш if((ControlDevExt>ModeFlag==MODE_ANALYSIS)&&(CountKeyList==NUMBER_OF_C HEK_KEYS)) { DbgPrint("BEGIN ALGORITM"); r=0; for (ik=0;ik<NUMBER_OF_CHEK_KEYS1;ik++) { // считаем те интервалы которые попали в границы эталона if ((keyEtalon[KeyList[ik]].KeyVector[KeyList[ik+1]]500000<keyInf[KeyList[ik]].KeyVector[KeyList[ik+1]])&& (keyInf[KeyList[ik]].KeyVector[KeyList[ik+1]]<keyEtalon[KeyList[ik]].KeyVector[KeyList[ik+ 1]]+500000)) {r++;} } // если почерки не сходятся (ALG_FACTOR=2, совпадений меньше половины) if (r<((NUMBER_OF_CHEK_KEYS1)/ALG_FACTOR)) { // блокируем клавиатуру и мыш ControlDevExt>ModeFlag=MODE_BLOCK; if(ControlDevExt->MouseLock) {*(ControlDevExt>MouseLock)=TRUE;} } ResetKeyInfo(keyInf); } // -------------------------------// если нажато 30 клавиш, заполняем KeyList по новому if(CountKeyList==NUMBER_OF_CHEK_KEYS) { for (ik=0;ik<NUMBER_OF_CHEK_KEYS;ik++) {KeyList[ik]=0;} CountKeyList=0; } KeyList[CountKeyList]=InputDataStart[i].MakeCode; CountKeyList++; break; } } } } } // если режим блокировки заменяем коды всех клавиш if(ControlDevExt->ModeFlag==MODE_BLOCK) { for (i=0;i<NumberOfKeys;i++) InputDataStart[i].MakeCode=69; } // передаем управление в callback функцию драйвера класса (*(PSERVICE_CALLBACK_ROUTINE) devExt->UpperConnectData.ClassService)( devExt->UpperConnectData.ClassDeviceObject, InputDataStart, InputDataEnd, InputDataConsumed); } 28 VOID AnalizKeyInfo( PKEY_INFO keyInf, USHORT t ) /* Функция подсчёта временных характеристк при отжатии клавишши Аргументы: keyInf - указатель на структуру почерка t - номер клавиши Возвращаемое значение: VOID */ { // время последнего отжатия keyInf[t].LastLastUp=KeQueryInterruptTime()-gDelay; // последнеие между отжатием и нажатием keyInf[t].LastFromDownToUp=(ULONG)(keyInf[t].LastLastUp-keyInf[t].LastDown); // последние между двумя отжатиями keyInf[t].LastBetweenTwoUp=keyInf[t].LastLastUp-keyInf[t].LastUp; // перезапоминаем последнее отжатие keyInf[t].LastUp=keyInf[t].LastLastUp; // последнее до другой keyInf[gLastKey].ToOtherBreak=(ULONG)(keyInf[t].LastLastUp-keyInf[gLastKey].LastLastUp); // если запуск не первый, считаем средние с деление на 2 if(keyInf[t].MeanFromDownToUp!=0){ // среднее между двумя отжатиями keyInf[t].MeanBetweenTwoUp=(ULONG)((keyInf[t].MeanBetweenTwoUp+ keyInf[t].LastBetweenTwoUp)/2); // среднеие меду нажатием и отжатием keyInf[t].MeanFromDownToUp=(ULONG)((keyInf[t].MeanFromDownToUp+ keyInf[t].LastFromDownToUp)/2); // среднее до другой keyInf[gLastKey].MeanToOtherBreak=(ULONG)((keyInf[gLastKey].MeanToOtherBreak+ keyInf[gLastKey].ToOtherBreak)/2); } // если первый запуск, средние значения считаем без деления на 2: if(keyInf[t].MeanFromDownToUp==0){ // среднее между двумя отжатиями keyInf[t].MeanBetweenTwoUp=(ULONG)((keyInf[t].MeanBetweenTwoUp+ keyInf[t].LastBetweenTwoUp)); // среднеие меду нажатием и отжатием keyInf[t].MeanFromDownToUp=(ULONG)((keyInf[t].MeanFromDownToUp+ keyInf[t].LastFromDownToUp)); // среднее до другой keyInf[gLastKey].MeanToOtherBreak=(ULONG)((keyInf[gLastKey].MeanToOtherBreak+ keyInf[gLastKey].ToOtherBreak)); } // первый запуск для вектора межклавишных взаимодействий if(keyInf[gLastKey].KeyVector[t]==0){ // время нажатия от клавиши gLastKey до t keyInf[gLastKey].KeyVector[t]= ((keyInf[gLastKey].ToOtherBreak +keyInf[gLastKey].KeyVector[t])); } // не первый запуск для вектора межклавишных взаимодействий if(keyInf[gLastKey].KeyVector[t]!=0){ // время нажатия от клавиши gLastKey до t 29 keyInf[gLastKey].KeyVector[t]= (ULONG)((keyInf[gLastKey].ToOtherBreak +keyInf[gLastKey].KeyVector[t])/2); } // количество нажатий данной клавишы keyInf[t].CountUp++; // запоминаем текущуб клавишу gLastKey=t; } VOID ResetKeyInfo( PKEY_INFO KeyInfo ) /* Функция обнуления структуры KEY_INFO Аргументы: keyInf - указатель на структуру KEY_INFO Возвращаемое значение: VOID */ { USHORT i,j; for(i=0;i<NUMBER_OF_KEYS;i++) { KeyInfo[i].CountUp=0; KeyInfo[i].LastBetweenTwoUp=0; KeyInfo[i].LastDown=0; KeyInfo[i].LastFromDownToUp=0; KeyInfo[i].LastUp=0; KeyInfo[i].LastLastUp=0; KeyInfo[i].MakeCode=i; KeyInfo[i].MeanBetweenTwoUp=0; KeyInfo[i].MeanFromDownToUp=0; KeyInfo[i].MeanToOtherBreak=0; KeyInfo[i].ToOtherBreak=0; for(j=0;j<NUMBER_OF_KEYS;j++) { KeyInfo[i].KeyVector[j]=0; } } } 30 #include #include #include #include #include #define #define #define #define #define #define "ntddk.h" "kbdmou.h" "ntddkbd.h" "ntdd8042.h" <stdio.h> GET_KEY_INFO CTL_CODE(FILE_DEVICE_UNKNOWN, 0x900, METHOD_BUFFERED, FILE_ANY_ACCESS) WRITE_FILE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x901, METHOD_BUFFERED, FILE_ANY_ACCESS) READ_FILE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x902, METHOD_BUFFERED, FILE_ANY_ACCESS) START_KBD_SCAN CTL_CODE(FILE_DEVICE_UNKNOWN, 0x903, METHOD_BUFFERED, FILE_ANY_ACCESS) RESET_KEY_INFO CTL_CODE(FILE_DEVICE_UNKNOWN, 0x904, METHOD_BUFFERED, FILE_ANY_ACCESS) GET_KEY_LIST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x905, METHOD_BUFFERED, FILE_ANY_ACCESS) #define NUMBER_OF_KEYS 104 #define NUMBER_OF_CHEK_KEYS 30 #define DEFAULT_LOG_FILE_NAME L"\\??\\C:\\keydata.dat" #define NTDEVICE_NAME_STRING #define SYMBOLIC_NAME_STRING L"\\Device\\KbdCtrlFilter" L"\\DosDevices\\KbdCtrlFilter" #define FLAG_WRITE 210 #define FLAG_READ 211 #define ALG_FACTOR 2 // чувствительность алгоритма анализа почерка #ifndef STATUS_CONTINUE_COMPLETION //required to build driver in Win2K and XP build environment #define STATUS_CONTINUE_COMPLETION STATUS_SUCCESS #endif //задание ярлыка для функции IoInitializeRemoveLock #define POOL_TAG 'arty' #define MODE_EDUCATION 100 #define MODE_BLOCK #define MODE_ANALYSIS 102 101 //Глобальные пнрнманные -----------USHORT PasswLock[7] 375127 USHORT PasswUnLock[7] 248975 ULONGLONG gLastUp ULONGLONG gLastLastUp отжатия ULONGLONG gDelay ={4,8,6,2,3,8,0}; // пароль на блокировки,переход в режим бокировки = ={3,5,9,10,8,6,0}; // пароль на разблокировку,переход в режим анализ = =0; =0; // время последнего отжатия // время предпоследнего =0; // глобальная задержка 31 USHORT клавиши gLastKey =0; FAST_MUTEX ControlMutex; устройства управления ULONG InstanceCount = 0; PDEVICE_OBJECT ControlDeviceObject; // номер полседней нажатой // для понижения IRQL до PASSIVE_LEVEL при создании // счётчик экземпляров устройств управления // указатель на объект устройство управления ULONG KeyList [NUMBER_OF_CHEK_KEYS]; ULONG CountKeyList=0; //----------------------------------// Структура, описывающая клавиатурный почерк typedef struct _KEY_INFO{ ULONG MakeCode; ULONG CountUp; ULONGLONG LastDown; ULONGLONG LastUp; ULONGLONG LastLastUp; ULONG LastFromDownToUp; ULONG MeanFromDownToUp; ULONGLONG LastBetweenTwoUp; ULONG MeanBetweenTwoUp; ULONG ToOtherBreak; ULONG MeanToOtherBreak; ULONG KeyVector[NUMBER_OF_KEYS]; // // // // // // // // // // // // код клавиши количество отжатий последнее нажатие последнее отжатие предпоследнее отжатие посленее нажатие-отпуск среднее нажатие отпуск последне между двумя отжатиями среднее между двумя отжатиями время до другой среднее время до другой среднее до определённой } KEY_INFO,*PKEY_INFO; typedef enum _DEVICE_PNP_STATE { NotStarted = 0, Started, StopPending, Stopped, RemovePending, SurpriseRemovePending, Deleted // // // // // // // еще не запущено устройство получило устройство получило устройство получило устройство получило устройство получило устройство получило START_DEVICE IRP QUERY_STOP IRP STOP_DEVICE IRP QUERY_REMOVE IRP SURPRISE_REMOVE IRP REMOVE_DEVICE IRP } DEVICE_PNP_STATE; #define INITIALIZE_PNP_STATE(_Data_) \ (_Data_)->DevicePnPState = NotStarted;\ (_Data_)->PreviousPnPState = NotStarted; #define SET_NEW_PNP_STATE(_Data_, _state_) \ (_Data_)->PreviousPnPState = (_Data_)->DevicePnPState;\ (_Data_)->DevicePnPState = (_state_); #define RESTORE_PREVIOUS_PNP_STATE(_Data_) \ (_Data_)->DevicePnPState = (_Data_)->PreviousPnPState;\ typedef enum _DEVICE_TYPE { DEVICE_TYPE_INVALID = 0, DEVICE_TYPE_FIDO, DEVICE_TYPE_CDO, } DEVICE_TYPE; typedef struct _COMMON_DEVICE_DATA { DEVICE_TYPE Type; } COMMON_DEVICE_DATA, *PCOMMON_DEVICE_DATA; //устройство расширение устройства стека typedef struct _DEVICE_EXTENSION { COMMON_DEVICE_DATA; PDEVICE_OBJECT Self; PDEVICE_OBJECT NextLowerDriver; добавлен. DEVICE_PNP_STATE DevicePnPState; DEVICE_PNP_STATE PreviousPnPState; IO_REMOVE_LOCK RemoveLock; CONNECT_DATA UpperConnectData; драйвер // указатель на сам объект устройство // вершина стека перед тем как фильтр будет // // // // текущее PnP состояние устройства предидущее PnP состояние устройства Removelock для защещённой выгрузки данные на соеденение, которые отправляет 32 PVOID (and context) PKEY_INFO в неё PKEY_INFO UpperContext; // Previous initialization and hook routines KeyInfo; // указатель на структуру почерка для записи KeyInfoTemp; // указатель на структуру почерка эталона } DEVICE_EXTENSION, *PDEVICE_EXTENSION; //устройство расширение устройства управления typedef struct _CONTROL_DEVICE_EXTENSION { COMMON_DEVICE_DATA; ULONG PVOID PDEVICE_OBJECT Deleted; ControlData; StackObject; // TRUE если устройство удалено // Store your control data here // объеект устройство подключенное к PULONG MouseLock; // указатель на флаг блокировки мыши в // устройстве расширения устройства стеку управления мыши ULONG ModeFlag; данный момент //в каком режиме работает драйвер в } CONTROL_DEVICE_EXTENSION, *PCONTROL_DEVICE_EXTENSION; //устройство расширение устройства управления мыши typedef struct _CONTROL_MOUSE_DEVICE_EXTENSION { COMMON_DEVICE_DATA; ULONG PVOID NTSTATUS ULONG Deleted; ControlData; status; MouseLock; // // // // TRUE если устройство удалено для сохранения ControlData NTSTATUS если TRUE мышь блоктрована } CONTROL_MOUSE_DEVICE_EXTENSION, *PCONTROL_MOUSE_DEVICE_EXTENSION; //Объявленя функций \/\/\/\/\/\/\/\/\/\/\/\/\/\/ PCHAR PnPMinorFunctionString ( UCHAR MinorFunction ); NTSTATUS FilterAddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject ); NTSTATUS FilterDispatchPnp ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS FilterDispatchPower( IN PDEVICE_OBJECT IN PIRP ); DeviceObject, Irp VOID FilterUnload( IN PDRIVER_OBJECT DriverObject ); NTSTATUS FilterPass ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ); NTSTATUS FilterStartCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, 33 IN PIRP IN PVOID ); Irp, Context NTSTATUS FilterDeviceUsageNotificationCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ); NTSTATUS FilterInternIoCtl ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); VOID KbFilter_ServiceCallback( IN IN IN IN ); PDEVICE_OBJECT DeviceObject, PKEYBOARD_INPUT_DATA InputDataStart, PKEYBOARD_INPUT_DATA InputDataEnd, OUT PULONG InputDataConsumed NTSTATUS FilterCreateControlObject( IN PDEVICE_OBJECT ); DeviceObject VOID FilterDeleteControlObject( ); NTSTATUS FilterDispatchIo( IN PDEVICE_OBJECT IN PIRP ); DeviceObject, Irp NTSTATUS ReadWriteKeyInfoToFile( IN PKEY_INFO ULONG ); keyinfo, flag VOID ResetKeyInfo( PKEY_INFO KeyInfo ); VOID AnalizKeyInfo( PKEY_INFO KeyInfo, USHORT t ); 34 5.2 Управляющее приложение #include "ControlApp.h" int WINAPI WinMain ( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow ) /* Точка входа программы Аргументы: hInstance - описателем экземпляра hPrevInstanceIrp - предыдущий экземпляр szCmdLine - указатель на командную строку iCmdShow - начальный вид Возвращаемое значение: int */ { static char HWND MSG WNDCLASSEX szAppName[] = "HexCalc" ; hwnd ; msg ; wndclass ; // Регистрация класса окна wndclass.cbSize = sizeof (wndclass) ; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = DLGWINDOWEXTRA ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (hInstance, szAppName) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 0) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; wndclass.hIconSm = LoadIcon (hInstance, szAppName) ; RegisterClassEx (&wndclass) ; // создание окна hwnd = CreateDialog (hInstance, szAppName, 0, NULL) ; hinst=hInstance; ShowWindow (hwnd, iCmdShow) ; // цикл обработки сообщений while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } 35 return msg.wParam ; } LRESULT CALLBACK WndProc ( HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam ) /* Точка входа программы Аргументы: hwnd - описатель окна iMsg - принятое сообщение wParam - параметр сообщения wParam - параметр сообщения Возвращаемое значение: LRESULT */ { DWORD bytesReturned; DWORD Code, i; int count,j,temp; char buf[100]; float p,pcount; char bufer[2000]; switch (iMsg) { case WM_CREATE: keyinfo=(PKEY_INFO)malloc(104*sizeof(KEY_INFO)); KeyList=(PULONG)malloc(NUMBER_OF_CHEK_KEYS*sizeof(ULONG)); hDevice = CreateFile( "\\\\.\\KbdCtrlFilter", GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); return 0; case WM_COMMAND : // нажата кнопка на нарисованой клавиатуре Code=(DWORD)(LOWORD (wParam))-999; if (Code<94 ) { sprintf(buf,"%d",Code); SetDlgItemText(hwnd,IDC_EDIT1,buf); sprintf(buf,"%d",keyinfo[Code].CountUp); SetDlgItemText(hwnd,IDC_EDIT2,buf); sprintf(buf,"%I64d",keyinfo[Code].LastDown); SetDlgItemText(hwnd,IDC_EDIT3,buf); 36 sprintf(buf,"%I64d",keyinfo[Code].LastUp); SetDlgItemText(hwnd,IDC_EDIT4,buf); sprintf(buf,"%d",keyinfo[Code].LastFromDownToUp); SetDlgItemText(hwnd,IDC_EDIT5,buf); sprintf(buf,"%d",keyinfo[Code].MeanFromDownToUp); SetDlgItemText(hwnd,IDC_EDIT7,buf); sprintf(buf,"%I64d",keyinfo[Code].LastBetweenTwoUp); SetDlgItemText(hwnd,IDC_EDIT8,buf); sprintf(buf,"%d",keyinfo[Code].MeanBetweenTwoUp); SetDlgItemText(hwnd,IDC_EDIT9,buf); sprintf(buf,"%d",keyinfo[Code].ToOtherBreak); SetDlgItemText(hwnd,IDC_EDIT12,buf); sprintf(buf,"%d",keyinfo[Code].MeanToOtherBreak); SetDlgItemText(hwnd,IDC_EDIT13,buf); pcount =0; for (i=0;i<104;i++) {pcount+=keyinfo[i].CountUp; } p=(double)(keyinfo[Code].CountUp/pcount); sprintf(buf,"%f",p); SetDlgItemText(hwnd,IDC_EDIT10,buf); j=0; for (i=0;i<NUMBER_OF_KEYS;i++)j+=sprintf( bufer+j,"%d%c", keyinfo[Code].KeyVector[i],' '); SetDlgItemText(hwnd,IDC_EDIT14,bufer); } // нажата кнопка получить KeyList if (LOWORD (wParam)==IDC_GET_KEY_LIST) { DeviceIoControl(hDevice, GET_KEY_LIST,0,0,KeyList, NUMBER_OF_CHEK_KEYS*sizeof(ULONG), &bytesReturned,NULL); j=0;temp=0; for (i=0;i<NUMBER_OF_CHEK_KEYS;i++) { if (KeyList[i]==0) {temp++;} j+=sprintf( bufer+j,"%d%c", KeyList[i],' '); } SetDlgItemText(hwnd,IDC_EDIT15,bufer); j=0; //if (temp==0){temp=1;} for (i=0;i<NUMBER_OF_CHEK_KEYS-temp-1;i++) j+=sprintf( bufer+j,"%d%c", keyinfo[KeyList[i]].KeyVector[KeyList[i+1]],' '); SetDlgItemText(hwnd,IDC_EDIT16,bufer); } // нажата кнопка пуск if (LOWORD (wParam)==IDC_PUSK) { DeviceIoControl(hDevice, START_KBD_SCAN,0,0,0, 0,&bytesReturned,NULL); 37 } // нажата кнопка сбросить все показатели if (LOWORD (wParam)==IDC_RESET) { DeviceIoControl(hDevice,RESET_KEY_INFO,0,0,0, 0,&bytesReturned,NULL); } // нажата кнопка сохраниьт все показатели if (LOWORD (wParam)==IDC_SAVE) { DeviceIoControl(hDevice,WRITE_FILE,0,0,0, 0,&bytesReturned,NULL); } // нажата кнопка обнавить данные if (LOWORD (wParam)==IDC_UPDATE) { DeviceIoControl(hDevice, GET_KEY_INFO,0,0,keyinfo, NUMBER_OF_KEYS*sizeof(KEY_INFO), &bytesReturned,NULL); count =0; for (i=0;i<NUMBER_OF_KEYS;i++) {count+=keyinfo[i].CountUp; } sprintf(buf,"%d",count); SetDlgItemText(hwnd,IDC_EDIT6,buf); } if (LOWORD (wParam)==IDC_ABOUT) { DialogBox (hinst, (LPCTSTR)IDD_ABOUT, hwnd, AboutDlgProc); } return 0; case WM_DESTROY : free(keyinfo); free(KeyList); CloseHandle(hDevice); PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, iMsg, wParam, lParam) ; } BOOL CALLBACK AboutDlgProc ( HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam ) /* Точка входа программы Аргументы: hInstance - описателем экземпляра hPrevInstanceIrp - предыдущий экземпляр szCmdLine - указатель на командную строку iCmdShow - начальный вид Возвращаемое значение: 38 BOOL */ { switch (iMsg) { case WM_COMMAND : switch (LOWORD (wParam)) { case IDOK : EndDialog (hDlg, 0) ; return TRUE ; } break ; } return FALSE ; } 39 ControlApp.h #include #include #include #include #include #include <windows.h> <limits.h> <stdlib.h> <string.h> <ctype.h> "resource.h" #define NUMBER_OF_KEYS 104 #define NUMBER_OF_CHEK_KEYS 30 typedef struct _KEY_INFO{ ULONG MakeCode; ULONG CountUp; ULONGLONG LastDown; ULONGLONG LastUp; ULONGLONG LastLastUp; ULONG LastFromDownToUp; ULONG MeanFromDownToUp; ULONGLONG LastBetweenTwoUp; ULONG MeanBetweenTwoUp; ULONG ToOtherBreak; ULONG MeanToOtherBreak; ULONG KeyVector[NUMBER_OF_KEYS]; } KEY_INFO,*PKEY_INFO; #define #define #define #define #define #define #define GET_KEY_INFO CTL_CODE(FILE_DEVICE_UNKNOWN, 0x900, METHOD_BUFFERED, FILE_ANY_ACCESS) WRITE_FILE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x901, METHOD_BUFFERED, FILE_ANY_ACCESS) READ_FILE CTL_CODE(FILE_DEVICE_UNKNOWN, 0x902, METHOD_BUFFERED, FILE_ANY_ACCESS) START_KBD_SCAN CTL_CODE(FILE_DEVICE_UNKNOWN, 0x903, METHOD_BUFFERED, FILE_ANY_ACCESS) RESET_KEY_INFO CTL_CODE(FILE_DEVICE_UNKNOWN, 0x904, METHOD_BUFFERED, FILE_ANY_ACCESS) GET_KEY_LIST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x905, METHOD_BUFFERED, FILE_ANY_ACCESS) ID_TIMER 1 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; BOOL CALLBACK AboutDlgProc (HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam); PKEY_INFO keyinfo; HANDLE hDevice; PLONG KeyList ; HINSTANCE hinst; 40 5.2 Драйвер фильтр мыши Код аналогичен клавиатурному фильтру, интерес составляет только MouFilter_ServiceCallback VOID MouFilter_ServiceCallback( IN IN IN IN ) PDEVICE_OBJECT DeviceObject, PMOUSE_INPUT_DATA InputDataStart, PMOUSE_INPUT_DATA InputDataEnd, OUT PULONG InputDataConsumed { USHORT i,num; PDEVICE_EXTENSION devExt; PCONTROL_MOUSE_DEVICE_EXTENSION MouCtrlDevExt; devExt = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; MouCtrlDevExt=(PCONTROL_MOUSE_DEVICE_EXTENSION)ControlDeviceObject>DeviceExtension; num=(USHORT)(InputDataEnd-InputDataStart); if(!MouCtrlDevExt->MouseLock) { (*(PSERVICE_CALLBACK_ROUTINE) devExt>UpperConnectData.ClassService)( devExt->UpperConnectData.ClassDeviceObject, InputDataStart, InputDataEnd, InputDataConsumed ); } } 41 6. ЛИТЕРАТУРА 1. Петзолд Ч. Программирование для Windows 95 в двух томах. «BHV — СанктПетербург», 2003 г. 2. Рихтер Д. Windows для профессионалов: создание эффективных Win32 приложений с учетом специфики 64-разрядной версии Windows/Пер, англ - 4-е изд. - СПб; Питер; М.: Издательско-торговый дом "Русская Редакция", 2001. - 752 с.; ил. 3. Румянцев П.В. Азбука программирования в WIN32 API 4. Сорокина С.И. Программирование драйверов и систем безопасности: Учеб. Пособие – Спб.: БХВ – Петербург, 2003. – 256 с.:ил. 5. Соломон Д. и Руссинович М.Внутреннее устройство Microsoft Windows 2000. Мастер-класс / Пер.с англ. — СПб.: Питер; М.: Издательско-торговый дом «Русская Редакция». 2004. — 746 стр.: ил. 6. Солдатов В.П. Программирование драйверов Windows. Изд. 2-е, перераб. и доп. М.: ООО «Бином-Пресс», 2004 г. 7. Савинков А.Ю. Лекционный материал по курсу «Операционные системы» 8. Таненбаум Э. Современные операционные системы. 2-е изд. – СПб.: Питер, 2002. – 1040 с.:ил. 9. Фролов А.В., Фролов Г.В. Программирование для Windows NT части 1,2. 1996 г. 10. http://www.wasm.ru/article.php?article=drvw2k16 11. MSDN Library, Copyright 1987-2005 Microsoft Corporation 12. Driver Development Kit (DDK) documentation, built on Tuesday, February 22, 2005 г. 42