Windows 驱动开发:设备栈、设备扩展与WDM驱动构建

在本文中,将探讨Windows驱动开发中的核心概念:设备栈和设备扩展,并指导如何使用Windows驱动模型(WDM)和C++在Windows 7 64位系统上构建驱动。

在开始之前,建议读者先了解设备驱动的基础知识,推荐阅读Toby Opferman的相关著作。此外,读者需要掌握如何构建驱动,并需要安装Windows驱动开发工具包(WDK)和Dbgview(可在Google上搜索)。

将编写在环 '0'(内核模式)执行的代码,处理器特权保护提供给执行任务大于环 '0' 的代码。代码可以自由修改以使用DbgPrint打印,CS寄存器。

注意最后两位将被设置为0,表示环 '0' 代码执行。环 '0' 代码需要访问一些标记为特权的内存部分,这些内存只能通过特权段访问(即,当前特权级别 < 3),而用户代码在CPL=3运行(因此无法访问它们)。

使用代码

附带的代码是用C++编写的,必须始终参考。将从编写设备入口函数开始(类似于C/C++用户模式程序中的 'main' 或 'winmain'):

extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING RegistryPath);

接下来,需要创建一个设备:

NtStatus = IoCreateDevice(pDriverObject, sizeof(PDEVICE_OBJECT), NULL, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDeviceObject_main);

IoCreateDevice允许创建一个设备并分配四个字节:sizeof(PDEVICE_OBJECT)。这段内存将属于创建的设备的上下文,并称为设备扩展。这种与设备扩展相关的内存始终可以使用设备指针访问。它通常用于存储通过调用ExAllocatePool创建的内存,并非常适合在不同驱动之间传递内存。其他驱动可以通过调用IoAttachDevice(稍后会有更多介绍)或IoGetDeviceObjectPointer来访问此指针。

附加驱动

RtlInitUnicodeString(&usDeviceToFilter, L"\\Device\\tcp"); NtStatus = IoAttachDevice(pDeviceObject_main, &usDeviceToFilter, (PDEVICE_OBJECT*)&pDeviceObject_main->DeviceExtension);

如前所述,使用IoAttachDevice,将其附加到设备\TCP,因为正在钩住TCP调用。当驱动被钩住时,执行钩住的驱动(创建的,而不是设备\TCP)将接收对这个设备的调用。

驱动现在位于设备栈的顶部(直到有人钩住设备\TCP)。驱动有责任将这些请求传递到栈中的下一层驱动。最低的驱动通常是端口驱动(也称为,miniports)。

注册IRP函数

请求和参数通过IRP(IO请求包)传递给设备,在分层驱动(如,使用设备栈)中。因此,每个设备栈都有一个IRP栈。必须注册所有可能的IRP函数,以便每个IRP都有一个函数:

for (uiIndex = 0; uiIndex < IRP_MJ_MAXIMUM_FUNCTION; uiIndex++) pDriverObject->MajorFunction[uiIndex] = UnSupportedFunction;

UnSupportedFunction除了将IRP传递到更低的驱动之外什么也不做:

NTSTATUS UnSupportedFunction(PDEVICE_OBJECT DeviceObject, PIRP Irp) { NTSTATUS NtStatus = STATUS_NOT_SUPPORTED; IoSkipCurrentIrpStackLocation(Irp); NtStatus = IoCallDriver((PDEVICE_OBJECT)DeviceObject->DeviceExtension, Irp); return NtStatus; }

必须跳过当前IRP处理,并将其盲目地传递给栈中的下一个驱动。

处理网络包

要嗅探网络包,只对IRP_MJ_INTERNAL_DEVICE_CONTROL感兴趣。添加以下行以确保调用与IO处理相关的适当函数:

pDriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = IOFunction;

IOFunction每次IRP的当前堆栈位置主要函数等于IRP_MJ_INTERNAL_DEVICE_CONTROL时都会被调用。要处理任何函数(在情况下是IOFunction),必须获取当前堆栈位置:

PIO_STACK_LOCATION StackIrpPointer= IoGetCurrentIrpStackLocation(Irp);

通过这个可以获取从用户模式代码传递下来的参数(参考代码):

PTDI_REQUEST_KERNEL_CONNECT TDI_connectRequest = (PTDI_REQUEST_KERNEL_CONNECT)(&StackIrpPointer->Parameters); PTA_ADDRESS TA_Address_data = ((PTRANSPORT_ADDRESS)(TDI_connectRequest->RequestConnectionInformation->RemoteAddress))->Address; PTDI_ADDRESS_IP TDI_data = (PTDI_ADDRESS_IP)(TA_Address_data->Address); { UINT Address = TDI_data->in_addr; UINT Port = TDI_data->sin_port; DbgPrint("address:%x Port: %x",Address,Port); }

还添加了发送和接收代码。对于发送代码,使用MmGetSystemAddressForMdlSafe,因为使用的是METHOD_IN_DIRECT。对于接收:

PCHAR pWriteDataBuffer = 0; DbgPrint(IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp,Complete,0,TRUE,TRUE,TRUE); NtStatus = IoCallDriver((PDEVICE_OBJECT)DeviceObject->DeviceExtension, Irp); "inside recv function to pull out data");

现在复制当前堆栈位置到下一个,而不是跳过它,因为正在调用IOSetCompletionRoutine。这将设置一个完成例程,当IO完成时会被调用。

启动驱动

驱动的安装方式与服务类似,将使用服务控制管理器进行此操作。

system("sc create Driver1 binPath=\"..//Driver.sys\" type= kernel"); system("sc start Driver1"); MessageBoxA(0,"UnloadFilter","UnloadFilter",0);"sc stop Driver1"); system("sc stop Driver1"); system("sc delete Driver1");
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485