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

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

📁 arm体系结构和编程,一份很好的ARM汇编编程资料
💻 HTM
📖 第 1 页 / 共 4 页
字号:
                  NtCreateFile,NtReadFile 等函数中,会创建一个 IRP,并用传入的参数初始化这个 IRP,然后将这个 
                  IRP 发给驱动,让驱动做处理。相应的 NtCreateFile 产生 IRP_MJ_CREATE 的 IRP 
                  ,NtReadFile 产生 IRP_MJ_READ 的 IRP。驱动得到这些 IRP ,根据情况做处理,对于 
                  IRP_MJ_READ ,会调用驱动中处理 IRP_MJ_READ 
                  的部分,可能最后引起读硬件的操作。<BR><BR><BR>2.2 
                  获得指定驱动的句柄<BR><BR>&nbsp;&nbsp;&nbsp; 
                  对于希望被应用程序使用的驱动,会在初始化的过程中,把能找到它设备对象的一个 SymbolicLink 
                  放在对象管理器命名空间(Object Manager Namespace)的 \??\ 下。这样用 
                  "\\\\.\\那个SymbolicLink的名字" 作为 CreateFile 的 lpFileName 参数,调用 
                  CreateFile ,得到的句柄就可以找到相应的驱动的那个设备对象(\\.\ 会被转换成 
                  \??\)。之后以这个句柄为参数使用 ReadFile,WriteFile,DeviceIoControl,产生的 IRP 
                  就被发到相应的设备对象。也就是说只要驱动把 设备对象的 SymbolicLink 放在 \??\ 下,并且应用程序知道这个 
                  SymbolicLink 的名字,就可以使用 CreateFile 得到相应的句柄。<BR><BR>HANDLE 
                  CreateFile(<BR>LPCTSTR lpFileName, // file name<BR>DWORD 
                  dwDesiredAccess, // access mode<BR>DWORD dwShareMode, // share 
                  mode<BR>LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 
                  SD<BR>DWORD dwCreationDisposition, // how to create<BR>DWORD 
                  dwFlagsAndAttributes, // file attributes<BR>HANDLE 
                  hTemplateFile // handle to template 
                  file<BR>);<BR><BR><BR>可以使用工具 WinObj 来查看 对象管理器命名空间(Object 
                  Manager Namespace) 。WinObj 可以从 http://www.sysinternals.com 
                  获得。关于内核对象和命名地址空间的详细介绍,可以参考《 JIURL玩玩Win2k 对象 
                  》,这篇文章可以在我的主页上找到。<BR><BR>在驱动的初始化过程中,会通过调用 IoCreateDevice 
                  创建设备对象,可以指定一个设备名作为IoCreateDevice 
                  的参数(也可以不指定,那样这个设备对象就没有名字)。这样这个设备对象会被放在 对象管理器命名空间(Object 
                  Manager Namespace)的 \Device\ 下。不过应用程序只能访问命名空间的 \??\ 
                  ,所以如果驱动希望把设备对象暴露给应用程序的话,会为设备对象创建一个 SymbolicLink 放在 \??\ 下。对于放在 
                  \Device\ 下的有名字的设备,其他驱动程序如果知道它的名字,就可以使用 
                  IoGetDeviceObjectPointer 得到这个设备对象的指针。<BR><BR>驱动可以通过 
                  IoCreateSymbolicLink ,在 \??\ 下建立设备对象的 SymbolicLink 
                  。这样应用程序必须要也知道该 SymbolicLink 的名字,然后就可以以这个符号链接名做参数使用 CreateFile 
                  ,得到句柄。从 IoCreateSymbolicLink 的参数,我们可以知道,只能使用 
                  IoCreateSymbolicLink 为有名字的设备对象建立 
                  SymbolicLink。<BR><BR>另一种方法是,使用一个 GUID 来标识一个设备接口。驱动使用标识设备的 GUID 
                  做参数调用IoRegisterDeviceInterface ,然后使用 IoSetDeviceInterfaceState 
                  ,就会为设备对象在 \??\ 下产生一个符号链接(SymbolicLink)。应用程序使用同一个 GUID 
                  做参数,使用API: SetupDiGetClassDevs, SetupDiEnumDeviceInterfaces, 
                  SetupDiGetDeviceInterfaceDetail 就可以得到创建的 \??\ 
                  下的符号链接名,就可以以这个符号链接名做参数使用 CreateFile 
                  ,得到句柄。<BR><BR>&nbsp;&nbsp;&nbsp; 
                  句柄简介。每个进程都有一个自己的句柄表,句柄表中放着内核对象的指针,句柄就是内核对象在句柄表中的索引。通过句柄就可以在进程的句柄表中找到对应的内核对象的指针。关于句柄的详细介绍,可以参考 
                  《 JIURL玩玩Win2k 进程线程篇 HANDLE_TABLE 》,这篇文章可以在我的主页上找到。<BR><BR>2.3 
                  一些结论<BR><BR>&nbsp;&nbsp;&nbsp; SymbolicLink 对象可以找到相应的 
                  设备对象。SymbolicLink 对象中保存着相应的 设备对象的地址。设备对象不保存它的 SymbolicLink 
                  对象的任何信息。SymbolicLink 对象的地址保存在对象管理器命名空间(Object Manager 
                  Namespace)中。也就是说只要知道 SymbolicLink 的名字,就可以在对象管理器命名空间中找到它。应用程序 
                  CreateFile 得到的句柄,通过这个句柄在进程的句柄表中找到的是一个文件对象(File Object)。文件对象 
                  对应的 设备对象 中不保存这个文件对象的任何信息。对应的 SymbolicLink 
                  对象中也不保存这个文件对象的任何信息。这个文件对象的地址,保存在应用程序的句柄表中,应用程序通过句柄可以找到这个文件对象。这个 
                  文件对象 中保存着对应的 设备对象 的地址。可以猜到,应用程序在用 CreateFile 创建的时候,会根据参数中的 
                  SymolicLink 名字,找到 SymolicLink 对象,进而找到该对象中保存的 设备对象 的地址,然后直接把找到的 
                  设备对象 的地址保存在文件对象中。文件对象的 +04 struct _DEVICE_OBJECT *DeviceObject 
                  处,保存着对应的设备对象的地址。<BR><BR>&nbsp;&nbsp;&nbsp; 
                  对于需要暴露接口给应用程序的驱动。首先,驱动中需要在 对象管理器命名空间的 \??\ 下,为设备对象建立一个 
                  SymbolicLink ,不管采取何种方式。之后,应用程序要知道这个 SymbolicLink 
                  的名字,不管采取何种方式。然后应用程序以 "\\\\.\\那个SymbolicLink的名字" 为参数使用 
                  CreateFile 得到一个句柄。这样,之后的 
                  DeviceIoControl(),WriteFile(),ReadFile() 使用前面用 CreateFile 
                  得到的句柄作为参数,他们可以通过这个句柄,找到对应的文件对象,而这个文件对象中保存有对应的 设备对象 的指针,这样就可以将 
                  IRP 发到这些设备。&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp; 
                  上面的结论是通过对一个小例子进行观察得到的。<BR><BR>2.4 IRP 
                  将被发往设备栈的栈顶<BR><BR>&nbsp;&nbsp;&nbsp; IRP 
                  将无论如何被发往设备栈的顶。CreateFile,ReadFile,WriteFile,DeviceIoControl,CloseHandle。他们最终都会产生一个 
                  IRP,发给一个设备对象。对于 CreateFile 来说通过 SymbolicLink的名字 
                  来找到一个设备对象。对于其他的几个函数,通过句柄,找到一个文件对象,文件对象中保存有设备对象的指针。不过产生的 IRP 
                  并不一定发给找到的这个设备对象,而是发给找到的这个设备对象所在设备栈的最顶上的一个设备对象。<BR><BR>&nbsp;&nbsp;&nbsp; 
                  而且通常我们传给 
                  CreateFile,ReadFile,WriteFile,DeviceIoControl,CloseHandle 
                  的参数所找到的那个设备对象,会是它所在设备栈的 PDO(也就是它所在设备栈最底下的一个设备对象)。CreateFile, 
                  ReadFile, WriteFile, DeviceIoControl, CloseHandle 
                  会首先通过找到的这个设备对象,获得它所在设备栈中最顶端的那个设备对象,然后将 IRP 
                  发向设备栈的最顶端的那个设备对象。所以不管我们通过参数找到的设备对象在它所在的设备栈中处于什么位置,顶端,中间,底下,不管处在什么位置,IRP 
                  都会发往这个设备栈的栈顶。<BR><BR>&nbsp;&nbsp;&nbsp; 上面的内容是通过跟踪 
                  nt!NtCreateFile 和 nt!NtReadFile 发现的。<BR><BR>2.5 
                  nt!NtCreateFile简介<BR><BR>&nbsp;&nbsp;&nbsp; 我们简单介绍一下 
                  nt!NtCreateFile。<BR><BR>通过一系列的调用 nt!NtCreateFile 最终会调用 
                  nt!IopParseDevice。下面的 call stack 
                  显示了这个调用过程。<BR><BR>...<BR>nt!IopParseDevice+0xa04<BR>nt!ObpLookupObjectName+0x4c4<BR>nt!ObOpenObjectByName+0xc5<BR>nt!IoCreateFile+0x3ec<BR>nt!NtCreateFile+0x2e<BR>nt!KiSystemService+0xc4<BR>...<BR><BR>在 
                  nt!IopParseDevice 中&nbsp;<BR><BR>调用 nt!IoGetAttachedDevice 
                  ,获得设备栈最顶端的设备对象。调用 IoAllocateIrp 创建 IRP。调用 nt!ObCreateObject 
                  创建文件对象。初始化这个文件对象。该文件对象的 +04 struct _DEVICE_OBJECT 
                  *DeviceObject 赋值为通过传入参数找到的那个设备对象。调用 nt!IopfCallDriver,也就是 
                  IoCallDriver,将 IRP 发给设备栈的栈顶。<BR><BR>驱动处理完这个 IRP 之后,返回 
                  nt!IopParseDevice 继续执行。nt!IopParseDevice 一路返回到 
                  nt!ObOpenObjectByName。在 nt!ObOpenObjectByName 中继续执行,调用 
                  nt!ObpCreateHandle 
                  在进程的句柄表中创建一个新的句柄,这个句柄对应的对象是刚才创建初始化的那个文件对象。<BR><BR>2.6 
                  nt!NtReadFile简介<BR><BR>&nbsp;&nbsp;&nbsp; 我们简单介绍一下 
                  nt!NtReadFile。传入参数中有前面 CreateFile 
                  打开的句柄,通过句柄可以在进程句柄表中找到一个文件对象,在这个文件对象中保存有一个设备对象的指针。调用 
                  IoGetRelatedDeviceObject 获得这个设备对象所在设备栈栈顶的设备对象。调用 IoAllocateIrp 
                  创建 IRP。初始化这个 IRP ,并根据传入的参数,设置好这个 IRP。然后调用 IoCallDriver 将这个 IRP 
                  发给设备对象,让驱动进行处理。发往的这个设备对象就是前面使用 IoGetRelatedDeviceObject 
                  所得到的设备栈栈顶的设备对象。下面的 call stack 
                  显示了这个调用过程。<BR><BR>...<BR>nt!IopfCallDriver+0x35<BR>nt!IopSynchronousServiceTail+0x60<BR>nt!NtReadFile+0x5f4<BR>nt!KiSystemService+0xc4<BR>...<BR><BR><B>3 
                  键盘驱动的应用层</B><BR><BR>&nbsp;&nbsp;&nbsp; 
                  哪一个应用程序在使用键盘驱动?它是如何使用键盘驱动的?这是讨论键盘驱动肯定要遇到的问题,我们现在就来简单的讨论它。<BR><BR>3.1 
                  键盘驱动的使用者<BR><BR>&nbsp;&nbsp;&nbsp; 键盘驱动的使用者是线程 
                  win32k!RawInputThread 。线程 win32k!RawInputThread 的进程是 
                  csrss.exe。<BR><BR>&nbsp;&nbsp;&nbsp; 我最早是通过 WinDbg 的 !irpfind 
                  命令看到了这一点。后来看键盘驱动时,观察kbdclass!KeyboardClassRead,kbdclass!KeyboardClassCreate 
                  的 call stack 也看到了这一点。<BR><BR>&nbsp;&nbsp;&nbsp; 
                  kbdclass!KeyboardClassCreate 是,键盘设备栈最顶端的设备对象的驱动中处理 
                  IRP_MJ_CREATE 的函数。所以当有人使用 CreateFile 
                  来打开键盘设备栈上的某个设备对象的句柄的时候,CreateFile 最终会发一个 IRP_MJ_CREATE 的 IRP 
                  给键盘设备栈最顶端的设备对象,这将导致 kbdclass!KeyboardClassCreate 
                  被调用。于是我们在这个函数上下断点,看看是谁引起了这个函数的调用。看看是谁要得到键盘的句柄。<BR><BR>&nbsp;&nbsp;&nbsp; 
                  在系统初始化的末期,在 kbdclass!KeyboardClassCreate 
                  上发生了打断,进入调试器。首先我们看看这时的当前线程是谁。<BR><BR>kd&gt; !thread<BR>THREAD 
                  fe42e5e0 Cid a0.bc Teb: 00000000 Win32Thread: e194a9e8 
                  RUNNING<BR>IRP List:<BR>fe43e9a8: (0006,0148) Flags: 00000884 
                  Mdl: 00000000<BR>Not impersonating<BR>Owning Process 
                  fe43b760<BR>Wait Start TickCount 5168 Elapsed Ticks: 
                  0<BR>Context Switch Count 9&nbsp;<BR>UserTime 
                  0:00:00.0000<BR>KernelTime 0:00:00.0250<BR>Start Address 
                  win32k!RawInputThread (0xa000e7cd)<BR>Stack Init f90f0000 
                  Current f90ef864 Base f90f0000 Limit f90ed000 Call 
                  0<BR>Priority 19 BasePriority 13 PriorityDecrement 0 
                  DecrementCount 0<BR><BR>ChildEBP RetAddr Args to 
                  Child<BR>f90ef608 8041f54b fe4f5df0 fe43e9a8 fe43e9b8 
                  kbdclass!KeyboardClassCreate<BR>f90ef61c 804a3e54 804a392a 
                  fe4dd718 f90ef90c nt!IopfCallDriver+0x35<BR>f90ef7a4 8044e27e 
                  fe4dd730 00000000 f90ef850 nt!IopParseDevice+0xa04<BR>f90ef810 
                  804957ae 00000000 f90ef900 00000000 
                  nt!ObpLookupObjectName+0x4c4<BR>f90ef920 804a78b8 00000000 
                  00000000 e18f5900 nt!ObOpenObjectByName+0xc5<BR>f90ef9f4 
                  804a0c5b e197101c 00100001 f90efb14 
                  nt!IoCreateFile+0x3ec<BR>f90efa34 80461691 e197101c 00100001 
                  f90efb14 nt!NtCreateFile+0x2e<BR>f90efa34 804009d1 e197101c 
                  00100001 f90efb14 nt!KiSystemService+0xc4<BR>f90efad8 a000e304 
                  e197101c 00100001 f90efb14 nt!ZwCreateFile+0xb<BR>f90efb2c 
                  a000e192 e1971008 80400b46 00000001 
                  win32k!OpenDevice+0x8e<BR>f90efb58 a000eb74 00000001 00000000 
                  00000000 win32k!ProcessDeviceChanges+0x92<BR>f90efda8 804524f6 
                  00000003 00000000 00000000 
                  win32k!RawInputThread+0x463<BR>f90efddc 80465b62 a000e7cd 
                  f8d5f7d0 00000000 nt!PspSystemThreadStartup+0x69<BR>00000000 
                  f000ff53 f000e2c3 f000ff53 f000ff53 
                  nt!KiThreadStartup+0x16<BR>f000ff53 00000000 00000000 00000000 
                  00000000 +0xf000ff53<BR><BR>看到 Start Address 为 
                  win32k!RawInputThread。说明线程 win32k!RawInputThread 在通过 
                  CreateFile 来获得键盘的句柄。<BR><BR>看到 Cid 为 a0.bc 。说明线程的进程为 
                  a0。<BR><BR>我们看看 a0 进程是谁。<BR><BR>kd&gt; !process a0 
                  0<BR>Searching for Process with Cid == a0<BR>PROCESS fe43b760 
                  SessionId: 0 Cid: 00a0 Peb: 7ffdf000 ParentCid: 
                  0090<BR>DirBase: 03642000 ObjectTable: fe43b6c8 TableSize: 
                  53.<BR>Image: csrss.exe<BR><BR>看到 a0 进程的 Image 为 
                  csrss.exe。<BR><BR>&nbsp;&nbsp;&nbsp; 
                  kbdclass!KeyboardClassRead 是,键盘设备栈最顶端的设备对象的驱动中处理 IRP_MJ_READ 
                  的函数。所以当有人使用 ReadFile 来要求读入数据的时候,ReadFile 最终会发一个 IRP_MJ_Read 的 
                  IRP 给键盘设备栈最顶端的设备对象,这将导致 kbdclass!KeyboardClassRead 
                  被调用。于是我们在这个函数上下断点,看看是谁引起了这个函数的调用。看看是谁要求从键盘读入数据。<BR><BR>&nbsp;&nbsp;&nbsp; 
                  在 kbdclass!KeyboardClassCreate 
                  上发生打断后,进入调试器。我们看看这时的当前线程是谁。<BR><BR>kd&gt; !thread<BR>THREAD 
                  fe42e5e0 Cid a0.bc Teb: 00000000 Win32Thread: e194a9e8 
                  RUNNING<BR>...<BR>Start Address win32k!RawInputThread 
                  (0xa000e7cd)<BR>...<BR><BR>看到 Start Address 为 
                  win32k!RawInputThread。说明线程 win32k!RawInputThread 在通过 ReadFile 
                  来要求从键盘读取数据。<BR><BR>看到 Cid 为 a0.bc 。说明线程的进程还是 
                  a0。<BR><BR>这些足以说明键盘驱动的使用者是线程 win32k!RawInputThread 。线程 
                  win32k!RawInputThread 的进程是 csrss.exe。<BR><BR>3.2 
                  win32k!RawInputThread 获得句柄简介<BR><BR>&nbsp;&nbsp;&nbsp; 
                  win32k!RawInputThread 会调用 nt!ZwCreateFile ,获得一个可以找到键盘设备栈的 PDO 
                  的句柄,供以后的 ZwReadFile,ZwDeviceIoControlFile 
                  等使用。<BR><BR>&nbsp;&nbsp;&nbsp; 首先我们看看断在 
                  kbdclass!KeyboardClassCreate 时的 call stack ,看看引起 
                  kbdclass!KeyboardClassCreate 的整个调用过程。<BR><BR># ChildEBP 
                  RetAddr Args to Child&nbsp;<BR>00 f90ef608 8041f54b fe4f5df0 
                  fe43e9a8 fe43e9b8 kbdclass!KeyboardClassCreate(struct 
                  _DEVICE_OBJECT * DeviceObject = 0xfe4f5df0, struct _IRP * Irp 
                  = 0xfe43e9a8) (CONV: stdcall)<BR>01 f90ef61c 804a3e54 804a392a 
                  fe4dd718 f90ef90c nt!IopfCallDriver+0x35 (FPO: [0,0,2])<BR>02 
                  f90ef7a4 8044e27e fe4dd730 00000000 f90ef850 
                  nt!IopParseDevice+0xa04 (FPO: [Non-Fpo])<BR>03 f90ef810 
                  804957ae 00000000 f90ef900 00000000 
                  nt!ObpLookupObjectName+0x4c4 (FPO: [Non-Fpo])<BR>04 f90ef920 
                  804a78b8 00000000 00000000 e18f5900 nt!ObOpenObjectByName+0xc5 
                  (FPO: [Non-Fpo])<BR>05 f90ef9f4 804a0c5b e197101c 00100001 
                  f90efb14 nt!IoCreateFile+0x3ec (FPO: [Non-Fpo])<BR>06 f90efa34 
                  80461691 e197101c 00100001 f90efb14 nt!NtCreateFile+0x2e (FPO: 
                  [Non-Fpo])<BR>07 f90efa34 804009d1 e197101c 00100001 f90efb14 
                  nt!KiSystemService+0xc4 (FPO: [0,0] TrapFrame @ 
                  f90efa68)<BR>08 f90efad8 a000e304 e197101c 00100001 f90efb14 
                  nt!ZwCreateFile+0xb (FPO: [11,0,0])<BR>09 f90efb2c a000e192 
                  e1971008 80400b46 00000001 win32k!OpenDevice+0x8e (FPO: 
                  [Non-Fpo])<BR>0a f90efb58 a000eb74 00000001 00000000 00000000 
                  win32k!ProcessDeviceChanges+0x92 (FPO: [EBP 0xf90efda8] 
                  [1,5,4])<BR>0b f90efda8 804524f6 00000003 00000000 00000000 
                  win32k!RawInputThread+0x463 (FPO: [Non-Fpo])<BR>0c f90efddc 
                  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><BR>&nbsp;&nbsp;&nbsp; 我简单的跟了一下 
                  win32k!RawInputThread 
                  获得句柄的过程,下面我对这个过程做一个简单的介绍。<BR><BR>win32k!RawInputThread 通过 
                  GUID_CLASS_KEYBOARD 获得键盘设备栈中的 PDO (简单的说 PDO 是设备栈最下面的那个设备对象)的 
                  SymbolicLink(符号链接)名。执行到 win32k!OpenDevice,它的一个参数可以找到 键盘设备栈的 
                  PDO 的符号链接(SymbolicLink)名。win32k!OpenDevice 有一个 
                  OBJECT_ATTRIBUTES 结构的局部变量,它自己初始化这个局部变量,用传入参数中的键盘设备栈的 PDO 
                  的符号链接(SymbolicLink)名 赋值这个 OBJECT_ATTRIBUTES +0x8 处的 

⌨️ 快捷键说明

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