在看了《寒江独钓--Windows内核安全编程》一书,根据书中所讲编写,代码中只对IRP_MJ_WRITE进行了过滤处理
//文件名comCap.c
#include <ntddk.h>
#include <ntstrsafe.h>#define NTSTRSAFE_LIB
#define CCP_MAX_COM_ID 32 //设定最大COM端口个数
//保存所有过滤设备指针
static PDEVICE_OBJECT s_fltobj[CCP_MAX_COM_ID]= { 0 };
static PDEVICE_OBJECT s_nextobj[CCP_MAX_COM_ID] = { 0 };
//与设备卸载相关的参数定义
#define DELAY_ONE_MICROSECOND (-10)
#define DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000)
#define DELAY_ONE_SECOND (DELAY_ONE_MILILISECOND*1000)
//生成一个虚拟设备,并与真实设备进行绑定
NTSTATUS ccpAttachDevice(PDRIVER_OBJECT driver, PDEVICE_OBJECT oldobj, PDEVICE_OBJECT *fltobj, PDEVICE_OBJECT *next)
{
NTSTATUS status;
PDEVICE_OBJECT topdev = NULL;
//fltobj是新生成的设备,然后绑定
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;
//将上面生成的虚拟设备fltobj绑定到另一个设备上oldobj上面
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)
{
//外面输入的是串口的ID,这里会改成字符串的形式
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;
}
//这个函数绑定所有的串口
void ccpAttachAllComs(PDRIVER_OBJECT driver)
{
ULONG i = 0;
PDEVICE_OBJECT com_ob = NULL;
NTSTATUS status;
for(i = 0; i<CCP_MAX_COM_ID; i++)
{
//获得object引用
com_ob = ccpOpenCom(i, &status);
if(com_ob == NULL)
{
continue;
}
//在这里绑定,并不管绑定是否成功
ccpAttachDevice(driver, com_ob, &s_fltobj[i], &s_nextobj[i]);
}
}
//IRP过滤处理
NTSTATUS ccpDispatch(PDEVICE_OBJECT device, PIRP irp)
{
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);
NTSTATUS status;
ULONG i = 0;
ULONG j = 0;
//首先要知道发送给了哪个设备,设备最多一共有CCP_MAX_COM_ID个,是前面的代码保存好的,都在s_fltobj中
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("comcap: Send Data: %02x\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;
}
//卸载例程函数
void ccpUnload(PDRIVER_OBJECT drv)
{
ULONG i = 0;
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]);
}
}
}
}
//驱动入口函数
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
size_t i = 0;
//所有的分发函数都设置成一样的
for(i=0; i<IRP_MJ_MAXIMUM_FUNCTION; i++)
{
driver->MajorFunction[i] = ccpDispatch;
}
//支持动态卸载
driver->DriverUnload = ccpUnload;
//绑定所有的串口
ccpAttachAllComs(driver);
//直接返回成功即可
return STATUS_SUCCESS;
}
符个截图看看效果: