驱动开发:内核封装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
屏幕上可输出如下提示,说明通信已经建立了。
驱动开发:内核封装WSK网络通信接口的更多相关文章
- Windows驱动开发-内核常用内存函数
搞内存常用函数 C语言 内核 malloc ExAllocatePool memset RtlFillMemory memcpy RtlMoveMemory free ExFreePool
- Linux 网络设备驱动开发(一) —— linux内核网络分层结构
Preface Linux内核对网络驱动程序使用统一的接口,并且对于网络设备采用面向对象的思想设计. Linux内核采用分层结构处理网络数据包.分层结构与网络协议的结构匹配,既能简化数据包处理流程,又 ...
- Windows内核安全与驱动开发
这篇是计算机中Windows Mobile/Symbian类的优质预售推荐<Windows内核安全与驱动开发>. 编辑推荐 本书适合计算机安全软件从业人员.计算机相关专业院校学生以及有一定 ...
- Linux驱动开发必看详解神秘内核(完全转载)
Linux驱动开发必看详解神秘内核 完全转载-链接:http://blog.chinaunix.net/uid-21356596-id-1827434.html IT168 技术文档]在开始步入L ...
- Linux内核(17) - 高效学习Linux驱动开发
这本<Linux内核修炼之道>已经开卖(网上的链接为: 卓越.当当.china-pub ),虽然是严肃文学,但为了保证流畅性,大部分文字我还都是斟词灼句,反复的念几遍才写上去的,尽量考虑到 ...
- 驱动开发:内核R3与R0内存映射拷贝
在上一篇博文<驱动开发:内核通过PEB得到进程参数>中我们通过使用KeStackAttachProcess附加进程的方式得到了该进程的PEB结构信息,本篇文章同样需要使用进程附加功能,但这 ...
- 驱动开发:内核特征码扫描PE代码段
在笔者上一篇文章<驱动开发:内核特征码搜索函数封装>中为了定位特征的方便我们封装实现了一个可以传入数组实现的SearchSpecialCode定位函数,该定位函数其实还不能算的上简单,本章 ...
- 驱动开发:内核枚举LoadImage映像回调
在笔者之前的文章<驱动开发:内核特征码搜索函数封装>中我们封装实现了特征码定位功能,本章将继续使用该功能,本次我们需要枚举内核LoadImage映像回调,在Win64环境下我们可以设置一个 ...
- 驱动开发:内核层InlineHook挂钩函数
在上一章<驱动开发:内核LDE64引擎计算汇编长度>中,LyShark教大家如何通过LDE64引擎实现计算反汇编指令长度,本章将在此基础之上实现内联函数挂钩,内核中的InlineHook函 ...
随机推荐
- [RootersCTF2019]I_<3_Flask-1|SSTI注入
1.打开之后很明显的提示flask框架,但是未提供参数,在源代码中发现了一个git地址,打开之后也是没有啥用,结果如下: 2.这里我们首先需要爆破出来参数进行信息的传递,就需要使用Arjun这一款工具 ...
- 【NOI P模拟赛】仙人掌(圆方树,树形DP)
题面 n n n 个点, m m m 条边. 1 ≤ n ≤ 1 0 5 , n − 1 ≤ m ≤ 2 × 1 0 5 1\leq n\leq 10^5,n-1\leq m\leq 2\times1 ...
- 【C标准库】详解feof函数与EOF
创作不易,多多支持! 再说此函数之前,先来说一下EOF是什么 EOF,为End Of File的缩写,通常在文本的最后存在此字符表示资料结束. 在C语言中,或更精确地说成C标准函式库中表示文件结束符. ...
- EL&JSTL笔记------jsp
今日内容 1. JSP: 1. 指令 2. 注释 3. 内置对象 2. MVC开发模式 3. EL表达式 4. JSTL标签 5. 三层架构 JSP: 1. 指令 * 作用:用于配置JSP页面,导入资 ...
- 【读书笔记】C#高级编程 第十章 集合
(一)概述 数组的大小是固定的.如果元素个数是动态的,就应使用集合类. List<T>是与数组相当的集合类.还有其它类型的集合:队列.栈.链表.字典和集. (二)列表 1.创建列表 调用默 ...
- 一文搞懂mysql索引底层逻辑,干货满满!
一.什么是索引 在mysql中,索引是一种特殊的数据库结构,由数据表中的一列或多列组合而成,可以用来快速查询数据表中有某一特定值的记录.通过索引,查询数据时不用读完记录的所有信息,而只是查询索引列即可 ...
- 《Java基础——循环语句》
Java基础--循环语句 1. while语句: 规则: 1. 首先计算表达式的值. 2. 若表达式为真,则执行循环语法,直至表达式为假,循环结束. 格式: while(表达式) 语句 ...
- ProxySQL(12):禁止多路路由
文章转载自:https://www.cnblogs.com/f-ck-need-u/p/9372447.html multiplexing multiplexing,作用是将语句分多路路由.开启了mu ...
- 1_Linux
一. Linux介绍 1.1 引言 在学习Linux之前, 大家先了解开发环境,生产,测试环境 开发环境: 平时大家大多是在Windows或者Mac操作系统下去编写代码进行开发,在开发环境中安装大量的 ...
- 移动端touch拖动事件和click事件冲突问题解决
通过一个悬浮球交互功能的案例来阐述问题,以及解决办法. 实现效果 类似微信里的悬浮窗效果,苹果手机的悬浮球功能效果 可以点击拖动,然后吸附在窗口边缘 点击悬浮球,可以跳转界面,或者更改悬浮球的形态 准 ...