https://blog.csdn.net/mergerly/article/details/4838655
通用键盘鼠标模拟(包括USB和PS2)
通过直接调用Kbdclass的回调函数KeyboardClassServiceCallback直接给上层发送键盘驱动。这个方法网上已经公开,参考Hook KeyboardClassServiceCallback实现键盘 Logger,其他的还有很多,可以到网上去查。
简单说一下没有公开的部分,就是按下和松开的模拟,已经扩展键的模拟。
模拟主要是构造KEYBOARD_INPUT_DATA结构,按下和松开的Flags分别对应KEY_MAKE、KEY_BREAK,然后调用KeyboardClassServiceCallback。这里直接用的sudami的代码,在此谢过,懒得改了。代码如下:
case IOCTL_KEY_DOWN :
{
if (ioBuf)
{
lKeyCode = *(ULONG*)ioBuf;
dprintf("[KeyMouse] KeymouseDispatchDeviceControl IOCTL_KEY_DOWN = 0x%x/n", lKeyCode);
dwSize = sizeof(KEYBOARD_INPUT_DATA);
__asm {
push eax
mov kid.UnitId,0 ; 构造 KEYBOARD_INPUT_DATA
mov eax,lKeyCode
mov kid.MakeCode,ax
mov kid.Flags,KEY_MAKE ;模拟按下
mov kid.Reserved,0
mov kid.ExtraInformation,0
lea eax,dwRet
push eax
lea eax,kid
add eax,dwSize
push eax
lea eax,kid
push eax
push g_kbDeviceObject
call orig_KeyboardClassServiceCallback ;利用 KeyboardClassServiceCallback 模拟按键
pop eax
}
status = STATUS_SUCCESS;
}
break;
}
case IOCTL_KEY_UP:
{
if (ioBuf)
{
lKeyCode = *(ULONG*)ioBuf;
dprintf("[KeyMouse] KeymouseDispatchDeviceControl IOCTL_KEY_UP = 0x%x/n", lKeyCode);
dwSize = sizeof(KEYBOARD_INPUT_DATA);
__asm {
push eax
mov kid.UnitId,0 ; 构造 KEYBOARD_INPUT_DATA
mov eax,lKeyCode
mov kid.MakeCode,ax
mov kid.Flags,KEY_BREAK ;模拟松开
mov kid.Reserved,0
mov kid.ExtraInformation,0
lea eax,dwRet
push eax
lea eax,kid
add eax,dwSize
push eax
lea eax,kid
push eax
push g_kbDeviceObject
call orig_KeyboardClassServiceCallback ;利用 KeyboardClassServiceCallback 模拟按键
pop eax
}
status = STATUS_SUCCESS;
}
break;
}
扩展键的区别是按下和松开的Flags分别对应KEY_E0、KEY_E1。其他和上面的一样,这里就不贴代码出来了。主要说一下扩展键有哪几个:(前面是MakeCode,后面代表按钮)
0x1D-RIGHT CONTROL 0x38-RIGHT ALT 0x48-↑ 键 0x50-↓ 键 0x4b-← 键 0x4d-→ 键 0x5B-LEFT WIN 0x5C-RIGHT WIN
重点说一下鼠标的模拟,原理和键盘的一样。查找驱动mouclass.sys中的MouseClassServiceCallback函数,然后获取//Device//PointerClass0设备对象指针,构造MOUSE_INPUT_DATA结构,然后调用MouseClassServiceCallback。难点就在与构造MOUSE_INPUT_DATA结构上面。
typedef struct _MOUSE_INPUT_DATA {
USHORT UnitId;
USHORT Flags;
union {
ULONG Buttons;
struct {
USHORT ButtonFlags;
USHORT ButtonData;
};
};
ULONG RawButtons;
LONG LastX;
LONG LastY;
ULONG ExtraInformation;
} MOUSE_INPUT_DATA, *PMOUSE_INPUT_DATA;
通过调试操作系统调用MouseClassServiceCallback的参数,主要的标示有3个。
Flags标志是标示鼠标的坐标属性(即相对坐标、绝对坐标等)
ButtonFlags标志是左右中键按下和松开的标志
LastX是鼠标X坐标,与Flags标志有关
LastY是鼠标Y坐标,与Flags标志有关
其他几项可以填0。
具体模拟代码如下:
case IOCTL_MOUSE_LEFT_BUTTON_DOWN:
{
MouseFlags = MOUSE_LEFT_BUTTON_DOWN;
goto __MouseCallBack;
}
case IOCTL_MOUSE_LEFT_BUTTON_UP:
{
MouseFlags = MOUSE_LEFT_BUTTON_UP;
goto __MouseCallBack;
}
case IOCTL_MOUSE_RIGHT_BUTTON_DOWN:
{
MouseFlags = MOUSE_RIGHT_BUTTON_DOWN;
goto __MouseCallBack;
}
case IOCTL_MOUSE_RIGHT_BUTTON_UP:
{
MouseFlags = MOUSE_RIGHT_BUTTON_UP;
goto __MouseCallBack;
}
case IOCTL_MOUSE_MIDDLE_BUTTON_DOWN:
{
MouseFlags = MOUSE_MIDDLE_BUTTON_DOWN;
goto __MouseCallBack;
}
case IOCTL_MOUSE_MIDDLE_BUTTON_UP:
{
MouseFlags = MOUSE_MIDDLE_BUTTON_UP;
__MouseCallBack:
mid.UnitId = 0;
mid.Flags = MOUSE_MOVE_RELATIVE;
mid.Buttons = 0;
mid.ButtonFlags = MouseFlags;
mid.RawButtons = 0;
mid.LastX = *((ULONG*)ioBuf);
mid.LastY = *((ULONG*)ioBuf+1);
mid.ExtraInformation = 0;
InputDataStart = ∣
InputDataEnd = InputDataStart+1;
orig_MouseClassServiceCallback(
g_mouDeviceObject,
InputDataStart,
InputDataEnd,
&InputDataConsumed
);
status = STATUS_SUCCESS;
break;
}
case IOCTL_MOUSE_MOVE_RELATIVE:
{
mid.Flags = MOUSE_MOVE_RELATIVE; //相对坐标
goto __MouseMoveCallBack;
}
case IOCTL_MOUSE_MOVE_ABSOLUTE:
{
mid.Flags = MOUSE_MOVE_ABSOLUTE; //绝对坐标
goto __MouseMoveCallBack;
}
case IOCTL_MOUSE_VIRTUAL_DESKTOP:
{
mid.Flags = MOUSE_VIRTUAL_DESKTOP; //虚拟桌面
__MouseMoveCallBack:
mid.UnitId = 1;
mid.Buttons = 0;
mid.RawButtons = 0;
mid.LastX = *((ULONG*)ioBuf);
mid.LastY = *((ULONG*)ioBuf+1);
mid.ExtraInformation = 0;
InputDataStart = ∣
InputDataEnd = InputDataStart+1;
orig_MouseClassServiceCallback(
g_mouDeviceObject,
InputDataStart,
InputDataEnd,
&InputDataConsumed
);
status = STATUS_SUCCESS;
break;
}
驱动在windows XP SP2上测试通过。