Доброго времени суток!
У меня имеется следующая проблема. Только начал разбираться с написанием драйверов, поэтому надеюсь на ваш более солидный опыт.
Один из модулей драйвера-фильтра, должен осуществлять анализ нжимаемых клавиш, поэтому подзадачей явл-ся написания подобия кейлоггера.
Создаваемый FDO-объект прикрепляется в цепочку устр-в классового драйвера KeyboardClass0. При получении моим драйвером IRP-пакета, регистрируется завершающая функция обратного вызова, к-я при прохождении пакета по стеку устр-в обратно и вызывается. Как известно, её вызов происходит на IRQL > PASSIVE_LEVEL, поэтому длительные операции обработки и анализа в ней производить нельзя. Для этого я передаю эти данные посредством очереди в анализирующий поток, работающий на PASSIVE_LEVEL, к-й их и производит. Для указания кол-ва эл-в в очереди используется семафор. В нем и собственно проблема:
Периодически(в зав-ти от интенсивности нажатия клавиш) на очередной попытке освобождения семафора получаем BSOD(DRIVER_IRQL_NOT_LESS_OR_EQUAL, STOP 0x000000D1) — попытка обратиться к страничной памяти, используя процесс ядра через IRQL высокого уровня. Объект семафора хранится в расширении данного FDO-объекта. То, что проблема в обращениях к семафору в т.ч. указывает работоспособность кода при коментировании вызова KeReleaseSemaphore() в в завершающей ф-и обр-го вызова. Насколько я понял KeReleaseSemaphore позволительно выз-ть на IRQL <= DISPATCH_LEVEL.
Прошу помочь разобраться с даннной проблемой. Спасибо.
Ниже привожу листинг функции обратного вызова:
NTSTATUS OnReadCompletion(IN PDEVICE_OBJECT pFDO, IN PIRP pIrp, IN PVOID Context)
{
PKFILTER_DEVICE_EXTENSION dx = (PKFILTER_DEVICE_EXTENSION) pFDO->DeviceExtension;
// В pIrp->AssociatedIrp.SystemBuffer содержится
// адресс массива стр-р KEYBOARD_INPUT_DATA, а в
// pIrp->IoStatus.Information его размер:
PKEYBOARD_INPUT_DATA keys;
int numKeys,i;
PKEY_DATA pkData; // стр-ра, хранящая инфу об одном нажатии
// Если запрос успешно обработан извлекаем инфу:if(pIrp->IoStatus.Status == STATUS_SUCCESS)
{
keys = (PKEYBOARD_INPUT_DATA) pIrp->AssociatedIrp.SystemBuffer;
numKeys = pIrp->IoStatus.Information/sizeof(KEYBOARD_INPUT_DATA);
for(i=0; i<numKeys;i++)
{
DbgPrint("<KFilter >\t[OnReadCompletion]\t ScanCode= %x\n", keys[i].MakeCode);
// Для размещения инф-и о нажатии выделяем небольшой
// неперемещаемый пул памяти, к-й далее помещается в
// очередь:
pkData = (PKEY_DATA) ExAllocatePool(NonPagedPool,sizeof(KEY_DATA));
// Заполняем стр-ру полученной инф-ей о нажатии:
pkData->kbd_inp_data = keys[i];
// Добавляем инф-ю о нажатии в очередь, для
// её последующего извлечения анализирующим потоком:
ExInterlockedInsertTailList(&(dx->QueueListHead), // ук-ль на заголовок списка
&(pkData->listEntry), // ук-ль на стр-ру LIST_ENTRY, вставляемого эл-та
&(dx->lockQueue)); // ук-ль на объект спин-блокировки для синхронизации доступа к списку
// Увеличиваем счетчик семафора, отображая, что есть
// событие для обработки в анализирующем потоке:
KeReleaseSemaphore( &(dx->semQueue),0,1,TRUE);
}
}
// Если необходима доп-я обработка - отмечаем:if(pIrp->PendingReturned)
IoMarkIrpPending(pIrp);
// Т.к. обработка нами IRP-пакета завершена, отмечаем это
// уменьшив кол-во необработанных пакетов:
dx->ulNumPendingIrps--;
#ifdef _DEBUG
DbgPrint("<KFilter >\t[DispatchRead]\t Count of Pending IRPs=%d.\n", dx->ulNumPendingIrps);
#endif
return pIrp->IoStatus.Status;
}
Описания стр-ры расширения устройства PKFILTER_DEVICE_EXTENSION и стр-ы, хранящей инфу о событии ввода KEY_DATA:
typedef struct _KFILTER_DEVICE_EXTENSION
{
PDEVICE_OBJECT pFDO; // ук-ль на FDO-объект данного драйвера
UNICODE_STRING ustrSymLinkName; // символьное Win32-имя данного драйвера
PDEVICE_OBJECT pTagFDO; // ук-ль на след-е в цепочке устройство находящееся ниже данного фильтра
PETHREAD pThreadObj; // ук-ль на объект анализирующего нажатия клавиш потока
BOOLEAN bThreadTerminate; // флаг состояния потока(pThreadObj)
LIST_ENTRY QueueListHead; //
KSPIN_LOCK lockQueue; // используется для синхронизации доступа к списку(очереди)
KSEMAPHORE semQueue; // будет отображать кол-во эл-в(символов) в очереди
BOOLEAN bHookMode; // флаг режима р-ты драйвера(TRUE-перехват событий ввода)
ULONG ulNumPendingIrps; // кол-во ожидающих завершения IRP-пакетов
} KFILTER_DEVICE_EXTENSION, *PKFILTER_DEVICE_EXTENSION;
// Стр-ру, представляющую собой 1 эл-т очереди
// событий ввода, к-я будет использоваться для передачи
// данных событий между процедурой OnReadCompletion, отлавливающей
// данные события, и анализирующим их потоком:typedef struct _KEY_DATA
{
KEYBOARD_INPUT_DATA kbd_inp_data;
LIST_ENTRY listEntry;
} KEY_DATA, *PKEY_DATA;
Решение проблемы оказалось в 4-м пар-ре KeReleaseSemaphore. При зн-и FALSE вместо TRUE работа корректна.
Но теперь у меня появились сомнения, правильно ли я понимаю зн-е 2-го и 4-го аргумента данной функции.
Прошу пояснить?
Здравствуйте, AviSergey,
AS>Решение проблемы оказалось в 4-м пар-ре KeReleaseSemaphore. При зн-и FALSE вместо TRUE работа корректна. AS>Но теперь у меня появились сомнения, правильно ли я понимаю зн-е 2-го и 4-го аргумента данной функции.
--
1. Well, из приведенного выше текста сложно понять, что именно Вы не понимаете
Описание 4-го параметра достаточно подробно описано здесь.
Для понимания второго параметра, которые определяет priroty boost, просмотрите соответствующий раздел Priority Boost в главе Processes, Threads and Jobs известной книги Windows Internals, Russinovitch and Solomon.
Спасибо за ответ. С параметрами разобрался.
ГМ>2. А почему Вы используете semaphore, а не event?
Т.к кроме сигнализации анализирующему потоку о наличии события ввода в очереди, необходимо учитывать их кол-во. При испольлзовании event-a за время, возможно более длительной, операции обработки после вызова KeWaitFor*.. в очередь может поступить несколько новых эл-в, а след-й вызов KeWaitFor*.. сбросит event в несигнальное состояние, в рез-те вместо обработки всех поступивших будет обработано только первое.
Здравствуйте, AviSergey, Вы писали:
AS>Здравствуйте, Геннадий Майко.
AS>Спасибо за ответ. С параметрами разобрался.
ГМ>>2. А почему Вы используете semaphore, а не event?
AS>Т.к кроме сигнализации анализирующему потоку о наличии события ввода в очереди, необходимо учитывать их кол-во. При испольлзовании event-a за время, возможно более длительной, операции обработки после вызова KeWaitFor*.. в очередь может поступить несколько новых эл-в, а след-й вызов KeWaitFor*.. сбросит event в несигнальное состояние, в рез-те вместо обработки всех поступивших будет обработано только первое.
Это решается с помощью цикла, псевдокод
ThreadHandler:
do
{
wait(event); // should be auto reset even
while( pickItemFromQueue() )
{
// process item
}
Ничего против такой реализации не имею.
Вариант на основе семафора мне так же кажется удовлетворительным.
Сейчас наращиваю проект, и след-м подэтапом необходим фильтр мыши. Попробывал сделать аналогично фильтру клавиатуры. Присоединил свой FDO-объект к цепочке устройств PointerClass0. Однако, даже при регистрации всех MajorFunction на одну функцию обработки в драйвер не один IRP-пакет от мыши не поступает. В DeviceTree созданное устр-во, добавленное к PointerClass0 отображаетя.
Поиск по Net-у четких рез-в не дал. Посредством каких пакетов при взаимодействии диспетчера ввода/вывода и драйвером mouclass передается инф-я о событии ввода (стр-ра MOUSE_INPUT_DATA)? И самое главное в чем может быть причина того что созданный FDO-объект фильтра мыши не получает IRP?
Здравствуйте, shrecher, Вы писали:
S>Есть же пример в WDK: src\input\moufiltr.
Спасибо за наводку. Пример рассмотрю.
Беглый взгляд на данный код зародил пару вопросов:
1) Как я понял реализация перехвата осущ-ся посредством установки фильтра поверх безымянного устройства, создаваемого функциональным драйвером i8042prt под устройством Device\PointerClass0. Насколько мне известно, буду рад если я ошибаюсь, драйвер i8042prt обрабатывает запросы только от PS/2 клавиатуры, поэтому рассматриваемый метод будет не пригоден для перехвата данных, вводимых через USB или беспроводную клавиатуру. Поправьте если не так?
2) Неужели нет возможности организации фильтра посредством установки поверх устройства «\Device\PointerClass0», созданного классовым драйвером MouClass? Подобно тому как это возможно успешно сделать над драйвером KbdClass для клавиатуры, в чем принципиальная причина отсутствия аналогии?