應(yīng)用程序和驅(qū)動(dòng)程序的通信
一、基礎(chǔ)介紹
1.1 設(shè)備與驅(qū)動(dòng)的關(guān)系
設(shè)備由驅(qū)動(dòng)去創(chuàng)建,訪問(wèn)一個(gè)設(shè)備,是首先得訪問(wèn)驅(qū)動(dòng)。如果驅(qū)動(dòng)在卸載的時(shí)候沒(méi)有刪除符號(hào),r3下也是不能去訪問(wèn)設(shè)備的。
驅(qū)動(dòng)程序和系統(tǒng)其他組件之間的交互是通過(guò)給設(shè)備發(fā)送或者接受發(fā)給設(shè)備的請(qǐng)求來(lái)交互的。換句話說(shuō),一個(gè)沒(méi)有任何設(shè)備的驅(qū)動(dòng)是不能按規(guī)范方式和系統(tǒng)交互的。當(dāng)然也不會(huì)收到任何IRP,分發(fā)函數(shù)也失去了意義。如果驅(qū)動(dòng)程序要和應(yīng)用程序之間通信,則需要生成設(shè)備。此外還必須為設(shè)備生成應(yīng)用程序可以訪問(wèn)的符號(hào)鏈接。
也就是說(shuō),應(yīng)用程序與驅(qū)動(dòng)的交互是通過(guò)設(shè)備來(lái)完成的,設(shè)備成了中間橋梁。下文中的符號(hào)鏈接又成了設(shè)備名的中間橋梁。記住,核心中介是設(shè)備。其他都是為他們的通信提供的機(jī)制。
1.2 設(shè)備符號(hào)鏈接名與設(shè)備名
Ring3不能直接訪問(wèn)設(shè)備,需要中間橋梁。這個(gè)中間橋梁是Ring0驅(qū)動(dòng)為設(shè)備名注冊(cè)的鏈接符號(hào)(Symbol Link)。
從圖中可以看出,R3要訪問(wèn)設(shè)備,是通過(guò)設(shè)備符號(hào)名去訪問(wèn)設(shè)備的,而R3應(yīng)用層下的設(shè)備符號(hào)名和設(shè)備管理器中的設(shè)備符號(hào)名有出入,必須,也只能寫(xiě)成\.\設(shè)備符號(hào)名 的方式 ,系統(tǒng)會(huì)自動(dòng)轉(zhuǎn)為??\設(shè)備符號(hào)名 ,緊接著通過(guò)設(shè)備符號(hào)名這個(gè)中間橋梁可以轉(zhuǎn)化為真正的R0下的設(shè)備名。
也就是說(shuō),符號(hào)鏈接名在應(yīng)用層只是簡(jiǎn)單的符號(hào)轉(zhuǎn)化過(guò)程,有些類似宏定義,設(shè)備符號(hào)名主體是相同的。而設(shè)備名和設(shè)備符號(hào)名可以不相同。因?yàn)樵O(shè)備符號(hào)名僅僅是個(gè)中間橋梁。
總結(jié):
- R0下的設(shè)備名格式為
\Device\自定義設(shè)備名
- R0下的設(shè)備符號(hào)名格式為
\??\自定義符號(hào)名
,其中自定義符號(hào)名
和自定義設(shè)備名
可以不一致。 - R3下不能直接訪問(wèn)設(shè)備,只能用設(shè)備符號(hào)名去訪問(wèn)設(shè)備,但是也不能直接使用
\??\設(shè)備符號(hào)名
去獲得設(shè)備名 - R3要訪問(wèn)設(shè)備,必須使用
\\.\設(shè)備符號(hào)名
,這個(gè)設(shè)備符號(hào)名由R0給定。
1.3 通信機(jī)制
- 應(yīng)用程序和驅(qū)動(dòng)程序的通信是通過(guò)
IRP (i/o request packet) (IO請(qǐng)求包)
來(lái)完成的。 - 利用其它進(jìn)程通信方式,如socket、pip、共享內(nèi)存等(注意r0的api調(diào)用方式不一致)
二、IRP方式
2.1 定義IO控制碼
可以看做一種通信協(xié)議,宏定義:
#define CTL_CODE( DeviceType, Function, Method, Access ) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
可以看到,這個(gè)宏四個(gè)參數(shù),自然是一個(gè)32位分成了4部分:
- 高16位存儲(chǔ)設(shè)備類型
- 14~15位訪問(wèn)權(quán)限
- 2~13位操作功能
- 最后0/1兩位就是確定緩沖區(qū)是如何與I/O和文件系統(tǒng)數(shù)據(jù)緩沖區(qū)進(jìn)行數(shù)據(jù)傳遞方式
自定義CTL_CODE
#define IOCTL_TEST_BUFFERED CTL_CODE(FILE_DEVICE_UNKNOWN, 0x900, METHOD_BUFFERED, FILE_ANY_ACCESS)
- IOCTL_TEST_BUFFERED:生成的IRP的MinorFunction
- FILE_DEVICE_UNKNOWN:設(shè)備對(duì)象的類型。設(shè)備類型可參考:http://blog.csdn.net/liyun123gx/article/details/38058965
- 0x900 :自定義的IO控制碼。自己定義時(shí)取0x800到0xFFF,因?yàn)?x0到0x7FF是微軟保留的。
- METHOD_BUFFERED:緩沖區(qū)進(jìn)行數(shù)據(jù)傳遞方式
- FILE_ANY_ACCESS :數(shù)據(jù)的操作模式。
內(nèi)存訪問(wèn)方式
有四種方式:
#define METHOD_BUFFERED 0
#define METHOD_IN_DIRECT 1
#define METHOD_OUT_DIRECT 2
#define METHOD_NEITHER 3
METHOD_BUFFERED:表示系統(tǒng)將用戶的輸入輸出都經(jīng)過(guò)pIrp->AssociatedIrp.SystemBuffer來(lái)緩沖,因此這種方式的通信比較安全。
METHOD_IN_DIRECT或METHOD_OUT_DIRECT:表示系統(tǒng)會(huì)將輸入緩沖在pIrp->AssociatedIrp.SystemBuffer中,并將輸出緩沖區(qū)鎖定,然后在內(nèi)核模式下重新映射一段地址,這樣也是比較安全的。
METHOD_IN_DIRECT和METHOD_OUT_DIRECT可稱為"直接方式",是指系統(tǒng)依然對(duì)Ring3的輸入緩沖區(qū)進(jìn)行緩沖,但是對(duì)Ring3的輸出緩沖區(qū)并沒(méi)有緩沖,而是在內(nèi)核中進(jìn)行了鎖定。這樣Ring3輸出緩沖區(qū)在驅(qū)動(dòng)程序完成I/O請(qǐng)求之前,都是無(wú)法訪問(wèn)的,從一定程度上保障了安全性。這兩種方式,對(duì)于Ring3的輸入緩沖區(qū)和METHOD_BUFFERED方式是一致的。對(duì)于Ring3的輸出緩沖區(qū),首先由系統(tǒng)鎖定,并使用pIrp->MdlAddress來(lái)描述這段內(nèi)存,驅(qū)動(dòng)程序需要使用MmGetSystemAddressForMdlSafe函數(shù)將這段內(nèi)存映射到內(nèi)核內(nèi)存地址(OutputBuffer),然后可以直接寫(xiě)入OutputBuffer地址,最終在驅(qū)動(dòng)派遣例程返回后,由系統(tǒng)解除這段內(nèi)存的鎖定。
METHOD_IN_DIRECT和METHOD_OUT_DIRECT方式的區(qū)別,僅在于打開(kāi)設(shè)備的權(quán)限上,當(dāng)以只讀權(quán)限打開(kāi)設(shè)備時(shí),METHOD_IN_DIRECT方式的IoControl將會(huì)成功,而METHOD_OUT_DIRECT方式將會(huì)失敗。如果以讀寫(xiě)權(quán)限打開(kāi)設(shè)備,兩種方式都會(huì)成功。
METHOD_NEITHER:“其他方式”,雖然通信的效率提高了,但是不夠安全。驅(qū)動(dòng)的派遣函數(shù)中輸入緩沖區(qū)可以通過(guò)I/O堆棧(IO_STACK_LOCATION)的stack->Parameters.DeviceIo Control.Type3InputBuffer得到。輸出緩沖區(qū)可以通過(guò)pIrp->UserBuffer得到。由于驅(qū)動(dòng)中的派遣函數(shù)不能保證傳遞進(jìn)來(lái)的用戶輸入和輸出地址,因此最好不要直接去讀寫(xiě)這些地址的緩沖區(qū)。應(yīng)該在讀寫(xiě)前使用ProbeForRead和ProbeForWrite函數(shù)探測(cè)地址是否可讀和可寫(xiě)。
METHOD_ NEITHER方式是不進(jìn)行緩沖的,在驅(qū)動(dòng)中可以直接使用Ring3的輸入輸出內(nèi)存地址,驅(qū)動(dòng)程序可以通過(guò)pIrpStack->Parameters.DeviceIoControl.Type3InputBuffer得到Ring3的輸入緩沖區(qū)地址(其中pIrpStack是IoGetCurrentIrpStackLocation(pIrp)的返回);通過(guò)pIrp-> UserBuffer得到Ring3的輸出緩沖區(qū)地址。
由于METHOD_NEITHER方式并不安全,因此最好對(duì)Type3InputBuffer讀取之前使用ProbeForRead函數(shù)進(jìn)行探測(cè),對(duì)UserBuffer寫(xiě)入之前使用ProbeForWrite函數(shù)進(jìn)行探測(cè),當(dāng)沒(méi)有發(fā)生異常時(shí),再進(jìn)行讀取和寫(xiě)入操作。
2.2 定義驅(qū)動(dòng)設(shè)備名,符號(hào)鏈接名
關(guān)于在Ring0層中要設(shè)置驅(qū)動(dòng)設(shè)備名的同時(shí)還要設(shè)置符號(hào)鏈接名的原因,是因?yàn)橹挥蟹?hào)鏈接名才可以被用戶模式下的應(yīng)用程序識(shí)別。
如果IoCreateDevice中沒(méi)有指定設(shè)備名稱,那么I/O管理器會(huì)自動(dòng)分配一個(gè)數(shù)字作為設(shè)備的名稱。
// 驅(qū)動(dòng)設(shè)備名:設(shè)備與設(shè)備之間通信
#define DEVNAME L"\\Device\\kmdfDriver2"
// 符號(hào)鏈接名:設(shè)備與Ring3之間通信
#define LNKNAME L"\\??\\kmdfDriver2"
2.3 設(shè)備同驅(qū)動(dòng)通信
驅(qū)動(dòng)程序要做的最后一步,先用IoCreateDevice函數(shù)創(chuàng)建設(shè)備對(duì)象,再用IoCreateSymbolicLink將符號(hào)鏈接名與設(shè)備對(duì)象名稱關(guān)聯(lián),大功告成,等待IO控制碼。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-405179.html
// 創(chuàng)建設(shè)備對(duì)象名稱
RtlInitUnicodeString(&DeviceObjectName,DEVICE_OBJECT_NAME);
// 創(chuàng)建設(shè)備對(duì)象
Status = IoCreateDevice(DriverObject,NULL,
&DeviceObjectName,
FILE_DEVICE_UNKNOWN,
0, FALSE,
&DeviceObject);
if (!NT_SUCCESS(Status))
{
return Status;
}
// 創(chuàng)建設(shè)備連接名稱
RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME);
// 將設(shè)備連接名稱與設(shè)備名稱關(guān)聯(lián)
Status = IoCreateSymbolicLink(&DeviceLinkName,&DeviceObjectName);
if (!NT_SUCCESS(Status))
{
IoDeleteDevice(DeviceObject);
return Status;
}
驅(qū)動(dòng)程序鋪墊打理好之后,應(yīng)用程序就可以由符號(hào)鏈接名通過(guò)CreateFile函數(shù)獲取到設(shè)備句柄DeviceHandle,再用本場(chǎng)的主角,DeviceIoControl通過(guò)這個(gè)DeviceHandle發(fā)送控制碼了。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-405179.html
BOOL WINAPI DeviceIoControl(
_In_ HANDLE hDevice, // CreateFile函數(shù)打開(kāi)的設(shè)備句柄
_In_ DWORD dwIoControlCode,// 自定義的控制碼
_In_opt_ LPVOID lpInBuffer, // 輸入緩沖區(qū)
_In_ DWORD nInBufferSize, // 輸入緩沖區(qū)的大小
_Out_opt_ LPVOID lpOutBuffer, // 輸出緩沖區(qū)
_In_ DWORD nOutBufferSize, // 輸出緩沖區(qū)的大小
_Out_opt_ LPDWORD lpBytesReturned, // 實(shí)際返回的字節(jié)數(shù),對(duì)應(yīng)驅(qū)動(dòng)程序中pIrp->IoStatus.Information。
_Inout_opt_ LPOVERLAPPED lpOverlapped // 重疊操作結(jié)構(gòu)指針。同步設(shè)為NULL,DeviceIoControl將進(jìn)行阻塞調(diào)用;否則,應(yīng)在編程時(shí)按異步操作設(shè)計(jì)
);
HANDLE CreateFile(
LPCTSTR lpFileName, // 打開(kāi)的文件名
DWORD dwDesiredAccess, // 訪問(wèn)權(quán)限
DWORD dwShareMode, // 共
到了這里,關(guān)于windows驅(qū)動(dòng)開(kāi)發(fā)7:應(yīng)用程序和驅(qū)動(dòng)程序的通信的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!