?? jiurl鍵盤驅動 2.htm
字號:
而且通常我們傳給 CreateFile,ReadFile,WriteFile,DeviceIoControl,CloseHandle
的參數所找到的那個設備對象,會是它所在設備棧的 PDO(也就是它所在設備棧最底下的一個設備對象)。CreateFile, ReadFile,
WriteFile, DeviceIoControl, CloseHandle
會首先通過找到的這個設備對象,獲得它所在設備棧中最頂端的那個設備對象,然后將 IRP
發向設備棧的最頂端的那個設備對象。所以不管我們通過參數找到的設備對象在它所在的設備棧中處于什么位置,頂端,中間,底下,不管處在什么位置,IRP
都會發往這個設備棧的棧頂。<BR><BR> 上面的內容是通過跟蹤 nt!NtCreateFile 和
nt!NtReadFile 發現的。<BR><BR>2.5 nt!NtCreateFile簡介<BR><BR>
我們簡單介紹一下 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 中 <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> 我們簡單介紹一下 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>
哪一個應用程序在使用鍵盤驅動?它是如何使用鍵盤驅動的?這是討論鍵盤驅動肯定要遇到的問題,我們現在就來簡單的討論它。<BR><BR>3.1
鍵盤驅動的使用者<BR><BR> 鍵盤驅動的使用者是線程 win32k!RawInputThread 。線程
win32k!RawInputThread 的進程是 csrss.exe。<BR><BR> 我最早是通過
WinDbg 的 !irpfind
命令看到了這一點。后來看鍵盤驅動時,觀察kbdclass!KeyboardClassRead,kbdclass!KeyboardClassCreate
的 call stack 也看到了這一點。<BR><BR>
kbdclass!KeyboardClassCreate 是,鍵盤設備棧最頂端的設備對象的驅動中處理 IRP_MJ_CREATE
的函數。所以當有人使用 CreateFile 來打開鍵盤設備棧上的某個設備對象的句柄的時候,CreateFile 最終會發一個
IRP_MJ_CREATE 的 IRP 給鍵盤設備棧最頂端的設備對象,這將導致 kbdclass!KeyboardClassCreate
被調用。于是我們在這個函數上下斷點,看看是誰引起了這個函數的調用。看看是誰要得到鍵盤的句柄。<BR><BR>
在系統初始化的末期,在 kbdclass!KeyboardClassCreate
上發生了打斷,進入調試器。首先我們看看這時的當前線程是誰。<BR><BR>kd> !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 <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> !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> kbdclass!KeyboardClassRead
是,鍵盤設備棧最頂端的設備對象的驅動中處理 IRP_MJ_READ 的函數。所以當有人使用 ReadFile 來要求讀入數據的時候,ReadFile
最終會發一個 IRP_MJ_Read 的 IRP 給鍵盤設備棧最頂端的設備對象,這將導致 kbdclass!KeyboardClassRead
被調用。于是我們在這個函數上下斷點,看看是誰引起了這個函數的調用??纯词钦l要求從鍵盤讀入數據。<BR><BR>
在 kbdclass!KeyboardClassCreate 上發生打斷后,進入調試器。我們看看這時的當前線程是誰。<BR><BR>kd>
!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> win32k!RawInputThread 會調用 nt!ZwCreateFile
,獲得一個可以找到鍵盤設備棧的 PDO 的句柄,供以后的 ZwReadFile,ZwDeviceIoControlFile
等使用。<BR><BR> 首先我們看看斷在 kbdclass!KeyboardClassCreate 時的
call stack ,看看引起 kbdclass!KeyboardClassCreate 的整個調用過程。<BR><BR># ChildEBP
RetAddr Args to Child <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> 我簡單的跟了一下
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 處的 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
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -