7.串口的过滤

设备绑定的内核API之一

驱动 --> 生成多个 --> 设备对象 --> 对应 --> 真实的一个设备

windows --> 发送消息 --> 设备对象

消息 --> 驱动会拦截 --> 转发给 --> 设备对象

驱动程序和驱动对象是一对一的关系,即一个.sys文件对应一个DRIVER_OBJECT

驱动对象和设备对象是一对多的关系,即一个DRIVER_OBJECT中会有多个DEVICE_OBJECT

驱动对象中保存第一个设备对象的指针,即DRIVER_OBJECT中的PDEVICE_OBJECT DeviceObject;指向第一个设备对象

设备对象中保存下一个设备对象的指针,形成设备链,即DEVICE_OBJECT中的struct _DEVICE_OBJECT *NextDevice;构成一个设备链

设备对象中保存驱动对象的指针,即DEVICE_OBJECT中的struct _DRIVER_OBJECT *DriverObject;指向驱动对象

一对多的数据关系,是多保存一的关键字段,即每个DEVICE_OBJECT中都有一个*DriverObject会找到自己所属的驱动对象

NTSTATUS

IoAttachDevice(

_In_ _When_(return==0, __drv_aliasesMem)

PDEVICE_OBJECT SourceDevice,//调用者生成的 用来过滤的 虚拟设备

_In_  PUNICODE_STRING TargetDevice,//要绑定的设备,是设备的名字,所以没有名字的设备不能用这个API

_Out_ PDEVICE_OBJECT *AttachedDevice//返回的指针 的指针,绑定成功后,被绑定的设备指针返回到这个地址

);

如果设备被多个其他设备绑定,其他设备会组成设备栈,后者居顶

绑定串口很容易,因为串口的名字是固定的

但是如果省内没有名字,则不是用上面这个API,可以用下面这个

IoAttachDeviceToDeviceStack

IoAttachDeviceToDeviceStackSafe <--这个更安全,但是要Windows xp 以上版本才能使用

上面这两个函数都是根据设备对象的指针而不是名字进行绑定的,所以可以绑定没有名字的串口

NTSTATUS

IoAttachDeviceToDeviceStackSafe(

_In_  PDEVICE_OBJECT SourceDevice,//过滤设备

_In_  PDEVICE_OBJECT TargetDevice,//要绑定的设备(设备栈中的设备)是指针

_Outptr_ PDEVICE_OBJECT *AttachedToDeviceObject//返回最终被绑定的设备,实际上就是绑定之前设备栈最顶层的设备

);

上面这个函数在 ntddk.h 定义

绑定设备的最终过程:生成一个过滤设备(就是设备栈中的设备,之后它会变成随便栈的最顶层)

生成设备

NTSTATUS

IoCreateDevice(

_In_  PDRIVER_OBJECT DriverObject,//本驱动的驱动对象,就是 DriverEntry传入的

_In_  ULONG DeviceExtensionSize,//设备扩展,填0

_In_opt_ PUNICODE_STRING DeviceName,//设备名字(过滤设备一般不要名字,所以填 NULL)

_In_  DEVICE_TYPE DeviceType,//设备类型(保持和被绑定的设备类型一致)

_In_  ULONG DeviceCharacteristics,//设备特征,填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();

分发函数,处理所有串口的写请求 串口--------------应用程序

NTSTATUS ccpDispatch(PDEVICE_OBJECT device,PIRP irp){

PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);

NTSTATUS status;

ULONG i,j;

//判断发送给哪个设备

for(i=0;i

if(s_fltobi[i] == device){

//所有的电源操作直接放过,直接发送,然后返回说已经处理

if(irpsp->MajorFunction == IRP_MJ_POWER){

PoStartNextPowerIrp(irp);

IoSkipCurrentIrpStackLocation(irp);

return PoCallDriver(s_nextobj[i],irp);

}

//只过滤写操作,获得写请求的缓冲区和长度

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

DbgPrint("comcap:send data: %2x\r\n",buf[j]);

}

}

//这些请求直接下发执行即可,不处理

IoSkipCurrentIrpStackLocation(irp);

return IpCallDriver(s_nextobj[i],irp);

}

}

//如果请求不在所绑定的设备中.则有问题,直接返回参数错误

irp->IpStatus.Information = 0;

irp->IpStatus.Status = STATUS_INVALID_PARAMETER;

IpCompleteRequest(irp,IO_NO_INCREMENT);

return STATUS_SUCCESS;

}

动态卸载

IoDetachDevice 解除设备绑定

IoDeleteDevice 删除生成的设备

KeDelayExecutionThread 延时

卸载过滤参数有一个问题,我们要终止这个过滤程序,但是一些IRP可能还在这个过滤程序的处理过程中

要取消这些请求很麻烦,而且不一定成功,所以这里的解决方案是等待5秒在卸载

代码:

#include

#include

//串口过滤驱动

//绑定过滤设备

NTSTATUS ccpAttachDevice(PDRIVER_OBJECT driver, PDEVICE_OBJECT oldobj, PDEVICE_OBJECT *fltobj, PDEVICE_OBJECT *next){

NTSTATUS status;

PDEVICE_OBJECT topdev = NULL;

//生成设备并绑定

status = IoCreateDevice(driver, 0, NULL, oldobj->DeviceType, 0, FALSE, fltobj);

if (status != STATUS_SUCCESS){ return status; }

//拷贝主要标志位

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;

//讲一个设备绑定到另一个设备上

topdev = IoAttachDeviceToDeviceStack(*fltobj, oldobj);

if (topdev == NULL){

IoDeleteDevice(*fltobj);

*fltobj = NULL;

status = STATUS_UNSUCCESSFUL;

return status;

}

*next = topdev;

//设置这个设备已经启动

(*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;

//根据ID转换成串口的名字

memset(name, 0, sizeof(WCHAR)* 32);

RtlStringCchPrintfW(name, 32, L"\\Device\\Serial%d", id);

RtlInitUnicodeString(&name_str, name);

//打开设备对象

*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]);

}

}

//分发函数

NTSTATUS ccpDispatch(PDEVICE_OBJECT device, PIRP irp){

PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);

NTSTATUS status;

ULONG i, j;

//发送给哪些设备(设备最多有 CCP_MAX_COM_ID 个)

for (i = 0; i < CCP_MAX_COM_ID; i++){

if (s_fltobj[i] == device){

//如果是电源操作,放过.直接发送,然后返回

if (irpsp->MajorFunction == IRP_MJ_POWER){

PoStartNextPowerIrp(irp);

IoSkipCurrentIrpStackLocation(irp);

return PoCallDriver(s_nextobj[i], irp);

}

//处理写请求操作.获得缓冲区及其长度,然后打印

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]); }

}

//其他请求不处理,直接下发

IoSkipCurrentIrpStackLocation(irp);

return IoCallDriver(s_nextobj[i], irp);

}

}

//如果不在绑定的设备中,则可能有问题,直接返回参数错误

irp->IoStatus.Information = 0;

irp->IoStatus.Status = STATUS_INVALID_PARAMETER;

IoCompleteRequest(irp, IO_NO_INCREMENT);

return STATUS_SUCCESS;

}

//动态卸载

#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 处理结束

interval.QuadPart = (5 * 1000 * DELAY_ONE_MILLISECOND);

KeDelayExecutionThread(KernelMode, FALSE, &interval);

//删除设备

for (i = 0; i < CCP_MAX_COM_ID; i++){

if (s_fltobj[i] != NULL){ IoDeleteDevice(s_fltobj[i]); }

}

DbgPrint("misaka: bye ,driver unload successfully.");

}

//驱动入口

NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path){

DbgPrint("misaka: hello ,wecome to use misaka driver service .");

size_t i;

//所有的分发函数都设置为一样的

for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++){ driver->MajorFunction[i] = ccpDispatch; }

//动态卸载

driver->DriverUnload = ccpUnload;

//绑定所有串口

ccpAttachAllComs(driver);

return STATUS_SUCCESS;

}

说明:因为wein7系统没有找到教程的超级终端,随意没办法测试,不过驱动打开是正常的.关闭也是正常的.串口控制大概也应该正常!

misaka: hello ,wecome to use misaka driver service .

misaka: bye ,driver unload successfully.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容