前言
在Vista及以后的系统中,微软提供了一系列内核中使用的socket套接字接口 Winsock Kernel
(简称WSK),
以进行内核中的网络开发,整体使用风格与应用层socket基本一致,这里先介绍客户端如何实现
WSK客户端
在WDK示例工程源码中,只提供了一个简易服务端的实现,所以在写客户端的过程中碰到了各种问题,
只能依靠相关API的MSDN说明进行摸索,最后实现了一个简易的客户端例子来给大家作为参考
相关数据定义
如下为所用到的相关信息的定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <ntddk.h> #include <wsk.h>
#define CTRL_DEVICE_NAME L"\\Device\\WskClient" #define CTRL_SYMLINK_NAME L"\\??\\WskClient"
#define IOCTL_INIT_SOCKET CTL_CODE(FILE_DEVICE_UNKNOWN, 0xA00, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_EXIT_SOCKET CTL_CODE(FILE_DEVICE_UNKNOWN, 0xA01, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_SEND_DATA CTL_CODE(FILE_DEVICE_UNKNOWN, 0xA02, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_RECV_DATA CTL_CODE(FILE_DEVICE_UNKNOWN, 0xA03, METHOD_BUFFERED, FILE_ANY_ACCESS)
typedef enum _SOCKET_STATE { None = 0, Creating, Closing, Ready, Sending, Receiving, } SOCKET_STATE, *PSOCKET_STATE; typedef struct _SOCKET_CONTEXT { PWSK_SOCKET Socket; SOCKET_STATE State; BOOLEAN IsUnload; KEVENT ExitEvent; SOCKADDR_IN LocalAddr; SOCKADDR_IN RemoteAddr; PIRP Irp; PMDL DataMdl; PVOID DataBuffer; ULONG DataLength; } SOCKET_CONTEXT, *PSOCKET_CONTEXT;
WSK_CLIENT_DISPATCH gClientDispatch = { MAKE_WSK_VERSION(1, 0), 0, NULL }; WSK_REGISTRATION gRegistration = { 0 }; PSOCKET_CONTEXT gConnectContext = NULL; PDEVICE_OBJECT gControlDevice = NULL;
|
驱动初始化操作
注册WSK框架需要用到 WskRegister
这个函数,如下所示
1 2 3 4
| NTSTATUS WskRegister( _In_ PWSK_CLIENT_NPI WskClientNpi, _Out_ PWSK_REGISTRATION WskRegistration );
|
这个注册函数需要用到 WSK_CLIENT_DISPATCH
WSK_REGISTRATION
WSK_CLIENT_NPI
三个结构体。
其中 WSK_CLIENT_DISPATCH
指定版本号和回调函数,这个回调可以在创建结束后提供一些详细的信息,
这里指定为空。而 WSK_REGISTRATION
相当于一个句柄,可以用于提供卸载时调用
驱动的入口函数如下所示,我们在这里做一些初始化操作,该例子只支持 单线程
+ 单Socket
操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { ULONG Index; NTSTATUS Status; UNICODE_STRING DeviceName; UNICODE_STRING SymLinkName; WSK_CLIENT_NPI WskClientNpi; UNREFERENCED_PARAMETER(RegistryPath); gConnectContext = AllocateSocketContext(); if (!gConnectContext) return STATUS_INSUFFICIENT_RESOURCES; WskClientNpi.ClientContext = NULL; WskClientNpi.Dispatch = &gClientDispatch; Status = WskRegister(&WskClientNpi, &gRegistration); if (!NT_SUCCESS(Status)) { FreeSocketContext(gConnectContext); return Status; } for (Index = 0; Index <= IRP_MJ_MAXIMUM_FUNCTION; Index++) DriverObject->MajorFunction[Index] = DispatchCommon; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchControl; DriverObject->DriverUnload = DriverUnload; RtlInitUnicodeString(&DeviceName, CTRL_DEVICE_NAME); RtlInitUnicodeString(&SymLinkName, CTRL_SYMLINK_NAME); Status = IoCreateDevice( DriverObject, 0, &DeviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &gControlDevice); if (!NT_SUCCESS(Status)) { WskDeregister(&gRegistration); FreeSocketContext(gConnectContext); return Status; } Status = IoCreateSymbolicLink(&SymLinkName, &DeviceName); if (!NT_SUCCESS(Status)) { IoDeleteDevice(gControlDevice); WskDeregister(&gRegistration); FreeSocketContext(gConnectContext); return Status; } return STATUS_SUCCESS; }
|
其中 AllocateSocketContext
函数用来申请所有的公共资源,使用公共资源是为了简化逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| PSOCKET_CONTEXT AllocateSocketContext() { PSOCKET_CONTEXT SocketContext = NULL; SocketContext = ExAllocatePool(NonPagedPool, sizeof(SOCKET_CONTEXT)); if (!SocketContext) return NULL; RtlZeroMemory(SocketContext, sizeof(SOCKET_CONTEXT)); SocketContext->Irp = IoAllocateIrp(1, FALSE); if (SocketContext->Irp == NULL) { FreeSocketContext(SocketContext); return NULL; } KeInitializeEvent(&SocketContext->ExitEvent, NotificationEvent, FALSE); SocketContext->LocalAddr.sin_family = AF_INET; return SocketContext; }
|
对应的 FreeSocketContext
是用来释放公共资源
1 2 3 4 5 6 7 8 9 10
| VOID FreeSocketContext(IN PSOCKET_CONTEXT SocketContext) { if (SocketContext->Irp) { IoFreeIrp(SocketContext->Irp); SocketContext->Irp = NULL; } FreeSocketBuffer(SocketContext); ExFreePool(SocketContext); }
|
接收和发送的缓冲区根据实际内容来进行申请
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| NTSTATUS AllocateSocketBuffer(IN PSOCKET_CONTEXT SocketContext, IN ULONG Length) { SocketContext->DataBuffer = ExAllocatePool(NonPagedPoolNx, Length); if (!SocketContext->DataBuffer) { FreeSocketBuffer(SocketContext); return STATUS_INSUFFICIENT_RESOURCES; } SocketContext->DataMdl = IoAllocateMdl( SocketContext->DataBuffer, Length, FALSE, FALSE, NULL); if (!SocketContext->DataMdl) { FreeSocketBuffer(SocketContext); return STATUS_INSUFFICIENT_RESOURCES; } MmBuildMdlForNonPagedPool(SocketContext->DataMdl); memset(SocketContext->DataBuffer, 0, Length); SocketContext->DataLength = Length; return STATUS_SUCCESS; }
|
释放接收和发送缓冲区的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| VOID FreeSocketBuffer(IN PSOCKET_CONTEXT SocketContext) { if (SocketContext->DataMdl) { IoFreeMdl(SocketContext->DataMdl); SocketContext->DataMdl = NULL; } if (SocketContext->DataBuffer) { ExFreePool(SocketContext->DataBuffer); SocketContext->DataBuffer = NULL; } SocketContext->DataLength = 0; }
|
派遣函数定义
因为我们这里只处理 IRP_MJ_DEVICE_CONTROL
操作,其他的统一直接完成返回成功不做处理
1 2 3 4 5 6 7 8
| NTSTATUS DispatchCommon(PDEVICE_OBJECT DeviceObject, PIRP Irp) { UNREFERENCED_PARAMETER(DeviceObject); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| NTSTATUS DispatchControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) { NTSTATUS Status = STATUS_INVALID_DEVICE_REQUEST; PIO_STACK_LOCATION IrpSp = NULL; PVOID DataBuffer = NULL; ULONG ControlCode = 0; ULONG OutputSize = 0; ULONG InputSize = 0; ULONG DataSize = 0; UNREFERENCED_PARAMETER(DeviceObject); IrpSp = IoGetCurrentIrpStackLocation(Irp); DataBuffer = Irp->AssociatedIrp.SystemBuffer; ControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode; OutputSize = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; InputSize = IrpSp->Parameters.DeviceIoControl.InputBufferLength; switch (ControlCode) { case IOCTL_INIT_SOCKET: if (!DataBuffer || (InputSize != sizeof(SOCKADDR_IN))) break; Status = IoCtrlInitSocket((PSOCKADDR_IN)DataBuffer); break; case IOCTL_EXIT_SOCKET: Status = IoCtrlExitSocket(); break; case IOCTL_SEND_DATA: if (!DataBuffer || !InputSize) break; Status = IoCtrlSendData(DataBuffer, InputSize, &DataSize); break; case IOCTL_RECV_DATA: if (!DataBuffer || !OutputSize) break; Status = IoCtrlRecvData(DataBuffer, OutputSize, &DataSize); break;
default: break; } Irp->IoStatus.Information = DataSize; Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return Status; }
|
控制操作封装函数
这里对如上所示的四种操作进行了二次封装,对应函数如下所示
1 2 3 4 5 6
| NTSTATUS IoCtrlInitSocket(IN PSOCKADDR_IN SrvAddr) { if (!SrvAddr) return STATUS_INVALID_PARAMETER; return OperationCreate(gConnectContext, SrvAddr); }
|
1 2 3 4
| NTSTATUS IoCtrlExitSocket() { return OperationClose(gConnectContext); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| NTSTATUS IoCtrlSendData(IN PVOID Buffer, IN ULONG Size, OUT PULONG Real) { NTSTATUS Status; if (!Buffer || !Size) return STATUS_INVALID_PARAMETER; Status = AllocateSocketBuffer(gConnectContext, Size); if (!NT_SUCCESS(Status)) return Status; memcpy(gConnectContext->DataBuffer, Buffer, Size); Status = OperationSend(gConnectContext); FreeSocketBuffer(gConnectContext); if (!NT_SUCCESS(Status)) return Status; if (Real) *Real = (ULONG)gConnectContext->Irp->IoStatus.Information; return STATUS_SUCCESS; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| NTSTATUS IoCtrlRecvData(IN PVOID Buffer, IN ULONG Size, OUT PULONG Real) { NTSTATUS Status; if (!Buffer || !Size) return STATUS_INVALID_PARAMETER; Status = AllocateSocketBuffer(gConnectContext, Size); if (!NT_SUCCESS(Status)) return Status; Status = OperationReceive(gConnectContext); if (!NT_SUCCESS(Status)) { FreeSocketBuffer(gConnectContext); return Status; } memcpy(Buffer, gConnectContext->DataBuffer, (ULONG)gConnectContext->Irp->IoStatus.Information); if (Real) *Real = (ULONG)gConnectContext->Irp->IoStatus.Information; FreeSocketBuffer(gConnectContext); return STATUS_SUCCESS; }
|
套接字操作函数
所有的套接字操作都需要设置完成函数,我们这里使用统一的完成函数来处理
1 2 3 4 5 6 7 8
| NTSTATUS SyncIrpCompRoutine(PDEVICE_OBJECT Reserved, PIRP Irp, PVOID Context) { UNREFERENCED_PARAMETER(Reserved); UNREFERENCED_PARAMETER(Irp); KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; }
|
在WSK中提供了一个同时创建和连接套接字的API函数 WskSocketConnect
用以简化操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| NTSTATUS OperationCreate(IN PSOCKET_CONTEXT SocketContext, IN PSOCKADDR_IN SrvAddr) { NTSTATUS Status; KEVENT CompEvent; WSK_PROVIDER_NPI WskProviderNpi; SOCKET_STATE State; BOOLEAN IsReleaseNPI = FALSE; if (SrvAddr == NULL) return STATUS_INVALID_PARAMETER; if (SocketContext->IsUnload || (SocketContext->State != None)) return STATUS_REQUEST_NOT_ACCEPTED; State = SocketContext->State; SocketContext->State = Creating; do { Status = WskCaptureProviderNPI( &gRegistration, WSK_INFINITE_WAIT, &WskProviderNpi); if (!NT_SUCCESS(Status)) break; IsReleaseNPI = TRUE; KeInitializeEvent(&CompEvent, NotificationEvent, FALSE); IoReuseIrp(SocketContext->Irp, STATUS_UNSUCCESSFUL); IoSetCompletionRoutine( SocketContext->Irp, SyncIrpCompRoutine, &CompEvent, TRUE, TRUE, TRUE); SocketContext->LocalAddr.sin_family = AF_INET; WskProviderNpi.Dispatch->WskSocketConnect( WskProviderNpi.Client, SOCK_STREAM, IPPROTO_TCP, (PSOCKADDR)&SocketContext->LocalAddr, (PSOCKADDR)SrvAddr, 0, NULL, NULL, NULL, NULL, NULL, SocketContext->Irp); KeWaitForSingleObject(&CompEvent, Executive, KernelMode, FALSE, NULL); if (!NT_SUCCESS(SocketContext->Irp->IoStatus.Status)) { Status = SocketContext->Irp->IoStatus.Status; break; } SocketContext->Socket = (PWSK_SOCKET)SocketContext->Irp->IoStatus.Information; Status = STATUS_SUCCESS; } while (0); if (IsReleaseNPI) WskReleaseProviderNPI(&gRegistration); if (NT_SUCCESS(Status)) SocketContext->State = Ready; else SocketContext->State = State; if (SocketContext->IsUnload) { OperationClose(SocketContext); KeSetEvent(&SocketContext->ExitEvent, IO_NO_INCREMENT, FALSE); } return Status; }
|
套接字的关闭流程如下,首先调用 WskDisconnect
断开连接,再调用 WskCloseSocket
关闭套接字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| NTSTATUS OperationClose(IN PSOCKET_CONTEXT SocketContext) { KEVENT CompEvent; PWSK_PROVIDER_BASIC_DISPATCH Dispatch1; PWSK_PROVIDER_CONNECTION_DISPATCH Dispatch2; if (SocketContext->State <= Closing) return STATUS_REQUEST_NOT_ACCEPTED; SocketContext->State = Closing; KeInitializeEvent(&CompEvent, NotificationEvent, FALSE); Dispatch2 = (PWSK_PROVIDER_CONNECTION_DISPATCH)SocketContext->Socket->Dispatch; IoReuseIrp(SocketContext->Irp, STATUS_UNSUCCESSFUL); IoSetCompletionRoutine( SocketContext->Irp, SyncIrpCompRoutine, &CompEvent, TRUE, TRUE, TRUE); Dispatch2->WskDisconnect( SocketContext->Socket, NULL, 0, SocketContext->Irp); KeWaitForSingleObject(&CompEvent, Executive, KernelMode, FALSE, NULL); Dispatch1 = (PWSK_PROVIDER_BASIC_DISPATCH)SocketContext->Socket->Dispatch; KeClearEvent(&CompEvent); IoReuseIrp(SocketContext->Irp, STATUS_UNSUCCESSFUL); IoSetCompletionRoutine( SocketContext->Irp, SyncIrpCompRoutine, &CompEvent, TRUE, TRUE, TRUE); Dispatch1->WskCloseSocket(SocketContext->Socket, SocketContext->Irp); KeWaitForSingleObject(&CompEvent, Executive, KernelMode, FALSE, NULL); if (SocketContext->IsUnload) KeSetEvent(&SocketContext->ExitEvent, IO_NO_INCREMENT, FALSE); SocketContext->State = None; return STATUS_SUCCESS; }
|
发送数据函数如下,使用 WskSend
发送,注意这里是阻塞操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| NTSTATUS OperationSend(IN PSOCKET_CONTEXT SocketContext) { NTSTATUS Status; WSK_BUF WskBuf; KEVENT CompEvent; PWSK_PROVIDER_CONNECTION_DISPATCH Dispatch; SOCKET_STATE State; if (SocketContext->IsUnload || (SocketContext->State != Ready)) return STATUS_REQUEST_NOT_ACCEPTED; State = SocketContext->State; SocketContext->State = Sending; do { KeInitializeEvent(&CompEvent, NotificationEvent, FALSE); Dispatch = (PWSK_PROVIDER_CONNECTION_DISPATCH)SocketContext->Socket->Dispatch; WskBuf.Offset = 0; WskBuf.Length = SocketContext->DataLength; WskBuf.Mdl = SocketContext->DataMdl; IoReuseIrp(SocketContext->Irp, STATUS_UNSUCCESSFUL); IoSetCompletionRoutine( SocketContext->Irp, SyncIrpCompRoutine, &CompEvent, TRUE, TRUE, TRUE); Dispatch->WskSend( SocketContext->Socket, &WskBuf, 0, SocketContext->Irp); KeWaitForSingleObject(&CompEvent, Executive, KernelMode, FALSE, NULL); if (!NT_SUCCESS(SocketContext->Irp->IoStatus.Status)) { Status = SocketContext->Irp->IoStatus.Status; OperationClose(SocketContext); break; } Status = STATUS_SUCCESS; SocketContext->State = Ready; } while (0); if (SocketContext->IsUnload) { OperationClose(SocketContext); KeSetEvent(&SocketContext->ExitEvent, IO_NO_INCREMENT, FALSE); } return Status; }
|
接收数据函数如下,使用 WskReceive
接收,同样也为阻塞函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| NTSTATUS OperationReceive(IN PSOCKET_CONTEXT SocketContext) { NTSTATUS Status; WSK_BUF WskBuf; KEVENT CompEvent; PWSK_PROVIDER_CONNECTION_DISPATCH Dispatch; SOCKET_STATE State; if (SocketContext->IsUnload || (SocketContext->State != Ready)) return STATUS_REQUEST_NOT_ACCEPTED; State = SocketContext->State; SocketContext->State = Sending; do { KeInitializeEvent(&CompEvent, NotificationEvent, FALSE); Dispatch = (PWSK_PROVIDER_CONNECTION_DISPATCH)SocketContext->Socket->Dispatch; WskBuf.Offset = 0; WskBuf.Length = SocketContext->DataLength; WskBuf.Mdl = SocketContext->DataMdl; memset(SocketContext->DataBuffer, 0, SocketContext->BufferLength); IoReuseIrp(SocketContext->Irp, STATUS_UNSUCCESSFUL); IoSetCompletionRoutine( SocketContext->Irp, SyncIrpCompRoutine, &CompEvent, TRUE, TRUE, TRUE); Dispatch->WskReceive( SocketContext->Socket, &WskBuf, 0, SocketContext->Irp); KeWaitForSingleObject(&CompEvent, Executive, KernelMode, FALSE, NULL); if (!NT_SUCCESS(SocketContext->Irp->IoStatus.Status)) { Status = SocketContext->Irp->IoStatus.Status; OperationClose(SocketContext); break; } Status = STATUS_SUCCESS; SocketContext->State = Ready; } while (0); if (SocketContext->IsUnload) { OperationClose(SocketContext); KeSetEvent(&SocketContext->ExitEvent, IO_NO_INCREMENT, FALSE); } return Status; }
|
通过以上函数的内容,可以看到WSK函数操作方式与应用层套接字基本一致