設(shè)備綁定的內(nèi)核API之一
驅(qū)動 --> 生成多個 --> 設(shè)備對象 --> 對應(yīng) --> 真實的一個設(shè)備
windows --> 發(fā)送消息 --> 設(shè)備對象
消息 --> 驅(qū)動會攔截 --> 轉(zhuǎn)發(fā)給 --> 設(shè)備對象
驅(qū)動程序和驅(qū)動對象是一對一的關(guān)系枣耀,即一個.sys文件對應(yīng)一個DRIVER_OBJECT
驅(qū)動對象和設(shè)備對象是一對多的關(guān)系,即一個DRIVER_OBJECT中會有多個DEVICE_OBJECT
驅(qū)動對象中保存第一個設(shè)備對象的指針十酣,即DRIVER_OBJECT中的PDEVICE_OBJECT DeviceObject;指向第一個設(shè)備對象
設(shè)備對象中保存下一個設(shè)備對象的指針,形成設(shè)備鏈,即DEVICE_OBJECT中的struct _DEVICE_OBJECT *NextDevice;構(gòu)成一個設(shè)備鏈
設(shè)備對象中保存驅(qū)動對象的指針谣光,即DEVICE_OBJECT中的struct _DRIVER_OBJECT *DriverObject;指向驅(qū)動對象
一對多的數(shù)據(jù)關(guān)系瞬场,是多保存一的關(guān)鍵字段野瘦,即每個DEVICE_OBJECT中都有一個*DriverObject會找到自己所屬的驅(qū)動對象
NTSTATUS
IoAttachDevice(
_In_ _When_(return==0, __drv_aliasesMem)
PDEVICE_OBJECT SourceDevice,//調(diào)用者生成的 用來過濾的 虛擬設(shè)備
_In_ ?PUNICODE_STRING TargetDevice,//要綁定的設(shè)備,是設(shè)備的名字,所以沒有名字的設(shè)備不能用這個API
_Out_ PDEVICE_OBJECT *AttachedDevice//返回的指針 的指針,綁定成功后,被綁定的設(shè)備指針返回到這個地址
);
如果設(shè)備被多個其他設(shè)備綁定,其他設(shè)備會組成設(shè)備棧,后者居頂
綁定串口很容易,因為串口的名字是固定的
但是如果省內(nèi)沒有名字,則不是用上面這個API,可以用下面這個
IoAttachDeviceToDeviceStack
IoAttachDeviceToDeviceStackSafe <--這個更安全,但是要Windows xp 以上版本才能使用
上面這兩個函數(shù)都是根據(jù)設(shè)備對象的指針而不是名字進(jìn)行綁定的,所以可以綁定沒有名字的串口
NTSTATUS
IoAttachDeviceToDeviceStackSafe(
_In_ ?PDEVICE_OBJECT SourceDevice,//過濾設(shè)備
_In_ ?PDEVICE_OBJECT TargetDevice,//要綁定的設(shè)備(設(shè)備棧中的設(shè)備)是指針
_Outptr_ PDEVICE_OBJECT *AttachedToDeviceObject//返回最終被綁定的設(shè)備,實際上就是綁定之前設(shè)備棧最頂層的設(shè)備
);
上面這個函數(shù)在 ntddk.h 定義
綁定設(shè)備的最終過程:生成一個過濾設(shè)備(就是設(shè)備棧中的設(shè)備,之后它會變成隨便棧的最頂層)
生成設(shè)備
NTSTATUS
IoCreateDevice(
_In_ ?PDRIVER_OBJECT DriverObject,//本驅(qū)動的驅(qū)動對象,就是 DriverEntry傳入的
_In_ ?ULONG DeviceExtensionSize,//設(shè)備擴(kuò)展,填0
_In_opt_ PUNICODE_STRING DeviceName,//設(shè)備名字(過濾設(shè)備一般不要名字,所以填 NULL)
_In_ ?DEVICE_TYPE DeviceType,//設(shè)備類型(保持和被綁定的設(shè)備類型一致)
_In_ ?ULONG DeviceCharacteristics,//設(shè)備特征,填0
_In_ ?BOOLEAN Exclusive,
_Outptr_result_nullonfailure_
_At_(*DeviceObject,
__drv_allocatesMem(Mem)
_When_((((_In_function_class_(DRIVER_INITIALIZE))
||(_In_function_class_(DRIVER_DISPATCH)))),
__drv_aliasesMem))
PDEVICE_OBJECT *DeviceObject
);
//串口名
UNICODE_STRING com_name = RTL_CONSTANT_STRING(L"\\Device\\Serial0");
NTSTATUS status = IoAttachDevice();
分發(fā)函數(shù),處理所有串口的寫請求 串口--------------應(yīng)用程序
NTSTATUS ccpDispatch(PDEVICE_OBJECT device,PIRP irp){
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);
NTSTATUS status;
ULONG i,j;
//判斷發(fā)送給哪個設(shè)備
for(i=0;i
if(s_fltobi[i] == device){
//所有的電源操作直接放過,直接發(fā)送,然后返回說已經(jīng)處理
if(irpsp->MajorFunction == IRP_MJ_POWER){
PoStartNextPowerIrp(irp);
IoSkipCurrentIrpStackLocation(irp);
return PoCallDriver(s_nextobj[i],irp);
}
//只過濾寫操作,獲得寫請求的緩沖區(qū)和長度
if(irpsp->MajorFunction == IRP_MJ_WRITE){
//如果是寫,獲得長度
ULONG len = irpsp->Parameters.Write.Length;
//然后獲得緩沖區(qū)
PUCHAR buf = NULL;
if(irp->MdlAddress != NULL){
buf = (PUCHAR)MmGetSystemAddressForMdlSafe(irp->MdlAddress,NormalPagePriority);
}else{
buf = (PUCHAR)irp->UserBuffer;
}
if(buf == NULL){
buf = (PUCHAR)irp->AssociatedIrp.SystemBuffer;
}
//打印內(nèi)容
for(j=0;j
DbgPrint("comcap:send data: %2x\r\n",buf[j]);
}
}
//這些請求直接下發(fā)執(zhí)行即可,不處理
IoSkipCurrentIrpStackLocation(irp);
return IpCallDriver(s_nextobj[i],irp);
}
}
//如果請求不在所綁定的設(shè)備中.則有問題,直接返回參數(shù)錯誤
irp->IpStatus.Information = 0;
irp->IpStatus.Status = STATUS_INVALID_PARAMETER;
IpCompleteRequest(irp,IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
動態(tài)卸載
IoDetachDevice 解除設(shè)備綁定
IoDeleteDevice 刪除生成的設(shè)備
KeDelayExecutionThread 延時
卸載過濾參數(shù)有一個問題,我們要終止這個過濾程序,但是一些IRP可能還在這個過濾程序的處理過程中
要取消這些請求很麻煩,而且不一定成功,所以這里的解決方案是等待5秒在卸載
代碼:
#include
#include
//串口過濾驅(qū)動
//綁定過濾設(shè)備
NTSTATUS ccpAttachDevice(PDRIVER_OBJECT driver, PDEVICE_OBJECT oldobj, PDEVICE_OBJECT *fltobj, PDEVICE_OBJECT *next){
NTSTATUS status;
PDEVICE_OBJECT topdev = NULL;
//生成設(shè)備并綁定
status = IoCreateDevice(driver, 0, NULL, oldobj->DeviceType, 0, FALSE, fltobj);
if (status != STATUS_SUCCESS){ return status; }
//拷貝主要標(biāo)志位
if (oldobj->Flags & DO_BUFFERED_IO){ (*fltobj)->Flags |= DO_BUFFERED_IO; }
if (oldobj->Flags & DO_DIRECT_IO){ (*fltobj)->Flags |= DO_DIRECT_IO; }
if (oldobj->Characteristics & FILE_DEVICE_SECURE_OPEN){ (*fltobj)->Characteristics |= FILE_DEVICE_SECURE_OPEN; }
(*fltobj)->Flags |= DO_POWER_PAGABLE;
//講一個設(shè)備綁定到另一個設(shè)備上
topdev = IoAttachDeviceToDeviceStack(*fltobj, oldobj);
if (topdev == NULL){
IoDeleteDevice(*fltobj);
*fltobj = NULL;
status = STATUS_UNSUCCESSFUL;
return status;
}
*next = topdev;
//設(shè)置這個設(shè)備已經(jīng)啟動
(*fltobj)->Flags = (*fltobj)->Flags & ~DO_DEVICE_INITIALIZING;
return STATUS_SUCCESS;
}
//打開串口
PDEVICE_OBJECT ccpOpenCom(ULONG id, NTSTATUS *status){
UNICODE_STRING name_str;
static WCHAR name[32] = { 0 };
PFILE_OBJECT fileobj = NULL;
PDEVICE_OBJECT devobj = NULL;
//根據(jù)ID轉(zhuǎn)換成串口的名字
memset(name, 0, sizeof(WCHAR)* 32);
RtlStringCchPrintfW(name, 32, L"\\Device\\Serial%d", id);
RtlInitUnicodeString(&name_str, name);
//打開設(shè)備對象
*status = IoGetDeviceObjectPointer(&name_str, FILE_ALL_ACCESS, &fileobj, &devobj);
//如果打開成功,把文件對象引用解除
if (*status == STATUS_SUCCESS){ ObDereferenceObject(fileobj); }
return devobj;
}
//綁定所有的串口
#define CCP_MAX_COM_ID 32
static PDEVICE_OBJECT s_fltobj[CCP_MAX_COM_ID] = { 0 };
static PDEVICE_OBJECT s_nextobj[CCP_MAX_COM_ID] = { 0 };
void ccpAttachAllComs(PDRIVER_OBJECT driver){
ULONG i;
PDEVICE_OBJECT com_ob;
NTSTATUS status;
for (i = 0; i < CCP_MAX_COM_ID; i++){
com_ob = ccpOpenCom(i, &status);
if (com_ob == NULL){ continue; }
ccpAttachDevice(driver, com_ob, &s_fltobj[i], &s_nextobj[i]);
}
}
//分發(fā)函數(shù)
NTSTATUS ccpDispatch(PDEVICE_OBJECT device, PIRP irp){
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);
NTSTATUS status;
ULONG i, j;
//發(fā)送給哪些設(shè)備(設(shè)備最多有 CCP_MAX_COM_ID 個)
for (i = 0; i < CCP_MAX_COM_ID; i++){
if (s_fltobj[i] == device){
//如果是電源操作,放過.直接發(fā)送,然后返回
if (irpsp->MajorFunction == IRP_MJ_POWER){
PoStartNextPowerIrp(irp);
IoSkipCurrentIrpStackLocation(irp);
return PoCallDriver(s_nextobj[i], irp);
}
//處理寫請求操作.獲得緩沖區(qū)及其長度,然后打印
if (irpsp->MajorFunction == IRP_MJ_WRITE){
ULONG len = irpsp->Parameters.Write.Length;
PUCHAR buf = NULL;
if (irp->MdlAddress != NULL){
buf = (PUCHAR)MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority);
}else{
buf = (PUCHAR)irp->UserBuffer;
}
if (buf == NULL){ buf = (PUCHAR)irp->AssociatedIrp.SystemBuffer; }
//打印
for (j = 0; j < len; ++j){ DbgPrint("misaka: send data %2x\r\n",buf[j]); }
}
//其他請求不處理,直接下發(fā)
IoSkipCurrentIrpStackLocation(irp);
return IoCallDriver(s_nextobj[i], irp);
}
}
//如果不在綁定的設(shè)備中,則可能有問題,直接返回參數(shù)錯誤
irp->IoStatus.Information = 0;
irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest(irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
//動態(tài)卸載
#define DELAY_ONE_MICROSECOND (-10)
#define DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000)
#define DELAY_ONE_SECOND (DELAY_ONE_MILLISECOND*1000)
void ccpUnload(PDRIVER_OBJECT drv){
ULONG i;
LARGE_INTEGER interval;
//解除綁定
for (i = 0; i < CCP_MAX_COM_ID; i++){
if (s_nextobj[i] != NULL){ IoDetachDevice(s_nextobj[i]); }
}
//睡眠5秒,等待所有的 IRP 處理結(jié)束
interval.QuadPart = (5 * 1000 * DELAY_ONE_MILLISECOND);
KeDelayExecutionThread(KernelMode, FALSE, &interval);
//刪除設(shè)備
for (i = 0; i < CCP_MAX_COM_ID; i++){
if (s_fltobj[i] != NULL){ IoDeleteDevice(s_fltobj[i]); }
}
DbgPrint("misaka: bye ,driver unload successfully.");
}
//驅(qū)動入口
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path){
DbgPrint("misaka: hello ,wecome to use misaka driver service .");
size_t i;
//所有的分發(fā)函數(shù)都設(shè)置為一樣的
for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++){ driver->MajorFunction[i] = ccpDispatch; }
//動態(tài)卸載
driver->DriverUnload = ccpUnload;
//綁定所有串口
ccpAttachAllComs(driver);
return STATUS_SUCCESS;
}
說明:因為wein7系統(tǒng)沒有找到教程的超級終端,隨意沒辦法測試,不過驅(qū)動打開是正常的.關(guān)閉也是正常的.串口控制大概也應(yīng)該正常!
misaka: hello ,wecome to use misaka driver service .
misaka: bye ,driver unload successfully.