Inserting keystrokes using kbfiltr

Doron (or others),

I need to (among other things) alter the behavior of the keypad (e.g.
keyboard) in our hardware. For example, turn one keypress into a sequence
of keypresses. The kbfiltr driver seems like the place to start,
particularly the Kbfiltr_ServiceCallback() routine. The documentation
(and code comments) indicate that “the callback can delete, transform, or
insert data.”

How do you actually “insert packets into the stream?” I have tried using
some memory created in the filter driver’s device extension and pointing
InputDataStart and InputDataEnd to it.

With a debug version of kbdclass, I can see that the
KeyboardClassServiceCallback() routine gets these “inserted packets” and
consumes them, but after that things go astray. The application (e.g.
Notepad) gets the inserted characters, but also gets a bunch of additional
characters that were typed earlier in time (and keyboard input is hosed up
then).

TIA, phil falck

You do not need to place the inserted packets into your device
extension, you can easily declare them on the stack and then call the
upper service callback function to insert them (the only issue arises is
if the upper service callback’s packet queue is full, in which case your
input will not be accepted, but that is very very very very rare).

As for the earlier input showing up, you need to synchronize that with
your own insertion. If you want to consume the input packets from
below, set the
InputDataConsumed pointer (the one passed to your service callback) to
the number of packets you are throwing away.

D

This posting is provided “AS IS” with no warranties, and confers no
rights

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of Phil Falck
Sent: Friday, December 05, 2003 8:46 AM
To: Windows System Software Devs Interest List
Subject: [ntdev] Inserting keystrokes using kbfiltr

Doron (or others),

I need to (among other things) alter the behavior of the keypad (e.g.
keyboard) in our hardware. For example, turn one keypress into a
sequence
of keypresses. The kbfiltr driver seems like the place to start,
particularly the Kbfiltr_ServiceCallback() routine. The documentation
(and code comments) indicate that “the callback can delete, transform,
or
insert data.”

How do you actually “insert packets into the stream?” I have tried
using
some memory created in the filter driver’s device extension and pointing
InputDataStart and InputDataEnd to it.

With a debug version of kbdclass, I can see that the
KeyboardClassServiceCallback() routine gets these “inserted packets” and
consumes them, but after that things go astray. The application (e.g.
Notepad) gets the inserted characters, but also gets a bunch of
additional
characters that were typed earlier in time (and keyboard input is hosed
up
then).

TIA, phil falck


Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256

You are currently subscribed to ntdev as: xxxxx@windows.microsoft.com
To unsubscribe send a blank email to xxxxx@lists.osr.com

Doron,
Sorry to bother you, but I must be doing something stupid. Here is a
simplified KbFilter_ServiceCallback() from kbfiltr.c.

In the example I am simply just trying to change a ‘z’ into a ‘d’ ‘i’.
(I.e. trying to replace a single character with a sequence of characters).

If you could point out the error of my ways I would greatly appreciate it.
TIA, phil

VOID
KbFilter_ServiceCallback(
IN PDEVICE_OBJECT DeviceObject,
IN PKEYBOARD_INPUT_DATA InputDataStart,
IN PKEYBOARD_INPUT_DATA InputDataEnd,
IN OUT PULONG InputDataConsumed
)
{
PDEVICE_EXTENSION devExt;
ULONG i;
KEYBOARD_INPUT_DATA kid[16];
BOOLEAN bInsert = FALSE;

devExt = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

// print out what keys came in
for (i = 0; i < (ULONG)(InputDataEnd-InputDataStart); i++)
KdPrint((“%d-MC:%02x; F:%02x; SP:%08x; EP:%08x; Consumed:%d\r\n”,
i,
(InputDataStart+i)->MakeCode,
(InputDataStart+i)->Flags,
InputDataStart,
InputDataEnd,
*InputDataConsumed));

// look for a ‘z’ pressed, then turn it into a ‘d’ ‘i’
if ((InputDataStart->MakeCode == 0x2c)&&(InputDataStart->Flags == 0))
{
bInsert = TRUE;
RtlZeroMemory(kid, 16*sizeof(KEYBOARD_INPUT_DATA));

kid[0].MakeCode = 0x20; // simulate a ‘d’ press
kid[0].Flags = 0x00;
kid[1].MakeCode = 0x20; // simulate a ‘d’ release
kid[1].Flags = 0x01;
kid[2].MakeCode = 0x17; // simulate a ‘i’ press
kid[2].Flags = 0x00;
kid[3].MakeCode = 0x17; // simulate a ‘i’ release
kid[3].Flags = 0x01;

*InputDataConsumed = 1; // none of 0, 1, 4 works
}

(*(PSERVICE_CALLBACK_ROUTINE)
devExt->UpperConnectData.ClassService)(
devExt->UpperConnectData.ClassDeviceObject,
bInsert ? &kid[0] : InputDataStart,
bInsert ? &kid[4] : InputDataEnd,
InputDataConsumed);
}

1 You should use the #defines (ie KEY_BREAK) instead of hardcoded
values, makes it easier to read.
2 Not sure why you are declaring 16 packets instead of the 4 you need

Do you see the lower edge reporting the packets you are trying to
consume?

D

This posting is provided “AS IS” with no warranties, and confers no
rights

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of Phil Falck
Sent: Monday, December 08, 2003 2:34 PM
To: Windows System Software Devs Interest List
Subject: [ntdev] RE: Inserting keystrokes using kbfiltr

Doron,
Sorry to bother you, but I must be doing something stupid. Here is a
simplified KbFilter_ServiceCallback() from kbfiltr.c.

In the example I am simply just trying to change a ‘z’ into a ‘d’ ‘i’.
(I.e. trying to replace a single character with a sequence of
characters).

If you could point out the error of my ways I would greatly appreciate
it.
TIA, phil

VOID
KbFilter_ServiceCallback(
IN PDEVICE_OBJECT DeviceObject,
IN PKEYBOARD_INPUT_DATA InputDataStart,
IN PKEYBOARD_INPUT_DATA InputDataEnd,
IN OUT PULONG InputDataConsumed
)
{
PDEVICE_EXTENSION devExt;
ULONG i;
KEYBOARD_INPUT_DATA kid[16];
BOOLEAN bInsert = FALSE;

devExt = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

// print out what keys came in
for (i = 0; i < (ULONG)(InputDataEnd-InputDataStart); i++)
KdPrint((“%d-MC:%02x; F:%02x; SP:%08x; EP:%08x;
Consumed:%d\r\n”,
i,
(InputDataStart+i)->MakeCode,
(InputDataStart+i)->Flags,
InputDataStart,
InputDataEnd,
*InputDataConsumed));

// look for a ‘z’ pressed, then turn it into a ‘d’ ‘i’
if ((InputDataStart->MakeCode == 0x2c)&&(InputDataStart->Flags ==
0))
{
bInsert = TRUE;
RtlZeroMemory(kid, 16*sizeof(KEYBOARD_INPUT_DATA));

kid[0].MakeCode = 0x20; // simulate a ‘d’ press
kid[0].Flags = 0x00;
kid[1].MakeCode = 0x20; // simulate a ‘d’ release
kid[1].Flags = 0x01;
kid[2].MakeCode = 0x17; // simulate a ‘i’ press
kid[2].Flags = 0x00;
kid[3].MakeCode = 0x17; // simulate a ‘i’ release
kid[3].Flags = 0x01;

*InputDataConsumed = 1; // none of 0, 1, 4 works
}

(*(PSERVICE_CALLBACK_ROUTINE)
devExt->UpperConnectData.ClassService)(
devExt->UpperConnectData.ClassDeviceObject,
bInsert ? &kid[0] : InputDataStart,
bInsert ? &kid[4] : InputDataEnd,
InputDataConsumed);
}


Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256

You are currently subscribed to ntdev as: xxxxx@windows.microsoft.com
To unsubscribe send a blank email to xxxxx@lists.osr.com

This isn’t one of my areas, but I wonder about a few things in your example:

  1. What if there are multiple events passed to the filter function? Isn’t
    setting ‘consumed’ going to eat all of them? But you are really only
    processing the first event.

  2. If a real user is pressing that ‘z’ key, you are probably going to get a
    single event which is the keydown, which you consume and replace. But you
    don’t seem to do anything with the now mis-matched keyup that will come
    along later. Perhaps getting a keyup without a keydown can confuse things
    in a state machine someplace?

  3. You have set input data consumed to some value other than what it had,
    and now you pass this UP the stack as well as having returned the value down
    the stack. Possibly having the data marked consumed by a lower filter is
    confusing the upper layers?

I think if I were inserting data I might do something like making a specific
call up the stack with my inserted data, then, if there was unconsumed data
in the original packet, pass this unconsumed data up the stack in a second
call. On the first call I would probably pass a pointer to a dummy
‘consumed’ local rather than the value passed into the filter. Also, if I
consumed a keydown, I’d probably also set some state that would live long
enough to tell me that I have to consume the keyup also (and any key
repeats, if they have already been generated at this level).

Loren

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of Phil Falck
Sent: Monday, December 08, 2003 2:34 PM
To: Windows System Software Devs Interest List
Subject: [ntdev] RE: Inserting keystrokes using kbfiltr

Doron,
Sorry to bother you, but I must be doing something stupid. Here is a
simplified KbFilter_ServiceCallback() from kbfiltr.c.

In the example I am simply just trying to change a ‘z’ into a ‘d’ ‘i’.
(I.e. trying to replace a single character with a sequence of
characters).

If you could point out the error of my ways I would greatly appreciate
it.
TIA, phil

VOID
KbFilter_ServiceCallback(
IN PDEVICE_OBJECT DeviceObject,
IN PKEYBOARD_INPUT_DATA InputDataStart,
IN PKEYBOARD_INPUT_DATA InputDataEnd,
IN OUT PULONG InputDataConsumed
)
{
PDEVICE_EXTENSION devExt;
ULONG i;
KEYBOARD_INPUT_DATA kid[16];
BOOLEAN bInsert = FALSE;

devExt = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

// print out what keys came in
for (i = 0; i < (ULONG)(InputDataEnd-InputDataStart); i++)
KdPrint((“%d-MC:%02x; F:%02x; SP:%08x; EP:%08x;
Consumed:%d\r\n”,
i,
(InputDataStart+i)->MakeCode,
(InputDataStart+i)->Flags,
InputDataStart,
InputDataEnd,
*InputDataConsumed));

// look for a ‘z’ pressed, then turn it into a ‘d’ ‘i’
if ((InputDataStart->MakeCode == 0x2c)&&(InputDataStart->Flags ==
0))
{
bInsert = TRUE;
RtlZeroMemory(kid, 16*sizeof(KEYBOARD_INPUT_DATA));

kid[0].MakeCode = 0x20; // simulate a ‘d’ press
kid[0].Flags = 0x00;
kid[1].MakeCode = 0x20; // simulate a ‘d’ release
kid[1].Flags = 0x01;
kid[2].MakeCode = 0x17; // simulate a ‘i’ press
kid[2].Flags = 0x00;
kid[3].MakeCode = 0x17; // simulate a ‘i’ release
kid[3].Flags = 0x01;

*InputDataConsumed = 1; // none of 0, 1, 4 works
}

(*(PSERVICE_CALLBACK_ROUTINE)
devExt->UpperConnectData.ClassService)(
devExt->UpperConnectData.ClassDeviceObject,
bInsert ? &kid[0] : InputDataStart,
bInsert ? &kid[4] : InputDataEnd,
InputDataConsumed);
}


Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256

You are currently subscribed to ntdev as: xxxxx@windows.microsoft.com
To unsubscribe send a blank email to xxxxx@lists.osr.com


Questions? First check the Kernel Driver FAQ at
http://www.osronline.com/article.cfm?id=256

You are currently subscribed to ntdev as: xxxxx@earthlink.net
To unsubscribe send a blank email to xxxxx@lists.osr.com

There are several things about this code that could be causing problems
(see inline):

Phil Falck wrote:

// look for a ‘z’ pressed, then turn it into a ‘d’ ‘i’
if ((InputDataStart->MakeCode == 0x2c)&&(InputDataStart->Flags == 0))

First one is: you’re only checking for the Z press. This code will
allow through the release from the ‘Z’.

{
bInsert = TRUE;
RtlZeroMemory(kid, 16*sizeof(KEYBOARD_INPUT_DATA));

kid[0].MakeCode = 0x20; // simulate a ‘d’ press
kid[0].Flags = 0x00;
kid[1].MakeCode = 0x20; // simulate a ‘d’ release
kid[1].Flags = 0x01;
kid[2].MakeCode = 0x17; // simulate a ‘i’ press
kid[2].Flags = 0x00;
kid[3].MakeCode = 0x17; // simulate a ‘i’ release
kid[3].Flags = 0x01;

*InputDataConsumed = 1; // none of 0, 1, 4 works

Second problem is that InputDataConsumed is set by MOUCLASS to indicate
how many codes *it* took off the queue. Setting this here has no effect,
because it will be reset by the ClassService call. Also, this means that
you can be trashing other characters besides the ‘Z’, because MOUCLASS
will be returning (usually) 4 in InputDataConsumed (which will cause
i8042 some amount of hurt… ranging from harmless to deadly depending
on how well it handles MOUCLASS eating more bytes than it sent).

}

(*(PSERVICE_CALLBACK_ROUTINE)
devExt->UpperConnectData.ClassService)(
devExt->UpperConnectData.ClassDeviceObject,
bInsert ? &kid[0] : InputDataStart,
bInsert ? &kid[4] : InputDataEnd,
InputDataConsumed);

At the very least you need to say “if (bInsert) InputDataConsumed = 1;”
here rather than above. This will trick i8042 into only removing the “z”
keydown from its queue… I’m pretty sure it will resend the remaining
keys, but I forget exactly how it does that…

What do you want to have happen on keyboard auto-repeat, BTW?

…/ray..