⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 jiurl键盘驱动 5.htm

📁 JIURL键盘驱动
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0058)http://jiurl.nease.net/cn/document/KbdDriver/JiurlKbd5.htm -->
<HTML><HEAD><TITLE>JIURL键盘驱动 5</TITLE>
<META http-equiv=Content-Type content="text/html; charset=gb2312">
<STYLE type=text/css>.title {
	FONT-WEIGHT: bold; FONT-SIZE: 21px; LINE-HEIGHT: 48px; FONT-FAMILY: "黑体", Arial, sans-serif; TEXT-DECORATION: none
}
.author {
	FONT-SIZE: 12px; LINE-HEIGHT: 16px; FONT-FAMILY: "宋体"
}
.content {
	FONT-SIZE: 14px; LINE-HEIGHT: 20px
}
</STYLE>

<META content="MSHTML 6.00.2900.2668" name=GENERATOR></HEAD>
<BODY bgColor=#f7f7f7 topMargin=5>
<DIV align=center>
<CENTER>
<TABLE height=29 cellSpacing=0 cellPadding=0 width="96%" border=0>
  <TBODY>
  <TR>
    <TD class=title width="100%" height=41>
      <P align=center><FONT face=宋体>JIURL键盘驱动 </FONT><FONT 
    face=宋体>5</FONT></P></TD></TR></CENTER>
  <TR>
    <TD class=author width="100%" height=9>
      <P align=center><FONT face=宋体>作者: <A 
      href="mailto:jiurl@mail.china.com">JIURL</A> </FONT></P></TD></TR>
  <TR>
    <TD class=author width="100%" height=6>
      <P align=center><FONT 
      face=宋体>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
      主页: <A href="http://jiurl.yeah.net/">http://jiurl.yeah.net/</A> 
    </FONT></P></TD></TR>
  <TR>
    <TD class=author width="100%" height=2>
      <P align=center><FONT face=宋体>&nbsp;&nbsp;&nbsp; 日期: 2003-12-13</FONT> 
    </P></TD></TR></TBODY></TABLE></DIV>
<DIV align=center>
<CENTER>
<TABLE height=1 cellSpacing=0 cellPadding=0 width="96%" border=0>
  <TBODY>
  <TR>
    <TD width="100%" height=1>
      <HR color=#396da5 SIZE=3>
    </TD></TR></TBODY></TABLE></CENTER></DIV>
<DIV align=center>
<TABLE class=content height=200 cellSpacing=0 cellPadding=0 width="96%" 
border=0>
  <TBODY>
  <TR>
    <TD vAlign=top width="131%" height=200>
      <P><B>7 键盘驱动的运作</B><BR><BR>7.1 输入数据队列简介<BR><BR>i8042prt 和 kbdclass 
      各有一个输入数据队列,他们是循环使用的缓冲区。他们的每个单元是一个 KEYBOARD_INPUT_DATA 结构。<BR><BR>i8042prt 
      的自定义的设备扩展中,保存着一些指针和计数值,用来使用它的那个输入数据队列。<BR>PKEYBOARD_INPUT_DATA 类型的 
      InputData , DataIn , DataOut , DataEnd。ULONG 类型的 
      InputCount。KEYBOARD_INPUT_DATA 类型的 CurrentInput 。<BR>InputData 
      这个指针,指向输入数据队列的开头。<BR>DataEnd 这个指针,指向输入数据队列的结尾。<BR>DataIn 
      这个指针,指向要进入队列的新数据,将要被放在队列中的位置。<BR>DataOut 
      这个指针,指向要出队列的数据,在队列中开始的位置。<BR>InputCount 这个值为输入数据队列中,数据的个数。<BR>CurrentInput 
      存放当前从i8042芯片中获得的数据。<BR><BR>kbdclass 
      的自定义的设备扩展中,保存着一些指针和计数值,用来使用它的那个输入数据队列。<BR>PKEYBOARD_INPUT_DATA 类型的 
      InputData , DataIn , DataOut , DataEnd。ULONG 类型的 InputCount。<BR>InputData 
      这个指针,指向输入数据队列的开头。<BR>DataEnd 这个指针,指向输入数据队列的结尾。<BR>DataIn 
      这个指针,指向要进入队列的新数据,将要被放在队列中的位置。<BR>DataOut 
      这个指针,指向要出队列的数据,在队列中开始的位置。<BR>InputCount 
      这个值为输入数据队列中,数据的个数。<BR><BR>KEYBOARD_INPUT_DATA 结构在 ntddkbd.h 
      中定义<BR><BR>typedef struct _KEYBOARD_INPUT_DATA { // 0xC<BR>USHORT 
      UnitId;<BR>USHORT MakeCode;<BR>USHORT Flags;<BR>USHORT Reserved;<BR>ULONG 
      ExtraInformation;<BR>} KEYBOARD_INPUT_DATA, 
      *PKEYBOARD_INPUT_DATA;<BR><BR><BR>7.2 
      键盘驱动对读请求的处理<BR><BR>&nbsp;&nbsp;&nbsp; 键盘驱动的应用层线程 
      win32k!RawInputThread,总是会发一个 IRP_MJ_READ 的 IRP 
      给键盘设备栈的栈顶设备对象,要求读入数据。键盘设备栈栈顶设备对象所在的驱动 kbdclass 的 
      kbdclass!KeyboardClassRead 处理这个 IRP_MJ_READ 的 IRP。在初始化过程中 
      win32k!RawInputThread 发第一个 IRP_MJ_READ 的 IRP。之后,每当这个 IRP_MJ_READ 的 IRP 
      得到数据结束返回给应用层之后,应用层又会再发一个 IRP_MJ_READ 的 IRP。<BR><BR>我们使用WinDbg的!irp命令,看看 
      kbdclass!KeyboardClassRead 传入的参数 Irp<BR><BR>kd&gt; !irp fe43e008<BR>Irp is 
      active with 6 stacks 6 is current (= 0xfe43e12c)<BR>No Mdl System buffer = 
      fe42d868 Thread fe42ed60: Irp stack trace.&nbsp;<BR>cmd flg cl Device File 
      Completion-Context<BR>[ 0, 0] 0 0 00000000 00000000 
      00000000-00000000&nbsp;<BR><BR>Args: 00000000 00000000 00000000 
      00000000<BR>[ 0, 0] 0 0 00000000 00000000 
      00000000-00000000&nbsp;<BR><BR>Args: 00000000 00000000 00000000 
      00000000<BR>[ 0, 0] 0 0 00000000 00000000 
      00000000-00000000&nbsp;<BR><BR>Args: 00000000 00000000 00000000 
      00000000<BR>[ 0, 0] 0 0 00000000 00000000 
      00000000-00000000&nbsp;<BR><BR>Args: 00000000 00000000 00000000 
      00000000<BR>[ 0, 0] 0 0 00000000 00000000 
      00000000-00000000&nbsp;<BR><BR>Args: 00000000 00000000 00000000 
      00000000<BR>&gt;[ 3, 0] 0 0 fe4f5df0 fe42d908 
      00000000-00000000&nbsp;<BR>\Driver\Kbdclass<BR>Args: 00000078 00000000 
      00000000 00000000<BR><BR>查看当前的 IO_STACK_LOCATION 中的详细内容<BR><BR>kd&gt; 
      !strct io_stack_location fe43e12c<BR>struct _IO_STACK_LOCATION 
      (sizeof=36)<BR>+00 byte MajorFunction = 03 .<BR>+01 byte MinorFunction = 
      00 .<BR>+02 byte Flags = 00 .<BR>+03 byte Control = 00 .<BR>+04 union 
      __unnamed19 Parameters<BR>+04 struct __unnamed23 Read<BR>+04 uint32 Length 
      = 00000078<BR>+08 uint32 Key = 00000000<BR>+0c union _LARGE_INTEGER 
      ByteOffset<BR>+0c uint32 LowPart = 00000000<BR>+10 int32 HighPart = 
      00000000<BR>+0c struct __unnamed3 u<BR>+0c uint32 LowPart = 
      00000000<BR>+10 int32 HighPart = 00000000<BR>+0c int64 QuadPart = 
      0000000000000000<BR>+14 struct _DEVICE_OBJECT *DeviceObject = 
      FE4F5DF0<BR>+18 struct _FILE_OBJECT *FileObject = FE42D908<BR>+1c function 
      *CompletionRoutine = 00000000<BR>+20 void *Context = 
      00000000<BR><BR>我们看到当前 IO_STACK_LOCATION 的 MajorFunction 为 0x03,即 
      IRP_MJ_READ。Parameters.Read.Length 为 0x78,表明 
      Irp-&gt;AssociatedIrp.SystemBuffer 指向0x78字节大小的空间,一个 KEYBOARD_INPUT_DATA 
      的大小为 0xc,所以 Irp-&gt;AssociatedIrp.SystemBuffer 
      中最多可以放10(十进制)个KEYBOARD_INPUT_DATA。<BR><BR>我这里 kbdclass 和 i8042prt 
      的输入数据队列的大小都为 0x64*sizeof(KEYBOARD_INPUT_DATA),即100(十进制)个 
      KEYBOARD_INPUT_DATA 的大小。<BR><BR>kbdclass!KeyboardClassRead 中,<BR>调用 
      IoMarkIrpPending(Irp) ,使这个 IRP pending,然后调用 IoStartPacket 。IoStartPacket 
      将调用驱动的 StartIo 例程,kbdclass 的 StartIo 例程是 
      KeyboardClassStartIo。<BR><BR>kbdclass!KeyboardClassStartIo 的作用是,检查 
      kbdclass 的输入数据队列中是否有数据,如果有的话,直接从 kbdclass 
      的输入数据队列中,取出数据,满足读请求。<BR><BR>kbdclass 的输入数据队列中的数据是从哪里来的呢?如果从 i8042prt 
      的输入数据队列中来的输入数据太多,一个 IRP_MJ_READ 的 IRP 读不完的话,则 IRP 读走它所能读的 10 个 
      KEYBOARD_INPUT_DATA,而剩余的数据就会被放入 kbdclass 的输入数据队列中。通常情况下,从 i8042prt 
      的输入数据队列中来的输入数据都能被一个 IRP_MJ_READ 的 IRP 
      全部取走,不会有剩余。<BR><BR>kbdclass!KeyboardClassStartIo 中,<BR>判断 
      deviceExtension-&gt;InputCount 是否为0。deviceExtension-&gt;InputCount 中保存着 
      kbdclass 的输入数据队列的中数据的个数。也就是判断 kbdclass 的输入数据队列中是否有数据。<BR>没有数据的话,通常是这种情况,设置 
      deviceExtension-&gt;RequestIsPending 为 TRUE,返回。<BR>有数据的话,调用 RtlMoveMemory 
      把kbdclass的输入数据队列中的数据复制到 Irp-&gt;AssociatedIrp.SystemBuffer 中,设置 
      deviceExtension-&gt;RequestIsPending 为 FALSE,调用 IoCompleteRequest 结束这个 
      IRP_MJ_READ 的 IRP。<BR><BR>简单的说,应用层发来一个 IRP_MJ_READ 的 IRP 要求读数据。<BR>如果 
      kbdclass 的输入数据队列中没有数据的话,这个 IRP_MJ_READ 的 IRP 就 pending 
      在那里,等待键盘上的键被按下,产生输入数据。通常都是这种情况。<BR>如果 kbdclass 
      的输入数据队列中有数据的话,也就是说键盘的输入数据太多,上一个 IRP_MJ_READ 的 IRP 没有取完,剩下的放在了 kbdclass 
      的输入数据队列中,那么现在这个 IRP_MJ_READ 的 IRP 从 kbdclass 
      的输入数据队列中直接取出数据,然后被完成。当我们按住一个键不放的时候,就能看到这种情况。<BR><BR>7.3 
      键盘驱动的中断<BR><BR>键盘驱动在初始化的过程中,调用 IoConnectInterrupt 连接了键盘的驱动。其中调用时的参数 
      ServiceRoutine 为 I8042KeyboardInterruptService ,参数 Vector 为 
      0xb3。<BR><BR>我们使用 WinDbg 的 !idt 命令,查看这时的中断描述符表<BR><BR>kd&gt; 
      !idt<BR><BR>IDT for processor #0<BR><BR>00: 804625e6 
      (nt!KiTrap00)<BR>...<BR>b3: fe4cf264 
      (Vector:b3,Irql:a,SyncIrql:a,Connected:TRUE,No:0,ShareVector:FALSE,Mode:Latched,ISR:i8042prt!I8042KeyboardInterruptService(fe1c1750))<BR>b4: 
      80461138 (nt!KiUnexpectedInterrupt132)<BR>...<BR>ff: 804613f0 
      (nt!KiUnexpectedInterrupt207)<BR><BR>可以看到中断向量为 b3 的中断服务例程的入口地址为 
      fe4cf264,而并不是 i8042prt!I8042KeyboardInterruptService 的入口地址 
      fe1c1750。那么这个入口地址是什么呢?原来,<BR><BR>调用 IoConnectInterrupt ,将产生一个 KINTERRUPT 
      结构,中断描述符表中保存的中断服务例程的入口地址就是这个 KINTERRUPT 结构的 DispatchCode 的地址。中断时,执行这个 
      DispatchCode ,DispatchCode 进行些处理后,会调用 nt!KiInterruptDispatch ,而 
      nt!KiInterruptDispatch 根据 KINTERRUPT 结构中的 ServiceRoutine ,也就是 
      IoConnectInterrupt 的参数 ServiceRoutine 的入口地址,调用驱动中的服务例程。<BR><BR>我们从 
      http://www.insidewindows.info/ntifs.h 可以找到 KINTERRUPT 的结构定义<BR><BR>typedef 
      struct _KINTERRUPT { // Size: 0x1E4<BR>/*000*/ CSHORT Type;<BR>/*002*/ 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -