• 驱动开发:内核封装WSK网络通信接口


    本章LyShark将带大家学习如何在内核中使用标准的Socket套接字通信接口,我们都知道Windows应用层下可直接调用WinSocket来实现网络通信,但在内核模式下应用层API接口无法使用,内核模式下有一套专有的WSK通信接口,我们对WSK进行封装,让其与应用层调用规范保持一致,并实现内核与内核直接通过Socket通信的案例。

    当然在早期如果需要实现网络通信一般都会采用TDI框架,但在新版本Windows10系统上虽然依然可以使用TDI接口,但是LyShark并不推荐使用,因为微软已经对接口搁置了,为了使WSK通信更加易用,我们需要封装内核层中的通信API,新建LySocket.hpp头文件,该文件中封装了WSK通信API接口,其封装格式与应用层接口保持了高度一致,当需要在内核中使用Socket通信时可直接引入本文件。

    我们需要使用WDM驱动程序,并配置以下参数。

    • 配置属性 -> 连接器 -> 输入-> 附加依赖 -> $(DDK_LIB_PATH)\Netio.lib
    • 配置属性 -> C/C++ -> 常规 -> 设置 警告等级2级 (警告视为错误关闭)

    配置好以后,我们就开始吧,先来看看服务端如何实现!

    对于服务端来说,驱动通信必须保证服务端开启多线程来处理异步请求,不然驱动加载后系统会处于等待状态,而一旦等待则系统将会卡死,那么对于服务端DriverEntry入口说我们不能让其等待,必须使用PsCreateSystemThread来启用系统线程,该函数属于WDM的一部分,官方定义如下;

    NTSTATUS PsCreateSystemThread(
      [out]           PHANDLE            ThreadHandle,
      [in]            ULONG              DesiredAccess,
      [in, optional]  POBJECT_ATTRIBUTES ObjectAttributes,
      [in, optional]  HANDLE             ProcessHandle,
      [out, optional] PCLIENT_ID         ClientId,
      [in]            PKSTART_ROUTINE    StartRoutine,
      [in, optional]  PVOID              StartContext
    );
    

    我们使用PsCreateSystemThread函数开辟线程TcpListenWorker在线程内部执行如下流程启动驱动服务端,由于我们自己封装实现了标准接口组,所以使用起来几乎与应用层无任何差异了。

    • CreateSocket 创建套接字
    • Bind 绑定套接字
    • Accept 等待接收请求
    • Receive 用于接收返回值
    • Send 用于发送返回值
    // 署名权
    // right to sign one's name on a piece of work
    // PowerBy: LyShark
    // Email: me@lyshark.com
    
    #include "LySocket.hpp"
    
    PETHREAD m_EThread = NULL;
    
    // 线程函数
    // PowerBy: LySHark
    VOID TcpListenWorker(PVOID Context)
    {
    	WSK_SOCKET* paccept_socket = NULL;
    	SOCKADDR_IN LocalAddress = { 0 };
    	SOCKADDR_IN RemoteAddress = { 0 };
    	NTSTATUS status = STATUS_UNSUCCESSFUL;
    
    	// 创建套接字
    	PWSK_SOCKET TcpSocket = CreateSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, WSK_FLAG_LISTEN_SOCKET);
    	if (TcpSocket == NULL)
    	{
    		return;
    	}
    
    	// 设置绑定地址
    	LocalAddress.sin_family = AF_INET;
    	LocalAddress.sin_addr.s_addr = INADDR_ANY;
    	LocalAddress.sin_port = HTON_SHORT(8888);
    
    	status = Bind(TcpSocket, (PSOCKADDR)&LocalAddress);
    	if (!NT_SUCCESS(status))
    	{
    		return;
    	}
    
    	// 循环接收
    	while (1)
    	{
    		CHAR* read_buffer = (CHAR*)ExAllocatePoolWithTag(NonPagedPool, 2048, "read");
    		paccept_socket = Accept(TcpSocket, (PSOCKADDR)&LocalAddress, (PSOCKADDR)&RemoteAddress);
    		if (paccept_socket == NULL)
    		{
    			continue;
    		}
    
    		// 接收数据
    		memset(read_buffer, 0, 2048);
    		int read_len = Receive(paccept_socket, read_buffer, 2048, 0);
    		if (read_len != 0)
    		{
    			DbgPrint("[内核A] => %s \n", read_buffer);
    
    			// 发送数据
    			char send_buffer[2048] = "Hi, lyshark.com B";
    			Send(paccept_socket, send_buffer, strlen(send_buffer), 0);
    
    			// 接收确认包
    			memset(read_buffer, 0, 2048);
    			Receive(paccept_socket, read_buffer, 2, 0);
    		}
    
    		// 清理堆
    		if (read_buffer != NULL)
    		{
    			ExFreePool(read_buffer);
    		}
    
    		// 关闭当前套接字
    		if (paccept_socket)
    		{
    			CloseSocket(paccept_socket);
    		}
    	}
    
    	if (TcpSocket)
    	{
    		CloseSocket(TcpSocket);
    	}
    	PsTerminateSystemThread(STATUS_SUCCESS);
    	return;
    }
    
    // 关闭套接字
    VOID UnDriver(PDRIVER_OBJECT driver)
    {
    	WSKCleanup();
    	KeWaitForSingleObject(m_EThread, Executive, KernelMode, FALSE, NULL);
    	if (m_EThread != NULL)
    	{
    		ObDereferenceObject(m_EThread);
    	}
    }
    
    NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
    {
    	DbgPrint("hello lyshark.com \n");
    
    	// 初始化
    	WSKStartup();
    
    	HANDLE hThread = NULL;
    	NTSTATUS status = STATUS_UNSUCCESSFUL;
    
    	// 创建系统线程
    	status = PsCreateSystemThread(&hThread, THREAD_ALL_ACCESS, NULL, NULL, NULL, TcpListenWorker, NULL);
    	if (!NT_SUCCESS(status))
    	{
    		return status;
    	}
    
    	// 获取线程EProcess结构
    	status = ObReferenceObjectByHandle(hThread, THREAD_ALL_ACCESS, NULL, KernelMode, (PVOID*)&m_EThread, NULL);
    	if (NT_SUCCESS(status) == FALSE)
    	{
    		return status;
    	}
    
    	ZwClose(hThread);
    	Driver->DriverUnload = UnDriver;
    	return STATUS_SUCCESS;
    }
    

    对于客户端来说,只需要创建套接字并连接到指定地址即可,这个过程大体上可以总结为如下;

    • CreateSocket 创建套接字
    • Bind 绑定套接字
    • Connect 链接服务端驱动
    • Send 发送数据到服务端
    • Receive 接收数据到服务端
    // 署名权
    // right to sign one's name on a piece of work
    // PowerBy: LyShark
    // Email: me@lyshark.com
    
    #include "LySocket.hpp"
    
    VOID UnDriver(PDRIVER_OBJECT driver)
    {
    	// 卸载并关闭Socket库
    	WSKCleanup();
    }
    
    NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
    {
    	DbgPrint("hello lyshark.com \n");
    	// 初始化
    	WSKStartup();
    
    	NTSTATUS      status = STATUS_SUCCESS;
    	SOCKADDR_IN   LocalAddress = { 0, };
    	SOCKADDR_IN   RemoteAddress = { 0, };
    
    	// 创建套接字
    	PWSK_SOCKET TcpSocket = CreateSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, WSK_FLAG_CONNECTION_SOCKET);
    	if (TcpSocket == NULL)
    	{
    		Driver->DriverUnload = UnDriver;
    		return STATUS_SUCCESS;
    	}
    
    	LocalAddress.sin_family = AF_INET;
    	LocalAddress.sin_addr.s_addr = INADDR_ANY;
    	status = Bind(TcpSocket, (PSOCKADDR)&LocalAddress);
    
    	// 绑定失败则关闭驱动
    	if (!NT_SUCCESS(status))
    	{
    		CloseSocket(TcpSocket);
    
    		Driver->DriverUnload = UnDriver;
    		return STATUS_SUCCESS;
    	}
    
    	// 初始化服务端地址与端口信息
    	ULONG address[4] = { 127, 0, 0, 1 };
    
    	RemoteAddress.sin_family = AF_INET;
    	RemoteAddress.sin_addr.s_addr = change_uint(address[0], address[1], address[2], address[3]);
    	RemoteAddress.sin_port = HTON_SHORT(8888);
    
    	status = Connect(TcpSocket, (PSOCKADDR)&RemoteAddress);
    
    	// 连接服务端,如果失败则关闭驱动
    	if (!NT_SUCCESS(status))
    	{
    		CloseSocket(TcpSocket);
    		Driver->DriverUnload = UnDriver;
    		return STATUS_SUCCESS;
    	}
    
    	// 发送数据
    	char send_buffer[2048] = "hello lyshark.com A";
    	Send(TcpSocket, send_buffer, strlen(send_buffer), 0);
    
    	// 接收数据
    	CHAR* read_buffer = (CHAR*)ExAllocatePoolWithTag(NonPagedPool, 2048, "read");
    
    	memset(read_buffer, 0, 1024);
    	Receive(TcpSocket, read_buffer, 2048, 0);
    	DbgPrint("[内核B] => %s \n", read_buffer);
    
    	// 发送确认包
    	Send(TcpSocket, "ok", 2, 0);
    
    	// 释放内存
    	ExFreePool(read_buffer);
    	CloseSocket(TcpSocket);
    	Driver->DriverUnload = UnDriver;
    	return STATUS_SUCCESS;
    }
    

    编译两个驱动程序,首先运行server.sys驱动,运行后该驱动会在后台等待客户端连接,接着运行client.sys屏幕上可输出如下提示,说明通信已经建立了。

  • 相关阅读:
    武汉新时标文化传媒有限公司模仿创作在短视频内容生产中的价值
    Pygame中Sprite类的使用2
    大数据基础架构选型
    [Hive] CTE 通用表达式 WITH关键字
    springboot萌宠宠物网店的开发与设计 毕业设计-附源码011042
    剑指 Offer 12. 矩阵中的路径
    台湾省应广科技PMS150G(PADAUK )纯I/O口单片机系列
    八大排序-01
    c/c++: 空语句检查;消耗掉空语句分号
    多个Lib module打包合成一个Aar
  • 原文地址:https://www.cnblogs.com/LyShark/p/16850992.html