首页
社区
课程
招聘
[旧帖] [求助]驱动初学者,关于串口过滤驱动的3个问题,请高手赐教。 0.00雪花
发表于: 2009-10-30 10:27 1642

[旧帖] [求助]驱动初学者,关于串口过滤驱动的3个问题,请高手赐教。 0.00雪花

2009-10-30 10:27
1642
驱动初学者,关于串口过滤驱动的3个问题,请高手赐教。
问题请看代码中的注释(红色的文字),或者直接查找 问题 就可以了。

申明:本代码参考了《寒江独钓——Windows内核安全编程》

/*
描述 : 串口过滤
*/
#include <ntddk.h>

// 过滤设备的设备扩展
typedef struct _COM_FILTER_DEV_EXT {
        // 串口号
        USHORT ComId;
        // 设备栈上位于过滤设备下一层的设备对象,卸载过滤设备时用
        PDEVICE_OBJECT LowerDeviceObject;
} COM_FILTER_DEV_EXT, *PCOM_FILTER_DEV_EXT;

// 驱动入口例程
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath);

// IRP处理例程
NTSTATUS Dispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);

// 驱动卸载例程
VOID DriverUnload(IN PDRIVER_OBJECT DriverObject);

// 挂载例程
VOID AttachCom(IN PDRIVER_OBJECT DriverObject, IN USHORT ComId);

// 定义各例程位于分页内存(PAGE)/非分页内存(???)/只运行一次就从内存释放(INIT)
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGE, Dispatch)
#pragma alloc_text(PAGE, DriverUnload)
#pragma alloc_text(INIT, AttachCom)
#endif

/*
驱动入口例程
*/
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
        USHORT idx;
        KdPrint(("DriverEntry in\n"));
        // 全部的IRP处理例程设置为Dispatch
        for (idx = 0; idx < IRP_MJ_MAXIMUM_FUNCTION; ++idx)
        {
                DriverObject->MajorFunction[idx] = Dispatch;
        }
        // 设置驱动卸载例程,否则无法动态卸载
        DriverObject->DriverUnload = DriverUnload;
        // 挂载串口的过滤驱动,这里0是第1个串口
    AttachCom(DriverObject, 0);
    KdPrint(("DriverEntry end\n"));
    return STATUS_SUCCESS;
}

/*
IRP处理例程
*/
NTSTATUS Dispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
        // 缓冲区的长度
        ULONG len;
        ULONG idx;
        // 缓冲区
        PUCHAR buf = NULL;
        // 过滤设备的设备扩展
        PCOM_FILTER_DEV_EXT filterDevExt = (PCOM_FILTER_DEV_EXT)DeviceObject->DeviceExtension;
        // 取得当前的IRP对应的StackLocation
        PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(Irp);
        KdPrint(("Dispatch in\n"));
        PAGED_CODE();
        switch (irpsp->MajorFunction)
        {
                case IRP_MJ_POWER :
                        // IRP_MJ_POWER 特殊处理
                        KdPrint(("IRP = IRP_MJ_POWER\n"));
                        // 准备处理下一个电源IRP
                        PoStartNextPowerIrp(Irp);
                        // 跳过当前堆栈,其实是为了平衡堆栈
                    IoSkipCurrentIrpStackLocation(Irp);
                    return PoCallDriver(filterDevExt->LowerDeviceObject, Irp);
                case IRP_MJ_WRITE :
                        // IRP_MJ_WRITE 发生时,意味着要向串口送出数据
                        KdPrint(("IRP = IRP_MJ_WRITE\n"));
                        // 取得缓冲区的长度
                        len = irpsp->Parameters.Write.Length;
                        if (Irp->MdlAddress != NULL) {
                                // 缓冲区是MdlAddress
                                buf = (PUCHAR)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
                        } else if (Irp->AssociatedIrp.SystemBuffer != NULL) {
                                // 缓冲区是SystemBuffer
                                buf = (PUCHAR)Irp->AssociatedIrp.SystemBuffer;
                        } else if (Irp->UserBuffer != NULL) {
                                // 缓冲区是UserBuffer
                                buf = (PUCHAR)Irp->UserBuffer;
                        }
                        for (idx = 0; idx < len; ++idx)
                        {
                                // 调试输出数据
                                KdPrint(("ComId = %2d Ascii = 0x%2x Char = %c\n", filterDevExt->ComId, buf[idx], buf[idx]));
                        }
                        // 跳过当前堆栈,其实是为了平衡堆栈
                        IoSkipCurrentIrpStackLocation(Irp);
                        return IoCallDriver(filterDevExt->LowerDeviceObject, Irp);
                        break;
                default:
                        // 其他的IRP,默认不处理
                        KdPrint(("IRP = Default\n"));
                        // 跳过当前堆栈,其实是为了平衡堆栈
                        IoSkipCurrentIrpStackLocation(Irp);
                    return IoCallDriver(filterDevExt->LowerDeviceObject, Irp);
                        break;
        }
}

/*
驱动卸载例程
*/
VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
        LARGE_INTEGER inteval;
        // 取得设备链(横向的)的第一个设备对象
        PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject;
        // 设置为5秒
        inteval.QuadPart = 5 * 1000 * 1000 * (-10);
        PAGED_CODE();
        KdPrint(("DriverUnload in\n"));
        while (deviceObject != NULL)
        {
                // 取得过滤设备的设备扩展
                PCOM_FILTER_DEV_EXT filterDevExt = (PCOM_FILTER_DEV_EXT)deviceObject->DeviceExtension;
                // 卸载过滤设备
                IoDetachDevice(filterDevExt->LowerDeviceObject);
                // 延时5秒。设备卸载后,就不会接收到新的IRP,假设5秒后,当前正在处理的IRP结束了,然后删除过滤设备对象,
                // 当然,如果5秒以后当前正在处理的IRP还没有结束,删除过滤设备对象以后就会蓝屏了。
                // 问题1 : 该方法不够严格,有没有更好的方法安全的把过滤设备删除?
                KeDelayExecutionThread(KernelMode, FALSE, &inteval);
                // 删除过滤设备对象,注意:设备对象删除以后仍然可以访问到NextDevice域
                IoDeleteDevice(deviceObject);
                deviceObject = deviceObject->NextDevice;
        }
        KdPrint(("DriverUnload end\n"));
}

/*
挂载例程
*/
VOID AttachCom(IN PDRIVER_OBJECT DriverObject, IN USHORT ComId)
{
        NTSTATUS status;
        // 串口名字
        UNICODE_STRING comName;
        // 文件对象
        PFILE_OBJECT fileObject = NULL;
        // 设备栈底层的设备对象
        PDEVICE_OBJECT targetDeviceObject = NULL;
        // 设备栈上位于过滤设备下一层的设备对象
        PDEVICE_OBJECT lowerDeviceObject = NULL;
        // 过滤设备对象
        PDEVICE_OBJECT filterDeviceObject = NULL;
        // 过滤设备对象的设备扩展
        PCOM_FILTER_DEV_EXT filterDevExt = NULL;
        KdPrint(("AttachCom in\n"));
        // 串口的名字,这里固定为第1个串口,可以修改为根据ComId绑定其他的串口
        RtlInitUnicodeString(&comName, L"\\Device\\Serial0");
        // 取得设备对象的指针
        status = IoGetDeviceObjectPointer(&comName, FILE_ALL_ACCESS, &fileObject, &targetDeviceObject);
        if (!NT_SUCCESS(status))
        {
                KdPrint(("IoGetDeviceObjectPointer failed\n"));
                // 如果失败,退出
                return;
        }
        // 如果成功,文件对象要解引用,否则会因为引用计数不正确,发生内存泄漏
        ObDereferenceObject(fileObject);
        // 生成过滤设备对象
        status = IoCreateDevice(DriverObject,
                                sizeof(COM_FILTER_DEV_EXT),
                                NULL,
                                targetDeviceObject->Type,
                                targetDeviceObject->Characteristics,
                                FALSE,
                                &filterDeviceObject);
        if (!NT_SUCCESS(status))
        {
                KdPrint(("IoCreateDevice failed\n"));
                // 如果失败,退出
                return;
        }
        // 把过滤设备对象的Flags属性设置为和要绑定的目标设备对象的Flags属性一致
        filterDeviceObject->Flags |= targetDeviceObject->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE);
        // 问题2 : filterDeviceObject->Characteristics是否需要设置FILE_DEVICE_SECTURE_OPEN属性,有什么意义?不设置会怎样呢?
        // 问题3 : filterDeviceObject->StackSize需要加1吗?加1和不加1各有什么意义?
        // 绑定到目标设备对象
        lowerDeviceObject = IoAttachDeviceToDeviceStack(filterDeviceObject, targetDeviceObject);
        if (lowerDeviceObject == NULL)
        {
                KdPrint(("IoAttachDeviceToDeviceStack failed\n"));
                // 如果失败,删除已经创建的过滤设备对象
                IoDeleteDevice(filterDeviceObject);
                return;
        }
        // 设置过滤设备对象的设备扩展的域
        filterDevExt = (PCOM_FILTER_DEV_EXT)filterDeviceObject->DeviceExtension;
        // 绑定的串口号
        filterDevExt->ComId = ComId;
        // 设备栈上位于过滤设备下一层的设备对象,卸载过滤设备时用
        filterDevExt->LowerDeviceObject = lowerDeviceObject;
        // 设置过滤设备为已经启动,可以开始接收IRP
        filterDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
        KdPrint(("AttachCom end\n"));
}

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (12)
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
自己顶上去
2009-10-30 19:38
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
再顶一次
2009-10-30 19:38
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
顶住!
2009-10-30 21:21
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
吃早饭前顶一次
2009-10-31 08:29
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
吃过晚饭再顶一次
2009-10-31 21:19
0
雪    币: 34
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
楼主,字体太小了,你改一下估计有人回你.
2009-10-31 22:43
0
雪    币: 15
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
StackSize 应该是附加后自动加上去的。没验证过
2009-11-1 11:04
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
好建议,我改
2009-11-1 11:11
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
如果是自动加上的,意味着不会有StackSize = StackSize + 1这样的代码,可是我在书上看到了这样的代码,两个例程一个不加,一个加,我的验证代码是都不加,暂时没有问题。只是有点疑惑,到底加不加。
2009-11-1 11:15
0
雪    币: 15
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
不知道楼主在哪看到 StackSize 加了。在串口例子中我没有看到。另外晚上我再试试附加设备后会不会自动加1
我也正在看寒江  有机会交流交流:qq:510852387
2009-11-1 12:03
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
在键盘过滤驱动的例子中看到的
2009-11-1 14:21
0
雪    币: 68
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
// 问题3 : filterDeviceObject->StackSize需要加1吗?加1和不加1各有什么意义?
这一句应该是多余的,wdk文档中有这么一句话,This routine sets the AlignmentRequirement in SourceDevice to the value in the next-lower device object and sets the StackSize to the value in the next-lower-object plus one.

你可以看看,IoAttachDeviceToDeviceStack(filterDeviceObject, targetDeviceObject);
一句中的filterDeviceObject->StackSize已经1了
2009-11-17 11:35
0
游客
登录 | 注册 方可回帖
返回
//