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

📄 jiurl键盘驱动 2.htm

📁 JIURL键盘驱动
💻 HTM
📖 第 1 页 / 共 3 页
字号:
      创建文件对象。初始化这个文件对象,+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>
<SCRIPT src="JIURL键盘驱动 2.files/nnselect.js"></SCRIPT>
</BODY></HTML>

⌨️ 快捷键说明

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