前言 :
這裡將介紹以檔案控制程式碼的方式呼叫其他驅動程式. 這種方式類似於在應用程式中呼叫驅動程式, 使用方法簡單, 不需要程式師對 Windows 底層瞭解過的的門檻.
準備一個標準驅動程式 :
在介紹驅動程式呼叫其他驅動程式之前, 首先準備一個標準驅動程式, 作為 "目標" 驅動程式. 本章後面介紹的所有驅動程式, 都以不同方法呼叫這個 "目標" 驅動程式.
這個 "目標" 驅動程式新建一個模擬裝置, 模擬裝置支援非同步讀取操作. 事先規定每次對裝置讀取需耗時 3s, 因為這樣可以很好地演示非同步讀取操作. 對於這樣的驅動程式, 應該設置一個計時器, 計時器的間隔設置為 3s. 另外在 IRP_MJ_READ 的派遣函式中不結束 IRP 請求, 而是將 IRP 請求 Pending, 並且再派遣函式退出前開啟計時器. 這樣 3s 後就會進入計時器的回呼函式中, 並在回乎函式中結束 IRP 請求.
我們把這個 "目標" 驅動程式命名為 DriverA, 呼叫 DriverA 的驅動程式被命名為 DriverB. 以下列出的是 DriverA 的部分程式碼 :
以上就是需要準備的 "標準" 驅動程式 DriverA. 接下來將介紹幾種不同方法, 使另外的驅動程式 (DriverB) 可以呼叫 DriverA 的讀取功能. 為了演示 DriverB 呼叫 DriverA, 並使讀者可以看清楚 log 資訊來自哪個驅動程式, 這裡讓 DriverA 在輸出 log 資訊前加上 "DriverA:" 這個Prefix, 而讓 DriverB 在輸出 log 資訊之前加上 "DriverB:" 的 Prefix.
在前面 (計時器) 的例子中, 都是讓應用程式做為驅動程式的 "客戶端". 而這裡 DriverA 的用戶端不再是應用程式, 而變成了另外的驅動程式 (DriverB). 其呼叫關係如下所示 :
獲得裝置控制碼 :
前言準備了 DriverA, 接著要介紹如何編寫 DriverB 並呼叫 DriverA. 下面回憶一下應用程式是如何呼叫驅動程式吧. 應該先用 CreateFile 函式打開裝置, 然後使用 ReadFile 函式讀取裝置, 最後用 CloseHandle 函式關閉裝置.
在驅動程式中, 打開裝置使用 ZwCreateFile 內核函式, 它會返回裝置控制碼. 這裡要討論一下如何用 ZwCreateFile 內核函式打開 "同步" 裝置和 "非同步" 裝置. 其函式宣告如下 :
ZwCreateFile 內核函式的大多數參數已經詳細介紹過, 這裡只介紹打開 "同步" 裝置和 "非同步" 裝置的區別. 如果打開 "同步" 裝置, 第二個參數 DesiredAccess 需要設置為 SYNCHRONIZE, 並且倒數第三個參數 CreateOptions 需要指定為 FILE_SYNCHRONOUS_IO_NONALERT 或者 FILE_SYNCHRONOUS_IO_ALERT. 範例代碼如下 :
如果打開了 "非同步" 裝置, 第二個參數 DesiredAccess 不能設置為 SYNCHRONIZE, 並且倒數第三個參數 CreateOptions 不能指定 FILE_SYNCHRONOUS_IO_NONALERT 或 FILE_SYNCHRONOUS_IO_ALERT.
同步呼叫 :
在應用程式中, ReadFile 函式既可以用來讀取檔案, 也可以用來讀取裝置. 同樣在驅動程式中, ZwReadFile 內核函式既可以用來讀取檔案, 也可以讀取裝置. 如果用 ZwReadFile 內核函式同步讀取裝置, 操作對應的 IRP_MJ_READ 請求被結束後, 函式才會返回, 否則函式會一直等待 IRP_MJ_READ 請求被結束. 而這裡介紹的 DriverB 是利用同步讀取方式呼叫 DriverA, 如用 ZwReadFile 內核函式同步讀取 DriverA 的裝置, 它的內部操作過程如下 :
明白上述過程後, 使用 ZwReadFile 內核函式進行同步讀取就變的簡單了. 下面程式碼演示了如何使用 ZwReadFile 內核函式讀取 DriverA 的裝置. 另外 DriverB 還需要應用程式的用戶端, 因此 DriverB 的派遣函式還需要結束來自應用程式的 IRP 請求, 底下是相關範例代碼 :
底下是執行結果 :
非同步呼叫方法一 :
非同步讀取主要是指 ZwReadFile 內核函式在沒有等待 DriverA 真正結束 IRP 請求時就已經退出. 如果 ZwReadFile 內核函式非同步讀取 DriverA 裝置時, 它的內部操作過程如下 :
ZwReadFile 內核函式退出後, 無法得知 "Pending" 的 IRP 何時被結束. 因此在呼叫 ZwReadFile 前可以為IRP 設置一個完成常式. 當 IRP 結束時就觸發這個完成常式. 在本例中將一個事件的指標傳遞給該完成常式, 在完成常式中觸發該事件. 一旦這個事件被觸發, 就可以知道 IRP_MJ_READ 請求已經結束. 下面程式碼演示了如何用 ZwReadFile 內核函式進行非同步讀取操作 :
以下是完成常式的程式碼. 完成常式主要是用一個事件通知 IRP 請求被結束.
底下是執行結果 :
非同步呼叫方法二 :
非同步方法一在非同步讀取時, 將一個事件的控制碼傳遞給 ZwReadFile 內核函式, 這個事件可以用來通知讀取操作何時完成. 而非同步方法二不用將事件控制碼傳遞給 ZwReadFile 函式, 而是透過檔案物件判斷是否讀取操作完畢. 每打開一個裝置, 都會伴隨一個關聯的檔案物件 (FILE_OBJECT). 利用內核函式 ObReferenceObjectByHandle 可以獲取和裝置相關的檔案物件指標.當 IRP_MJ_READ 請求被結束後, 檔案物件的子欄位 Event 會被設置, 因此用檔案物件的 Event 子欄位可以當做同步處理使用. 以下是相關的部分程式碼 :
透過符號連結打開裝置 :
前面介紹的例子都是透過裝置名打開裝置. 但是很多情況, 使用者是不容易知道具體的裝置名, 而只知道符號連結. 例如 "C:" 代表第一個硬碟分割區. 而 "C:" 就是一個符號連結, 他指向一個磁碟分割裝置. 尤其在 WDK 驅動程式中, 透過符號連結打開裝置是常用的方法.
利用 ZwOpenSymbolicLinkObject 內核函式先得到符號連結的控制碼, 然後使用 ZwQuerySymbolicLinkObject 內核函式找到裝置名. 透過裝置名就可以方便的打開裝置了. 底下示相關的範例代碼 :
底下是執行結果 :
這裡將介紹以檔案控制程式碼的方式呼叫其他驅動程式. 這種方式類似於在應用程式中呼叫驅動程式, 使用方法簡單, 不需要程式師對 Windows 底層瞭解過的的門檻.
準備一個標準驅動程式 :
在介紹驅動程式呼叫其他驅動程式之前, 首先準備一個標準驅動程式, 作為 "目標" 驅動程式. 本章後面介紹的所有驅動程式, 都以不同方法呼叫這個 "目標" 驅動程式.
這個 "目標" 驅動程式新建一個模擬裝置, 模擬裝置支援非同步讀取操作. 事先規定每次對裝置讀取需耗時 3s, 因為這樣可以很好地演示非同步讀取操作. 對於這樣的驅動程式, 應該設置一個計時器, 計時器的間隔設置為 3s. 另外在 IRP_MJ_READ 的派遣函式中不結束 IRP 請求, 而是將 IRP 請求 Pending, 並且再派遣函式退出前開啟計時器. 這樣 3s 後就會進入計時器的回呼函式中, 並在回乎函式中結束 IRP 請求.
我們把這個 "目標" 驅動程式命名為 DriverA, 呼叫 DriverA 的驅動程式被命名為 DriverB. 以下列出的是 DriverA 的部分程式碼 :
以上就是需要準備的 "標準" 驅動程式 DriverA. 接下來將介紹幾種不同方法, 使另外的驅動程式 (DriverB) 可以呼叫 DriverA 的讀取功能. 為了演示 DriverB 呼叫 DriverA, 並使讀者可以看清楚 log 資訊來自哪個驅動程式, 這裡讓 DriverA 在輸出 log 資訊前加上 "DriverA:" 這個Prefix, 而讓 DriverB 在輸出 log 資訊之前加上 "DriverB:" 的 Prefix.
在前面 (計時器) 的例子中, 都是讓應用程式做為驅動程式的 "客戶端". 而這裡 DriverA 的用戶端不再是應用程式, 而變成了另外的驅動程式 (DriverB). 其呼叫關係如下所示 :
獲得裝置控制碼 :
前言準備了 DriverA, 接著要介紹如何編寫 DriverB 並呼叫 DriverA. 下面回憶一下應用程式是如何呼叫驅動程式吧. 應該先用 CreateFile 函式打開裝置, 然後使用 ReadFile 函式讀取裝置, 最後用 CloseHandle 函式關閉裝置.
在驅動程式中, 打開裝置使用 ZwCreateFile 內核函式, 它會返回裝置控制碼. 這裡要討論一下如何用 ZwCreateFile 內核函式打開 "同步" 裝置和 "非同步" 裝置. 其函式宣告如下 :
ZwCreateFile 內核函式的大多數參數已經詳細介紹過, 這裡只介紹打開 "同步" 裝置和 "非同步" 裝置的區別. 如果打開 "同步" 裝置, 第二個參數 DesiredAccess 需要設置為 SYNCHRONIZE, 並且倒數第三個參數 CreateOptions 需要指定為 FILE_SYNCHRONOUS_IO_NONALERT 或者 FILE_SYNCHRONOUS_IO_ALERT. 範例代碼如下 :
- // 同步打開裝置
- // 設定了 FILE_SYNCHRONOUS_IO_NONALERT 或者 FILE_SYNCHRONOUS_IO_ALERT 為同步打開裝置
- ntStatus = ZwCreateFile(&hDevice,
- FILE_READ_ATTRIBUTES|SYNCHRONIZE,
- &objectAttributes,
- &status_block,
- NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
- FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
同步呼叫 :
在應用程式中, ReadFile 函式既可以用來讀取檔案, 也可以用來讀取裝置. 同樣在驅動程式中, ZwReadFile 內核函式既可以用來讀取檔案, 也可以讀取裝置. 如果用 ZwReadFile 內核函式同步讀取裝置, 操作對應的 IRP_MJ_READ 請求被結束後, 函式才會返回, 否則函式會一直等待 IRP_MJ_READ 請求被結束. 而這裡介紹的 DriverB 是利用同步讀取方式呼叫 DriverA, 如用 ZwReadFile 內核函式同步讀取 DriverA 的裝置, 它的內部操作過程如下 :
明白上述過程後, 使用 ZwReadFile 內核函式進行同步讀取就變的簡單了. 下面程式碼演示了如何使用 ZwReadFile 內核函式讀取 DriverA 的裝置. 另外 DriverB 還需要應用程式的用戶端, 因此 DriverB 的派遣函式還需要結束來自應用程式的 IRP 請求, 底下是相關範例代碼 :
底下是執行結果 :
非同步呼叫方法一 :
非同步讀取主要是指 ZwReadFile 內核函式在沒有等待 DriverA 真正結束 IRP 請求時就已經退出. 如果 ZwReadFile 內核函式非同步讀取 DriverA 裝置時, 它的內部操作過程如下 :
ZwReadFile 內核函式退出後, 無法得知 "Pending" 的 IRP 何時被結束. 因此在呼叫 ZwReadFile 前可以為IRP 設置一個完成常式. 當 IRP 結束時就觸發這個完成常式. 在本例中將一個事件的指標傳遞給該完成常式, 在完成常式中觸發該事件. 一旦這個事件被觸發, 就可以知道 IRP_MJ_READ 請求已經結束. 下面程式碼演示了如何用 ZwReadFile 內核函式進行非同步讀取操作 :
以下是完成常式的程式碼. 完成常式主要是用一個事件通知 IRP 請求被結束.
底下是執行結果 :
非同步呼叫方法二 :
非同步方法一在非同步讀取時, 將一個事件的控制碼傳遞給 ZwReadFile 內核函式, 這個事件可以用來通知讀取操作何時完成. 而非同步方法二不用將事件控制碼傳遞給 ZwReadFile 函式, 而是透過檔案物件判斷是否讀取操作完畢. 每打開一個裝置, 都會伴隨一個關聯的檔案物件 (FILE_OBJECT). 利用內核函式 ObReferenceObjectByHandle 可以獲取和裝置相關的檔案物件指標.當 IRP_MJ_READ 請求被結束後, 檔案物件的子欄位 Event 會被設置, 因此用檔案物件的 Event 子欄位可以當做同步處理使用. 以下是相關的部分程式碼 :
透過符號連結打開裝置 :
前面介紹的例子都是透過裝置名打開裝置. 但是很多情況, 使用者是不容易知道具體的裝置名, 而只知道符號連結. 例如 "C:" 代表第一個硬碟分割區. 而 "C:" 就是一個符號連結, 他指向一個磁碟分割裝置. 尤其在 WDK 驅動程式中, 透過符號連結打開裝置是常用的方法.
利用 ZwOpenSymbolicLinkObject 內核函式先得到符號連結的控制碼, 然後使用 ZwQuerySymbolicLinkObject 內核函式找到裝置名. 透過裝置名就可以方便的打開裝置了. 底下示相關的範例代碼 :
底下是執行結果 :
This message was edited 22 times. Last update was at 08/02/2011 17:44:20
沒有留言:
張貼留言