www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > 芯聞號(hào) > 充電吧
[導(dǎo)讀]一段寫的比較好的文章,收藏用,不知道怎么用的可以看看,也可以學(xué)習(xí)下調(diào)試的一些用法應(yīng)用程序如何使用驅(qū)動(dòng)應(yīng)用程序中使用 CreateFile,ReadFile,WriteFile,DeviceIoCont

一段寫的比較好的文章,收藏用,不知道怎么用的可以看看,也可以學(xué)習(xí)下調(diào)試的一些用法

應(yīng)用程序如何使用驅(qū)動(dòng)

應(yīng)用程序中使用 CreateFile,ReadFile,WriteFile,DeviceIoControl,CloseHandle 來指示驅(qū)動(dòng)程序完成某種任務(wù)。比如我們在應(yīng)用程序中使用 ReadFile 來讓驅(qū)動(dòng)讀取硬件設(shè)備,我們在應(yīng)用程序中使用 WriteFile 來讓驅(qū)動(dòng)寫硬件設(shè)備,我們在應(yīng)用程序中使用 DeviceIoContorl 來讓驅(qū)動(dòng)完成某些驅(qū)動(dòng)支持的功能。而 ReadFile, WriteFile, DeviceIoControl 這三個(gè) api 都需要一個(gè)句柄作為參數(shù),以確定他們是要哪個(gè)驅(qū)動(dòng)來完成他們的請求。這個(gè)句柄是通過 CreateFile 獲得的。使用 CloseHandle 關(guān)閉這個(gè)句柄。簡單的說就是,應(yīng)用程序中,首先要通過 CreateFile 獲得一個(gè)句柄,之后應(yīng)用程序可以以這個(gè)句柄為參數(shù),使用 ReadFile,WriteFile,DeviceIoControl 讓驅(qū)動(dòng)程序執(zhí)行某種操作。當(dāng)不再使用時(shí),通過 CloseHandle 關(guān)閉這個(gè)句柄。

這幾個(gè) api 都位于 KERNEL32.DLL 中,他們最終會(huì)通過系統(tǒng)服務(wù)(int 2e)調(diào)用內(nèi)核中的相應(yīng)的函數(shù),如 NtCreateFile,NtReadFile 等。而 NtCreateFile,NtReadFile 等函數(shù)中,會(huì)創(chuàng)建一個(gè) IRP,并用傳入的參數(shù)初始化這個(gè) IRP,然后將這個(gè) IRP 發(fā)給驅(qū)動(dòng),讓驅(qū)動(dòng)做處理。相應(yīng)的 NtCreateFile 產(chǎn)生 IRP_MJ_CREATE 的 IRP ,NtReadFile 產(chǎn)生 IRP_MJ_READ 的 IRP。驅(qū)動(dòng)得到這些 IRP ,根據(jù)情況做處理,對于 IRP_MJ_READ ,會(huì)調(diào)用驅(qū)動(dòng)中處理 IRP_MJ_READ 的部分,可能最后引起讀硬件的操作。


?獲得指定驅(qū)動(dòng)的句柄

對于希望被應(yīng)用程序使用的驅(qū)動(dòng),會(huì)在初始化的過程中,把能找到它設(shè)備對象的一個(gè) SymbolicLink 放在對象管理器命名空間(Object Manager Namespace)的 /??/ 下。這樣用 "http:////.//那個(gè)SymbolicLink的名字" 作為 CreateFile 的 lpFileName 參數(shù),調(diào)用 CreateFile ,得到的句柄就可以找到相應(yīng)的驅(qū)動(dòng)的那個(gè)設(shè)備對象(//./ 會(huì)被轉(zhuǎn)換成 /??/)。之后以這個(gè)句柄為參數(shù)使用 ReadFile,WriteFile,DeviceIoControl,產(chǎn)生的 IRP 就被發(fā)到相應(yīng)的設(shè)備對象。也就是說只要驅(qū)動(dòng)把 設(shè)備對象的 SymbolicLink 放在 /??/ 下,并且應(yīng)用程序知道這個(gè) SymbolicLink 的名字,就可以使用 CreateFile 得到相應(yīng)的句柄。

HANDLE CreateFile(
LPCTSTR lpFileName, // file name
DWORD dwDesiredAccess, // access mode
DWORD dwShareMode, // share mode
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // SD
DWORD dwCreationDisposition, // how to create
DWORD dwFlagsAndAttributes, // file attributes
HANDLE hTemplateFile // handle to template file
);


可以使用工具 WinObj 來查看 對象管理器命名空間(Object Manager Namespace) 。WinObj 可以從 http://www.sysinternals.com 獲得。關(guān)于內(nèi)核對象和命名地址空間的詳細(xì)介紹,可以參考《 JIURL玩玩Win2k 對象 》,這篇文章可以在我的主頁上找到。

在驅(qū)動(dòng)的初始化過程中,會(huì)通過調(diào)用 IoCreateDevice 創(chuàng)建設(shè)備對象,可以指定一個(gè)設(shè)備名作為IoCreateDevice 的參數(shù)(也可以不指定,那樣這個(gè)設(shè)備對象就沒有名字)。這樣這個(gè)設(shè)備對象會(huì)被放在 對象管理器命名空間(Object Manager Namespace)的 /Device/ 下。不過應(yīng)用程序只能訪問命名空間的 /??/ ,所以如果驅(qū)動(dòng)希望把設(shè)備對象暴露給應(yīng)用程序的話,會(huì)為設(shè)備對象創(chuàng)建一個(gè) SymbolicLink 放在 /??/ 下。對于放在 /Device/ 下的有名字的設(shè)備,其他驅(qū)動(dòng)程序如果知道它的名字,就可以使用 IoGetDeviceObjectPointer 得到這個(gè)設(shè)備對象的指針。

驅(qū)動(dòng)可以通過 IoCreateSymbolicLink ,在 /??/ 下建立設(shè)備對象的 SymbolicLink 。這樣應(yīng)用程序必須要也知道該 SymbolicLink 的名字,然后就可以以這個(gè)符號(hào)鏈接名做參數(shù)使用 CreateFile ,得到句柄。從 IoCreateSymbolicLink 的參數(shù),我們可以知道,只能使用 IoCreateSymbolicLink 為有名字的設(shè)備對象建立 SymbolicLink。

另一種方法是,使用一個(gè) GUID 來標(biāo)識(shí)一個(gè)設(shè)備接口。驅(qū)動(dòng)使用標(biāo)識(shí)設(shè)備的 GUID 做參數(shù)調(diào)用IoRegisterDeviceInterface ,然后使用 IoSetDeviceInterfaceState ,就會(huì)為設(shè)備對象在 /??/ 下產(chǎn)生一個(gè)符號(hào)鏈接(SymbolicLink)。應(yīng)用程序使用同一個(gè) GUID 做參數(shù),使用API: SetupDiGetClassDevs, SetupDiEnumDeviceInterfaces, SetupDiGetDeviceInterfaceDetail 就可以得到創(chuàng)建的 /??/ 下的符號(hào)鏈接名,就可以以這個(gè)符號(hào)鏈接名做參數(shù)使用 CreateFile ,得到句柄。

句柄簡介。每個(gè)進(jìn)程都有一個(gè)自己的句柄表,句柄表中放著內(nèi)核對象的指針,句柄就是內(nèi)核對象在句柄表中的索引。通過句柄就可以在進(jìn)程的句柄表中找到對應(yīng)的內(nèi)核對象的指針。關(guān)于句柄的詳細(xì)介紹,可以參考 《 JIURL玩玩Win2k 進(jìn)程線程篇 HANDLE_TABLE 》,這篇文章可以在我的主頁上找到。

?一些結(jié)論

SymbolicLink 對象可以找到相應(yīng)的 設(shè)備對象。SymbolicLink 對象中保存著相應(yīng)的 設(shè)備對象的地址。設(shè)備對象不保存它的 SymbolicLink 對象的任何信息。SymbolicLink 對象的地址保存在對象管理器命名空間(Object Manager Namespace)中。也就是說只要知道 SymbolicLink 的名字,就可以在對象管理器命名空間中找到它。應(yīng)用程序 CreateFile 得到的句柄,通過這個(gè)句柄在進(jìn)程的句柄表中找到的是一個(gè)文件對象(File Object)。文件對象 對應(yīng)的 設(shè)備對象 中不保存這個(gè)文件對象的任何信息。對應(yīng)的 SymbolicLink 對象中也不保存這個(gè)文件對象的任何信息。這個(gè)文件對象的地址,保存在應(yīng)用程序的句柄表中,應(yīng)用程序通過句柄可以找到這個(gè)文件對象。這個(gè) 文件對象 中保存著對應(yīng)的 設(shè)備對象 的地址??梢圆碌?,應(yīng)用程序在用 CreateFile 創(chuàng)建的時(shí)候,會(huì)根據(jù)參數(shù)中的 SymolicLink 名字,找到 SymolicLink 對象,進(jìn)而找到該對象中保存的 設(shè)備對象 的地址,然后直接把找到的 設(shè)備對象 的地址保存在文件對象中。文件對象的 +04 struct _DEVICE_OBJECT *DeviceObject 處,保存著對應(yīng)的設(shè)備對象的地址。

對于需要暴露接口給應(yīng)用程序的驅(qū)動(dòng)。首先,驅(qū)動(dòng)中需要在 對象管理器命名空間的 /??/ 下,為設(shè)備對象建立一個(gè) SymbolicLink ,不管采取何種方式。之后,應(yīng)用程序要知道這個(gè) SymbolicLink 的名字,不管采取何種方式。然后應(yīng)用程序以 "http:////.//那個(gè)SymbolicLink的名字" 為參數(shù)使用 CreateFile 得到一個(gè)句柄。這樣,之后的 DeviceIoControl(),WriteFile(),ReadFile() 使用前面用 CreateFile 得到的句柄作為參數(shù),他們可以通過這個(gè)句柄,找到對應(yīng)的文件對象,而這個(gè)文件對象中保存有對應(yīng)的 設(shè)備對象 的指針,這樣就可以將 IRP 發(fā)到這些設(shè)備。?

上面的結(jié)論是通過對一個(gè)小例子進(jìn)行觀察得到的。

?IRP 將被發(fā)往設(shè)備棧的棧頂

IRP 將無論如何被發(fā)往設(shè)備棧的頂。CreateFile,ReadFile,WriteFile,DeviceIoControl,CloseHandle。他們最終都會(huì)產(chǎn)生一個(gè) IRP,發(fā)給一個(gè)設(shè)備對象。對于 CreateFile 來說通過 SymbolicLink的名字 來找到一個(gè)設(shè)備對象。對于其他的幾個(gè)函數(shù),通過句柄,找到一個(gè)文件對象,文件對象中保存有設(shè)備對象的指針。不過產(chǎn)生的 IRP 并不一定發(fā)給找到的這個(gè)設(shè)備對象,而是發(fā)給找到的這個(gè)設(shè)備對象所在設(shè)備棧的最頂上的一個(gè)設(shè)備對象。

而且通常我們傳給 CreateFile,ReadFile,WriteFile,DeviceIoControl,CloseHandle 的參數(shù)所找到的那個(gè)設(shè)備對象,會(huì)是它所在設(shè)備棧的 PDO(也就是它所在設(shè)備棧最底下的一個(gè)設(shè)備對象)。CreateFile, ReadFile, WriteFile, DeviceIoControl, CloseHandle 會(huì)首先通過找到的這個(gè)設(shè)備對象,獲得它所在設(shè)備棧中最頂端的那個(gè)設(shè)備對象,然后將 IRP 發(fā)向設(shè)備棧的最頂端的那個(gè)設(shè)備對象。所以不管我們通過參數(shù)找到的設(shè)備對象在它所在的設(shè)備棧中處于什么位置,頂端,中間,底下,不管處在什么位置,IRP 都會(huì)發(fā)往這個(gè)設(shè)備棧的棧頂。

上面的內(nèi)容是通過跟蹤 nt!NtCreateFile 和 nt!NtReadFile 發(fā)現(xiàn)的。

?nt!NtCreateFile簡介

我們簡單介紹一下 nt!NtCreateFile。

通過一系列的調(diào)用 nt!NtCreateFile 最終會(huì)調(diào)用 nt!IopParseDevice。下面的 call stack 顯示了這個(gè)調(diào)用過程。

...
nt!IopParseDevice+0xa04
nt!ObpLookupObjectName+0x4c4
nt!ObOpenObjectByName+0xc5
nt!IoCreateFile+0x3ec
nt!NtCreateFile+0x2e
nt!KiSystemService+0xc4
...

在 nt!IopParseDevice 中?

調(diào)用 nt!IoGetAttachedDevice ,獲得設(shè)備棧最頂端的設(shè)備對象。調(diào)用 IoAllocateIrp 創(chuàng)建 IRP。調(diào)用 nt!ObCreateObject 創(chuàng)建文件對象。初始化這個(gè)文件對象。該文件對象的 +04 struct _DEVICE_OBJECT *DeviceObject 賦值為通過傳入?yún)?shù)找到的那個(gè)設(shè)備對象。調(diào)用 nt!IopfCallDriver,也就是 IoCallDriver,將 IRP 發(fā)給設(shè)備棧的棧頂。

驅(qū)動(dòng)處理完這個(gè) IRP 之后,返回 nt!IopParseDevice 繼續(xù)執(zhí)行。nt!IopParseDevice 一路返回到 nt!ObOpenObjectByName。在 nt!ObOpenObjectByName 中繼續(xù)執(zhí)行,調(diào)用 nt!ObpCreateHandle 在進(jìn)程的句柄表中創(chuàng)建一個(gè)新的句柄,這個(gè)句柄對應(yīng)的對象是剛才創(chuàng)建初始化的那個(gè)文件對象。

?nt!NtReadFile簡介

我們簡單介紹一下 nt!NtReadFile。傳入?yún)?shù)中有前面 CreateFile 打開的句柄,通過句柄可以在進(jìn)程句柄表中找到一個(gè)文件對象,在這個(gè)文件對象中保存有一個(gè)設(shè)備對象的指針。調(diào)用 IoGetRelatedDeviceObject 獲得這個(gè)設(shè)備對象所在設(shè)備棧棧頂?shù)脑O(shè)備對象。調(diào)用 IoAllocateIrp 創(chuàng)建 IRP。初始化這個(gè) IRP ,并根據(jù)傳入的參數(shù),設(shè)置好這個(gè) IRP。然后調(diào)用 IoCallDriver 將這個(gè) IRP 發(fā)給設(shè)備對象,讓驅(qū)動(dòng)進(jìn)行處理。發(fā)往的這個(gè)設(shè)備對象就是前面使用 IoGetRelatedDeviceObject 所得到的設(shè)備棧棧頂?shù)脑O(shè)備對象。下面的 call stack 顯示了這個(gè)調(diào)用過程。

...
nt!IopfCallDriver+0x35
nt!IopSynchronousServiceTail+0x60
nt!NtReadFile+0x5f4
nt!KiSystemService+0xc4
...

?鍵盤驅(qū)動(dòng)的應(yīng)用層

哪一個(gè)應(yīng)用程序在使用鍵盤驅(qū)動(dòng)?它是如何使用鍵盤驅(qū)動(dòng)的?這是討論鍵盤驅(qū)動(dòng)肯定要遇到的問題,我們現(xiàn)在就來簡單的討論它。

3.1 鍵盤驅(qū)動(dòng)的使用者

鍵盤驅(qū)動(dòng)的使用者是線程 win32k!RawInputThread 。線程 win32k!RawInputThread 的進(jìn)程是 csrss.exe。

我最早是通過 WinDbg 的 !irpfind 命令看到了這一點(diǎn)。后來看鍵盤驅(qū)動(dòng)時(shí),觀察kbdclass!KeyboardClassRead,kbdclass!KeyboardClassCreate 的 call stack 也看到了這一點(diǎn)。

kbdclass!KeyboardClassCreate 是,鍵盤設(shè)備棧最頂端的設(shè)備對象的驅(qū)動(dòng)中處理 IRP_MJ_CREATE 的函數(shù)。所以當(dāng)有人使用 CreateFile 來打開鍵盤設(shè)備棧上的某個(gè)設(shè)備對象的句柄的時(shí)候,CreateFile 最終會(huì)發(fā)一個(gè) IRP_MJ_CREATE 的 IRP 給鍵盤設(shè)備棧最頂端的設(shè)備對象,這將導(dǎo)致 kbdclass!KeyboardClassCreate 被調(diào)用。于是我們在這個(gè)函數(shù)上下斷點(diǎn),看看是誰引起了這個(gè)函數(shù)的調(diào)用??纯词钦l要得到鍵盤的句柄。

在系統(tǒng)初始化的末期,在 kbdclass!KeyboardClassCreate 上發(fā)生了打斷,進(jìn)入調(diào)試器。首先我們看看這時(shí)的當(dāng)前線程是誰。

kd> !thread
THREAD fe42e5e0 Cid a0.bc Teb: 00000000 Win32Thread: e194a9e8 RUNNING
IRP List:
fe43e9a8: (0006,0148) Flags: 00000884 Mdl: 00000000
Not impersonating
Owning Process fe43b760
Wait Start TickCount 5168 Elapsed Ticks: 0
Context Switch Count 9?
UserTime 0:00:00.0000
KernelTime 0:00:00.0250
Start Address win32k!RawInputThread (0xa000e7cd)
Stack Init f90f0000 Current f90ef864 Base f90f0000 Limit f90ed000 Call 0
Priority 19 BasePriority 13 PriorityDecrement 0 DecrementCount 0

ChildEBP RetAddr Args to Child
f90ef608 8041f54b fe4f5df0 fe43e9a8 fe43e9b8 kbdclass!KeyboardClassCreate
f90ef61c 804a3e54 804a392a fe4dd718 f90ef90c nt!IopfCallDriver+0x35
f90ef7a4 8044e27e fe4dd730 00000000 f90ef850 nt!IopParseDevice+0xa04
f90ef810 804957ae 00000000 f90ef900 00000000 nt!ObpLookupObjectName+0x4c4
f90ef920 804a78b8 00000000 00000000 e18f5900 nt!ObOpenObjectByName+0xc5
f90ef9f4 804a0c5b e197101c 00100001 f90efb14 nt!IoCreateFile+0x3ec
f90efa34 80461691 e197101c 00100001 f90efb14 nt!NtCreateFile+0x2e
f90efa34 804009d1 e197101c 00100001 f90efb14 nt!KiSystemService+0xc4
f90efad8 a000e304 e197101c 00100001 f90efb14 nt!ZwCreateFile+0xb
f90efb2c a000e192 e1971008 80400b46 00000001 win32k!OpenDevice+0x8e
f90efb58 a000eb74 00000001 00000000 00000000 win32k!ProcessDeviceChanges+0x92
f90efda8 804524f6 00000003 00000000 00000000 win32k!RawInputThread+0x463
f90efddc 80465b62 a000e7cd f8d5f7d0 00000000 nt!PspSystemThreadStartup+0x69
00000000 f000ff53 f000e2c3 f000ff53 f000ff53 nt!KiThreadStartup+0x16
f000ff53 00000000 00000000 00000000 00000000 +0xf000ff53

看到 Start Address 為 win32k!RawInputThread。說明線程 win32k!RawInputThread 在通過 CreateFile 來獲得鍵盤的句柄。

看到 Cid 為 a0.bc 。說明線程的進(jìn)程為 a0。

我們看看 a0 進(jìn)程是誰。

kd> !process a0 0
Searching for Process with Cid == a0
PROCESS fe43b760 SessionId: 0 Cid: 00a0 Peb: 7ffdf000 ParentCid: 0090
DirBase: 03642000 ObjectTable: fe43b6c8 TableSize: 53.
Image: csrss.exe

看到 a0 進(jìn)程的 Image 為 csrss.exe。

kbdclass!KeyboardClassRead 是,鍵盤設(shè)備棧最頂端的設(shè)備對象的驅(qū)動(dòng)中處理 IRP_MJ_READ 的函數(shù)。所以當(dāng)有人使用 ReadFile 來要求讀入數(shù)據(jù)的時(shí)候,ReadFile 最終會(huì)發(fā)一個(gè) IRP_MJ_Read 的 IRP 給鍵盤設(shè)備棧最頂端的設(shè)備對象,這將導(dǎo)致 kbdclass!KeyboardClassRead 被調(diào)用。于是我們在這個(gè)函數(shù)上下斷點(diǎn),看看是誰引起了這個(gè)函數(shù)的調(diào)用??纯词钦l要求從鍵盤讀入數(shù)據(jù)。

在 kbdclass!KeyboardClassCreate 上發(fā)生打斷后,進(jìn)入調(diào)試器。我們看看這時(shí)的當(dāng)前線程是誰。

kd> !thread
THREAD fe42e5e0 Cid a0.bc Teb: 00000000 Win32Thread: e194a9e8 RUNNING
...
Start Address win32k!RawInputThread (0xa000e7cd)
...

看到 Start Address 為 win32k!RawInputThread。說明線程 win32k!RawInputThread 在通過 ReadFile 來要求從鍵盤讀取數(shù)據(jù)。

看到 Cid 為 a0.bc 。說明線程的進(jìn)程還是 a0。

這些足以說明鍵盤驅(qū)動(dòng)的使用者是線程 win32k!RawInputThread 。線程 win32k!RawInputThread 的進(jìn)程是 csrss.exe。

?win32k!RawInputThread 獲得句柄簡介

win32k!RawInputThread 會(huì)調(diào)用 nt!ZwCreateFile ,獲得一個(gè)可以找到鍵盤設(shè)備棧的 PDO 的句柄,供以后的 ZwReadFile,ZwDeviceIoControlFile 等使用。

首先我們看看斷在 kbdclass!KeyboardClassCreate 時(shí)的 call stack ,看看引起 kbdclass!KeyboardClassCreate 的整個(gè)調(diào)用過程。

# ChildEBP RetAddr Args to Child?
00 f90ef608 8041f54b fe4f5df0 fe43e9a8 fe43e9b8 kbdclass!KeyboardClassCreate(struct _DEVICE_OBJECT * DeviceObject = 0xfe4f5df0, struct _IRP * Irp = 0xfe43e9a8) (CONV: stdcall)
01 f90ef61c 804a3e54 804a392a fe4dd718 f90ef90c nt!IopfCallDriver+0x35 (FPO: [0,0,2])
02 f90ef7a4 8044e27e fe4dd730 00000000 f90ef850 nt!IopParseDevice+0xa04 (FPO: [Non-Fpo])
03 f90ef810 804957ae 00000000 f90ef900 00000000 nt!ObpLookupObjectName+0x4c4 (FPO: [Non-Fpo])
04 f90ef920 804a78b8 00000000 00000000 e18f5900 nt!ObOpenObjectByName+0xc5 (FPO: [Non-Fpo])
05 f90ef9f4 804a0c5b e197101c 00100001 f90efb14 nt!IoCreateFile+0x3ec (FPO: [Non-Fpo])
06 f90efa34 80461691 e197101c 00100001 f90efb14 nt!NtCreateFile+0x2e (FPO: [Non-Fpo])
07 f90efa34 804009d1 e197101c 00100001 f90efb14 nt!KiSystemService+0xc4 (FPO: [0,0] TrapFrame @ f90efa68)
08 f90efad8 a000e304 e197101c 00100001 f90efb14 nt!ZwCreateFile+0xb (FPO: [11,0,0])
09 f90efb2c a000e192 e1971008 80400b46 00000001 win32k!OpenDevice+0x8e (FPO: [Non-Fpo])
0a f90efb58 a000eb74 00000001 00000000 00000000 win32k!ProcessDeviceChanges+0x92 (FPO: [EBP 0xf90efda8] [1,5,4])
0b f90efda8 804524f6 00000003 00000000 00000000 win32k!RawInputThread+0x463 (FPO: [Non-Fpo])
0c f90efddc 80465b62 a000e7cd f8d5f7d0 00000000 nt!PspSystemThreadStartup+0x69 (FPO: [Non-Fpo])
0d 00000000 f000ff53 f000e2c3 f000ff53 f000ff53 nt!KiThreadStartup+0x16
WARNING: Frame IP not in any known module. Following frames may be wrong.
0e f000ff53 00000000 00000000 00000000 00000000 0xf000ff53


我簡單的跟了一下 win32k!RawInputThread 獲得句柄的過程,下面我對這個(gè)過程做一個(gè)簡單的介紹。

win32k!RawInputThread 通過 GUID_CLASS_KEYBOARD 獲得鍵盤設(shè)備棧中的 PDO (簡單的說 PDO 是設(shè)備棧最下面的那個(gè)設(shè)備對象)的 SymbolicLink(符號(hào)鏈接)名。執(zhí)行到 win32k!OpenDevice,它的一個(gè)參數(shù)可以找到 鍵盤設(shè)備棧的 PDO 的符號(hào)鏈接(SymbolicLink)名。win32k!OpenDevice 有一個(gè) OBJECT_ATTRIBUTES 結(jié)構(gòu)的局部變量,它自己初始化這個(gè)局部變量,用傳入?yún)?shù)中的鍵盤設(shè)備棧的 PDO 的符號(hào)鏈接(SymbolicLink)名 賦值這個(gè) OBJECT_ATTRIBUTES +0x8 處的 PUNICODE_STRING ObjectName 。然后調(diào)用 ZwCreateFile。ZwCreateFile 完成得到句柄的工作,最后通過傳入的參數(shù)返回得到的句柄。win32k!RawInputThread 把得到的句柄保存起來,供后面的 ReadFile, DeviceIoControl等使用。

ZwCreateFile 通過系統(tǒng)服務(wù),調(diào)用內(nèi)核中的 NtCreateFile。NtCreateFile 執(zhí)行到 nt!IopParseDevice 中 ,
調(diào)用 nt!IoGetAttachedDevice ,通過 PDO 的設(shè)備對象獲得鍵盤設(shè)備棧最頂端的設(shè)備對象。用得到的這個(gè)設(shè)備對象的 +30 char StackSize 作為參數(shù)來 IoAllocateIrp,創(chuàng)建 IRP。調(diào)用 nt!ObCreateObject 創(chuàng)建文件對象。初始化這個(gè)文件對象,+04 struct _DEVICE_OBJECT *DeviceObject 賦值為鍵盤設(shè)備棧的 PDO。調(diào)用 nt!IopfCallDriver,將 IRP 發(fā)往驅(qū)動(dòng),讓驅(qū)動(dòng)進(jìn)行相應(yīng)的處理。之后一系列返回,回到 nt!ObOpenObjectByName。在 nt!ObOpenObjectByName 中繼續(xù)執(zhí)行,調(diào)用 nt!ObpCreateHandle 在進(jìn)程(csrss.exe)的句柄表中創(chuàng)建一個(gè)新的句柄,這個(gè)句柄對應(yīng)的對象是剛才創(chuàng)建初始化的那個(gè)文件對象,文件對象中的 DeviceObject 指向鍵盤設(shè)備棧的 PDO。在 nt!ObpCreateHandle 前后,我們使用命令 !handle 0 3 a0 (a0 為此時(shí) csrss.exe進(jìn)程的進(jìn)程id),觀察 csrss.exe進(jìn)程 句柄表的前后變化,看到了多出來的那一個(gè)文件對象。

?win32k!RawInputThread 如何從鍵盤驅(qū)動(dòng)得到按鍵的數(shù)據(jù)

win32k!RawInputThread 在獲得了句柄之后,會(huì)以這個(gè)句柄為參數(shù),調(diào)用 nt!ZwReadFile,向鍵盤驅(qū)動(dòng)要求讀入數(shù)據(jù)。nt!ZwReadFile 中會(huì)創(chuàng)建一個(gè) IRP_MJ_READ 的 IRP 發(fā)給鍵盤驅(qū)動(dòng),告訴鍵盤驅(qū)動(dòng)要求讀入數(shù)據(jù)。鍵盤驅(qū)動(dòng)通常會(huì)使這個(gè) IRP Pending (通常情況下是這樣,詳細(xì)的情況我們在鍵盤驅(qū)動(dòng)部分討論)。也就是說這個(gè) IRP_MJ_READ 不會(huì)被滿足,它會(huì)一直被放在那里,等待著來自鍵盤的數(shù)據(jù)。而發(fā)出這個(gè)讀請求的線程 win32k!RawInputThread 也會(huì)等待,等待著這個(gè)讀操作的完成。

命中注定,這個(gè) IRP 匆匆的出現(xiàn),然后用它一生中絕大部分時(shí)間,開始一個(gè)靜靜的等待,而當(dāng)它等到的時(shí)候,它就會(huì)匆匆的消失。它的一生或許很短,或許很長,取決于它所等待著的出現(xiàn)。它在平靜的等待著什么呢?

它在等待著你,按下鍵盤上的鍵。我們來說明一下鍵盤數(shù)據(jù)的源頭,鍵盤數(shù)據(jù)的源頭就是鍵盤,當(dāng)鍵盤上有鍵被按下時(shí),就產(chǎn)生了那個(gè) IRP_MJ_READ IRP 等待著的對象。

當(dāng)鍵盤上有鍵被按下時(shí),將觸發(fā)鍵盤的那個(gè)中斷,引起中斷服務(wù)例程的執(zhí)行,鍵盤中斷的中斷服務(wù)例程由鍵盤驅(qū)動(dòng)提供。鍵盤驅(qū)動(dòng)從端口讀取掃描碼,經(jīng)過一些列的處理之后,最后把從鍵盤得到的數(shù)據(jù)交給 IRP,然后結(jié)束這個(gè) IRP。

這個(gè) IRP 的結(jié)束,將導(dǎo)致 win32k!RawInputThread 線程對這個(gè)讀操作的等待結(jié)束。win32k!RawInputThread 線程將會(huì)對得到的數(shù)據(jù)作出處理,分發(fā)給合適的進(jìn)程。一旦把輸入數(shù)據(jù)處理完之后,win32k!RawInputThread 線程會(huì)立刻再調(diào)用一個(gè) nt!ZwReadFile,向鍵盤驅(qū)動(dòng)要求讀入數(shù)據(jù)。于是又開始一個(gè)等待,等待著鍵盤上的鍵被按下。

簡單的說,win32k!RawInputThread 線程總是 nt!ZwReadFile 要求讀入數(shù)據(jù)。然后等待鍵盤上的鍵被按下。當(dāng)鍵盤上的鍵被按下,win32k!RawInputThread 處理 nt!ZwReadFile 得到的數(shù)據(jù),然后再 nt!ZwReadFile 要求讀入數(shù)據(jù),再等待鍵盤上的鍵被按下。

上面所介紹的內(nèi)容,是當(dāng)我在看到鍵盤驅(qū)動(dòng)對于 IRP_MJ_READ 的處理,在 kbdclass!KeyboardClassRead 中,IRP 并沒有獲得數(shù)據(jù),而是被 IoMarkIrpPending 時(shí),想了想,了解到的。

我簡單的跟了一下 win32k!RawInputThread 從鍵盤驅(qū)動(dòng)獲得按鍵數(shù)據(jù)的過程,下面我對這個(gè)過程做一個(gè)簡單的介紹。

首先我們看看斷在 kbdclass!KeyboardClassRead 時(shí)的 call stack ,看看引起 kbdclass!KeyboardClassRead 的整個(gè)調(diào)用過程。

# ChildEBP RetAddr Args to Child?
00 f90ef8dc 8041f54b fe4f5df0 fe43e9a8 fe43e9a8 kbdclass!KeyboardClassRead(struct _DEVICE_OBJECT * Device = 0xfe4f5df0, struct _IRP * Irp = 0xfe43e9a8) (CONV: stdcall)
01 f90ef8f0 804ba5e8 fe43eacc fe43e9a8 00000000 nt!IopfCallDriver+0x35 (FPO: [0,0,2])
02 f90ef904 804a2d4c fe4f5df0 fe43e9a8 fe42d668 nt!IopSynchronousServiceTail+0x60 (FPO: [Non-Fpo])
03 f90ef9d8 80461691 000000d4 00000000 a005c962 nt!NtReadFile+0x5f4 (FPO: [Non-Fpo])
04 f90ef9d8 804011d5 000000d4 00000000 a005c962 nt!KiSystemService+0xc4 (FPO: [0,0] TrapFrame @ f90efa04)
05 f90efa74 a005c91d 000000d4 00000000 a005c962 nt!ZwReadFile+0xb (FPO: [9,0,0])
06 f90efaa8 a005c991 e1971008 fe43e9e8 80430982 win32k!StartDeviceRead+0x8c (FPO: [1,0,3])
07 f90efab4 80430982 e1971008 e1971028 00000000 win32k!InputApc+0x41 (FPO: [3,0,1])
08 f90efae8 80403a44 00000000 00000000 00000000 nt!KiDeliverApc+0xdb (FPO: [Non-Fpo])
09 f90efb08 8042d33d 80400b46 00000001 00000000 nt!KiSwapThread+0xfc (FPO: [EBP 0xf90efb3c] [0,0,4])
0a f90efb3c a000eaf5 00000004 fe42e5a8 00000001 nt!KeWaitForMultipleObjects+0x266 (FPO: [Non-Fpo])
0b f90efda8 804524f6 00000002 00000000 00000000 win32k!RawInputThread+0x3c2 (FPO: [Non-Fpo])
0c f9dc 80465b62 a000e7cd f8d5f7d0 00000000 nt!PspSystemThreadStartup+0x69 (FPO: [Non-Fpo])
0d 00000000 f000ff53 f000e2c3 f000ff53 f000ff53 nt!KiThreadStartup+0x16
WARNING: Frame IP not in any known module. Following frames may be wrong.
0e f000ff53 00000000 00000000 00000000 00000000 0xf000ff53

線程 win32k!RawInputThread 調(diào)用 nt!ZwReadFile 要求讀入數(shù)據(jù)。

NTSTATUS?
ZwReadFile(
IN HANDLE FileHandle,
IN HANDLE Event OPTIONAL,
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
IN PVOID ApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID Buffer,
IN ULONG Length,
IN PLARGE_INTEGER ByteOffset OPTIONAL,
IN PULONG Key OPTIONAL
);

我們看到調(diào)用時(shí),參數(shù) FileHandle 正是前面 win32k!RawInputThread 用 ZwCreateFile 得到的句柄。而參數(shù) ApcRoutine 為 win32k!InputApc 的入口地址。也就是說當(dāng) ReadFile 的 IRP 結(jié)束時(shí),win32k!InputApc 將被調(diào)用。ZwReadFile 通過系統(tǒng)服務(wù),最終調(diào)用 nt!NtReadFile。

nt!NtReadFile 中。作為參數(shù)傳入的那個(gè)句柄,對應(yīng)著一個(gè)文件對象,這個(gè)文件對象中保存著鍵盤設(shè)備棧中的 PDO 的設(shè)備對象的指針。以這個(gè)句柄為參數(shù)調(diào)用 ObReferenceObjectByHandle,獲得句柄對應(yīng)的文件對象。之后用得到的文件對象做參數(shù)調(diào)用 IoGetRelatedDeviceObject ,這將獲得鍵盤設(shè)備棧中最頂端的設(shè)備對象的指針。用得到的鍵盤設(shè)備棧中最頂端的設(shè)備對象的 +30 char StackSize 作參數(shù),調(diào)用 nt!IoAllocateIrp,構(gòu)造 IRP ,然后初始化這個(gè) IRP,用傳入的參數(shù)設(shè)置這個(gè) IRP。然后調(diào)用 IoCallDriver ,將這個(gè) IRP 發(fā)給鍵盤驅(qū)動(dòng)。

鍵盤驅(qū)動(dòng)通常會(huì)調(diào)用 IoMarkIrpPending 使這個(gè) IRP Pending。通常情況下是這樣,詳細(xì)的情況我們在鍵盤驅(qū)動(dòng)部分討論。于是這個(gè) IRP 就在那里等待。關(guān)于這個(gè)等待的 IRP ,我們可以使用 WinDbg 的 !irpfind 命令找到它。反過來這也解釋了,我們使用 !irpfind 為什么總能看到一個(gè) pending 的發(fā)給鍵盤設(shè)備棧棧頂?shù)?IRP_MJ_READ 的 IRP。

kd> !irpfind
unable to get large pool allocation table - either wrong symbols or pool tagging is disabled

Searching NonPaged pool (fe313000 : fe52b000) for Tag: Irp?

Irp [ Thread ] irpStack: (Mj,Mn) DevObj [Driver]
...
fe439008 [fe427940] irpStack: ( 3, 0) fe4f5df0 [ /Driver/Kbdclass]
...

這個(gè) IRP 的地址為 fe439008 ,我們看看它的詳細(xì)情況

kd> !irp fe439008
Irp is active with 6 stacks 6 is current (= 0xfe43912c)
No Mdl System buffer = fe426568 Thread fe427940: Irp stack trace.?
cmd flg cl Device File Completion-Context
[ 0, 0] 0 0 00000000 00000000 00000000-00000000?

Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000?

Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000?

Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000?

Args: 00000000 00000000 00000000 00000000
[ 0, 0] 0 0 00000000 00000000 00000000-00000000?

Args: 00000000 00000000 00000000 00000000
>[ 3, 0] 0 1 fe4f5df0 fe426608 00000000-00000000 pending
/Driver/Kbdclass
Args: 00000078 00000000 00000000 00000000
看到了這個(gè) IRP pending。

這時(shí) 線程 win32k!RawInputThread 會(huì)在一個(gè) nt!KeWaitForMultipleObjects 上等待,等待的對象之一就是,希望從鍵盤驅(qū)動(dòng)中讀數(shù)據(jù)的那個(gè) IRP 。

當(dāng)鍵盤上有鍵被按下時(shí),引發(fā)中斷,導(dǎo)致驅(qū)動(dòng)從端口讀取按鍵的掃描碼。驅(qū)動(dòng)經(jīng)過一系列處理,最后調(diào)用 IoCompleteRequest 結(jié)束那個(gè)等待著的 IRP。

IRP 的結(jié)束會(huì)使得 線程 win32k!RawInputThread 在 nt!KeWaitForMultipleObjects 上對從鍵盤讀取數(shù)據(jù)的等待的結(jié)束。這將使得前面 ZwReadFile 的傳入?yún)?shù) ApcRoutine 即 win32k!InputApc 被執(zhí)行。

win32k!InputApc 中。調(diào)用兩個(gè)函數(shù),win32k!ProcessKeyboardInput,win32k!StartDeviceRead。win32k!ProcessKeyboardInput 負(fù)責(zé)處理剛才讀到的輸入數(shù)據(jù),比如分發(fā)給應(yīng)該得到這個(gè)鍵盤按鍵的進(jìn)程。數(shù)據(jù)處理完之后,也就是 win32k!ProcessKeyboardInput 結(jié)束之后。win32k!StartDeviceRead 被調(diào)用,win32k!StartDeviceRead 會(huì)調(diào)用 nt!ZwReadFile 要求讀入數(shù)據(jù)。

補(bǔ)充

win32k 實(shí)際是一個(gè)驅(qū)動(dòng)程序,不屬于應(yīng)用程序,所以把 win32k!RawInputThread 叫做鍵盤驅(qū)動(dòng)的使用層或許更合適。至于 win32k!RawInputThread 如何把得到的鍵盤上的按鍵分發(fā)給各個(gè)進(jìn)程,我們不研究。曾經(jīng)使我奇怪的是,為什么 ZwCreateFile 的參數(shù),能找到的設(shè)備對象是鍵盤設(shè)備棧的 PDO,而 ZwCreateFile 產(chǎn)生的 IRP 卻是發(fā)給鍵盤設(shè)備棧的棧頂。為什么 ZwReadFile 句柄所找到的設(shè)備對象是鍵盤設(shè)備棧的 PDO,而 ZwReadFile 產(chǎn)生的 IRP 卻是發(fā)給鍵盤設(shè)備棧最頂端的設(shè)備對象。后來跟蹤 NtCreateFile,NtReadFile 找到了原因。從中我們也可以看出,CreateFile,ReadFile,WriteFile,DeviceIoControl,CloseHandle 產(chǎn)生的 IRP 都是發(fā)給設(shè)備棧的棧頂?shù)?,然?IRP 在設(shè)備棧上自上而下。
?

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運(yùn)營商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡稱"軟通動(dòng)力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉