串口的过滤很简单,在安全开发中意义也不大,只是作为学习,为之后的键盘过滤和文件系统过滤等打基础。
串口
串口是Windows中的一种设备,它比较简单,且有固定名字。第一个串口名为"\Device\Serial0",第二个为"\Device\Serial1",以此类推。
过滤
过滤: 在不影响上层和下层接口的情况下,在Windows系统内核中加入新的层,从而不需要修改上层的软件或者下层的真实驱动程序,就加入了新的功能。
实现过滤: 首先生成一个过滤设备(虚拟的设备对象),将其绑定在一个真实的设备上。一旦绑定,则原本操作系统要发送给真实设备的请求,会先发送到过滤设备上,我们可以通过分析请求来提前捕获到数据。
生成过滤设备对象
函数IoCreateDevice用于创建设备对象。
NTSTATUS
IoCreateDevice(
IN PDRIVER_OBJECT DriverObject, // 驱动对象
IN ULONG DeviceExtensionSize, // 设备扩展,0
IN PUNICODE_STRING DeviceName, // 设备名称,过滤设备不需要,NULL
IN DEVICE_TYPE DeviceType, // 设备类型,要与被绑定设备一致
IN ULONG DeviceCharacteristics, // 设备特征,0
IN BOOLEAN Exclusive, // FALSE
OUT PDEVICE_OBJECT *DeviceObject); // 生成的设备对象
获得目标设备对象
在已知设备名的情况下,可以用IoGetDeviceObjectPointer函数。
在得到设备对象的同时,还会得到一个文件对象,注意之后必须把这个文件对象解引用,否则会引起内存泄露。
NTSTATUS
IoGetDeviceObjectPointer(
IN PUNICODE_STRING ObjectName, // 设备名
IN ACCESS_MASK DesiredAccess, // 访问权限
OUT PFILE_OBJECT *FileObject, // 文件对象
OUT PDEVICE_OBJECT *DeviceObject); // 设备对象
绑定设备
Windows提供了一系列内核API来实现设备绑定。
1. IoAttachDevice
Windows中许多设备对象都是有名字的,但不是所有的都有,只有有名字的设备,才能用IoAttachDevice绑定。
如果一个设备已经被其他设备绑定了,则它们会形成一个设备栈。这时IoAttachDevice会绑定设备栈最顶层的那个设备。
NTSTATUS
IoAttachDevice(
IN PDEVICE_OBJECT SourceDevice, // 过滤设备对象
IN PUNICODE_STRING TargetDeviceName, // 目标设备名
OUT PDEVICE_OBJECT *AttachedDevice); // 被绑定的设备对象
2. IoAttachDeviceToDeviceStackSafe
对于没有名字的设备,可以用IoAttachDeviceToDeviceStackSafe绑定,它也是绑定设备栈最顶层的那个设备。
NTSTATUS
IoAttachDeviceToDeviceStackSafe(
IN PDEVICE_OBJECT SourceDevice, // 过滤设备对象
IN PDEVICE_OBJECT TargetDevice, // 要绑定的设备对象
IN OUT PDEVICE_OBJECT *AttachedToDeviceObject); // 最终被绑定的设备对象
注意在设备绑定时,要把过滤设备对象的多个子域设置成和目标一致,包括标志和特征。
串口的过滤
创建过滤设备并绑定到串口上之后,当操作系统要发送IRP请求到串口上时,会先发送到我们的过滤设备上,我们可以对其进行过滤处理。
IRP有很多种类,常见的有读请求(IRP_MJ_READ)和写请求(IRP_MJ_WRITE)等。
过滤处理的结果有三种:
- 允许请求通过:过滤设备只是简单获取一下请求的信息,不对其做任何处理,请求正常下发。
- 禁止请求通过:该请求结束,并返回错误,应用程序会受到失败的信息。
- 完成请求:该请求结束,并返回成功。
测试代码
#include "Ring0.h"
#include <ntstrsafe.h>
PDEVICE_OBJECT FltDevices[32] = {
0 };
PDEVICE_OBJECT AttachedDevices[32] = {
0 };
NTSTATUS
OpenDevice(ULONG Index, PDEVICE_OBJECT *DeviceObject)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PFILE_OBJECT FileObject = NULL;
UNICODE_STRING DeviceName;
WCHAR Name[32] = {
0 };
RtlStringCchPrintfW(Name, 32, L"\\Device\\Serial%d", Index);
RtlInitUnicodeString(&DeviceName, Name);
Status = IoGetDeviceObjectPointer(&DeviceName,
FILE_ALL_ACCESS,
&FileObject,
DeviceObject);
if (!NT_SUCCESS(Status))
{
return Status;
}
ObDereferenceObject(FileObject);
return Status;
}
NTSTATUS
AttachDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT TargetDevice,
PDEVICE_OBJECT *FltDevice, PDEVICE_OBJECT *AttachedDevice)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
// 创建过滤设备
Status = IoCreateDevice(DriverObject,
0,
NULL,
TargetDevice->DeviceType,
0,
FALSE,
FltDevice);
if (!NT_SUCCESS(Status))
{
return Status;
}
// 设置重要标志位
if (TargetDevice->Flags & DO_BUFFERED_IO)
{
(*FltDevice)->Flags |= DO_BUFFERED_IO;
}
if (TargetDevice->Flags & DO_DIRECT_IO)
{
(*FltDevice)->Flags |= DO_DIRECT_IO;
}
if (TargetDevice->Characteristics & FILE_DEVICE_SECURE_OPEN)
{
(*FltDevice)->Characteristics |= FILE_DEVICE_SECURE_OPEN;
}
(*FltDevice)->Flags |= DO_POWER_PAGABLE;
// 绑定设备
Status = IoAttachDeviceToDeviceStackSafe(*FltDevice,
TargetDevice,
AttachedDevice);
if (!NT_SUCCESS(Status))
{
return Status;
}
// 设置设备已启动
(*FltDevice)->Flags &= DO_DEVICE_INITIALIZING;
return Status;
}
NTSTATUS
ControlThroughDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
PIO_STACK_LOCATION IoStackLocation; //当前IRP调用栈空间
//获得当前IRP的栈空间(即本层设备对应的IO_STACK_LOCATION)
IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
for (ULONG i = 0; i < 32; i++)
{
if (FltDevices[i] == DeviceObject)
{
// 所有的电源操作,全部直接放过
if (IoStackLocation->MajorFunction == IRP_MJ_POWER)
{
// 直接发送,然后返回说已经被处理了
PoStartNextPowerIrp(Irp);
IoSkipCurrentIrpStackLocation(Irp);
return PoCallDriver(AttachedDevices[i], Irp);
}
// 写请求
if (IoStackLocation->MajorFunction == IRP_MJ_WRITE)
{
ULONG Length = IoStackLocation->Parameters.Write.Length;
PUCHAR Buffer = NULL;
if (Irp->MdlAddress != NULL)
{
Buffer = (PUCHAR)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
}
else
{
Buffer = (PUCHAR)Irp->UserBuffer;
}
if (Buffer == NULL)
{
Buffer = (PUCHAR)Irp->AssociatedIrp.SystemBuffer;
}
for (ULONG j = 0; j < Length; j++)
{
DbgPrint("comcap: Send Data: %2x\r\n", Buffer[j]);
}
}
// 直接下发请求
IoSkipCurrentIrpStackLocation(Irp);
return IoCallDriver(AttachedDevices[i], Irp);
}
}
//构造数据包,发到用户层
Irp->IoStatus.Status = 0;
Irp->IoStatus.Information = STATUS_INVALID_PARAMETER;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS
DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
// 解引用
UNREFERENCED_PARAMETER(RegisterPath);
NTSTATUS Status = STATUS_SUCCESS;
PDEVICE_OBJECT TargetDevice = NULL;
UNICODE_STRING ObjectName;
// 绑定所有串口,假定一共有32个串口
for (ULONG i = 0; i < 32; i++)
{
Status = OpenDevice(i, &TargetDevice);
if (!NT_SUCCESS(Status) || TargetDevice == NULL)
{
continue;
}
AttachDevice(DriverObject, TargetDevice, &FltDevices[i], &AttachedDevices[i]);
}
// 设置派遣函数例程
for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
DriverObject->MajorFunction[i] = ControlThroughDispatch;
}
// 设置驱动卸载例程
DriverObject->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
void DriverUnload(PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
// 解除绑定
for (ULONG i = 0; i < 32; i++)
{
if (AttachedDevices[i] != NULL)
{
IoDetachDevice(AttachedDevices[i]);
}
}
#define DELAY_ONE_MICROSECOND (-10)
#define DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND * 1000)
#define DELAY_ONE_SECOND (DELAY_ONE_MILLISECOND * 1000)
// 睡眠5秒,等待所有的IRP处理结束
LARGE_INTEGER Interval;
Interval.QuadPart = (5000 * DELAY_ONE_MILLISECOND);
KeDelayExecutionThread(KernelMode, FALSE, &Interval);
// 删除过滤设备
for (ULONG i = 0; i < 32; i++)
{
if (FltDevices[i] != NULL)
{
IoDeleteDevice(FltDevices[i]);
}
}
}