《Windows内核安全编程》第三章 串口的过滤

1. 细碎知识点

  1. Windows中串口设备具有固定的名字,第一个串口叫"\Device\Serial0",第二个叫"\Device\Serial1"
  2. 快速构造UNICODE_STRING:UNICODE_STRING com_name = RTL_CONSTANT_STRING(L"\Device\Serial0");
  3. 过滤设备一般不需要名称,IoCreateDevice传入NULL即可
  4. 一个驱动中生成从属于其他驱动的设备对象,理论上是可以的
  5. IRP是上层设备之间传递请求的常见数据结构,但并非唯一的数据结构
  6. 串口设备接受到的请求都是IRP
  7. 设备卸载的相关API:IoDetachDevice、IoDeleteDevice、KeDelayExecutionThread

2. 设备绑定的API

IoAttachDevice绑定有名设备
NTSTATUS IoAttachDevice (
	IN PDEVICE_OBJECT SourceDevice,
	IN PUNICODE_STRING TargetDevice,
	OUT PDEVICE_OBJECT *AttachDevice
);

SourceDevice:调用者生成的用来过滤的虚拟设备
TargetDevice:字符串,要被绑定的设备的名字
AttachDevice:返回被绑定的设备对象的指针

IoAttachDevice只能用来绑定具有名称的设备,且总是会绑定设备栈上最顶层的那个设备

IoAttachDeviceToDeviceStack、IoAttachDeviceToDeviceStackSafe绑定无名设备
NTSTATUS IoAttachDeviceToDeviceStackSafe( 
	IN PDEVICE_OBJECT SourceDevice, // 过滤设备
	IN PDEVICE_OBJECT TargetDevice, // 要被绑定的设备栈中的设备
	IN OUT PDEVICE_OBJECT *AttachedToDeviceObject// 返回最终被绑定的设备
); 

PDEVICE_OBJECT IoAttachDeviceToDeviceStack( 
	IN PDEVICE_OBJECT SourceDevice, 
	IN PDEVICE_OBJECT TargetDevice 
); 

TargetDevice:指针,要被绑定的设备的指针
返回值:返回最终被绑定的设备

IoAttachDeviceToDeviceStack只能再windows2000下使用,推荐使用IoAttachDeviceToDeviceStackSafe

3. 生成过滤设备并绑定

NTSTATUS IoCreateDevice( 
	IN PDRIVER_OBJECT DriverObject, 			//本驱动的驱动对象,DriverEntry传入
	IN ULONG DeviceExtensionSize, 				//设备扩展
	IN PUNICODE_STRING DeviceName OPTIONAL, 	//设备名,过滤驱动一般传入NULL即可
	IN DEVICE_TYPE DeviceType, 					//设备类型,与要过滤的设备保持一致
	IN ULONG DeviceCharacteristics, 			//凭经验直接填0
	IN BOOLEAN Exclusive, 						//是否独占,一般FALSE
	OUT PDEVICE_OBJECT *DeviceObject 			//输出创建好的设备对象
);

绑定示例代码:

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

从名字获得设备对象

NTSTATUS 
IoGetDeviceObjectPointer( 
	IN PUNICODE_STRING ObjectName, 			//要获取设备对象的设备名
	IN ACCESS_MASK DesiredAccess, 			//期望获得的权限
	OUT PFILE_OBJECT *FileObject, 			//获得设备对象同时获得的文件对象
	OUT PDEVICE_OBJECT *DeviceObject 		//获得的设备对象
); 

FileObject在获得后不用时,必须通过ObDereferenceObject解除引用,否则内存泄漏

IRP请求的区分

IRP都具有一个主功能号和一个次功能号

// 这里的 irpsp 称为 IRP 的栈空间,IoGetCurrentIrpStackLocation 获得当前栈空间
// 栈空间是非常重要的数据结构
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp); 
if(irpsp->MajorFunction == IRP_MJ_WRITE) 
{ 
 // 如果是写…
} 
else if(irpsp->MajorFunction == IRP_MJ_READ) 
{ 
 // 如果是读…
}

IRP的过滤行为

  1. 请求被允许通过,过滤不做任何事情
    // 跳过当前栈空间
    IoSkipCurrentIrpStackLocation(irp); 
    // 将请求发送到对应的真实设备。记得我们前面把真实设备都保存在 s_nextobj 
    // 数组中。那么这里 i 应该是多少?这取决于现在的 IRP 发到了哪个
    // 过滤设备上。后面讲解分发函数时读者将了解到这一点
    status = IoCallDriver(s_nextobj[i],irp); 
    
  2. 请求直接被否决,过滤禁止这个请求通过
  3. 过滤完成了请求,但对请求进行了修改

从串口中获取IRP传递的数据

//缓冲区的获取
PBYTE buffer = NULL; 
if(irp->MdlAddress != NULL) 
	buffer = (PBYTE)MmGetSystemAddressForMdlSafe(irp->MdlAddress); 
else 
	buffer = (PBYTE)irp->UserBuffer; 
if(buffer == NULL) 
	buffer = (PBYTE)irp->AssociatedIrp.SystemBuffer; 

//缓冲区大小的获取
ULONG length = irpsp->Parameters.Write.Length;

串口过滤的完整分发函数

NTSTATUS ccpDispatch(PDEVICE_OBJECT device,PIRP irp) 
{ 
	PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp); 
	NTSTATUS status; 
	ULONG i,j; 
	// 首先要知道发送给了哪个设备。设备最多一共有 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: %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]); 
	} 
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值