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

📄 csdn技术中心 jiurl键盘驱动 2.htm

📁 arm体系结构和编程,一份很好的ARM汇编编程资料
💻 HTM
📖 第 1 页 / 共 4 页
字号:
                  PUNICODE_STRING ObjectName 。然后调用 ZwCreateFile。ZwCreateFile 
                  完成得到句柄的工作,最后通过传入的参数返回得到的句柄。win32k!RawInputThread 
                  把得到的句柄保存起来,供后面的 ReadFile, 
                  DeviceIoControl等使用。<BR><BR>ZwCreateFile 通过系统服务,调用内核中的 
                  NtCreateFile。NtCreateFile 执行到 nt!IopParseDevice 中 ,<BR>调用 
                  nt!IoGetAttachedDevice ,通过 PDO 
                  的设备对象获得键盘设备栈最顶端的设备对象。用得到的这个设备对象的 +30 char StackSize 作为参数来 
                  IoAllocateIrp,创建 IRP。调用 nt!ObCreateObject 创建文件对象。初始化这个文件对象,+04 
                  struct _DEVICE_OBJECT *DeviceObject 赋值为键盘设备栈的 PDO。调用 
                  nt!IopfCallDriver,将 IRP 发往驱动,让驱动进行相应的处理。之后一系列返回,回到 
                  nt!ObOpenObjectByName。在 nt!ObOpenObjectByName 中继续执行,调用 
                  nt!ObpCreateHandle 
                  在进程(csrss.exe)的句柄表中创建一个新的句柄,这个句柄对应的对象是刚才创建初始化的那个文件对象,文件对象中的 
                  DeviceObject 指向键盘设备栈的 PDO。在 nt!ObpCreateHandle 前后,我们使用命令 
                  !handle 0 3 a0 (a0 为此时 csrss.exe进程的进程id),观察 csrss.exe进程 
                  句柄表的前后变化,看到了多出来的那一个文件对象。<BR><BR>3.3 win32k!RawInputThread 
                  如何从键盘驱动得到按键的数据<BR><BR>&nbsp;&nbsp;&nbsp; win32k!RawInputThread 
                  在获得了句柄之后,会以这个句柄为参数,调用 nt!ZwReadFile,向键盘驱动要求读入数据。nt!ZwReadFile 
                  中会创建一个 IRP_MJ_READ 的 IRP 发给键盘驱动,告诉键盘驱动要求读入数据。键盘驱动通常会使这个 IRP 
                  Pending (通常情况下是这样,详细的情况我们在键盘驱动部分讨论)。也就是说这个 IRP_MJ_READ 
                  不会被满足,它会一直被放在那里,等待着来自键盘的数据。而发出这个读请求的线程 win32k!RawInputThread 
                  也会等待,等待着这个读操作的完成。<BR><BR>&nbsp;&nbsp;&nbsp; 命中注定,这个 IRP 
                  匆匆的出现,然后用它一生中绝大部分时间,开始一个静静的等待,而当它等到的时候,它就会匆匆的消失。它的一生或许很短,或许很长,取决于它所等待着的出现。它在平静的等待着什么呢?<BR><BR>&nbsp;&nbsp;&nbsp; 
                  它在等待着你,按下键盘上的键。我们来说明一下键盘数据的源头,键盘数据的源头就是键盘,当键盘上有键被按下时,就产生了那个 
                  IRP_MJ_READ IRP 等待着的对象。<BR><BR>&nbsp;&nbsp;&nbsp; 
                  当键盘上有键被按下时,将触发键盘的那个中断,引起中断服务例程的执行,键盘中断的中断服务例程由键盘驱动提供。键盘驱动从端口读取扫描码,经过一些列的处理之后,最后把从键盘得到的数据交给 
                  IRP,然后结束这个 IRP。<BR><BR>&nbsp;&nbsp;&nbsp; 这个 IRP 的结束,将导致 
                  win32k!RawInputThread 线程对这个读操作的等待结束。win32k!RawInputThread 
                  线程将会对得到的数据作出处理,分发给合适的进程。一旦把输入数据处理完之后,win32k!RawInputThread 
                  线程会立刻再调用一个 
                  nt!ZwReadFile,向键盘驱动要求读入数据。于是又开始一个等待,等待着键盘上的键被按下。<BR><BR>&nbsp;&nbsp;&nbsp; 
                  简单的说,win32k!RawInputThread 线程总是 nt!ZwReadFile 
                  要求读入数据。然后等待键盘上的键被按下。当键盘上的键被按下,win32k!RawInputThread 处理 
                  nt!ZwReadFile 得到的数据,然后再 nt!ZwReadFile 
                  要求读入数据,再等待键盘上的键被按下。<BR><BR>&nbsp;&nbsp;&nbsp; 
                  上面所介绍的内容,是当我在看到键盘驱动对于 IRP_MJ_READ 的处理,在 
                  kbdclass!KeyboardClassRead 中,IRP 并没有获得数据,而是被 IoMarkIrpPending 
                  时,想了想,了解到的。<BR><BR>&nbsp;&nbsp;&nbsp; 我简单的跟了一下 
                  win32k!RawInputThread 
                  从键盘驱动获得按键数据的过程,下面我对这个过程做一个简单的介绍。<BR><BR>&nbsp;&nbsp;&nbsp; 
                  首先我们看看断在 kbdclass!KeyboardClassRead 时的 call stack ,看看引起 
                  kbdclass!KeyboardClassRead 的整个调用过程。<BR><BR># ChildEBP RetAddr 
                  Args to Child&nbsp;<BR>00 f90ef8dc 8041f54b fe4f5df0 fe43e9a8 
                  fe43e9a8 kbdclass!KeyboardClassRead(struct _DEVICE_OBJECT * 
                  Device = 0xfe4f5df0, struct _IRP * Irp = 0xfe43e9a8) (CONV: 
                  stdcall)<BR>01 f90ef8f0 804ba5e8 fe43eacc fe43e9a8 00000000 
                  nt!IopfCallDriver+0x35 (FPO: [0,0,2])<BR>02 f90ef904 804a2d4c 
                  fe4f5df0 fe43e9a8 fe42d668 nt!IopSynchronousServiceTail+0x60 
                  (FPO: [Non-Fpo])<BR>03 f90ef9d8 80461691 000000d4 00000000 
                  a005c962 nt!NtReadFile+0x5f4 (FPO: [Non-Fpo])<BR>04 f90ef9d8 
                  804011d5 000000d4 00000000 a005c962 nt!KiSystemService+0xc4 
                  (FPO: [0,0] TrapFrame @ f90efa04)<BR>05 f90efa74 a005c91d 
                  000000d4 00000000 a005c962 nt!ZwReadFile+0xb (FPO: 
                  [9,0,0])<BR>06 f90efaa8 a005c991 e1971008 fe43e9e8 80430982 
                  win32k!StartDeviceRead+0x8c (FPO: [1,0,3])<BR>07 f90efab4 
                  80430982 e1971008 e1971028 00000000 win32k!InputApc+0x41 (FPO: 
                  [3,0,1])<BR>08 f90efae8 80403a44 00000000 00000000 00000000 
                  nt!KiDeliverApc+0xdb (FPO: [Non-Fpo])<BR>09 f90efb08 8042d33d 
                  80400b46 00000001 00000000 nt!KiSwapThread+0xfc (FPO: [EBP 
                  0xf90efb3c] [0,0,4])<BR>0a f90efb3c a000eaf5 00000004 fe42e5a8 
                  00000001 nt!KeWaitForMultipleObjects+0x266 (FPO: 
                  [Non-Fpo])<BR>0b f90efda8 804524f6 00000002 00000000 00000000 
                  win32k!RawInputThread+0x3c2 (FPO: [Non-Fpo])<BR>0c f9dc 
                  80465b62 a000e7cd f8d5f7d0 00000000 
                  nt!PspSystemThreadStartup+0x69 (FPO: [Non-Fpo])<BR>0d 00000000 
                  f000ff53 f000e2c3 f000ff53 f000ff53 
                  nt!KiThreadStartup+0x16<BR>WARNING: Frame IP not in any known 
                  module. Following frames may be wrong.<BR>0e f000ff53 00000000 
                  00000000 00000000 00000000 
                  0xf000ff53<BR><BR>&nbsp;&nbsp;&nbsp; 线程 win32k!RawInputThread 
                  调用 nt!ZwReadFile 
                  要求读入数据。<BR><BR>NTSTATUS&nbsp;<BR>ZwReadFile(<BR>IN HANDLE 
                  FileHandle,<BR>IN HANDLE Event OPTIONAL,<BR>IN PIO_APC_ROUTINE 
                  ApcRoutine OPTIONAL,<BR>IN PVOID ApcContext OPTIONAL,<BR>OUT 
                  PIO_STATUS_BLOCK IoStatusBlock,<BR>OUT PVOID Buffer,<BR>IN 
                  ULONG Length,<BR>IN PLARGE_INTEGER ByteOffset OPTIONAL,<BR>IN 
                  PULONG Key OPTIONAL<BR>);<BR><BR>我们看到调用时,参数 FileHandle 正是前面 
                  win32k!RawInputThread 用 ZwCreateFile 得到的句柄。而参数 ApcRoutine 为 
                  win32k!InputApc 的入口地址。也就是说当 ReadFile 的 IRP 结束时,win32k!InputApc 
                  将被调用。ZwReadFile 通过系统服务,最终调用 
                  nt!NtReadFile。<BR><BR>nt!NtReadFile 
                  中。作为参数传入的那个句柄,对应着一个文件对象,这个文件对象中保存着键盘设备栈中的 PDO 
                  的设备对象的指针。以这个句柄为参数调用 
                  ObReferenceObjectByHandle,获得句柄对应的文件对象。之后用得到的文件对象做参数调用 
                  IoGetRelatedDeviceObject 
                  ,这将获得键盘设备栈中最顶端的设备对象的指针。用得到的键盘设备栈中最顶端的设备对象的 +30 char StackSize 
                  作参数,调用 nt!IoAllocateIrp,构造 IRP ,然后初始化这个 IRP,用传入的参数设置这个 
                  IRP。然后调用 IoCallDriver ,将这个 IRP 发给键盘驱动。<BR><BR>键盘驱动通常会调用 
                  IoMarkIrpPending 使这个 IRP 
                  Pending。通常情况下是这样,详细的情况我们在键盘驱动部分讨论。于是这个 IRP 就在那里等待。关于这个等待的 IRP 
                  ,我们可以使用 WinDbg 的 !irpfind 命令找到它。反过来这也解释了,我们使用 !irpfind 
                  为什么总能看到一个 pending 的发给键盘设备栈栈顶的 IRP_MJ_READ 的 IRP。<BR><BR>kd&gt; 
                  !irpfind<BR>unable to get large pool allocation table - either 
                  wrong symbols or pool tagging is disabled<BR><BR>Searching 
                  NonPaged pool (fe313000 : fe52b000) for Tag: Irp?<BR><BR>Irp [ 
                  Thread ] irpStack: (Mj,Mn) DevObj [Driver]<BR>...<BR>fe439008 
                  [fe427940] irpStack: ( 3, 0) fe4f5df0 [ 
                  \Driver\Kbdclass]<BR>...<BR><BR>这个 IRP 的地址为 fe439008 
                  ,我们看看它的详细情况<BR><BR>kd&gt; !irp fe439008<BR>Irp is active with 
                  6 stacks 6 is current (= 0xfe43912c)<BR>No Mdl System buffer = 
                  fe426568 Thread fe427940: 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 1 fe4f5df0 fe426608 
                  00000000-00000000 pending<BR>\Driver\Kbdclass<BR>Args: 
                  00000078 00000000 00000000 00000000<BR>看到了这个 IRP 
                  pending。<BR><BR>这时 线程 win32k!RawInputThread 会在一个 
                  nt!KeWaitForMultipleObjects 上等待,等待的对象之一就是,希望从键盘驱动中读数据的那个 IRP 
                  。<BR><BR>当键盘上有键被按下时,引发中断,导致驱动从端口读取按键的扫描码。驱动经过一系列处理,最后调用 
                  IoCompleteRequest 结束那个等待着的 IRP。<BR><BR>IRP 的结束会使得 线程 
                  win32k!RawInputThread 在 nt!KeWaitForMultipleObjects 
                  上对从键盘读取数据的等待的结束。这将使得前面 ZwReadFile 的传入参数 ApcRoutine 即 
                  win32k!InputApc 被执行。<BR><BR>win32k!InputApc 
                  中。调用两个函数,win32k!ProcessKeyboardInput,win32k!StartDeviceRead。win32k!ProcessKeyboardInput 
                  负责处理刚才读到的输入数据,比如分发给应该得到这个键盘按键的进程。数据处理完之后,也就是 
                  win32k!ProcessKeyboardInput 结束之后。win32k!StartDeviceRead 
                  被调用,win32k!StartDeviceRead 会调用 nt!ZwReadFile 
                  要求读入数据。<BR><BR>3.4 补充<BR><BR>&nbsp;&nbsp;&nbsp; win32k 
                  实际是一个驱动程序,不属于应用程序,所以把 win32k!RawInputThread 叫做键盘驱动的使用层或许更合适。至于 
                  win32k!RawInputThread 如何把得到的键盘上的按键分发给各个进程,我们不研究。曾经使我奇怪的是,为什么 
                  ZwCreateFile 的参数,能找到的设备对象是键盘设备栈的 PDO,而 ZwCreateFile 产生的 IRP 
                  却是发给键盘设备栈的栈顶。为什么 ZwReadFile 句柄所找到的设备对象是键盘设备栈的 PDO,而 ZwReadFile 
                  产生的 IRP 却是发给键盘设备栈最顶端的设备对象。后来跟踪 NtCreateFile,NtReadFile 
                  找到了原因。从中我们也可以看出,CreateFile,ReadFile,WriteFile,DeviceIoControl,CloseHandle 
                  产生的 IRP 都是发给设备栈的栈顶的,然后 IRP 
                  在设备栈上自上而下。<BR><BR><BR>欢迎交流,欢迎交朋友,<BR>欢迎访问<BR>主页 <A 
                  href="http://jiurl.yeah.net/" 
                  target=_blank>http://jiurl.yeah.net/</A> <A 
                  href="http://jiurl.nease.net/" 
                  target=_blank>http://jiurl.nease.net/</A> 论坛 <A 
                  href="http://jiurl.cosoft.org.cn/forum" 
                  target=_blank>http://jiurl.cosoft.org.cn/forum</A> 
                  <P>f啊k,不带你们这样的啊,有好事不叫我。 
                  <P>  
                  <P>  </P></TD></TR></TBODY></TABLE></DIV></SPAN><BR>
            <DIV 
            style="FONT-SIZE: 14px; LINE-HEIGHT: 25px"><STRONG>作者Blog:</STRONG><A 
            id=ArticleContent1_ArticleContent1_AuthorBlogLink 
            href="http://blog.csdn.net/imquestion/" 
            target=_blank>http://blog.csdn.net/imquestion/</A></DIV>
            <DIV 
            style="FONT-SIZE: 14px; COLOR: #900; LINE-HEIGHT: 25px"><STRONG>相关文章</STRONG></DIV>
            <TABLE id=ArticleContent1_ArticleContent1_RelatedArticles 
            cellSpacing=0 border=0>
              <TBODY>
              <TR>
                <TD><A 
                  href="http://dev.csdn.net/article/25/25628.shtm">gzip原理与实现</A> 
                </TD></TR>
              <TR>
                <TD><A href="http://dev.csdn.net/article/25/25053.shtm">Gzip 
                  Zlib PNG 压缩算法 Gzip源码详解</A> </TD></TR>
              <TR>
                <TD><A 
                  href="http://dev.csdn.net/article/22/22763.shtm">JIURL键盘驱动 
                  3</A> </TD></TR>
              <TR>
                <TD><A href="http://dev.csdn.net/article/22/22726.shtm">IRP 
                  乱杂谈</A> </TD></TR>
              <TR>
                <TD><A 
                  href="http://dev.csdn.net/article/22/22707.shtm">JIURL键盘驱动 
                  2</A> </TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><A 
      name=#Comment></A>
      <TABLE cellPadding=0 width="100%" border=0>
        <TBODY>
        <TR>
          <TD>
            <TABLE cellSpacing=0 cellPadding=0 width="100%" align=center 
            bgColor=#006699 border=0>
              <TBODY>
              <TR bgColor=#006699>
                <TD id=white align=middle width=556 bgColor=#006699><FONT 
                  color=#ffffff>对该文的评论</FONT> </TD></TR></TBODY></TABLE>
            <DIV align=right><A id=CommnetList1_CommnetList1_Morelink 
            href="http://comment.csdn.net/Comment.aspx?c=2&amp;s=22707">【评论】</A> 
            <A id=CommnetList1_CommnetList1_Hyperlink1 
            href="javascript:window.close();">【关闭】</A> <A 
            href="mailto:webmaster@csdn.net">【报告bug】</A> 
      </DIV><BR></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></FORM><!-- 版权 -->
<DIV align=center>
<SCRIPT language=JavaScript src="CSDN技术中心 JIURL键盘驱动 2.files/footer_gb.js" 
type=text/javascript></SCRIPT>
</DIV><!-- /版权 -->
<SCRIPT>
      document.write("<img src=http://count.csdn.net/count/pageview1.asp?columnid=4&itemid=11 border=0 width=0 height=0>");
    </SCRIPT>

<SCRIPT>document.write("<img src=http://counter.csdn.net/pv.aspx?id=37 border=0 width=0 height=0>");</SCRIPT>

<SCRIPT language=JavaScript 
src="CSDN技术中心 JIURL键盘驱动 2.files/common.htm"></SCRIPT>
</BODY></HTML>

⌨️ 快捷键说明

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